diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-12-10 03:22:34 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2018-12-10 03:22:34 -0500 |
commit | c4aa8b2a8b63a269d63acafc0358a7b9f5d9e563 (patch) | |
tree | d04a7861e542665bb7c03aac729a72c957be8abc /drivers/hv | |
parent | 48d365de7746a000afd7f9f7cc1821cbb6fe9b45 (diff) | |
parent | 40e020c129cfc991e8ab4736d2665351ffd1468d (diff) |
Merge 4.20-rc6 into char-misc-next
This should resolve the hv driver merge conflict.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/hv')
-rw-r--r-- | drivers/hv/channel_mgmt.c | 188 | ||||
-rw-r--r-- | drivers/hv/connection.c | 24 | ||||
-rw-r--r-- | drivers/hv/hyperv_vmbus.h | 7 |
3 files changed, 154 insertions, 65 deletions
diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 82e673671087..d01689079e9b 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c | |||
@@ -434,60 +434,16 @@ void vmbus_free_channels(void) | |||
434 | } | 434 | } |
435 | } | 435 | } |
436 | 436 | ||
437 | /* | 437 | /* Note: the function can run concurrently for primary/sub channels. */ |
438 | * vmbus_process_offer - Process the offer by creating a channel/device | 438 | static void vmbus_add_channel_work(struct work_struct *work) |
439 | * associated with this offer | ||
440 | */ | ||
441 | static void vmbus_process_offer(struct vmbus_channel *newchannel) | ||
442 | { | 439 | { |
443 | struct vmbus_channel *channel; | 440 | struct vmbus_channel *newchannel = |
444 | bool fnew = true; | 441 | container_of(work, struct vmbus_channel, add_channel_work); |
442 | struct vmbus_channel *primary_channel = newchannel->primary_channel; | ||
445 | unsigned long flags; | 443 | unsigned long flags; |
446 | u16 dev_type; | 444 | u16 dev_type; |
447 | int ret; | 445 | int ret; |
448 | 446 | ||
449 | /* Make sure this is a new offer */ | ||
450 | mutex_lock(&vmbus_connection.channel_mutex); | ||
451 | |||
452 | /* | ||
453 | * Now that we have acquired the channel_mutex, | ||
454 | * we can release the potentially racing rescind thread. | ||
455 | */ | ||
456 | atomic_dec(&vmbus_connection.offer_in_progress); | ||
457 | |||
458 | list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { | ||
459 | if (!uuid_le_cmp(channel->offermsg.offer.if_type, | ||
460 | newchannel->offermsg.offer.if_type) && | ||
461 | !uuid_le_cmp(channel->offermsg.offer.if_instance, | ||
462 | newchannel->offermsg.offer.if_instance)) { | ||
463 | fnew = false; | ||
464 | break; | ||
465 | } | ||
466 | } | ||
467 | |||
468 | if (fnew) | ||
469 | list_add_tail(&newchannel->listentry, | ||
470 | &vmbus_connection.chn_list); | ||
471 | |||
472 | mutex_unlock(&vmbus_connection.channel_mutex); | ||
473 | |||
474 | if (!fnew) { | ||
475 | /* | ||
476 | * Check to see if this is a sub-channel. | ||
477 | */ | ||
478 | if (newchannel->offermsg.offer.sub_channel_index != 0) { | ||
479 | /* | ||
480 | * Process the sub-channel. | ||
481 | */ | ||
482 | newchannel->primary_channel = channel; | ||
483 | spin_lock_irqsave(&channel->lock, flags); | ||
484 | list_add_tail(&newchannel->sc_list, &channel->sc_list); | ||
485 | spin_unlock_irqrestore(&channel->lock, flags); | ||
486 | } else { | ||
487 | goto err_free_chan; | ||
488 | } | ||
489 | } | ||
490 | |||
491 | dev_type = hv_get_dev_type(newchannel); | 447 | dev_type = hv_get_dev_type(newchannel); |
492 | 448 | ||
493 | init_vp_index(newchannel, dev_type); | 449 | init_vp_index(newchannel, dev_type); |
@@ -505,27 +461,26 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) | |||
505 | /* | 461 | /* |
506 | * This state is used to indicate a successful open | 462 | * This state is used to indicate a successful open |
507 | * so that when we do close the channel normally, we | 463 | * so that when we do close the channel normally, we |
508 | * can cleanup properly | 464 | * can cleanup properly. |
509 | */ | 465 | */ |
510 | newchannel->state = CHANNEL_OPEN_STATE; | 466 | newchannel->state = CHANNEL_OPEN_STATE; |
511 | 467 | ||
512 | if (!fnew) { | 468 | if (primary_channel != NULL) { |
513 | struct hv_device *dev | 469 | /* newchannel is a sub-channel. */ |
514 | = newchannel->primary_channel->device_obj; | 470 | struct hv_device *dev = primary_channel->device_obj; |
515 | 471 | ||
516 | if (vmbus_add_channel_kobj(dev, newchannel)) | 472 | if (vmbus_add_channel_kobj(dev, newchannel)) |
517 | goto err_free_chan; | 473 | goto err_deq_chan; |
474 | |||
475 | if (primary_channel->sc_creation_callback != NULL) | ||
476 | primary_channel->sc_creation_callback(newchannel); | ||
518 | 477 | ||
519 | if (channel->sc_creation_callback != NULL) | ||
520 | channel->sc_creation_callback(newchannel); | ||
521 | newchannel->probe_done = true; | 478 | newchannel->probe_done = true; |
522 | return; | 479 | return; |
523 | } | 480 | } |
524 | 481 | ||
525 | /* | 482 | /* |
526 | * Start the process of binding this offer to the driver | 483 | * Start the process of binding the primary channel to the driver |
527 | * We need to set the DeviceObject field before calling | ||
528 | * vmbus_child_dev_add() | ||
529 | */ | 484 | */ |
530 | newchannel->device_obj = vmbus_device_create( | 485 | newchannel->device_obj = vmbus_device_create( |
531 | &newchannel->offermsg.offer.if_type, | 486 | &newchannel->offermsg.offer.if_type, |
@@ -554,13 +509,28 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel) | |||
554 | 509 | ||
555 | err_deq_chan: | 510 | err_deq_chan: |
556 | mutex_lock(&vmbus_connection.channel_mutex); | 511 | mutex_lock(&vmbus_connection.channel_mutex); |
557 | list_del(&newchannel->listentry); | 512 | |
513 | /* | ||
514 | * We need to set the flag, otherwise | ||
515 | * vmbus_onoffer_rescind() can be blocked. | ||
516 | */ | ||
517 | newchannel->probe_done = true; | ||
518 | |||
519 | if (primary_channel == NULL) { | ||
520 | list_del(&newchannel->listentry); | ||
521 | } else { | ||
522 | spin_lock_irqsave(&primary_channel->lock, flags); | ||
523 | list_del(&newchannel->sc_list); | ||
524 | spin_unlock_irqrestore(&primary_channel->lock, flags); | ||
525 | } | ||
526 | |||
558 | mutex_unlock(&vmbus_connection.channel_mutex); | 527 | mutex_unlock(&vmbus_connection.channel_mutex); |
559 | 528 | ||
560 | if (newchannel->target_cpu != get_cpu()) { | 529 | if (newchannel->target_cpu != get_cpu()) { |
561 | put_cpu(); | 530 | put_cpu(); |
562 | smp_call_function_single(newchannel->target_cpu, | 531 | smp_call_function_single(newchannel->target_cpu, |
563 | percpu_channel_deq, newchannel, true); | 532 | percpu_channel_deq, |
533 | newchannel, true); | ||
564 | } else { | 534 | } else { |
565 | percpu_channel_deq(newchannel); | 535 | percpu_channel_deq(newchannel); |
566 | put_cpu(); | 536 | put_cpu(); |
@@ -568,14 +538,104 @@ err_deq_chan: | |||
568 | 538 | ||
569 | vmbus_release_relid(newchannel->offermsg.child_relid); | 539 | vmbus_release_relid(newchannel->offermsg.child_relid); |
570 | 540 | ||
571 | err_free_chan: | ||
572 | free_channel(newchannel); | 541 | free_channel(newchannel); |
573 | } | 542 | } |
574 | 543 | ||
575 | /* | 544 | /* |
545 | * vmbus_process_offer - Process the offer by creating a channel/device | ||
546 | * associated with this offer | ||
547 | */ | ||
548 | static void vmbus_process_offer(struct vmbus_channel *newchannel) | ||
549 | { | ||
550 | struct vmbus_channel *channel; | ||
551 | struct workqueue_struct *wq; | ||
552 | unsigned long flags; | ||
553 | bool fnew = true; | ||
554 | |||
555 | mutex_lock(&vmbus_connection.channel_mutex); | ||
556 | |||
557 | /* | ||
558 | * Now that we have acquired the channel_mutex, | ||
559 | * we can release the potentially racing rescind thread. | ||
560 | */ | ||
561 | atomic_dec(&vmbus_connection.offer_in_progress); | ||
562 | |||
563 | list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) { | ||
564 | if (!uuid_le_cmp(channel->offermsg.offer.if_type, | ||
565 | newchannel->offermsg.offer.if_type) && | ||
566 | !uuid_le_cmp(channel->offermsg.offer.if_instance, | ||
567 | newchannel->offermsg.offer.if_instance)) { | ||
568 | fnew = false; | ||
569 | break; | ||
570 | } | ||
571 | } | ||
572 | |||
573 | if (fnew) | ||
574 | list_add_tail(&newchannel->listentry, | ||
575 | &vmbus_connection.chn_list); | ||
576 | else { | ||
577 | /* | ||
578 | * Check to see if this is a valid sub-channel. | ||
579 | */ | ||
580 | if (newchannel->offermsg.offer.sub_channel_index == 0) { | ||
581 | mutex_unlock(&vmbus_connection.channel_mutex); | ||
582 | /* | ||
583 | * Don't call free_channel(), because newchannel->kobj | ||
584 | * is not initialized yet. | ||
585 | */ | ||
586 | kfree(newchannel); | ||
587 | WARN_ON_ONCE(1); | ||
588 | return; | ||
589 | } | ||
590 | /* | ||
591 | * Process the sub-channel. | ||
592 | */ | ||
593 | newchannel->primary_channel = channel; | ||
594 | spin_lock_irqsave(&channel->lock, flags); | ||
595 | list_add_tail(&newchannel->sc_list, &channel->sc_list); | ||
596 | spin_unlock_irqrestore(&channel->lock, flags); | ||
597 | } | ||
598 | |||
599 | mutex_unlock(&vmbus_connection.channel_mutex); | ||
600 | |||
601 | /* | ||
602 | * vmbus_process_offer() mustn't call channel->sc_creation_callback() | ||
603 | * directly for sub-channels, because sc_creation_callback() -> | ||
604 | * vmbus_open() may never get the host's response to the | ||
605 | * OPEN_CHANNEL message (the host may rescind a channel at any time, | ||
606 | * e.g. in the case of hot removing a NIC), and vmbus_onoffer_rescind() | ||
607 | * may not wake up the vmbus_open() as it's blocked due to a non-zero | ||
608 | * vmbus_connection.offer_in_progress, and finally we have a deadlock. | ||
609 | * | ||
610 | * The above is also true for primary channels, if the related device | ||
611 | * drivers use sync probing mode by default. | ||
612 | * | ||
613 | * And, usually the handling of primary channels and sub-channels can | ||
614 | * depend on each other, so we should offload them to different | ||
615 | * workqueues to avoid possible deadlock, e.g. in sync-probing mode, | ||
616 | * NIC1's netvsc_subchan_work() can race with NIC2's netvsc_probe() -> | ||
617 | * rtnl_lock(), and causes deadlock: the former gets the rtnl_lock | ||
618 | * and waits for all the sub-channels to appear, but the latter | ||
619 | * can't get the rtnl_lock and this blocks the handling of | ||
620 | * sub-channels. | ||
621 | */ | ||
622 | INIT_WORK(&newchannel->add_channel_work, vmbus_add_channel_work); | ||
623 | wq = fnew ? vmbus_connection.handle_primary_chan_wq : | ||
624 | vmbus_connection.handle_sub_chan_wq; | ||
625 | queue_work(wq, &newchannel->add_channel_work); | ||
626 | } | ||
627 | |||
628 | /* | ||
576 | * We use this state to statically distribute the channel interrupt load. | 629 | * We use this state to statically distribute the channel interrupt load. |
577 | */ | 630 | */ |
578 | static int next_numa_node_id; | 631 | static int next_numa_node_id; |
632 | /* | ||
633 | * init_vp_index() accesses global variables like next_numa_node_id, and | ||
634 | * it can run concurrently for primary channels and sub-channels: see | ||
635 | * vmbus_process_offer(), so we need the lock to protect the global | ||
636 | * variables. | ||
637 | */ | ||
638 | static DEFINE_SPINLOCK(bind_channel_to_cpu_lock); | ||
579 | 639 | ||
580 | /* | 640 | /* |
581 | * Starting with Win8, we can statically distribute the incoming | 641 | * Starting with Win8, we can statically distribute the incoming |
@@ -611,6 +671,8 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) | |||
611 | return; | 671 | return; |
612 | } | 672 | } |
613 | 673 | ||
674 | spin_lock(&bind_channel_to_cpu_lock); | ||
675 | |||
614 | /* | 676 | /* |
615 | * Based on the channel affinity policy, we will assign the NUMA | 677 | * Based on the channel affinity policy, we will assign the NUMA |
616 | * nodes. | 678 | * nodes. |
@@ -693,6 +755,8 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) | |||
693 | channel->target_cpu = cur_cpu; | 755 | channel->target_cpu = cur_cpu; |
694 | channel->target_vp = hv_cpu_number_to_vp_number(cur_cpu); | 756 | channel->target_vp = hv_cpu_number_to_vp_number(cur_cpu); |
695 | 757 | ||
758 | spin_unlock(&bind_channel_to_cpu_lock); | ||
759 | |||
696 | free_cpumask_var(available_mask); | 760 | free_cpumask_var(available_mask); |
697 | } | 761 | } |
698 | 762 | ||
diff --git a/drivers/hv/connection.c b/drivers/hv/connection.c index f4d08c8ac7f8..4fe117b761ce 100644 --- a/drivers/hv/connection.c +++ b/drivers/hv/connection.c | |||
@@ -190,6 +190,20 @@ int vmbus_connect(void) | |||
190 | goto cleanup; | 190 | goto cleanup; |
191 | } | 191 | } |
192 | 192 | ||
193 | vmbus_connection.handle_primary_chan_wq = | ||
194 | create_workqueue("hv_pri_chan"); | ||
195 | if (!vmbus_connection.handle_primary_chan_wq) { | ||
196 | ret = -ENOMEM; | ||
197 | goto cleanup; | ||
198 | } | ||
199 | |||
200 | vmbus_connection.handle_sub_chan_wq = | ||
201 | create_workqueue("hv_sub_chan"); | ||
202 | if (!vmbus_connection.handle_sub_chan_wq) { | ||
203 | ret = -ENOMEM; | ||
204 | goto cleanup; | ||
205 | } | ||
206 | |||
193 | INIT_LIST_HEAD(&vmbus_connection.chn_msg_list); | 207 | INIT_LIST_HEAD(&vmbus_connection.chn_msg_list); |
194 | spin_lock_init(&vmbus_connection.channelmsg_lock); | 208 | spin_lock_init(&vmbus_connection.channelmsg_lock); |
195 | 209 | ||
@@ -280,10 +294,14 @@ void vmbus_disconnect(void) | |||
280 | */ | 294 | */ |
281 | vmbus_initiate_unload(false); | 295 | vmbus_initiate_unload(false); |
282 | 296 | ||
283 | if (vmbus_connection.work_queue) { | 297 | if (vmbus_connection.handle_sub_chan_wq) |
284 | drain_workqueue(vmbus_connection.work_queue); | 298 | destroy_workqueue(vmbus_connection.handle_sub_chan_wq); |
299 | |||
300 | if (vmbus_connection.handle_primary_chan_wq) | ||
301 | destroy_workqueue(vmbus_connection.handle_primary_chan_wq); | ||
302 | |||
303 | if (vmbus_connection.work_queue) | ||
285 | destroy_workqueue(vmbus_connection.work_queue); | 304 | destroy_workqueue(vmbus_connection.work_queue); |
286 | } | ||
287 | 305 | ||
288 | if (vmbus_connection.int_page) { | 306 | if (vmbus_connection.int_page) { |
289 | free_pages((unsigned long)vmbus_connection.int_page, 0); | 307 | free_pages((unsigned long)vmbus_connection.int_page, 0); |
diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index f17c06a5e74b..313a07f5efa1 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h | |||
@@ -333,7 +333,14 @@ struct vmbus_connection { | |||
333 | struct list_head chn_list; | 333 | struct list_head chn_list; |
334 | struct mutex channel_mutex; | 334 | struct mutex channel_mutex; |
335 | 335 | ||
336 | /* | ||
337 | * An offer message is handled first on the work_queue, and then | ||
338 | * is further handled on handle_primary_chan_wq or | ||
339 | * handle_sub_chan_wq. | ||
340 | */ | ||
336 | struct workqueue_struct *work_queue; | 341 | struct workqueue_struct *work_queue; |
342 | struct workqueue_struct *handle_primary_chan_wq; | ||
343 | struct workqueue_struct *handle_sub_chan_wq; | ||
337 | }; | 344 | }; |
338 | 345 | ||
339 | 346 | ||