From 894cbc31360102fe51babdb82be69885f317843b Mon Sep 17 00:00:00 2001 From: Alex Elder Date: Tue, 25 Nov 2014 16:54:02 -0600 Subject: [PATCH] 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 --- drivers/staging/greybus/operation.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) 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) -- 2.39.5