From: Alex Elder Date: Tue, 25 Nov 2014 17:33:15 +0000 (-0600) Subject: greybus: use special operation result valus X-Git-Tag: v4.9-rc1~119^2~378^2~21^2~1816 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=3deb37d4ad04c6cb18564f5af2c88c10fa6bfc76;p=karo-tx-linux.git greybus: use special operation result valus This is more or less re-implementing this commit: 96f95d4 greybus: update gbuf status for completion handlers But this time we're doing this for an operation, not the gbuf. Define an initial operation result value (-EBADR) to signify that no valid result has been set. Nobody should ever set that value after the operation is initially created. Since only the operation core code sets the result, an attempt to set -EBADR would be a bug. Define another known operation result value (-EINPROGRESS) for an outgoing operation whose request has been sent but whose response has not yet been successfully received. This should the first (non-initial) result value set, and it should happen exactly once. Any other attempt to set this value once set would be a bug. Finally, once the request message is in flight, the result value will be set exactly once more, to indicate the final result of the operation. 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 49b0f19b4e50..82fd7e98074c 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -66,17 +66,43 @@ struct gb_operation_msg_hdr { /* XXX Could be per-host device, per-module, or even per-connection */ static DEFINE_SPINLOCK(gb_operations_lock); +/* + * Set an operation's result. Initially an outgoing operation's + * errno value is -EBADR. If no error occurs before sending the + * request message the only valid value operation->errno can be + * set to is -EINPROGRESS, indicating the request has been (or + * rather is about to be) sent. At that point nobody should + * be looking at the result until the reponse arrives. + * + * The first time the result gets set after the request has been + * sent, that result "sticks." That is, if two concurrent threads + * race to set the result, the first one wins. The return value + * tells the caller whether its result was recorded; if not the + * has nothing more to do. + */ static bool gb_operation_result_set(struct gb_operation *operation, int result) { - if (operation->errno) + if (WARN_ON(result == -EBADR)) + return false; + + if (result == -EINPROGRESS) { + if (WARN_ON(operation->errno != -EBADR)) + return false; + } else if (operation->errno != -EINPROGRESS) { return false; + } operation->errno = result; + return true; } int gb_operation_result(struct gb_operation *operation) { - return operation->errno; + int result = operation->errno; + + WARN_ON(result == -EINPROGRESS); + + return result; } static void gb_pending_operation_insert(struct gb_operation *operation) @@ -350,6 +376,7 @@ gb_operation_create_common(struct gb_connection *connection, bool outgoing, goto err_request; operation->response->operation = operation; } + operation->errno = -EBADR; /* Initial value--means "never set" */ INIT_WORK(&operation->work, gb_operation_work); operation->callback = NULL; /* set at submit time */ @@ -475,6 +502,7 @@ int gb_operation_request_send(struct gb_operation *operation, schedule_delayed_work(&operation->timeout_work, timeout); /* All set, send the request */ + gb_operation_result_set(operation, -EINPROGRESS); ret = gb_message_send(operation->request, GFP_KERNEL); if (ret || callback) return ret;