aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hv
diff options
context:
space:
mode:
authorDexuan Cui <decui@microsoft.com>2015-03-27 12:10:09 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-04-03 10:18:02 -0400
commitd43e2fe7da320310834467a3fd87a10adb25a221 (patch)
treee978c5e8f751ba913b29bcb1dacedbcb3d2a9dc6 /drivers/hv
parent652594c7dfd9bf6392e3a727bc69d89a2562d953 (diff)
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')
-rw-r--r--drivers/hv/channel_mgmt.c157
-rw-r--r--drivers/hv/connection.c6
-rw-r--r--drivers/hv/hyperv_vmbus.h2
3 files changed, 30 insertions, 135 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 @@
23#include <linux/kernel.h> 23#include <linux/kernel.h>
24#include <linux/sched.h> 24#include <linux/sched.h>
25#include <linux/wait.h> 25#include <linux/wait.h>
26#include <linux/delay.h>
27#include <linux/mm.h> 26#include <linux/mm.h>
28#include <linux/slab.h> 27#include <linux/slab.h>
29#include <linux/list.h> 28#include <linux/list.h>
@@ -33,11 +32,6 @@
33 32
34#include "hyperv_vmbus.h" 33#include "hyperv_vmbus.h"
35 34
36struct vmbus_rescind_work {
37 struct work_struct work;
38 struct vmbus_channel *channel;
39};
40
41/** 35/**
42 * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message 36 * vmbus_prep_negotiate_resp() - Create default response for Hyper-V Negotiate message
43 * @icmsghdrp: Pointer to msg header structure 37 * @icmsghdrp: Pointer to msg header structure
@@ -134,20 +128,6 @@ fw_error:
134 128
135EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); 129EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp);
136 130
137static void vmbus_sc_creation_cb(struct work_struct *work)
138{
139 struct vmbus_channel *newchannel = container_of(work,
140 struct vmbus_channel,
141 work);
142 struct vmbus_channel *primary_channel = newchannel->primary_channel;
143
144 /*
145 * On entry sc_creation_callback has been already verified to
146 * be non-NULL.
147 */
148 primary_channel->sc_creation_callback(newchannel);
149}
150
151/* 131/*
152 * alloc_channel - Allocate and initialize a vmbus channel object 132 * alloc_channel - Allocate and initialize a vmbus channel object
153 */ 133 */
@@ -206,40 +186,6 @@ static void free_channel(struct vmbus_channel *channel)
206 queue_work(vmbus_connection.work_queue, &channel->work); 186 queue_work(vmbus_connection.work_queue, &channel->work);
207} 187}
208 188
209static void process_rescind_fn(struct work_struct *work)
210{
211 struct vmbus_rescind_work *rc_work;
212 struct vmbus_channel *channel;
213 struct device *dev;
214
215 rc_work = container_of(work, struct vmbus_rescind_work, work);
216 channel = rc_work->channel;
217
218 /*
219 * We have already acquired a reference on the channel
220 * and so it cannot vanish underneath us.
221 * It is possible (while very unlikely) that we may
222 * get here while the processing of the initial offer
223 * is still not complete. Deal with this situation by
224 * just waiting until the channel is in the correct state.
225 */
226
227 while (channel->work.func != release_channel)
228 msleep(1000);
229
230 if (channel->device_obj) {
231 dev = get_device(&channel->device_obj->device);
232 if (dev) {
233 vmbus_device_unregister(channel->device_obj);
234 put_device(dev);
235 }
236 } else {
237 hv_process_channel_removal(channel,
238 channel->offermsg.child_relid);
239 }
240 kfree(work);
241}
242
243static void percpu_channel_enq(void *arg) 189static void percpu_channel_enq(void *arg)
244{ 190{
245 struct vmbus_channel *channel = arg; 191 struct vmbus_channel *channel = arg;
@@ -302,46 +248,6 @@ void vmbus_free_channels(void)
302 } 248 }
303} 249}
304 250
305static void vmbus_do_device_register(struct work_struct *work)
306{
307 struct hv_device *device_obj;
308 int ret;
309 unsigned long flags;
310 struct vmbus_channel *newchannel = container_of(work,
311 struct vmbus_channel,
312 work);
313
314 ret = vmbus_device_register(newchannel->device_obj);
315 if (ret != 0) {
316 pr_err("unable to add child device object (relid %d)\n",
317 newchannel->offermsg.child_relid);
318 spin_lock_irqsave(&vmbus_connection.channel_lock, flags);
319 list_del(&newchannel->listentry);
320 device_obj = newchannel->device_obj;
321 newchannel->device_obj = NULL;
322 spin_unlock_irqrestore(&vmbus_connection.channel_lock, flags);
323
324 if (newchannel->target_cpu != get_cpu()) {
325 put_cpu();
326 smp_call_function_single(newchannel->target_cpu,
327 percpu_channel_deq, newchannel, true);
328 } else {
329 percpu_channel_deq(newchannel);
330 put_cpu();
331 }
332
333 kfree(device_obj);
334 if (!newchannel->rescind) {
335 free_channel(newchannel);
336 return;
337 }
338 }
339 /*
340 * The next state for this channel is to be freed.
341 */
342 INIT_WORK(&newchannel->work, release_channel);
343}
344
345/* 251/*
346 * vmbus_process_offer - Process the offer by creating a channel/device 252 * vmbus_process_offer - Process the offer by creating a channel/device
347 * associated with this offer 253 * associated with this offer
@@ -410,19 +316,8 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
410 316
411 newchannel->state = CHANNEL_OPEN_STATE; 317 newchannel->state = CHANNEL_OPEN_STATE;
412 channel->num_sc++; 318 channel->num_sc++;
413 if (channel->sc_creation_callback != NULL) { 319 if (channel->sc_creation_callback != NULL)
414 /* 320 channel->sc_creation_callback(newchannel);
415 * We need to invoke the sub-channel creation
416 * callback; invoke this in a seperate work
417 * context since we are currently running on
418 * the global work context in which we handle
419 * messages from the host.
420 */
421 INIT_WORK(&newchannel->work,
422 vmbus_sc_creation_cb);
423 queue_work(newchannel->controlwq,
424 &newchannel->work);
425 }
426 321
427 return; 322 return;
428 } 323 }
@@ -453,13 +348,13 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
453 * Add the new device to the bus. This will kick off device-driver 348 * Add the new device to the bus. This will kick off device-driver
454 * binding which eventually invokes the device driver's AddDevice() 349 * binding which eventually invokes the device driver's AddDevice()
455 * method. 350 * method.
456 * Invoke this call on the per-channel work context.
457 * Until we return from this function, rescind offer message
458 * cannot be processed as we are running on the global message
459 * handling work.
460 */ 351 */
461 INIT_WORK(&newchannel->work, vmbus_do_device_register); 352 if (vmbus_device_register(newchannel->device_obj) != 0) {
462 queue_work(newchannel->controlwq, &newchannel->work); 353 pr_err("unable to add child device object (relid %d)\n",
354 newchannel->offermsg.child_relid);
355 kfree(newchannel->device_obj);
356 goto err_deq_chan;
357 }
463 return; 358 return;
464 359
465err_deq_chan: 360err_deq_chan:
@@ -613,31 +508,35 @@ static void vmbus_onoffer_rescind(struct vmbus_channel_message_header *hdr)
613{ 508{
614 struct vmbus_channel_rescind_offer *rescind; 509 struct vmbus_channel_rescind_offer *rescind;
615 struct vmbus_channel *channel; 510 struct vmbus_channel *channel;
616 struct vmbus_rescind_work *rc_work; 511 unsigned long flags;
512 struct device *dev;
617 513
618 rescind = (struct vmbus_channel_rescind_offer *)hdr; 514 rescind = (struct vmbus_channel_rescind_offer *)hdr;
619 channel = relid2channel(rescind->child_relid, true); 515 channel = relid2channel(rescind->child_relid);
620 516
621 if (channel == NULL) { 517 if (channel == NULL) {
622 hv_process_channel_removal(NULL, rescind->child_relid); 518 hv_process_channel_removal(NULL, rescind->child_relid);
623 return; 519 return;
624 } 520 }
625 521
626 /* 522 spin_lock_irqsave(&channel->lock, flags);
627 * We have acquired a reference on the channel and have posted 523 channel->rescind = true;
628 * the rescind state. Perform further cleanup in a work context 524 spin_unlock_irqrestore(&channel->lock, flags);
629 * that is different from the global work context in which 525
630 * we process messages from the host (we are currently executing 526 if (channel->device_obj) {
631 * on that global context. 527 /*
632 */ 528 * We will have to unregister this device from the
633 rc_work = kzalloc(sizeof(struct vmbus_rescind_work), GFP_KERNEL); 529 * driver core.
634 if (!rc_work) { 530 */
635 pr_err("Unable to allocate memory for rescind processing "); 531 dev = get_device(&channel->device_obj->device);
636 return; 532 if (dev) {
533 vmbus_device_unregister(channel->device_obj);
534 put_device(dev);
535 }
536 } else {
537 hv_process_channel_removal(channel,
538 channel->offermsg.child_relid);
637 } 539 }
638 rc_work->channel = channel;
639 INIT_WORK(&rc_work->work, process_rescind_fn);
640 schedule_work(&rc_work->work);
641} 540}
642 541
643/* 542/*
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c
index 8bcd3071c84f..583d7d42b46d 100644
--- a/drivers/hv/connection.c
+++ b/drivers/hv/connection.c
@@ -270,7 +270,7 @@ static struct vmbus_channel *pcpu_relid2channel(u32 relid)
270 * relid2channel - Get the channel object given its 270 * relid2channel - Get the channel object given its
271 * child relative id (ie channel id) 271 * child relative id (ie channel id)
272 */ 272 */
273struct vmbus_channel *relid2channel(u32 relid, bool rescind) 273struct vmbus_channel *relid2channel(u32 relid)
274{ 274{
275 struct vmbus_channel *channel; 275 struct vmbus_channel *channel;
276 struct vmbus_channel *found_channel = NULL; 276 struct vmbus_channel *found_channel = NULL;
@@ -282,8 +282,6 @@ struct vmbus_channel *relid2channel(u32 relid, bool rescind)
282 list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { 282 list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {
283 if (channel->offermsg.child_relid == relid) { 283 if (channel->offermsg.child_relid == relid) {
284 found_channel = channel; 284 found_channel = channel;
285 if (rescind)
286 found_channel->rescind = true;
287 break; 285 break;
288 } else if (!list_empty(&channel->sc_list)) { 286 } else if (!list_empty(&channel->sc_list)) {
289 /* 287 /*
@@ -294,8 +292,6 @@ struct vmbus_channel *relid2channel(u32 relid, bool rescind)
294 sc_list); 292 sc_list);
295 if (cur_sc->offermsg.child_relid == relid) { 293 if (cur_sc->offermsg.child_relid == relid) {
296 found_channel = cur_sc; 294 found_channel = cur_sc;
297 if (rescind)
298 found_channel->rescind = true;
299 break; 295 break;
300 } 296 }
301 } 297 }
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h
index f40a5a935ab6..887287ad411f 100644
--- a/drivers/hv/hyperv_vmbus.h
+++ b/drivers/hv/hyperv_vmbus.h
@@ -715,7 +715,7 @@ void vmbus_device_unregister(struct hv_device *device_obj);
715/* VmbusChildDeviceDestroy( */ 715/* VmbusChildDeviceDestroy( */
716/* struct hv_device *); */ 716/* struct hv_device *); */
717 717
718struct vmbus_channel *relid2channel(u32 relid, bool rescind); 718struct vmbus_channel *relid2channel(u32 relid);
719 719
720void vmbus_free_channels(void); 720void vmbus_free_channels(void);
721 721