summaryrefslogtreecommitdiff
path: root/drivers/staging/greybus
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/greybus')
-rw-r--r--drivers/staging/greybus/loopback.c1
-rw-r--r--drivers/staging/greybus/operation.c50
-rw-r--r--drivers/staging/greybus/operation.h2
3 files changed, 39 insertions, 14 deletions
diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c
index a8329daf1e57..43692b86e0b2 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);