diff options
author | Dexuan Cui <decui@microsoft.com> | 2015-03-27 12:10:09 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-04-03 10:18:02 -0400 |
commit | d43e2fe7da320310834467a3fd87a10adb25a221 (patch) | |
tree | e978c5e8f751ba913b29bcb1dacedbcb3d2a9dc6 | |
parent | 652594c7dfd9bf6392e3a727bc69d89a2562d953 (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>
-rw-r--r-- | drivers/hv/channel_mgmt.c | 157 | ||||
-rw-r--r-- | drivers/hv/connection.c | 6 | ||||
-rw-r--r-- | drivers/hv/hyperv_vmbus.h | 2 |
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 | ||
36 | struct 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 | ||
135 | EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); | 129 | EXPORT_SYMBOL_GPL(vmbus_prep_negotiate_resp); |
136 | 130 | ||
137 | static 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 | ||
209 | static 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 | |||
243 | static void percpu_channel_enq(void *arg) | 189 | static 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 | ||
305 | static 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 | ||
465 | err_deq_chan: | 360 | err_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 | */ |
273 | struct vmbus_channel *relid2channel(u32 relid, bool rescind) | 273 | struct 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 | ||
718 | struct vmbus_channel *relid2channel(u32 relid, bool rescind); | 718 | struct vmbus_channel *relid2channel(u32 relid); |
719 | 719 | ||
720 | void vmbus_free_channels(void); | 720 | void vmbus_free_channels(void); |
721 | 721 | ||