Drivers: hv: vmbus: Handle both rescind and offer messages in the same context

Execute both ressind and offer messages in the same work context. This serializes these
operations and naturally addresses the various corner cases.

Signed-off-by: K. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
K. Y. Srinivasan 2015-02-28 11:18:18 -08:00 committed by Greg Kroah-Hartman
parent ed6cfcc5fd
commit 2dd37cb815
1 changed files with 61 additions and 52 deletions

View File

@ -134,6 +134,34 @@ fw_error:
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);
}
}
/*