summaryrefslogtreecommitdiff
path: root/drivers/hv/channel_mgmt.c
diff options
context:
space:
mode:
authorK. Y. Srinivasan <kys@microsoft.com>2015-02-28 11:18:18 -0800
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-03-01 19:31:02 -0800
commit2dd37cb81580dce6dfb8c5a7d5c37b904a188ae7 (patch)
tree652a850ab117b2f005b9d63f507080844642e317 /drivers/hv/channel_mgmt.c
parented6cfcc5fdf2ebca320b6f74c836e555e18216e1 (diff)
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>
Diffstat (limited to 'drivers/hv/channel_mgmt.c')
-rw-r--r--drivers/hv/channel_mgmt.c115
1 files changed, 62 insertions, 53 deletions
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 @@ 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);
-
- goto done_init_rescind;
+ /*
+ * 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);
+
+ 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);
+ }
}
/*