greybus: update operation result atomically

An operation result can be set both in and out of interrupt context.
For example, a response message could be arriving at the same time a
timeout of the operation is getting processed.  We therefore need to
ensure the result is accessed atomically.

Protect updates to the errno field using the operations spinlock.

Signed-off-by: Alex Elder <elder@linaro.org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
This commit is contained in:
Alex Elder 2014-11-25 16:54:02 -06:00 committed by Greg Kroah-Hartman
parent aa3a4d1209
commit 894cbc3136
1 changed files with 21 additions and 7 deletions

View File

@ -82,18 +82,32 @@ 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)
{ {
int prev;
/* Nobody should be setting -EBADR */
if (WARN_ON(result == -EBADR)) if (WARN_ON(result == -EBADR))
return false; return false;
/* Are we sending the request message? */
if (result == -EINPROGRESS) { if (result == -EINPROGRESS) {
if (WARN_ON(operation->errno != -EBADR)) /* Yes, but verify the result has not already been set */
return false; spin_lock_irq(&gb_operations_lock);
} else if (operation->errno != -EINPROGRESS) { prev = operation->errno;
return false; if (prev == -EBADR)
} operation->errno = result;
operation->errno = result; spin_unlock_irq(&gb_operations_lock);
return true; return !WARN_ON(prev != -EBADR);
}
/* Trying to set final status; only the first one succeeds */
spin_lock_irq(&gb_operations_lock);
prev = operation->errno;
if (prev == -EINPROGRESS)
operation->errno = result;
spin_unlock_irq(&gb_operations_lock);
return prev == -EINPROGRESS;
} }
int gb_operation_result(struct gb_operation *operation) int gb_operation_result(struct gb_operation *operation)