Commit 894cbc31 authored by Alex Elder's avatar Alex Elder Committed by Greg Kroah-Hartman
Browse files

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: default avatarAlex Elder <elder@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent aa3a4d12
Loading
Loading
Loading
Loading
+20 −6
Original line number Diff line number Diff line
@@ -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);
	}

	/* 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 true;
	return prev == -EINPROGRESS;
}

int gb_operation_result(struct gb_operation *operation)