Commit 509879bd authored by K. Y. Srinivasan's avatar K. Y. Srinivasan Committed by Greg Kroah-Hartman

Drivers: hv: Introduce a policy for controlling channel affinity

Introduce a mechanism to control how channels will be affinitized. We will
support two policies:

1. HV_BALANCED: All performance critical channels will be dstributed
evenly amongst all the available NUMA nodes. Once the Node is assigned,
we will assign the CPU based on a simple round robin scheme.

2. HV_LOCALIZED: Only the primary channels are distributed across all
NUMA nodes. Sub-channels will be in the same NUMA node as the primary
channel. This is the current behaviour.

The default policy will be the HV_BALANCED as it can minimize the remote
memory access on NUMA machines with applications that span NUMA nodes.
Signed-off-by: default avatarK. Y. Srinivasan <kys@microsoft.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent bb08d431
...@@ -356,6 +356,7 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid) ...@@ -356,6 +356,7 @@ void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid)
* We need to free the bit for init_vp_index() to work in the case * We need to free the bit for init_vp_index() to work in the case
* of sub-channel, when we reload drivers like hv_netvsc. * of sub-channel, when we reload drivers like hv_netvsc.
*/ */
if (channel->affinity_policy == HV_LOCALIZED)
cpumask_clear_cpu(channel->target_cpu, cpumask_clear_cpu(channel->target_cpu,
&primary_channel->alloced_cpus_in_node); &primary_channel->alloced_cpus_in_node);
...@@ -548,17 +549,17 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) ...@@ -548,17 +549,17 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
} }
/* /*
* We distribute primary channels evenly across all the available * Based on the channel affinity policy, we will assign the NUMA
* NUMA nodes and within the assigned NUMA node we will assign the * nodes.
* first available CPU to the primary channel.
* The sub-channels will be assigned to the CPUs available in the
* NUMA node evenly.
*/ */
if (!primary) {
if ((channel->affinity_policy == HV_BALANCED) || (!primary)) {
while (true) { while (true) {
next_node = next_numa_node_id++; next_node = next_numa_node_id++;
if (next_node == nr_node_ids) if (next_node == nr_node_ids) {
next_node = next_numa_node_id = 0; next_node = next_numa_node_id = 0;
continue;
}
if (cpumask_empty(cpumask_of_node(next_node))) if (cpumask_empty(cpumask_of_node(next_node)))
continue; continue;
break; break;
...@@ -582,15 +583,17 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) ...@@ -582,15 +583,17 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
cur_cpu = -1; cur_cpu = -1;
if (primary->affinity_policy == HV_LOCALIZED) {
/* /*
* Normally Hyper-V host doesn't create more subchannels than there * Normally Hyper-V host doesn't create more subchannels
* are VCPUs on the node but it is possible when not all present VCPUs * than there are VCPUs on the node but it is possible when not
* on the node are initialized by guest. Clear the alloced_cpus_in_node * all present VCPUs on the node are initialized by guest.
* to start over. * Clear the alloced_cpus_in_node to start over.
*/ */
if (cpumask_equal(&primary->alloced_cpus_in_node, if (cpumask_equal(&primary->alloced_cpus_in_node,
cpumask_of_node(primary->numa_node))) cpumask_of_node(primary->numa_node)))
cpumask_clear(&primary->alloced_cpus_in_node); cpumask_clear(&primary->alloced_cpus_in_node);
}
while (true) { while (true) {
cur_cpu = cpumask_next(cur_cpu, &available_mask); cur_cpu = cpumask_next(cur_cpu, &available_mask);
...@@ -601,11 +604,14 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) ...@@ -601,11 +604,14 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
continue; continue;
} }
if (primary->affinity_policy == HV_LOCALIZED) {
/* /*
* NOTE: in the case of sub-channel, we clear the sub-channel * NOTE: in the case of sub-channel, we clear the
* related bit(s) in primary->alloced_cpus_in_node in * sub-channel related bit(s) in
* hv_process_channel_removal(), so when we reload drivers * primary->alloced_cpus_in_node in
* like hv_netvsc in SMP guest, here we're able to re-allocate * hv_process_channel_removal(), so when we
* reload drivers like hv_netvsc in SMP guest, here
* we're able to re-allocate
* bit from primary->alloced_cpus_in_node. * bit from primary->alloced_cpus_in_node.
*/ */
if (!cpumask_test_cpu(cur_cpu, if (!cpumask_test_cpu(cur_cpu,
...@@ -615,6 +621,10 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type) ...@@ -615,6 +621,10 @@ static void init_vp_index(struct vmbus_channel *channel, u16 dev_type)
cpumask_set_cpu(cur_cpu, alloced_mask); cpumask_set_cpu(cur_cpu, alloced_mask);
break; break;
} }
} else {
cpumask_set_cpu(cur_cpu, alloced_mask);
break;
}
} }
channel->target_cpu = cur_cpu; channel->target_cpu = cur_cpu;
......
...@@ -674,6 +674,11 @@ enum hv_signal_policy { ...@@ -674,6 +674,11 @@ enum hv_signal_policy {
HV_SIGNAL_POLICY_EXPLICIT, HV_SIGNAL_POLICY_EXPLICIT,
}; };
enum hv_numa_policy {
HV_BALANCED = 0,
HV_LOCALIZED,
};
enum vmbus_device_type { enum vmbus_device_type {
HV_IDE = 0, HV_IDE = 0,
HV_SCSI, HV_SCSI,
...@@ -876,6 +881,18 @@ struct vmbus_channel { ...@@ -876,6 +881,18 @@ struct vmbus_channel {
*/ */
bool low_latency; bool low_latency;
/*
* NUMA distribution policy:
* We support teo policies:
* 1) Balanced: Here all performance critical channels are
* distributed evenly amongst all the NUMA nodes.
* This policy will be the default policy.
* 2) Localized: All channels of a given instance of a
* performance critical service will be assigned CPUs
* within a selected NUMA node.
*/
enum hv_numa_policy affinity_policy;
}; };
static inline void set_channel_lock_state(struct vmbus_channel *c, bool state) static inline void set_channel_lock_state(struct vmbus_channel *c, bool state)
...@@ -895,6 +912,12 @@ static inline void set_channel_signal_state(struct vmbus_channel *c, ...@@ -895,6 +912,12 @@ static inline void set_channel_signal_state(struct vmbus_channel *c,
c->signal_policy = policy; c->signal_policy = policy;
} }
static inline void set_channel_affinity_state(struct vmbus_channel *c,
enum hv_numa_policy policy)
{
c->affinity_policy = policy;
}
static inline void set_channel_read_state(struct vmbus_channel *c, bool state) static inline void set_channel_read_state(struct vmbus_channel *c, bool state)
{ {
c->batched_reading = state; c->batched_reading = state;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment