greybus: operation: fix operation-destroy race

Make sure to acquire the connection-list lock atomically when releasing
the final reference.

This allows the list to be traversed and references to be acquired
(while holding the lock) without racing with the destructor.

Suggested-by: Greg Kroah-Hartman <gregkh@google.com>
Signed-off-by: Johan Hovold <johan@hovoldconsulting.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Johan Hovold 2015-07-09 15:17:58 +02:00 committed by Greg Kroah-Hartman
parent 5c1ac69455
commit 85109f7ddd
1 changed files with 6 additions and 5 deletions

View File

@ -528,14 +528,12 @@ EXPORT_SYMBOL_GPL(gb_operation_get);
static void _gb_operation_destroy(struct kref *kref) static void _gb_operation_destroy(struct kref *kref)
{ {
struct gb_operation *operation; struct gb_operation *operation;
unsigned long flags;
operation = container_of(kref, struct gb_operation, kref); operation = container_of(kref, struct gb_operation, kref);
/* XXX Make sure it's not in flight */ /* XXX Make sure it's not in flight */
spin_lock_irqsave(&gb_operations_lock, flags);
list_del(&operation->links); list_del(&operation->links);
spin_unlock_irqrestore(&gb_operations_lock, flags); spin_unlock(&gb_operations_lock);
if (operation->response) if (operation->response)
gb_operation_message_free(operation->response); gb_operation_message_free(operation->response);
@ -550,8 +548,11 @@ static void _gb_operation_destroy(struct kref *kref)
*/ */
void gb_operation_put(struct gb_operation *operation) void gb_operation_put(struct gb_operation *operation)
{ {
if (!WARN_ON(!operation)) if (WARN_ON(!operation))
kref_put(&operation->kref, _gb_operation_destroy); return;
kref_put_spinlock_irqsave(&operation->kref, _gb_operation_destroy,
&gb_operations_lock);
} }
EXPORT_SYMBOL_GPL(gb_operation_put); EXPORT_SYMBOL_GPL(gb_operation_put);