summaryrefslogtreecommitdiffstats
path: root/drivers/staging/greybus/operation.c
diff options
context:
space:
mode:
authorJohan Hovold <johan@hovoldconsulting.com>2015-07-14 15:43:26 +0200
committerGreg Kroah-Hartman <gregkh@google.com>2015-07-15 12:39:13 -0700
commitfd7134a3cd0a1b3f91e66922534a796145c85eef (patch)
tree2516e903d1dccb76d9bc42672a0d5c031eb7419d /drivers/staging/greybus/operation.c
parent3eeac7e37ce9856e53693772dfe81a79b57b5a00 (diff)
greybus: operation: make cancellation synchronous
Make sure to wait for the operation to become inactive before returning after having cancelled an operation. This makes sure that any ongoing operation completion callbacks have finished. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Diffstat (limited to 'drivers/staging/greybus/operation.c')
-rw-r--r--drivers/staging/greybus/operation.c24
1 files changed, 22 insertions, 2 deletions
diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c
index abe44c18fb9e..5e8ea0289053 100644
--- a/drivers/staging/greybus/operation.c
+++ b/drivers/staging/greybus/operation.c
@@ -10,6 +10,8 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
#include <linux/workqueue.h>
#include "greybus.h"
@@ -23,6 +25,9 @@ static struct kmem_cache *gb_message_cache;
/* Workqueue to handle Greybus operation completions. */
static struct workqueue_struct *gb_operation_workqueue;
+/* Wait queue for synchronous cancellations. */
+static DECLARE_WAIT_QUEUE_HEAD(gb_operation_cancellation_queue);
+
/*
* Protects access to connection operations lists, as well as
* updates to operation->errno.
@@ -41,7 +46,15 @@ static inline void gb_operation_get_active(struct gb_operation *operation)
/* Caller holds operation reference. */
static inline void gb_operation_put_active(struct gb_operation *operation)
{
- atomic_dec(&operation->active);
+ if (atomic_dec_and_test(&operation->active)) {
+ if (atomic_read(&operation->waiters))
+ wake_up(&gb_operation_cancellation_queue);
+ }
+}
+
+static inline bool gb_operation_is_active(struct gb_operation *operation)
+{
+ return atomic_read(&operation->active);
}
/*
@@ -463,6 +476,7 @@ gb_operation_create_common(struct gb_connection *connection, u8 type,
init_completion(&operation->completion);
kref_init(&operation->kref);
atomic_set(&operation->active, 0);
+ atomic_set(&operation->waiters, 0);
spin_lock_irqsave(&gb_operations_lock, flags);
list_add_tail(&operation->links, &connection->operations);
@@ -873,7 +887,8 @@ void gb_connection_recv(struct gb_connection *connection,
}
/*
- * Cancel an operation, and record the given error to indicate why.
+ * Cancel an operation synchronously, and record the given error to indicate
+ * why.
*/
void gb_operation_cancel(struct gb_operation *operation, int errno)
{
@@ -890,6 +905,11 @@ void gb_operation_cancel(struct gb_operation *operation, int errno)
gb_operation_put(operation);
}
}
+
+ atomic_inc(&operation->waiters);
+ wait_event(gb_operation_cancellation_queue,
+ !gb_operation_is_active(operation));
+ atomic_dec(&operation->waiters);
}
EXPORT_SYMBOL_GPL(gb_operation_cancel);