[PATCH] staging: greybus: operation: add generic timeout support

Johan Hovold johan at kernel.org
Mon Jan 23 12:04:14 UTC 2017


Add a struct timer_list to struct gb_operation and use that to implement
generic operation timeouts.

This simplifies the synchronous operation handling somewhat while also
providing a generic timeout mechanism that drivers can use for
asynchronous operations.

Signed-off-by: Johan Hovold <johan at kernel.org>
---
 drivers/staging/greybus/loopback.c  |  1 +
 drivers/staging/greybus/operation.c | 50 ++++++++++++++++++++++++++-----------
 drivers/staging/greybus/operation.h |  2 ++
 3 files changed, 39 insertions(+), 14 deletions(-)

diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c
index 6c2a41c638c3..4bee33f62fd4 100644
--- a/drivers/staging/greybus/loopback.c
+++ b/drivers/staging/greybus/loopback.c
@@ -629,6 +629,7 @@ static int gb_loopback_async_operation(struct gb_loopback *gb, int type,
 	mutex_lock(&gb->mutex);
 	ret = gb_operation_request_send(operation,
 					gb_loopback_async_operation_callback,
+					0,
 					GFP_KERNEL);
 	if (ret)
 		goto error;
diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c
index 0123109a1070..3023012808d9 100644
--- a/drivers/staging/greybus/operation.c
+++ b/drivers/staging/greybus/operation.c
@@ -273,18 +273,40 @@ static void gb_operation_request_handle(struct gb_operation *operation)
 static void gb_operation_work(struct work_struct *work)
 {
 	struct gb_operation *operation;
+	int ret;
 
 	operation = container_of(work, struct gb_operation, work);
 
-	if (gb_operation_is_incoming(operation))
+	if (gb_operation_is_incoming(operation)) {
 		gb_operation_request_handle(operation);
-	else
+	} else {
+		ret = del_timer_sync(&operation->timer);
+		if (!ret) {
+			/* Cancel request message if scheduled by timeout. */
+			if (gb_operation_result(operation) == -ETIMEDOUT)
+				gb_message_cancel(operation->request);
+		}
+
 		operation->callback(operation);
+	}
 
 	gb_operation_put_active(operation);
 	gb_operation_put(operation);
 }
 
+static void gb_operation_timeout(unsigned long arg)
+{
+	struct gb_operation *operation = (void *)arg;
+
+	if (gb_operation_result_set(operation, -ETIMEDOUT)) {
+		/*
+		 * A stuck request message will be cancelled from the
+		 * workqueue.
+		 */
+		queue_work(gb_operation_completion_wq, &operation->work);
+	}
+}
+
 static void gb_operation_message_init(struct gb_host_device *hd,
 				struct gb_message *message, u16 operation_id,
 				size_t payload_size, u8 type)
@@ -518,6 +540,9 @@ gb_operation_create_common(struct gb_connection *connection, u8 type,
 						 gfp_flags)) {
 			goto err_request;
 		}
+
+		setup_timer(&operation->timer, gb_operation_timeout,
+			    (unsigned long)operation);
 	}
 
 	operation->flags = op_flags;
@@ -679,6 +704,7 @@ static void gb_operation_sync_callback(struct gb_operation *operation)
  * gb_operation_request_send() - send an operation request message
  * @operation:	the operation to initiate
  * @callback:	the operation completion callback
+ * @timeout:	operation timeout in milliseconds, or zero for no timeout
  * @gfp:	the memory flags to use for any allocations
  *
  * The caller has filled in any payload so the request message is ready to go.
@@ -693,6 +719,7 @@ static void gb_operation_sync_callback(struct gb_operation *operation)
  */
 int gb_operation_request_send(struct gb_operation *operation,
 				gb_operation_callback callback,
+				unsigned int timeout,
 				gfp_t gfp)
 {
 	struct gb_connection *connection = operation->connection;
@@ -742,6 +769,11 @@ int gb_operation_request_send(struct gb_operation *operation,
 	if (ret)
 		goto err_put_active;
 
+	if (timeout) {
+		operation->timer.expires = jiffies + msecs_to_jiffies(timeout);
+		add_timer(&operation->timer);
+	}
+
 	return 0;
 
 err_put_active:
@@ -763,26 +795,16 @@ int gb_operation_request_send_sync_timeout(struct gb_operation *operation,
 						unsigned int timeout)
 {
 	int ret;
-	unsigned long timeout_jiffies;
 
 	ret = gb_operation_request_send(operation, gb_operation_sync_callback,
-					GFP_KERNEL);
+					timeout, GFP_KERNEL);
 	if (ret)
 		return ret;
 
-	if (timeout)
-		timeout_jiffies = msecs_to_jiffies(timeout);
-	else
-		timeout_jiffies = MAX_SCHEDULE_TIMEOUT;
-
-	ret = wait_for_completion_interruptible_timeout(&operation->completion,
-							timeout_jiffies);
+	ret = wait_for_completion_interruptible(&operation->completion);
 	if (ret < 0) {
 		/* Cancel the operation if interrupted */
 		gb_operation_cancel(operation, -ECANCELED);
-	} else if (ret == 0) {
-		/* Cancel the operation if op timed out */
-		gb_operation_cancel(operation, -ETIMEDOUT);
 	}
 
 	return gb_operation_result(operation);
diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h
index de09a2c7de54..7529f01b2529 100644
--- a/drivers/staging/greybus/operation.h
+++ b/drivers/staging/greybus/operation.h
@@ -98,6 +98,7 @@ struct gb_operation {
 	struct work_struct	work;
 	gb_operation_callback	callback;
 	struct completion	completion;
+	struct timer_list	timer;
 
 	struct kref		kref;
 	atomic_t		waiters;
@@ -164,6 +165,7 @@ bool gb_operation_response_alloc(struct gb_operation *operation,
 
 int gb_operation_request_send(struct gb_operation *operation,
 				gb_operation_callback callback,
+				unsigned int timeout,
 				gfp_t gfp);
 int gb_operation_request_send_sync_timeout(struct gb_operation *operation,
 						unsigned int timeout);
-- 
2.10.2



More information about the devel mailing list