From: Alex Elder Date: Tue, 25 Nov 2014 22:54:02 +0000 (-0600) Subject: greybus: update operation result atomically X-Git-Tag: v4.9-rc1~119^2~378^2~21^2~1813 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=894cbc31360102fe51babdb82be69885f317843b;p=karo-tx-linux.git 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 Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index c3864bde5200..b4ef82015750 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -82,18 +82,32 @@ static DEFINE_SPINLOCK(gb_operations_lock); */ static bool gb_operation_result_set(struct gb_operation *operation, int result) { + int prev; + + /* Nobody should be setting -EBADR */ if (WARN_ON(result == -EBADR)) return false; + /* Are we sending the request message? */ if (result == -EINPROGRESS) { - if (WARN_ON(operation->errno != -EBADR)) - return false; - } else if (operation->errno != -EINPROGRESS) { - return false; + /* Yes, but verify the result has not already been set */ + spin_lock_irq(&gb_operations_lock); + prev = operation->errno; + if (prev == -EBADR) + operation->errno = result; + spin_unlock_irq(&gb_operations_lock); + + return !WARN_ON(prev != -EBADR); } - operation->errno = result; - return true; + /* 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)