udev/net: implement IRQAffinityPolicy=spread with topology awareness
Implement the spread policy for IRQ affinity distribution using a topology-aware algorithm. The algorithm: 1. Discovers CPU topology from sysfs (NUMA node, package, die/L3, core) 2. Groups CPUs by L3 cache domain (die) with equidistant ordering 3. Round-robins across dies, spreading IRQs across the system 4. Uses first hyperthread of each core before second hyperthreads 5. Applies IRQ affinity via /proc/irq/<n>/smp_affinity When there are more IRQs than CPUs, queues wrap around using round-robin.
This commit is contained in:
@@ -154,6 +154,63 @@ static int numa_max_node(void) {
|
||||
return max_node;
|
||||
}
|
||||
|
||||
int numa_get_node_from_cpu(unsigned cpu, unsigned *ret) {
|
||||
_cleanup_closedir_ DIR *d = NULL;
|
||||
int r;
|
||||
|
||||
assert(ret);
|
||||
|
||||
d = opendir("/sys/devices/system/node");
|
||||
if (!d)
|
||||
return -errno;
|
||||
|
||||
FOREACH_DIRENT(de, d, break) {
|
||||
char p[STRLEN("/sys/devices/system/node/node/cpulist") + DECIMAL_STR_MAX(unsigned) + 1];
|
||||
_cleanup_(cpu_set_done) CPUSet cpus = {};
|
||||
_cleanup_free_ char *cpulist = NULL;
|
||||
const char *n;
|
||||
unsigned node;
|
||||
|
||||
if (de->d_type != DT_DIR)
|
||||
continue;
|
||||
|
||||
n = startswith(de->d_name, "node");
|
||||
if (!n)
|
||||
continue;
|
||||
|
||||
r = safe_atou(n, &node);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to parse node number %s to unsigned, ignoring: %m", n);
|
||||
continue;
|
||||
}
|
||||
|
||||
xsprintf(p, "/sys/devices/system/node/node%u/cpulist", node);
|
||||
|
||||
r = read_virtual_file(p, SIZE_MAX, &cpulist, /* ret_size= */ NULL);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to read %s, ignoring: %m", p);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = parse_cpu_set(cpulist, &cpus);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to parse cpu set %s, ignoring: %m", cpulist);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (CPU_ISSET_S(cpu, cpus.allocated, cpus.set)) {
|
||||
*ret = node;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* CPU not found in any NUMA node, assume node 0 */
|
||||
log_debug("CPU %u not found in any NUMA node, assuming node 0.", cpu);
|
||||
*ret = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int numa_mask_add_all(CPUSet *mask) {
|
||||
int m;
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ static inline void numa_policy_reset(NUMAPolicy *p) {
|
||||
int apply_numa_policy(const NUMAPolicy *policy);
|
||||
int numa_to_cpu_set(const NUMAPolicy *policy, CPUSet *ret);
|
||||
|
||||
int numa_get_node_from_cpu(unsigned cpu, unsigned *ret);
|
||||
|
||||
int numa_mask_add_all(CPUSet *mask);
|
||||
|
||||
DECLARE_STRING_TABLE_LOOKUP(mpol, int);
|
||||
|
||||
Reference in New Issue
Block a user