diff options
author | Bryan O'Donoghue <bryan.odonoghue@linaro.org> | 2015-12-11 13:46:52 +0000 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@google.com> | 2015-12-11 16:18:34 -0800 |
commit | 36f241fff4720f205e36bda5900d4ef7b6662bd7 (patch) | |
tree | 3acd4559d588c7da11931ab5747856f24bb310cc /drivers | |
parent | c7aae4e613ef471f9f785e4b85de4d46d552c86b (diff) |
greybus: loopback: Wait for all async operations to complete on exit
On gb_loopback_connection_exit() we should ensure every issued asynchronous
operation completes before exiting connection_exit(). This patch introduces
a waitqueue with a counter which represents the number of incomplete
asynchronous operations. When the counter reaches zero connection_exit()
will complete. At the point which we wait for outstanding operations to
complete the connection-specific loopback thread will have ceased to issue
new operations. Tested with both synchronous and asynchronous operations.
Reviewed-by: Johan Hovold <johan@hovoldconsulting.com>
Suggested-by: Johan Hovold <johan@hovoldconsulting.com>
Signed-off-by: Bryan O'Donoghue <bryan.odonoghue@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/staging/greybus/loopback.c | 15 |
1 files changed, 15 insertions, 0 deletions
diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index eb6a0138fb1d..4814f948eea3 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -21,6 +21,7 @@ #include <linux/list_sort.h> #include <linux/spinlock.h> #include <linux/workqueue.h> +#include <linux/atomic.h> #include <asm/div64.h> @@ -73,6 +74,8 @@ struct gb_loopback { struct list_head entry; struct device *dev; wait_queue_head_t wq; + wait_queue_head_t wq_completion; + atomic_t outstanding_operations; /* Per connection stats */ struct gb_loopback_stats latency; @@ -433,6 +436,8 @@ static void __gb_loopback_async_operation_destroy(struct kref *kref) list_del(&op_async->entry); if (op_async->operation) gb_operation_put(op_async->operation); + atomic_dec(&op_async->gb->outstanding_operations); + wake_up(&op_async->gb->wq_completion); kfree(op_async); } @@ -472,6 +477,12 @@ static struct gb_loopback_async_operation * return found ? op_async : NULL; } +static void gb_loopback_async_wait_all(struct gb_loopback *gb) +{ + wait_event(gb->wq_completion, + !atomic_read(&gb->outstanding_operations)); +} + static void gb_loopback_async_operation_callback(struct gb_operation *operation) { struct gb_loopback_async_operation *op_async; @@ -597,6 +608,7 @@ static int gb_loopback_async_operation(struct gb_loopback *gb, int type, do_gettimeofday(&op_async->ts); op_async->pending = true; + atomic_inc(&gb->outstanding_operations); ret = gb_operation_request_send(operation, gb_loopback_async_operation_callback, GFP_KERNEL); @@ -1063,6 +1075,8 @@ static int gb_loopback_connection_init(struct gb_connection *connection) return -ENOMEM; init_waitqueue_head(&gb->wq); + init_waitqueue_head(&gb->wq_completion); + atomic_set(&gb->outstanding_operations, 0); gb_loopback_reset_stats(gb); /* Reported values to user-space for min/max timeouts */ @@ -1162,6 +1176,7 @@ static void gb_loopback_connection_exit(struct gb_connection *connection) kfifo_free(&gb->kfifo_ts); gb_connection_latency_tag_disable(connection); debugfs_remove(gb->file); + gb_loopback_async_wait_all(gb); spin_lock_irqsave(&gb_dev.lock, flags); gb_dev.count--; |