From 067906f6906922ad784452218b09bfb2b9519643 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Thu, 6 Aug 2015 12:44:55 +0530 Subject: [PATCH] greybus: svc: Handle hotplug request from a workqueue Bringing up a module can be time consuming, as that may require lots of initialization on the module side. Over that, we may also need to download the firmware first and flash that on the module. In order to make other hotplug events to not wait for all this to finish, handle most of module hotplug stuff outside of the hotplug callback, with help of a workqueue. Signed-off-by: Viresh Kumar Reviewed-by: Alex Elder Signed-off-by: Greg Kroah-Hartman --- drivers/staging/greybus/svc.c | 77 ++++++++++++++++++++++++++--------- 1 file changed, 57 insertions(+), 20 deletions(-) diff --git a/drivers/staging/greybus/svc.c b/drivers/staging/greybus/svc.c index 4d59cbb0b337..4bf55e0e4f34 100644 --- a/drivers/staging/greybus/svc.c +++ b/drivers/staging/greybus/svc.c @@ -8,6 +8,7 @@ */ #include "greybus.h" +#include #define CPORT_FLAGS_E2EFC (1) #define CPORT_FLAGS_CSD_N (2) @@ -19,6 +20,12 @@ struct gb_svc { u8 version_minor; }; +struct svc_hotplug { + struct work_struct work; + struct gb_connection *connection; + struct gb_svc_intf_hotplug_request data; +}; + static struct ida greybus_svc_device_id_map; /* @@ -253,13 +260,19 @@ static int gb_svc_hello(struct gb_operation *op) return 0; } -static int gb_svc_intf_hotplug_recv(struct gb_operation *op) +/* + * 'struct svc_hotplug' should be freed by svc_process_hotplug() before it + * returns, irrespective of success or Failure in bringing up the module. + */ +static void svc_process_hotplug(struct work_struct *work) { - struct gb_message *request = op->request; - struct gb_svc_intf_hotplug_request *hotplug = request->payload; - struct gb_svc *svc = op->connection->private; - struct greybus_host_device *hd = op->connection->bundle->intf->hd; - struct device *dev = &op->connection->dev; + struct svc_hotplug *svc_hotplug = container_of(work, struct svc_hotplug, + work); + struct gb_svc_intf_hotplug_request *hotplug = &svc_hotplug->data; + struct gb_connection *connection = svc_hotplug->connection; + struct gb_svc *svc = connection->private; + struct greybus_host_device *hd = connection->bundle->intf->hd; + struct device *dev = &connection->dev; struct gb_interface *intf; u8 intf_id, device_id; u32 unipro_mfg_id; @@ -268,19 +281,8 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) u32 ara_prod_id; int ret; - if (request->payload_size < sizeof(*hotplug)) { - dev_err(dev, "%s: short hotplug request received (%zu < %zu)\n", - __func__, request->payload_size, sizeof(*hotplug)); - return -EINVAL; - } - /* * Grab the information we need. - * - * XXX I'd really like to acknowledge receipt, and then - * XXX continue processing the request. There's no need - * XXX for the SVC to wait. In fact, it might be best to - * XXX have the SVC get acknowledgement before we proceed. */ intf_id = hotplug->intf_id; unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id); @@ -293,7 +295,7 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) if (!intf) { dev_err(dev, "%s: Failed to create interface with id %hhu\n", __func__, intf_id); - return -EINVAL; + goto free_svc_hotplug; } /* @@ -346,7 +348,7 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) goto svc_id_free; } - return 0; + goto free_svc_hotplug; svc_id_free: /* @@ -357,8 +359,43 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op) ida_simple_remove(&greybus_svc_device_id_map, device_id); destroy_interface: gb_interface_remove(hd, intf_id); +free_svc_hotplug: + kfree(svc_hotplug); +} - return ret; +/* + * Bringing up a module can be time consuming, as that may require lots of + * initialization on the module side. Over that, we may also need to download + * the firmware first and flash that on the module. + * + * In order to make other hotplug events to not wait for all this to finish, + * handle most of module hotplug stuff outside of the hotplug callback, with + * help of a workqueue. + */ +static int gb_svc_intf_hotplug_recv(struct gb_operation *op) +{ + struct gb_message *request = op->request; + struct svc_hotplug *svc_hotplug; + + if (request->payload_size < sizeof(svc_hotplug->data)) { + dev_err(&op->connection->dev, + "%s: short hotplug request received (%zu < %zu)\n", + __func__, request->payload_size, + sizeof(svc_hotplug->data)); + return -EINVAL; + } + + svc_hotplug = kmalloc(sizeof(*svc_hotplug), GFP_ATOMIC); + if (!svc_hotplug) + return -ENOMEM; + + svc_hotplug->connection = op->connection; + memcpy(&svc_hotplug->data, op->request->payload, sizeof(svc_hotplug->data)); + + INIT_WORK(&svc_hotplug->work, svc_process_hotplug); + queue_work(system_unbound_wq, &svc_hotplug->work); + + return 0; } static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op)