|
@@ -134,6 +134,34 @@ fw_error:
|
|
|
|
|
|
EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
|
|
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
|
|
* 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);
|
|
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)
|
|
void vmbus_free_channels(void)
|
|
{
|
|
{
|
|
struct vmbus_channel *channel;
|
|
struct vmbus_channel *channel;
|
|
@@ -281,11 +286,8 @@ void vmbus_free_channels(void)
|
|
* vmbus_process_offer - Process the offer by creating a channel/device
|
|
* vmbus_process_offer - Process the offer by creating a channel/device
|
|
* associated with this offer
|
|
* 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;
|
|
struct vmbus_channel *channel;
|
|
bool fnew = true;
|
|
bool fnew = true;
|
|
bool enq = false;
|
|
bool enq = false;
|
|
@@ -349,9 +351,19 @@ static void vmbus_process_offer(struct work_struct *work)
|
|
|
|
|
|
newchannel->state = CHANNEL_OPEN_STATE;
|
|
newchannel->state = CHANNEL_OPEN_STATE;
|
|
if (channel->sc_creation_callback != NULL)
|
|
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;
|
|
goto err_free_chan;
|
|
@@ -392,15 +404,9 @@ static void vmbus_process_offer(struct work_struct *work)
|
|
kfree(newchannel->device_obj);
|
|
kfree(newchannel->device_obj);
|
|
goto err_free_chan;
|
|
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;
|
|
return;
|
|
|
|
+
|
|
err_free_chan:
|
|
err_free_chan:
|
|
free_channel(newchannel);
|
|
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_grp = (u8)offer->monitorid / 32;
|
|
newchannel->monitor_bit = (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;
|
|
rescind = (struct vmbus_channel_rescind_offer *)hdr;
|
|
channel = relid2channel(rescind->child_relid);
|
|
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;
|
|
return;
|
|
|
|
+ }
|
|
|
|
|
|
spin_lock_irqsave(&channel->lock, flags);
|
|
spin_lock_irqsave(&channel->lock, flags);
|
|
channel->rescind = true;
|
|
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);
|
|
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);
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
/*
|