Commit dbec2729 authored by Johan Hovold's avatar Johan Hovold Committed by Greg Kroah-Hartman
Browse files

staging: greybus: operation: add generic timeout support



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: default avatarJohan Hovold <johan@kernel.org>
Acked-by: default avatarBryan O'Donoghue <pure.logic@nexus-software.ie>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a769f30c
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -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;
+36 −14
Original line number Diff line number Diff line
@@ -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);
+2 −0
Original line number Diff line number Diff line
@@ -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);