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.
93 lines
2.9 KiB
C
93 lines
2.9 KiB
C
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include "alloc-util.h"
|
|
#include "sort-util.h"
|
|
|
|
/* hey glibc, APIs with callbacks without a user pointer are so useless */
|
|
void *xbsearch_r(const void *key, const void *base, size_t nmemb, size_t size,
|
|
comparison_userdata_fn_t compar, void *arg) {
|
|
size_t l, u, idx;
|
|
const void *p;
|
|
int comparison;
|
|
|
|
assert(!size_multiply_overflow(nmemb, size));
|
|
|
|
l = 0;
|
|
u = nmemb;
|
|
while (l < u) {
|
|
idx = (l + u) / 2;
|
|
p = (const uint8_t*) base + idx * size;
|
|
comparison = compar(key, p, arg);
|
|
if (comparison < 0)
|
|
u = idx;
|
|
else if (comparison > 0)
|
|
l = idx + 1;
|
|
else
|
|
return (void *)p;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void* bsearch_safe_internal(const void *key, const void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
|
|
/**
|
|
* Normal bsearch requires base to be nonnull. Here were require
|
|
* that only if nmemb > 0.
|
|
*/
|
|
|
|
if (nmemb <= 0)
|
|
return NULL;
|
|
|
|
assert(base);
|
|
return (void*) bsearch(key, base, nmemb, size, compar);
|
|
}
|
|
|
|
void qsort_safe(void *base, size_t nmemb, size_t size, comparison_fn_t compar) {
|
|
/**
|
|
* Normal qsort requires base to be nonnull. Here were require
|
|
* that only if nmemb > 0.
|
|
*/
|
|
|
|
if (nmemb <= 1)
|
|
return;
|
|
|
|
assert(base);
|
|
qsort(base, nmemb, size, compar);
|
|
}
|
|
|
|
void qsort_r_safe(void *base, size_t nmemb, size_t size, comparison_userdata_fn_t compar, void *userdata) {
|
|
if (nmemb <= 1)
|
|
return;
|
|
|
|
assert(base);
|
|
qsort_r(base, nmemb, size, compar, userdata);
|
|
}
|
|
|
|
int cmp_int(const int *a, const int *b) {
|
|
/* This is called from qsort()s inner loops. Correctly implemented qsort will never pass NULL so we
|
|
just suppress the check via POINTER_MAY_BE_NULL instead of assert() to avoid the runtime cost. */
|
|
POINTER_MAY_BE_NULL(a);
|
|
POINTER_MAY_BE_NULL(b);
|
|
|
|
return CMP(*a, *b);
|
|
}
|
|
|
|
int cmp_uint16(const uint16_t *a, const uint16_t *b) {
|
|
/* This is called from qsort()s inner loops. Correctly implemented qsort will never pass NULL so we
|
|
just suppress the check via POINTER_MAY_BE_NULL instead of assert() to avoid the runtime cost. */
|
|
POINTER_MAY_BE_NULL(a);
|
|
POINTER_MAY_BE_NULL(b);
|
|
|
|
return CMP(*a, *b);
|
|
}
|
|
|
|
int cmp_unsigned(const unsigned *a, const unsigned *b) {
|
|
/* This is called from qsort()s inner loops. Correctly implemented qsort will never pass NULL so we
|
|
just suppress the check via POINTER_MAY_BE_NULL instead of assert() to avoid the runtime cost. */
|
|
POINTER_MAY_BE_NULL(a);
|
|
POINTER_MAY_BE_NULL(b);
|
|
|
|
return CMP(*a, *b);
|
|
}
|