]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
greybus: operation: make cancellation synchronous
authorJohan Hovold <johan@hovoldconsulting.com>
Tue, 14 Jul 2015 13:43:26 +0000 (15:43 +0200)
committerGreg Kroah-Hartman <gregkh@google.com>
Wed, 15 Jul 2015 19:39:13 +0000 (12:39 -0700)
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>
drivers/staging/greybus/connection.c
drivers/staging/greybus/operation.c
drivers/staging/greybus/operation.h

index 65a2bd5891d9959fd0753e3842a0696bd9310390..b9f9b11b1b65f2f335e2fbb14599ce038e8b0c83 100644 (file)
@@ -227,7 +227,6 @@ void gb_connection_destroy(struct gb_connection *connection)
        if (WARN_ON(!connection))
                return;
 
-       /* XXX Need to wait for any outstanding requests to complete */
        if (WARN_ON(!list_empty(&connection->operations))) {
                list_for_each_entry_safe(operation, next,
                                         &connection->operations, links)
index abe44c18fb9e2cc61234d96cc891ef52ed790f24..5e8ea0289053d7210512c728275ef4820e527cc6 100644 (file)
@@ -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);
 
index b32386636f6ecb1ae22fa285f29f1ab02750eadf..c8aaf90a006ac9be2db80adfab79797efb42bd64 100644 (file)
@@ -128,6 +128,7 @@ struct gb_operation {
 
        struct kref             kref;
        atomic_t                active;
+       atomic_t                waiters;
 
        struct list_head        links;          /* connection->operations */
 };