diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index b93389124ec4..f8528e1e3a77 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -134,6 +134,34 @@ bool vmbus_prep_negotiate_resp(struct icmsg_hdr *icmsghdrp, EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); +static void vmbus_process_device_unregister(struct work_struct *work) +{ + struct device *dev; + struct vmbus_channel *channel = container_of(work, + struct vmbus_channel, + work); + + dev = get_device(&channel->device_obj->device); + if (dev) { + vmbus_device_unregister(channel->device_obj); + put_device(dev); + } +} + +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 */ @@ -244,29 +272,6 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) free_channel(channel); } -/* - * vmbus_process_rescind_offer - - * Rescind the offer by initiating a device removal - */ -static void vmbus_process_rescind_offer(struct work_struct *work) -{ - struct vmbus_channel *channel = container_of(work, - struct vmbus_channel, - work); - struct device *dev; - - 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); - } -} - void vmbus_free_channels(void) { struct vmbus_channel *channel; @@ -281,11 +286,8 @@ void vmbus_free_channels(void) * vmbus_process_offer - Process the offer by creating a channel/device * associated with this offer */ -static void vmbus_process_offer(struct work_struct *work) +static void vmbus_process_offer(struct vmbus_channel *newchannel) { - struct vmbus_channel *newchannel = container_of(work, - struct vmbus_channel, - work); struct vmbus_channel *channel; bool fnew = true; bool enq = false; @@ -349,9 +351,19 @@ static void vmbus_process_offer(struct work_struct *work) newchannel->state = CHANNEL_OPEN_STATE; if (channel->sc_creation_callback != NULL) - channel->sc_creation_callback(newchannel); + /* + * 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); - goto done_init_rescind; + return; } goto err_free_chan; @@ -392,15 +404,9 @@ static void vmbus_process_offer(struct work_struct *work) kfree(newchannel->device_obj); goto err_free_chan; } -done_init_rescind: - spin_lock_irqsave(&newchannel->lock, flags); - /* The next possible work is rescind handling */ - INIT_WORK(&newchannel->work, vmbus_process_rescind_offer); - /* Check if rescind offer was already received */ - if (newchannel->rescind) - queue_work(newchannel->controlwq, &newchannel->work); - spin_unlock_irqrestore(&newchannel->lock, flags); + return; + err_free_chan: free_channel(newchannel); } @@ -526,8 +532,7 @@ static void vmbus_onoffer(struct vmbus_channel_message_header *hdr) newchannel->monitor_grp = (u8)offer->monitorid / 32; newchannel->monitor_bit = (u8)offer->monitorid % 32; - INIT_WORK(&newchannel->work, vmbus_process_offer); - queue_work(newchannel->controlwq, &newchannel->work); + vmbus_process_offer(newchannel); } /* @@ -544,24 +549,28 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr) rescind = (struct vmbus_channel_rescind_offer *)hdr; channel = relid2channel(rescind->child_relid); - if (channel == NULL) - /* Just return here, no channel found */ + if (channel == NULL) { + hv_process_channel_removal(NULL, rescind->child_relid); return; + } spin_lock_irqsave(&channel->lock, flags); channel->rescind = true; - /* - * channel->work.func != vmbus_process_rescind_offer means we are still - * processing offer request and the rescind offer processing should be - * postponed. It will be done at the very end of vmbus_process_offer() - * as rescind flag is being checked there. - */ - if (channel->work.func == vmbus_process_rescind_offer) - /* work is initialized for vmbus_process_rescind_offer() from - * vmbus_process_offer() where the channel got created */ - queue_work(channel->controlwq, &channel->work); - spin_unlock_irqrestore(&channel->lock, flags); + + if (channel->device_obj) { + /* + * We will have to unregister this device from the + * driver core. Do this in the per-channel work context. + * Note that we are currently executing on the global + * workq for handling messages from the host. + */ + INIT_WORK(&channel->work, vmbus_process_device_unregister); + queue_work(channel->controlwq, &channel->work); + } else { + hv_process_channel_removal(channel, + channel->offermsg.child_relid); + } } /*