Skip to content

Commit d570aec

Browse files
andrea-parriliuw
authored andcommitted
Drivers: hv: vmbus: Synchronize init_vp_index() vs. CPU hotplug
init_vp_index() may access the cpu_online_mask mask via its calls of cpumask_of_node(). Make sure to protect these accesses with a cpus_read_lock() critical section. Also, remove some (hardcoded) instances of CPU(0) from init_vp_index() and replace them with VMBUS_CONNECT_CPU. The connect CPU can not go offline, since Hyper-V does not provide a way to change it. Finally, order the accesses of target_cpu from init_vp_index() and hv_synic_cleanup() by relying on the channel_mutex; this is achieved by moving the call of init_vp_index() into vmbus_process_offer(). Signed-off-by: Andrea Parri (Microsoft) <[email protected]> Link: https://lore.kernel.org/r/[email protected] Reviewed-by: Michael Kelley <[email protected]> Signed-off-by: Wei Liu <[email protected]>
1 parent 8ef4c4a commit d570aec

File tree

2 files changed

+38
-16
lines changed

2 files changed

+38
-16
lines changed

drivers/hv/channel_mgmt.c

Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
#include <linux/module.h>
1919
#include <linux/completion.h>
2020
#include <linux/delay.h>
21+
#include <linux/cpu.h>
2122
#include <linux/hyperv.h>
2223
#include <asm/mshyperv.h>
2324

@@ -466,13 +467,8 @@ static void vmbus_add_channel_work(struct work_struct *work)
466467
container_of(work, struct vmbus_channel, add_channel_work);
467468
struct vmbus_channel *primary_channel = newchannel->primary_channel;
468469
unsigned long flags;
469-
u16 dev_type;
470470
int ret;
471471

472-
dev_type = hv_get_dev_type(newchannel);
473-
474-
init_vp_index(newchannel, dev_type);
475-
476472
/*
477473
* This state is used to indicate a successful open
478474
* so that when we do close the channel normally, we
@@ -504,7 +500,7 @@ static void vmbus_add_channel_work(struct work_struct *work)
504500
if (!newchannel->device_obj)
505501
goto err_deq_chan;
506502

507-
newchannel->device_obj->device_id = dev_type;
503+
newchannel->device_obj->device_id = hv_get_dev_type(newchannel);
508504
/*
509505
* Add the new device to the bus. This will kick off device-driver
510506
* binding which eventually invokes the device driver's AddDevice()
@@ -560,6 +556,25 @@ static void vmbus_process_offer(struct vmbus_channel *newchannel)
560556
unsigned long flags;
561557
bool fnew = true;
562558

559+
/*
560+
* Initialize the target_CPU before inserting the channel in
561+
* the chn_list and sc_list lists, within the channel_mutex
562+
* critical section:
563+
*
564+
* CPU1 CPU2
565+
*
566+
* [vmbus_process_offer()] [hv_syninc_cleanup()]
567+
*
568+
* STORE target_cpu LOCK channel_mutex
569+
* LOCK channel_mutex SEARCH chn_list
570+
* INSERT chn_list LOAD target_cpu
571+
* UNLOCK channel_mutex UNLOCK channel_mutex
572+
*
573+
* Forbids: CPU2's SEARCH from seeing CPU1's INSERT &&
574+
* CPU2's LOAD from *not* seing CPU1's STORE
575+
*/
576+
init_vp_index(newchannel, hv_get_dev_type(newchannel));
577+
563578
mutex_lock(&vmbus_connection.channel_mutex);
564579

565580
/* Remember the channels that should be cleaned up upon suspend. */
@@ -656,7 +671,7 @@ static DEFINE_SPINLOCK(bind_channel_to_cpu_lock);
656671
* channel interrupt load by binding a channel to VCPU.
657672
*
658673
* For pre-win8 hosts or non-performance critical channels we assign the
659-
* first CPU in the first NUMA node.
674+
* VMBUS_CONNECT_CPU.
660675
*
661676
* Starting with win8, performance critical channels will be distributed
662677
* evenly among all the available NUMA nodes. Once the node is assigned,
@@ -675,17 +690,22 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
675690
!alloc_cpumask_var(&available_mask, GFP_KERNEL)) {
676691
/*
677692
* Prior to win8, all channel interrupts are
678-
* delivered on cpu 0.
693+
* delivered on VMBUS_CONNECT_CPU.
679694
* Also if the channel is not a performance critical
680-
* channel, bind it to cpu 0.
681-
* In case alloc_cpumask_var() fails, bind it to cpu 0.
695+
* channel, bind it to VMBUS_CONNECT_CPU.
696+
* In case alloc_cpumask_var() fails, bind it to
697+
* VMBUS_CONNECT_CPU.
682698
*/
683-
channel->numa_node = 0;
684-
channel->target_cpu = 0;
685-
channel->target_vp = hv_cpu_number_to_vp_number(0);
699+
channel->numa_node = cpu_to_node(VMBUS_CONNECT_CPU);
700+
channel->target_cpu = VMBUS_CONNECT_CPU;
701+
channel->target_vp =
702+
hv_cpu_number_to_vp_number(VMBUS_CONNECT_CPU);
686703
return;
687704
}
688705

706+
/* No CPUs can come up or down during this. */
707+
cpus_read_lock();
708+
689709
/*
690710
* Serializes the accesses to the global variable next_numa_node_id.
691711
* See also the header comment of the spin lock declaration.
@@ -723,6 +743,7 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
723743
channel->target_vp = hv_cpu_number_to_vp_number(target_cpu);
724744

725745
spin_unlock(&bind_channel_to_cpu_lock);
746+
cpus_read_unlock();
726747

727748
free_cpumask_var(available_mask);
728749
}

drivers/hv/hv.c

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,10 @@ int hv_synic_cleanup(unsigned int cpu)
256256

257257
/*
258258
* Search for channels which are bound to the CPU we're about to
259-
* cleanup. In case we find one and vmbus is still connected we need to
260-
* fail, this will effectively prevent CPU offlining. There is no way
261-
* we can re-bind channels to different CPUs for now.
259+
* cleanup. In case we find one and vmbus is still connected, we
260+
* fail; this will effectively prevent CPU offlining.
261+
*
262+
* TODO: Re-bind the channels to different CPUs.
262263
*/
263264
mutex_lock(&vmbus_connection.channel_mutex);
264265
list_for_each_entry(channel, &vmbus_connection.chn_list, listentry) {

0 commit comments

Comments
 (0)