Skip to content

Commit d4ebfca

Browse files
yuqijin16sfrothwell
authored andcommitted
lib: optimize cpumask_local_spread()
In multi-processor and NUMA system, I/O driver will find cpu cores that which shall be bound IRQ. When cpu cores in the local numa have been used up, it is better to find the node closest to the local numa node for performance, instead of choosing any online cpu immediately. On arm64 or x86 platform that has 2-sockets and 4-NUMA nodes, if the network card is located in node2 of socket1, while the number queues of network card is greater than the number of cores of node2, when all cores of node2 has been bound to the queues, the remaining queues will be bound to the cores of node0. That's not friendly to performance. Let's improve it and find the nearest unused node through NUMA distance for the non-local NUMA nodes. On Huawei Kunpeng 920 server, there are 4 NUMA node(0 - 3) in the 2-cpu system(0 - 1). The topology of this server is as follows: available: 4 nodes (0-3) node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 node 0 size: 63379 MB node 0 free: 61899 MB node 1 cpus: 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 node 1 size: 64509 MB node 1 free: 63942 MB node 2 cpus: 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 node 2 size: 64509 MB node 2 free: 63056 MB node 3 cpus: 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 node 3 size: 63997 MB node 3 free: 63420 MB node distances: node 0 1 2 3 0: 10 16 32 33 1: 16 10 25 32 2: 32 25 10 16 3: 33 32 16 10 We perform PS (parameter server) business test, the behavior of the service is that the client initiates a request through the network card, the server responds to the request after calculation. When two PS processes run on node2 and node3 separately and the network card is located on 'node2' which is in cpu1, the performance of node2 (26W QPS) and node3 (22W QPS) is different. It is better that the NIC queues are bound to the cpu1 cores in turn, then XPS will also be properly initialized, while cpumask_local_spread only considers the local node. When the number of NIC queues exceeds the number of cores in the local node, it returns to the online core directly. So when PS runs on node3 sending a calculated request, the performance is not as good as the node2. The IRQ from 369-392 will be bound from NUMA node0 to NUMA node3 with this patch, before the patch: Euler:/sys/bus/pci # cat /proc/irq/369/smp_affinity_list 0 Euler:/sys/bus/pci # cat /proc/irq/370/smp_affinity_list 1 ... Euler:/sys/bus/pci # cat /proc/irq/391/smp_affinity_list 22 Euler:/sys/bus/pci # cat /proc/irq/392/smp_affinity_list 23 After the patch: Euler:/sys/bus/pci # cat /proc/irq/369/smp_affinity_list 72 Euler:/sys/bus/pci # cat /proc/irq/370/smp_affinity_list 73 ... Euler:/sys/bus/pci # cat /proc/irq/391/smp_affinity_list 94 Euler:/sys/bus/pci # cat /proc/irq/392/smp_affinity_list 95 So the performance of the node3 is the same as node2 that is 26W QPS when the network card is still in 'node2' with the patch. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: Yuqi Jin <[email protected]> Signed-off-by: Shaokun Zhang <[email protected]> Cc: Dave Hansen <[email protected]> Cc: Rusty Russell <[email protected]> Cc: Juergen Gross <[email protected]> Cc: Paul Burton <[email protected]> Cc: Michal Hocko <[email protected]> Cc: Michael Ellerman <[email protected]> Cc: Mike Rapoport <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Stephen Rothwell <[email protected]>
1 parent a221b5f commit d4ebfca

File tree

1 file changed

+47
-13
lines changed

1 file changed

+47
-13
lines changed

lib/cpumask.c

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -193,20 +193,47 @@ void __init free_bootmem_cpumask_var(cpumask_var_t mask)
193193
}
194194
#endif
195195

196+
static int find_nearest_node(int node, bool *used)
197+
{
198+
int i, min_dist, node_id = -1;
199+
200+
/* Choose the first unused node to compare */
201+
for (i = 0; i < nr_node_ids; i++) {
202+
if (used[i] == false) {
203+
min_dist = node_distance(node, i);
204+
node_id = i;
205+
break;
206+
}
207+
}
208+
209+
/* Compare and return the nearest node */
210+
for (i = 0; i < nr_node_ids; i++) {
211+
if (node_distance(node, i) < min_dist && used[i] == false) {
212+
min_dist = node_distance(node, i);
213+
node_id = i;
214+
}
215+
}
216+
217+
return node_id;
218+
}
219+
196220
/**
197221
* cpumask_local_spread - select the i'th cpu with local numa cpu's first
198222
* @i: index number
199223
* @node: local numa_node
200224
*
201225
* This function selects an online CPU according to a numa aware policy;
202-
* local cpus are returned first, followed by non-local ones, then it
203-
* wraps around.
226+
* local cpus are returned first, followed by the next one which is the
227+
* nearest unused NUMA node based on NUMA distance, then it wraps around.
204228
*
205229
* It's not very efficient, but useful for setup.
206230
*/
207231
unsigned int cpumask_local_spread(unsigned int i, int node)
208232
{
209-
int cpu, hk_flags;
233+
static DEFINE_SPINLOCK(spread_lock);
234+
static bool used[MAX_NUMNODES];
235+
unsigned long flags;
236+
int cpu, hk_flags, j, id;
210237
const struct cpumask *mask;
211238

212239
hk_flags = HK_FLAG_DOMAIN | HK_FLAG_MANAGED_IRQ;
@@ -220,20 +247,27 @@ unsigned int cpumask_local_spread(unsigned int i, int node)
220247
return cpu;
221248
}
222249
} else {
223-
/* NUMA first. */
224-
for_each_cpu_and(cpu, cpumask_of_node(node), mask) {
225-
if (i-- == 0)
226-
return cpu;
250+
spin_lock_irqsave(&spread_lock, flags);
251+
memset(used, 0, nr_node_ids * sizeof(bool));
252+
/* select node according to the distance from local node */
253+
for (j = 0; j < nr_node_ids; j++) {
254+
id = find_nearest_node(node, used);
255+
if (id < 0)
256+
break;
257+
258+
for_each_cpu_and(cpu, cpumask_of_node(id), mask)
259+
if (i-- == 0) {
260+
spin_unlock_irqrestore(&spread_lock,
261+
flags);
262+
return cpu;
263+
}
264+
used[id] = true;
227265
}
266+
spin_unlock_irqrestore(&spread_lock, flags);
228267

229-
for_each_cpu(cpu, mask) {
230-
/* Skip NUMA nodes, done above. */
231-
if (cpumask_test_cpu(cpu, cpumask_of_node(node)))
232-
continue;
233-
268+
for_each_cpu(cpu, mask)
234269
if (i-- == 0)
235270
return cpu;
236-
}
237271
}
238272
BUG();
239273
}

0 commit comments

Comments
 (0)