greybus: cancel operation on timeout

If an operation times out, we need to cancel whatever message it
has in-flight.  Do that instead of completing the operation, in the
timeout handler.  When the in-flight request message is canceled its
completion function will lead to the proper completion of the
operation.

Change gb_operation_cancel() so it takes the errno that it's
supposed to assign as the result of the operation.

Note that we want to preserve the original -ETIMEDOUT error, so
don't overwrite the operation result value if it has already been
set.

Signed-off-by: Alex Elder <elder@linaro.org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c
index 8cb2af3..6503546 100644
--- a/drivers/staging/greybus/connection.c
+++ b/drivers/staging/greybus/connection.c
@@ -212,7 +212,7 @@
 	if (WARN_ON(!list_empty(&connection->operations))) {
 		list_for_each_entry_safe(operation, next,
 					 &connection->operations, links)
-			gb_operation_cancel(operation);
+			gb_operation_cancel(operation, -ESHUTDOWN);
 	}
 	spin_lock_irq(&gb_connections_lock);
 	list_del(&connection->interface_links);
diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c
index 32cd235..dc12e6d 100644
--- a/drivers/staging/greybus/operation.c
+++ b/drivers/staging/greybus/operation.c
@@ -215,20 +215,13 @@
 
 /*
  * Timeout call for the operation.
- *
- * If this fires, something went wrong, so mark the result as timed out, and
- * run the completion handler, which (hopefully) should clean up the operation
- * properly.
  */
-static void operation_timeout(struct work_struct *work)
+static void gb_operation_timeout(struct work_struct *work)
 {
 	struct gb_operation *operation;
 
 	operation = container_of(work, struct gb_operation, timeout_work.work);
-	pr_debug("%s: timeout!\n", __func__);
-
-	operation->errno = -ETIMEDOUT;
-	gb_operation_complete(operation);
+	gb_operation_cancel(operation, -ETIMEDOUT);
 }
 
 /*
@@ -376,7 +369,7 @@
 	INIT_WORK(&operation->work, gb_operation_work);
 	operation->callback = NULL;	/* set at submit time */
 	init_completion(&operation->completion);
-	INIT_DELAYED_WORK(&operation->timeout_work, operation_timeout);
+	INIT_DELAYED_WORK(&operation->timeout_work, gb_operation_timeout);
 	kref_init(&operation->kref);
 
 	spin_lock_irq(&gb_operations_lock);
@@ -633,11 +626,11 @@
 }
 
 /*
- * Cancel an operation.
+ * Cancel an operation, and record the given error to indicate why.
  */
-void gb_operation_cancel(struct gb_operation *operation)
+void gb_operation_cancel(struct gb_operation *operation, int errno)
 {
-	operation->canceled = true;
+	operation->errno = errno;
 	gb_message_cancel(operation->request);
 	gb_message_cancel(operation->response);
 }
diff --git a/drivers/staging/greybus/operation.h b/drivers/staging/greybus/operation.h
index befce15..d24e5e0 100644
--- a/drivers/staging/greybus/operation.h
+++ b/drivers/staging/greybus/operation.h
@@ -69,7 +69,6 @@
 	struct gb_message	*request;
 	struct gb_message	*response;
 	u16			id;
-	bool			canceled;
 
 	int			errno;		/* Operation result */
 
@@ -99,7 +98,7 @@
 				gb_operation_callback callback);
 int gb_operation_response_send(struct gb_operation *operation);
 
-void gb_operation_cancel(struct gb_operation *operation);
+void gb_operation_cancel(struct gb_operation *operation, int errno);
 int gb_operation_wait(struct gb_operation *operation);
 
 int gb_operation_status_map(u8 status);