diff options
author | Dexuan Cui <decui@microsoft.com> | 2015-03-27 09:10:09 -0700 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-04-03 16:18:02 +0200 |
commit | d43e2fe7da320310834467a3fd87a10adb25a221 (patch) | |
tree | e978c5e8f751ba913b29bcb1dacedbcb3d2a9dc6 /drivers/hv/channel_mgmt.c | |
parent | 652594c7dfd9bf6392e3a727bc69d89a2562d953 (diff) | |
download | lwn-d43e2fe7da320310834467a3fd87a10adb25a221.tar.gz lwn-d43e2fe7da320310834467a3fd87a10adb25a221.zip |
hv: don't schedule new works in vmbus_onoffer()/vmbus_onoffer_rescind()
Since the 2 fucntions can safely run in vmbus_connection.work_queue without
hang, we don't need to schedule new work items into the per-channel workqueue.
Actally we can even remove the per-channel workqueue now -- we'll do it
in the next patch.
Signed-off-by: Dexuan Cui <decui@microsoft.com>
Cc: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hv/channel_mgmt.c')
-rw-r--r-- | drivers/hv/channel_mgmt.c | 157 |
1 files changed, 28 insertions, 129 deletions
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 287f07b1bef6..d69864d4a4d7 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -23,7 +23,6 @@ #include <linux/kernel.h> #include <linux/sched.h> #include <linux/wait.h> -#include <linux/delay.h> #include <linux/mm.h> #include <linux/slab.h> #include <linux/list.h> @@ -33,11 +32,6 @@ #include "hyperv_vmbus.h" -struct vmbus_rescind_work { - struct work_struct work; - struct vmbus_channel *channel; -}; - /** * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message * @icmsghdrp: Pointer to msg header structure @@ -134,20 +128,6 @@ fw_error: EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); -static void vmbus_sc_creation_cb(struct work_struct *work) -{ - struct vmbus_channel *newchannel = container_of(work, - struct vmbus_channel, - work); - struct vmbus_channel *primary_channel = newchannel->primary_channel; - - /* - * On entry sc_creation_callback has been already verified to - * be non-NULL. - */ - primary_channel->sc_creation_callback(newchannel); -} - /* * alloc_channel - Allocate and initialize a vmbus channel object */ @@ -206,40 +186,6 @@ static void free_channel(struct vmbus_channel *channel) queue_work(vmbus_connection.work_queue, &channel->work); } -static void process_rescind_fn(struct work_struct *work) -{ - struct vmbus_rescind_work *rc_work; - struct vmbus_channel *channel; - struct device *dev; - - rc_work = container_of(work, struct vmbus_rescind_work, work); - channel = rc_work->channel; - - /* - * We have already acquired a reference on the channel - * and so it cannot vanish underneath us. - * It is possible (while very unlikely) that we may - * get here while the processing of the initial offer - * is still not complete. Deal with this situation by - * just waiting until the channel is in the correct state. - */ - - while (channel->work.func != release_channel) - msleep(1000); - - if (channel->device_obj) { - dev = get_device(&channel->device_obj->device); - if (dev) { - vmbus_device_unregister(channel->device_obj); - put_device(dev); - } - } else { - hv_process_channel_removal(channel, - channel->offermsg.child_relid); - } - kfree(work); -} - static void percpu_channel_enq(void *arg) { struct vmbus_channel *channel = arg; @@ -302,46 +248,6 @@ void vmbus_free_channels(void) } } -static void vmbus_do_device_register(struct work_struct *work) -{ - struct hv_device *device_obj; - int ret; - unsigned long flags; - struct vmbus_channel *newchannel = container_of(work, - struct vmbus_channel, - work); - - ret = vmbus_device_register(newchannel->device_obj); - if (ret != 0) { - pr_err("unable to add child device object (relid %d)\n", - newchannel->offermsg.child_relid); - spin_lock_irqsave(&vmbus_connection.channel_lock, flags); - list_del(&newchannel->listentry); - device_obj = newchannel->device_obj; - newchannel->device_obj = NULL; - spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags); - - if (newchannel->target_cpu != get_cpu()) { - put_cpu(); - smp_call_function_single(newchannel->target_cpu, - percpu_channel_deq, newchannel, true); - } else { - percpu_channel_deq(newchannel); - put_cpu(); - } - - kfree(device_obj); - if (!newchannel->rescind) { - free_channel(newchannel); - return; - } - } - /* - * The next state for this channel is to be freed. - */ - INIT_WORK(&newchannel->work, release_channel); -} - /* * vmbus_process_offer - Process the offer by creating a channel/device * associated with this offer @@ -410,19 +316,8 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) newchannel->state = CHANNEL_OPEN_STATE; channel->num_sc++; - if (channel->sc_creation_callback != NULL) { - /* - * We need to invoke the sub-channel creation - * callback; invoke this in a seperate work - * context since we are currently running on - * the global work context in which we handle - * messages from the host. - */ - INIT_WORK(&newchannel->work, - vmbus_sc_creation_cb); - queue_work(newchannel->controlwq, - &newchannel->work); - } + if (channel->sc_creation_callback != NULL) + channel->sc_creation_callback(newchannel); return; } @@ -453,13 +348,13 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) * Add the new device to the bus. This will kick off device-driver * binding which eventually invokes the device driver's AddDevice() * method. - * Invoke this call on the per-channel work context. - * Until we return from this function, rescind offer message - * cannot be processed as we are running on the global message - * handling work. */ - INIT_WORK(&newchannel->work, vmbus_do_device_register); - queue_work(newchannel->controlwq, &newchannel->work); + if (vmbus_device_register(newchannel->device_obj) != 0) { + pr_err("unable to add child device object (relid %d)\n", + newchannel->offermsg.child_relid); + kfree(newchannel->device_obj); + goto err_deq_chan; + } return; err_deq_chan: @@ -613,31 +508,35 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) { struct vmbus_channel_rescind_offer *rescind; struct vmbus_channel *channel; - struct vmbus_rescind_work *rc_work; + unsigned long flags; + struct device *dev; rescind = (struct vmbus_channel_rescind_offer *)hdr; - channel = relid2channel(rescind->child_relid, true); + channel = relid2channel(rescind->child_relid); if (channel == NULL) { hv_process_channel_removal(NULL, rescind->child_relid); return; } - /* - * We have acquired a reference on the channel and have posted - * the rescind state. Perform further cleanup in a work context - * that is different from the global work context in which - * we process messages from the host (we are currently executing - * on that global context. - */ - rc_work = kzalloc(sizeof(struct vmbus_rescind_work), GFP_KERNEL); - if (!rc_work) { - pr_err("Unable to allocate memory for rescind processing "); - return; + spin_lock_irqsave(&channel->lock, flags); + channel->rescind = true; + spin_unlock_irqrestore(&channel->lock, flags); + + if (channel->device_obj) { + /* + * We will have to unregister this device from the + * driver core. + */ + dev = get_device(&channel->device_obj->device); + if (dev) { + vmbus_device_unregister(channel->device_obj); + put_device(dev); + } + } else { + hv_process_channel_removal(channel, + channel->offermsg.child_relid); } - rc_work->channel = channel; - INIT_WORK(&rc_work->work, process_rescind_fn); - schedule_work(&rc_work->work); } /* |