dmaengine: dmatest: Add option to exercise transfer termination

Message ID 20230322-dmatest-terminate-v1-1-2dc6bfaa018b@axis.com
State New
Headers
Series dmaengine: dmatest: Add option to exercise transfer termination |

Commit Message

Vincent Whitchurch March 22, 2023, 2:50 p.m. UTC
  Add a module parameter to allow tests to terminate transfers after a
random delay.  This can be used to try to provoke races in the handling
of ongoing transfers in drivers' implementations of
->device_terminate_all().

Signed-off-by: Vincent Whitchurch <vincent.whitchurch@axis.com>
---
 drivers/dma/dmatest.c | 33 +++++++++++++++++++++++++--------
 1 file changed, 25 insertions(+), 8 deletions(-)


---
base-commit: e8d018dd0257f744ca50a729e3d042cf2ec9da65
change-id: 20230322-dmatest-terminate-bad403208afc

Best regards,
  

Patch

diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c
index ffe621695e47..c3a7681efea8 100644
--- a/drivers/dma/dmatest.c
+++ b/drivers/dma/dmatest.c
@@ -88,6 +88,12 @@  static bool polled;
 module_param(polled, bool, 0644);
 MODULE_PARM_DESC(polled, "Use polling for completion instead of interrupts");
 
+/* Note that throughput stats can be misleading when this is used. */
+static unsigned int terminate_delay_us;
+module_param(terminate_delay_us, uint, 0644);
+MODULE_PARM_DESC(terminate_delay_us,
+		 "Terminate transfers after randomly delaying up to the specified time in microseconds (default: no termination)");
+
 /**
  * struct dmatest_params - test parameters.
  * @buf_size:		size of the memcpy test buffer
@@ -684,6 +690,7 @@  static int dmatest_func(void *data)
 	       (params->iterations && total_tests >= params->iterations))) {
 		struct dma_async_tx_descriptor *tx = NULL;
 		struct dmaengine_unmap_data *um;
+		bool terminated = false;
 		dma_addr_t *dsts;
 		unsigned int len;
 
@@ -827,19 +834,27 @@  static int dmatest_func(void *data)
 		} else {
 			dma_async_issue_pending(chan);
 
-			wait_event_freezable_timeout(thread->done_wait,
-					done->done,
-					msecs_to_jiffies(params->timeout));
+			if (terminate_delay_us) {
+				fsleep(dmatest_random() % terminate_delay_us);
+
+				dmaengine_terminate_sync(chan);
+				terminated = true;
+			}
+
+			if (!terminated)
+				wait_event_freezable_timeout(thread->done_wait,
+						done->done,
+						msecs_to_jiffies(params->timeout));
 
 			status = dma_async_is_tx_complete(chan, cookie, NULL,
 							  NULL);
 		}
 
-		if (!done->done) {
+		if (!terminated && !done->done) {
 			result("test timed out", total_tests, src->off, dst->off,
 			       len, 0);
 			goto error_unmap_continue;
-		} else if (status != DMA_COMPLETE &&
+		} else if (!terminated && status != DMA_COMPLETE &&
 			   !(dma_has_cap(DMA_COMPLETION_NO_ORDER,
 					 dev->cap_mask) &&
 			     status == DMA_OUT_OF_ORDER)) {
@@ -852,9 +867,11 @@  static int dmatest_func(void *data)
 
 		dmaengine_unmap_put(um);
 
-		if (params->noverify) {
-			verbose_result("test passed", total_tests, src->off,
-				       dst->off, len, 0);
+		if (params->noverify || terminated) {
+			const char *msg = terminated ? "test terminated" : "test passed";
+
+			verbose_result(msg, total_tests, src->off, dst->off,
+					len, 0);
 			continue;
 		}