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:
parent
2bf4c87605
commit
184ab534de
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue