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 <viresh.kumar@linaro.org>
Reviewed-by: Alex Elder <elder@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Viresh Kumar 2015-08-06 12:44:55 +05:30 committed by Greg Kroah-Hartman
parent 6d05ad3c42
commit 067906f690
1 changed files with 57 additions and 20 deletions
drivers/staging/greybus

View File

@ -8,6 +8,7 @@
*/ */
#include "greybus.h" #include "greybus.h"
#include <linux/workqueue.h>
#define CPORT_FLAGS_E2EFC (1) #define CPORT_FLAGS_E2EFC (1)
#define CPORT_FLAGS_CSD_N (2) #define CPORT_FLAGS_CSD_N (2)
@ -19,6 +20,12 @@ struct gb_svc {
u8 version_minor; 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; static struct ida greybus_svc_device_id_map;
/* /*
@ -253,13 +260,19 @@ static int gb_svc_hello(struct gb_operation *op)
return 0; 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 svc_hotplug *svc_hotplug = container_of(work, struct svc_hotplug,
struct gb_svc_intf_hotplug_request *hotplug = request->payload; work);
struct gb_svc *svc = op->connection->private; struct gb_svc_intf_hotplug_request *hotplug = &svc_hotplug->data;
struct greybus_host_device *hd = op->connection->bundle->intf->hd; struct gb_connection *connection = svc_hotplug->connection;
struct device *dev = &op->connection->dev; struct gb_svc *svc = connection->private;
struct greybus_host_device *hd = connection->bundle->intf->hd;
struct device *dev = &connection->dev;
struct gb_interface *intf; struct gb_interface *intf;
u8 intf_id, device_id; u8 intf_id, device_id;
u32 unipro_mfg_id; u32 unipro_mfg_id;
@ -268,19 +281,8 @@ static int gb_svc_intf_hotplug_recv(struct gb_operation *op)
u32 ara_prod_id; u32 ara_prod_id;
int ret; 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. * 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; intf_id = hotplug->intf_id;
unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_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) { if (!intf) {
dev_err(dev, "%s: Failed to create interface with id %hhu\n", dev_err(dev, "%s: Failed to create interface with id %hhu\n",
__func__, intf_id); __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; goto svc_id_free;
} }
return 0; goto free_svc_hotplug;
svc_id_free: 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); ida_simple_remove(&greybus_svc_device_id_map, device_id);
destroy_interface: destroy_interface:
gb_interface_remove(hd, intf_id); 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) static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op)