#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"
 /* 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.
 /* 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);
 }
 
 /*
        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);
 }
 
 /*
- * 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)
 {
                        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);