greybus: operation: fix locking issues

Fix unconditional re-enabling of interrupts in various operation
functions that can all be called with local interrupts disabled from USB
completion handlers.

Signed-off-by: Johan Hovold <johan@hovoldconsulting.com>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
This commit is contained in:
Johan Hovold 2015-03-02 12:34:40 +01:00 committed by Greg Kroah-Hartman
parent 2bf4c87605
commit 184ab534de
1 changed files with 14 additions and 10 deletions

View File

@ -94,6 +94,7 @@ static DEFINE_SPINLOCK(gb_operations_lock);
*/ */
static bool gb_operation_result_set(struct gb_operation *operation, int result) static bool gb_operation_result_set(struct gb_operation *operation, int result)
{ {
unsigned long flags;
int prev; int prev;
if (result == -EINPROGRESS) { if (result == -EINPROGRESS) {
@ -104,13 +105,13 @@ static bool gb_operation_result_set(struct gb_operation *operation, int result)
* and record an implementation error if it's * and record an implementation error if it's
* set at any other time. * set at any other time.
*/ */
spin_lock_irq(&gb_operations_lock); spin_lock_irqsave(&gb_operations_lock, flags);
prev = operation->errno; prev = operation->errno;
if (prev == -EBADR) if (prev == -EBADR)
operation->errno = result; operation->errno = result;
else else
operation->errno = -EILSEQ; operation->errno = -EILSEQ;
spin_unlock_irq(&gb_operations_lock); spin_unlock_irqrestore(&gb_operations_lock, flags);
WARN_ON(prev != -EBADR); WARN_ON(prev != -EBADR);
return true; return true;
@ -128,11 +129,11 @@ static bool gb_operation_result_set(struct gb_operation *operation, int result)
if (WARN_ON(result == -EBADR)) if (WARN_ON(result == -EBADR))
result = -EILSEQ; /* Nobody should be setting -EBADR */ result = -EILSEQ; /* Nobody should be setting -EBADR */
spin_lock_irq(&gb_operations_lock); spin_lock_irqsave(&gb_operations_lock, flags);
prev = operation->errno; prev = operation->errno;
if (prev == -EINPROGRESS) if (prev == -EINPROGRESS)
operation->errno = result; /* First and final result */ operation->errno = result; /* First and final result */
spin_unlock_irq(&gb_operations_lock); spin_unlock_irqrestore(&gb_operations_lock, flags);
return prev == -EINPROGRESS; return prev == -EINPROGRESS;
} }
@ -151,15 +152,16 @@ static struct gb_operation *
gb_operation_find(struct gb_connection *connection, u16 operation_id) gb_operation_find(struct gb_connection *connection, u16 operation_id)
{ {
struct gb_operation *operation; struct gb_operation *operation;
unsigned long flags;
bool found = false; bool found = false;
spin_lock_irq(&gb_operations_lock); spin_lock_irqsave(&gb_operations_lock, flags);
list_for_each_entry(operation, &connection->operations, links) list_for_each_entry(operation, &connection->operations, links)
if (operation->id == operation_id) { if (operation->id == operation_id) {
found = true; found = true;
break; break;
} }
spin_unlock_irq(&gb_operations_lock); spin_unlock_irqrestore(&gb_operations_lock, flags);
return found ? operation : NULL; return found ? operation : NULL;
} }
@ -490,6 +492,7 @@ gb_operation_create_common(struct gb_connection *connection, u8 type,
{ {
struct greybus_host_device *hd = connection->hd; struct greybus_host_device *hd = connection->hd;
struct gb_operation *operation; struct gb_operation *operation;
unsigned long flags;
gfp_t gfp_flags; gfp_t gfp_flags;
/* /*
@ -525,9 +528,9 @@ gb_operation_create_common(struct gb_connection *connection, u8 type,
init_completion(&operation->completion); init_completion(&operation->completion);
kref_init(&operation->kref); kref_init(&operation->kref);
spin_lock_irq(&gb_operations_lock); spin_lock_irqsave(&gb_operations_lock, flags);
list_add_tail(&operation->links, &connection->operations); list_add_tail(&operation->links, &connection->operations);
spin_unlock_irq(&gb_operations_lock); spin_unlock_irqrestore(&gb_operations_lock, flags);
return operation; return operation;
@ -593,13 +596,14 @@ void gb_operation_get(struct gb_operation *operation)
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_irq(&gb_operations_lock); spin_lock_irqsave(&gb_operations_lock, flags);
list_del(&operation->links); list_del(&operation->links);
spin_unlock_irq(&gb_operations_lock); spin_unlock_irqrestore(&gb_operations_lock, flags);
gb_operation_message_free(operation->response); gb_operation_message_free(operation->response);
gb_operation_message_free(operation->request); gb_operation_message_free(operation->request);