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: Johan Hovold <johan@kernel.org> Acked-by: Bryan O'Donoghue <pure.logic@nexus-software.ie> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
a769f30c7b
commit
dbec27298b
3 changed files with 39 additions and 14 deletions
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue