greybus: connection: implement proper connection closure
authorJohan Hovold <johan@hovoldconsulting.com>
Fri, 27 May 2016 15:26:36 +0000 (17:26 +0200)
committerGreg Kroah-Hartman <gregkh@google.com>
Fri, 27 May 2016 19:24:17 +0000 (12:24 -0700)
Implement proper connection closure, which includes sending ping
requests on the connection being tore down while coordinating with the
remote interface as well as the SVC.

This specifically implements the new ping operation, which in case of
offloaded connections is handled by the host-device driver in an
implementation-defined manner through a new callback.

Note that the normal connection tear-down procedure is executed in case
of failed connection establishment due to failed connected operation.
Specifically, the disconnecting request is sent also in case the
connected operation never succeeded. This is needed since the interface
may have enabled FCT flow upon receiving the connected request.

Signed-off-by: Johan Hovold <johan@hovoldconsulting.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
drivers/staging/greybus/connection.c
drivers/staging/greybus/connection.h
drivers/staging/greybus/greybus_protocols.h
drivers/staging/greybus/hd.h
drivers/staging/greybus/operation.c

index f3a3915..7706751 100644 (file)
@@ -389,6 +389,21 @@ gb_connection_svc_connection_destroy(struct gb_connection *connection)
                                  connection->intf_cport_id);
 }
 
+static void
+gb_connection_svc_connection_quiescing(struct gb_connection *connection)
+{
+       struct gb_host_device *hd = connection->hd;
+
+       if (gb_connection_is_static(connection))
+               return;
+
+       gb_svc_connection_quiescing(hd->svc,
+                                       hd->svc->ap_intf_id,
+                                       connection->hd_cport_id,
+                                       connection->intf->interface_id,
+                                       connection->intf_cport_id);
+}
+
 /* Inform Interface about active CPorts */
 static int gb_connection_control_connected(struct gb_connection *connection)
 {
@@ -424,7 +439,26 @@ static int gb_connection_control_connected(struct gb_connection *connection)
        return 0;
 }
 
-/* Inform Interface about inactive CPorts */
+static void
+gb_connection_control_disconnecting(struct gb_connection *connection)
+{
+       struct gb_control *control;
+       u16 cport_id = connection->intf_cport_id;
+       int ret;
+
+       if (gb_connection_is_static(connection))
+               return;
+
+       control = connection->intf->control;
+
+       ret = gb_control_disconnecting_operation(control, cport_id);
+       if (ret) {
+               dev_err(&connection->hd->dev,
+                               "%s: failed to send disconnecting: %d\n",
+                               connection->name, ret);
+       }
+}
+
 static void
 gb_connection_control_disconnected(struct gb_connection *connection)
 {
@@ -447,10 +481,56 @@ gb_connection_control_disconnected(struct gb_connection *connection)
        }
 }
 
+static int gb_connection_ping_operation(struct gb_connection *connection)
+{
+       struct gb_operation *operation;
+       int ret;
+
+       operation = gb_operation_create_core(connection,
+                                               GB_REQUEST_TYPE_PING,
+                                               0, 0, 0,
+                                               GFP_KERNEL);
+       if (!operation)
+               return -ENOMEM;
+
+       ret = gb_operation_request_send_sync(operation);
+
+       gb_operation_put(operation);
+
+       return ret;
+}
+
+static int gb_connection_ping(struct gb_connection *connection)
+{
+       struct gb_host_device *hd = connection->hd;
+       int ret;
+
+       if (gb_connection_is_static(connection))
+               return 0;
+
+       if (gb_connection_is_offloaded(connection)) {
+               if (!hd->driver->cport_ping)
+                       return 0;
+
+               ret = hd->driver->cport_ping(hd, connection->intf_cport_id);
+       } else {
+               ret = gb_connection_ping_operation(connection);
+       }
+
+       if (ret) {
+               dev_err(&hd->dev, "%s: failed to send ping: %d\n",
+                               connection->name, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
 /*
  * Cancel all active operations on a connection.
  *
- * Locking: Called with connection lock held and state set to DISABLED.
+ * Locking: Called with connection lock held and state set to DISABLED or
+ * DISCONNECTING.
  */
 static void gb_connection_cancel_operations(struct gb_connection *connection,
                                                int errno)
@@ -559,17 +639,24 @@ static int _gb_connection_enable(struct gb_connection *connection, bool rx)
 
        ret = gb_connection_control_connected(connection);
        if (ret)
-               goto err_flush_operations;
+               goto err_control_disconnecting;
 
        return 0;
 
-err_flush_operations:
+err_control_disconnecting:
+       gb_connection_control_disconnecting(connection);
+
        spin_lock_irq(&connection->lock);
-       connection->state = GB_CONNECTION_STATE_DISABLED;
+       connection->state = GB_CONNECTION_STATE_DISCONNECTING;
        gb_connection_cancel_operations(connection, -ESHUTDOWN);
        spin_unlock_irq(&connection->lock);
 
+       gb_connection_ping(connection);
        gb_connection_hd_cport_features_disable(connection);
+       gb_connection_svc_connection_quiescing(connection);
+       gb_connection_ping(connection);
+       gb_connection_control_disconnected(connection);
+       connection->state = GB_CONNECTION_STATE_DISABLED;
 err_svc_connection_destroy:
        gb_connection_svc_connection_destroy(connection);
 err_hd_cport_disable:
@@ -642,14 +729,22 @@ void gb_connection_disable(struct gb_connection *connection)
        if (connection->state == GB_CONNECTION_STATE_DISABLED)
                goto out_unlock;
 
-       gb_connection_control_disconnected(connection);
+       gb_connection_control_disconnecting(connection);
 
        spin_lock_irq(&connection->lock);
-       connection->state = GB_CONNECTION_STATE_DISABLED;
+       connection->state = GB_CONNECTION_STATE_DISCONNECTING;
        gb_connection_cancel_operations(connection, -ESHUTDOWN);
        spin_unlock_irq(&connection->lock);
 
+       gb_connection_ping(connection);
        gb_connection_hd_cport_features_disable(connection);
+       gb_connection_svc_connection_quiescing(connection);
+       gb_connection_ping(connection);
+
+       gb_connection_control_disconnected(connection);
+
+       connection->state = GB_CONNECTION_STATE_DISABLED;
+
        gb_connection_svc_connection_destroy(connection);
        gb_connection_hd_cport_disable(connection);
 
index 9a35e61..af171f5 100644 (file)
 #define GB_CONNECTION_FLAG_CONTROL     BIT(4)
 
 enum gb_connection_state {
-       GB_CONNECTION_STATE_DISABLED    = 0,
-       GB_CONNECTION_STATE_ENABLED_TX  = 1,
-       GB_CONNECTION_STATE_ENABLED     = 2,
+       GB_CONNECTION_STATE_DISABLED            = 0,
+       GB_CONNECTION_STATE_ENABLED_TX          = 1,
+       GB_CONNECTION_STATE_ENABLED             = 2,
+       GB_CONNECTION_STATE_DISCONNECTING       = 3,
 };
 
 struct gb_operation;
index 216a1d1..6afd0a6 100644 (file)
@@ -96,6 +96,7 @@ struct gb_operation_msg_hdr {
 
 
 /* Generic request types */
+#define GB_REQUEST_TYPE_PING                   0x00
 #define GB_REQUEST_TYPE_PROTOCOL_VERSION       0x01
 #define GB_REQUEST_TYPE_INVALID                        0x7f
 
index ad22962..7321cfd 100644 (file)
@@ -21,6 +21,7 @@ struct gb_hd_driver {
        void (*cport_release)(struct gb_host_device *hd, u16 cport_id);
        int (*cport_enable)(struct gb_host_device *hd, u16 cport_id);
        int (*cport_disable)(struct gb_host_device *hd, u16 cport_id);
+       int (*cport_ping)(struct gb_host_device *hd, u16 cport_id);
        int (*message_send)(struct gb_host_device *hd, u16 dest_cport_id,
                        struct gb_message *message, gfp_t gfp_mask);
        void (*message_cancel)(struct gb_message *message);
index fb4a29e..7906a95 100644 (file)
@@ -46,7 +46,6 @@ static int gb_operation_get_active(struct gb_operation *operation)
        unsigned long flags;
 
        spin_lock_irqsave(&connection->lock, flags);
-
        switch (connection->state) {
        case GB_CONNECTION_STATE_ENABLED:
                break;
@@ -54,6 +53,10 @@ static int gb_operation_get_active(struct gb_operation *operation)
                if (gb_operation_is_incoming(operation))
                        goto err_unlock;
                break;
+       case GB_CONNECTION_STATE_DISCONNECTING:
+               if (!gb_operation_is_core(operation))
+                       goto err_unlock;
+               break;
        default:
                goto err_unlock;
        }