count_cpu_cores__() allocates the CPU affinity mask based on the
number of online CPUs returned by sysconf(_SC_NPROCESSORS_ONLN).
On Linux, sched_getaffinity() returns EINVAL if the supplied mask is
smaller than the kernel affinity mask.

This can happen when the possible CPU range exceeds the online CPU
count, for example on systems or VMs configured for CPU hotplug.  In
that case OVS falls back to the online CPU count and may ignore the
process affinity mask when sizing handler threads.

Retry sched_getaffinity() with larger dynamically allocated CPU masks
on EINVAL, while preserving the existing fallback behavior for other
failures.  This follows the sched_getaffinity(2) guidance for large
CPU affinity masks: probe with dynamically allocated masks of increasing
size until the call no longer fails with EINVAL.

Testing:
- Ran make check TESTSUITEFLAGS=-j4: 2751 tests passed, 6 skipped.
- Built an Open vSwitch RPM with this patch.
- Installed the RPM on a Rocky Linux 9.7 VM running Linux 6.6.119,
  with CPUs 0-159 possible, CPUs 0-19 online, and ovs-vswitchd
  limited to CPUs 6-9; verified that per-cpu handler threads dropped
  from 20 to 5 after restart, following the process affinity mask.

Assisted-by: GPT-5, Codex
Signed-off-by: zwtop <[email protected]>
---
 lib/ovs-thread.c | 28 ++++++++++++++++++++++++----
 1 file changed, 24 insertions(+), 4 deletions(-)

diff --git a/lib/ovs-thread.c b/lib/ovs-thread.c
index 243791de8..141721e8b 100644
--- a/lib/ovs-thread.c
+++ b/lib/ovs-thread.c
@@ -647,15 +647,35 @@ count_cpu_cores__(void)
 #endif
 #ifdef __linux__
     if (n_cores > 0) {
-        cpu_set_t *set = CPU_ALLOC(n_cores);
-
-        if (set) {
-            size_t size = CPU_ALLOC_SIZE(n_cores);
+        enum { CPU_ALLOC_INITIAL = 1024 };
+        enum { CPU_ALLOC_MAX = 1024 << 8 };
+        size_t n_alloc = MAX((size_t) n_cores, (size_t) CPU_ALLOC_INITIAL);
+        size_t max_n_alloc = MAX(n_alloc, (size_t) CPU_ALLOC_MAX);
+
+        /* sched_getaffinity() returns EINVAL when 'size' is smaller than the
+         * kernel affinity mask.  This can happen when the possible CPU range
+         * exceeds the online CPU count. */
+        for (;;) {
+            cpu_set_t *set = CPU_ALLOC(n_alloc);
+            size_t size = CPU_ALLOC_SIZE(n_alloc);
+
+            if (!set) {
+                break;
+            }
+            CPU_ZERO_S(size, set);
 
             if (!sched_getaffinity(0, size, set)) {
                 n_cores = CPU_COUNT_S(size, set);
+                CPU_FREE(set);
+                break;
             }
+
+            int error = errno;
             CPU_FREE(set);
+            if (error != EINVAL || n_alloc >= max_n_alloc) {
+                break;
+            }
+            n_alloc = MIN(n_alloc << 2, max_n_alloc);
         }
     }
 #endif
-- 
2.47.3

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to