Blame SOURCES/0272-Rework-cpu-affinity-parsing.patch

ddca0b
From 61e5aed87f1b82a51c6ea8ccde96805cb63e5b15 Mon Sep 17 00:00:00 2001
ddca0b
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
ddca0b
Date: Tue, 21 May 2019 08:45:19 +0200
ddca0b
Subject: [PATCH] Rework cpu affinity parsing
ddca0b
ddca0b
The CPU_SET_S api is pretty bad. In particular, it has a parameter for the size
ddca0b
of the array, but operations which take two (CPU_EQUAL_S) or even three arrays
ddca0b
(CPU_{AND,OR,XOR}_S) still take just one size. This means that all arrays must
ddca0b
be of the same size, or buffer overruns will occur. This is exactly what our
ddca0b
code would do, if it received an array of unexpected size over the network.
ddca0b
("Unexpected" here means anything different from what cpu_set_malloc() detects
ddca0b
as the "right" size.)
ddca0b
ddca0b
Let's rework this, and store the size in bytes of the allocated storage area.
ddca0b
ddca0b
The code will now parse any number up to 8191, independently of what the current
ddca0b
kernel supports. This matches the kernel maximum setting for any architecture,
ddca0b
to make things more portable.
ddca0b
ddca0b
Fixes #12605.
ddca0b
ddca0b
(cherry picked from commit 0985c7c4e22c8dbbea4398cf3453da45ebf63800)
ddca0b
ddca0b
Related: #1734787
ddca0b
---
ddca0b
 src/basic/cpu-set-util.c     | 133 +++++++++++++++++++++-----
ddca0b
 src/basic/cpu-set-util.h     |  47 ++++++---
ddca0b
 src/core/dbus-execute.c      |  35 ++-----
ddca0b
 src/core/execute.c           |  12 +--
ddca0b
 src/core/execute.h           |   4 +-
ddca0b
 src/core/load-fragment.c     |  31 +-----
ddca0b
 src/core/main.c              |  14 +--
ddca0b
 src/nspawn/nspawn-settings.c |  33 +------
ddca0b
 src/nspawn/nspawn-settings.h |   4 +-
ddca0b
 src/nspawn/nspawn.c          |  29 +++---
ddca0b
 src/shared/bus-unit-util.c   |   4 +-
ddca0b
 src/test/test-cpu-set-util.c | 179 +++++++++++++++++++----------------
ddca0b
 src/test/test-sizeof.c       |   3 +
ddca0b
 13 files changed, 286 insertions(+), 242 deletions(-)
ddca0b
ddca0b
diff --git a/src/basic/cpu-set-util.c b/src/basic/cpu-set-util.c
ddca0b
index 8f24a2601a..fe440f6381 100644
ddca0b
--- a/src/basic/cpu-set-util.c
ddca0b
+++ b/src/basic/cpu-set-util.c
ddca0b
@@ -15,14 +15,15 @@
ddca0b
 #include "macro.h"
ddca0b
 #include "parse-util.h"
ddca0b
 #include "string-util.h"
ddca0b
+#include "util.h"
ddca0b
 
ddca0b
-char* cpu_set_to_string(const cpu_set_t *set, size_t setsize) {
ddca0b
+char* cpu_set_to_string(const CPUSet *a) {
ddca0b
         _cleanup_free_ char *str = NULL;
ddca0b
         size_t allocated = 0, len = 0;
ddca0b
         int i, r;
ddca0b
 
ddca0b
-        for (i = 0; (size_t) i < setsize * 8; i++) {
ddca0b
-                if (!CPU_ISSET_S(i, setsize, set))
ddca0b
+        for (i = 0; (size_t) i < a->allocated * 8; i++) {
ddca0b
+                if (!CPU_ISSET_S(i, a->allocated, a->set))
ddca0b
                         continue;
ddca0b
 
ddca0b
                 if (!GREEDY_REALLOC(str, allocated, len + 1 + DECIMAL_STR_MAX(int)))
ddca0b
@@ -65,24 +66,74 @@ cpu_set_t* cpu_set_malloc(unsigned *ncpus) {
ddca0b
         }
ddca0b
 }
ddca0b
 
ddca0b
-int parse_cpu_set_internal(
ddca0b
+static int cpu_set_realloc(CPUSet *cpu_set, unsigned ncpus) {
ddca0b
+        size_t need;
ddca0b
+
ddca0b
+        assert(cpu_set);
ddca0b
+
ddca0b
+        need = CPU_ALLOC_SIZE(ncpus);
ddca0b
+        if (need > cpu_set->allocated) {
ddca0b
+                cpu_set_t *t;
ddca0b
+
ddca0b
+                t = realloc(cpu_set->set, need);
ddca0b
+                if (!t)
ddca0b
+                        return -ENOMEM;
ddca0b
+
ddca0b
+                memzero((uint8_t*) t + cpu_set->allocated, need - cpu_set->allocated);
ddca0b
+
ddca0b
+                cpu_set->set = t;
ddca0b
+                cpu_set->allocated = need;
ddca0b
+        }
ddca0b
+
ddca0b
+        return 0;
ddca0b
+}
ddca0b
+
ddca0b
+static int cpu_set_add(CPUSet *cpu_set, unsigned cpu) {
ddca0b
+        int r;
ddca0b
+
ddca0b
+        if (cpu >= 8192)
ddca0b
+                /* As of kernel 5.1, CONFIG_NR_CPUS can be set to 8192 on PowerPC */
ddca0b
+                return -ERANGE;
ddca0b
+
ddca0b
+        r = cpu_set_realloc(cpu_set, cpu + 1);
ddca0b
+        if (r < 0)
ddca0b
+                return r;
ddca0b
+
ddca0b
+        CPU_SET_S(cpu, cpu_set->allocated, cpu_set->set);
ddca0b
+        return 0;
ddca0b
+}
ddca0b
+
ddca0b
+int cpu_set_add_all(CPUSet *a, const CPUSet *b) {
ddca0b
+        int r;
ddca0b
+
ddca0b
+        /* Do this backwards, so if we fail, we fail before changing anything. */
ddca0b
+        for (unsigned cpu_p1 = b->allocated * 8; cpu_p1 > 0; cpu_p1--)
ddca0b
+                if (CPU_ISSET_S(cpu_p1 - 1, b->allocated, b->set)) {
ddca0b
+                        r = cpu_set_add(a, cpu_p1 - 1);
ddca0b
+                        if (r < 0)
ddca0b
+                                return r;
ddca0b
+                }
ddca0b
+
ddca0b
+        return 0;
ddca0b
+}
ddca0b
+
ddca0b
+int parse_cpu_set_full(
ddca0b
                 const char *rvalue,
ddca0b
-                cpu_set_t **cpu_set,
ddca0b
+                CPUSet *cpu_set,
ddca0b
                 bool warn,
ddca0b
                 const char *unit,
ddca0b
                 const char *filename,
ddca0b
                 unsigned line,
ddca0b
                 const char *lvalue) {
ddca0b
 
ddca0b
-        _cleanup_cpu_free_ cpu_set_t *c = NULL;
ddca0b
+        _cleanup_(cpu_set_reset) CPUSet c = {};
ddca0b
         const char *p = rvalue;
ddca0b
-        unsigned ncpus = 0;
ddca0b
 
ddca0b
-        assert(rvalue);
ddca0b
+        assert(p);
ddca0b
 
ddca0b
         for (;;) {
ddca0b
                 _cleanup_free_ char *word = NULL;
ddca0b
-                unsigned cpu, cpu_lower, cpu_upper;
ddca0b
+                unsigned cpu_lower, cpu_upper;
ddca0b
                 int r;
ddca0b
 
ddca0b
                 r = extract_first_word(&p, &word, WHITESPACE ",", EXTRACT_QUOTES);
ddca0b
@@ -93,31 +144,63 @@ int parse_cpu_set_internal(
ddca0b
                 if (r == 0)
ddca0b
                         break;
ddca0b
 
ddca0b
-                if (!c) {
ddca0b
-                        c = cpu_set_malloc(&ncpus);
ddca0b
-                        if (!c)
ddca0b
-                                return warn ? log_oom() : -ENOMEM;
ddca0b
-                }
ddca0b
-
ddca0b
                 r = parse_range(word, &cpu_lower, &cpu_upper);
ddca0b
                 if (r < 0)
ddca0b
                         return warn ? log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU affinity '%s'", word) : r;
ddca0b
-                if (cpu_lower >= ncpus || cpu_upper >= ncpus)
ddca0b
-                        return warn ? log_syntax(unit, LOG_ERR, filename, line, EINVAL, "CPU out of range '%s' ncpus is %u", word, ncpus) : -EINVAL;
ddca0b
 
ddca0b
                 if (cpu_lower > cpu_upper) {
ddca0b
                         if (warn)
ddca0b
-                                log_syntax(unit, LOG_WARNING, filename, line, 0, "Range '%s' is invalid, %u > %u, ignoring", word, cpu_lower, cpu_upper);
ddca0b
-                        continue;
ddca0b
+                                log_syntax(unit, LOG_WARNING, filename, line, 0, "Range '%s' is invalid, %u > %u, ignoring.",
ddca0b
+                                           word, cpu_lower, cpu_upper);
ddca0b
+
ddca0b
+                        /* Make sure something is allocated, to distinguish this from the empty case */
ddca0b
+                        r = cpu_set_realloc(&c, 1);
ddca0b
+                        if (r < 0)
ddca0b
+                                return r;
ddca0b
                 }
ddca0b
 
ddca0b
-                for (cpu = cpu_lower; cpu <= cpu_upper; cpu++)
ddca0b
-                        CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
ddca0b
+                for (unsigned cpu_p1 = MIN(cpu_upper, UINT_MAX-1) + 1; cpu_p1 > cpu_lower; cpu_p1--) {
ddca0b
+                        r = cpu_set_add(&c, cpu_p1 - 1);
ddca0b
+                        if (r < 0)
ddca0b
+                                return warn ? log_syntax(unit, LOG_ERR, filename, line, r,
ddca0b
+                                                         "Cannot add CPU %u to set: %m", cpu_p1 - 1) : r;
ddca0b
+                }
ddca0b
         }
ddca0b
 
ddca0b
-        /* On success, sets *cpu_set and returns ncpus for the system. */
ddca0b
-        if (c)
ddca0b
-                *cpu_set = TAKE_PTR(c);
ddca0b
+        /* On success, transfer ownership to the output variable */
ddca0b
+        *cpu_set = c;
ddca0b
+        c = (CPUSet) {};
ddca0b
+
ddca0b
+        return 0;
ddca0b
+}
ddca0b
+
ddca0b
+int parse_cpu_set_extend(
ddca0b
+                const char *rvalue,
ddca0b
+                CPUSet *old,
ddca0b
+                bool warn,
ddca0b
+                const char *unit,
ddca0b
+                const char *filename,
ddca0b
+                unsigned line,
ddca0b
+                const char *lvalue) {
ddca0b
+
ddca0b
+        _cleanup_(cpu_set_reset) CPUSet cpuset = {};
ddca0b
+        int r;
ddca0b
+
ddca0b
+        r = parse_cpu_set_full(rvalue, &cpuset, true, unit, filename, line, lvalue);
ddca0b
+        if (r < 0)
ddca0b
+                return r;
ddca0b
+
ddca0b
+        if (!cpuset.set) {
ddca0b
+                /* An empty assignment resets the CPU list */
ddca0b
+                cpu_set_reset(old);
ddca0b
+                return 0;
ddca0b
+        }
ddca0b
+
ddca0b
+        if (!old->set) {
ddca0b
+                *old = cpuset;
ddca0b
+                cpuset = (CPUSet) {};
ddca0b
+                return 0;
ddca0b
+        }
ddca0b
 
ddca0b
-        return (int) ncpus;
ddca0b
+        return cpu_set_add_all(old, &cpuset);
ddca0b
 }
ddca0b
diff --git a/src/basic/cpu-set-util.h b/src/basic/cpu-set-util.h
ddca0b
index 20612a8876..eb31b362fe 100644
ddca0b
--- a/src/basic/cpu-set-util.h
ddca0b
+++ b/src/basic/cpu-set-util.h
ddca0b
@@ -12,23 +12,40 @@
ddca0b
 DEFINE_TRIVIAL_CLEANUP_FUNC(cpu_set_t*, CPU_FREE);
ddca0b
 #define _cleanup_cpu_free_ _cleanup_(CPU_FREEp)
ddca0b
 
ddca0b
-static inline cpu_set_t* cpu_set_mfree(cpu_set_t *p) {
ddca0b
-        if (p)
ddca0b
-                CPU_FREE(p);
ddca0b
-        return NULL;
ddca0b
-}
ddca0b
-
ddca0b
 cpu_set_t* cpu_set_malloc(unsigned *ncpus);
ddca0b
 
ddca0b
-char* cpu_set_to_string(const cpu_set_t *set, size_t setsize);
ddca0b
-int parse_cpu_set_internal(const char *rvalue, cpu_set_t **cpu_set, bool warn, const char *unit, const char *filename, unsigned line, const char *lvalue);
ddca0b
-
ddca0b
-static inline int parse_cpu_set_and_warn(const char *rvalue, cpu_set_t **cpu_set, const char *unit, const char *filename, unsigned line, const char *lvalue) {
ddca0b
-        assert(lvalue);
ddca0b
-
ddca0b
-        return parse_cpu_set_internal(rvalue, cpu_set, true, unit, filename, line, lvalue);
ddca0b
+/* This wraps the libc interface with a variable to keep the allocated size. */
ddca0b
+typedef struct CPUSet {
ddca0b
+        cpu_set_t *set;
ddca0b
+        size_t allocated; /* in bytes */
ddca0b
+} CPUSet;
ddca0b
+
ddca0b
+static inline void cpu_set_reset(CPUSet *a) {
ddca0b
+        assert((a->allocated > 0) == !!a->set);
ddca0b
+        if (a->set)
ddca0b
+                CPU_FREE(a->set);
ddca0b
+        *a = (CPUSet) {};
ddca0b
 }
ddca0b
 
ddca0b
-static inline int parse_cpu_set(const char *rvalue, cpu_set_t **cpu_set){
ddca0b
-        return parse_cpu_set_internal(rvalue, cpu_set, false, NULL, NULL, 0, NULL);
ddca0b
+int cpu_set_add_all(CPUSet *a, const CPUSet *b);
ddca0b
+
ddca0b
+char* cpu_set_to_string(const CPUSet *a);
ddca0b
+int parse_cpu_set_full(
ddca0b
+                const char *rvalue,
ddca0b
+                CPUSet *cpu_set,
ddca0b
+                bool warn,
ddca0b
+                const char *unit,
ddca0b
+                const char *filename, unsigned line,
ddca0b
+                const char *lvalue);
ddca0b
+int parse_cpu_set_extend(
ddca0b
+                const char *rvalue,
ddca0b
+                CPUSet *old,
ddca0b
+                bool warn,
ddca0b
+                const char *unit,
ddca0b
+                const char *filename,
ddca0b
+                unsigned line,
ddca0b
+                const char *lvalue);
ddca0b
+
ddca0b
+static inline int parse_cpu_set(const char *rvalue, CPUSet *cpu_set){
ddca0b
+        return parse_cpu_set_full(rvalue, cpu_set, false, NULL, NULL, 0, NULL);
ddca0b
 }
ddca0b
diff --git a/src/core/dbus-execute.c b/src/core/dbus-execute.c
ddca0b
index d9f4445745..08946627e3 100644
ddca0b
--- a/src/core/dbus-execute.c
ddca0b
+++ b/src/core/dbus-execute.c
ddca0b
@@ -220,7 +220,7 @@ static int property_get_cpu_affinity(
ddca0b
         assert(reply);
ddca0b
         assert(c);
ddca0b
 
ddca0b
-        return sd_bus_message_append_array(reply, 'y', c->cpuset, CPU_ALLOC_SIZE(c->cpuset_ncpus));
ddca0b
+        return sd_bus_message_append_array(reply, 'y', c->cpu_set.set, c->cpu_set.allocated);
ddca0b
 }
ddca0b
 
ddca0b
 static int property_get_timer_slack_nsec(
ddca0b
@@ -1560,37 +1560,22 @@ int bus_exec_context_set_transient_property(
ddca0b
 
ddca0b
                 if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
ddca0b
                         if (n == 0) {
ddca0b
-                                c->cpuset = cpu_set_mfree(c->cpuset);
ddca0b
-                                c->cpuset_ncpus = 0;
ddca0b
+                                cpu_set_reset(&c->cpu_set);
ddca0b
                                 unit_write_settingf(u, flags, name, "%s=", name);
ddca0b
                         } else {
ddca0b
                                 _cleanup_free_ char *str = NULL;
ddca0b
-                                size_t ncpus;
ddca0b
+                                const CPUSet set = {(cpu_set_t*) a, n};
ddca0b
 
ddca0b
-                                str = cpu_set_to_string(a, n);
ddca0b
+                                str = cpu_set_to_string(&set);
ddca0b
                                 if (!str)
ddca0b
                                         return -ENOMEM;
ddca0b
 
ddca0b
-                                ncpus = CPU_SIZE_TO_NUM(n);
ddca0b
-
ddca0b
-                                if (!c->cpuset || c->cpuset_ncpus < ncpus) {
ddca0b
-                                        cpu_set_t *cpuset;
ddca0b
-
ddca0b
-                                        cpuset = CPU_ALLOC(ncpus);
ddca0b
-                                        if (!cpuset)
ddca0b
-                                                return -ENOMEM;
ddca0b
-
ddca0b
-                                        CPU_ZERO_S(n, cpuset);
ddca0b
-                                        if (c->cpuset) {
ddca0b
-                                                CPU_OR_S(CPU_ALLOC_SIZE(c->cpuset_ncpus), cpuset, c->cpuset, (cpu_set_t*) a);
ddca0b
-                                                CPU_FREE(c->cpuset);
ddca0b
-                                        } else
ddca0b
-                                                CPU_OR_S(n, cpuset, cpuset, (cpu_set_t*) a);
ddca0b
-
ddca0b
-                                        c->cpuset = cpuset;
ddca0b
-                                        c->cpuset_ncpus = ncpus;
ddca0b
-                                } else
ddca0b
-                                        CPU_OR_S(n, c->cpuset, c->cpuset, (cpu_set_t*) a);
ddca0b
+                                /* We forego any optimizations here, and always create the structure using
ddca0b
+                                 * cpu_set_add_all(), because we don't want to care if the existing size we
ddca0b
+                                 * got over dbus is appropriate. */
ddca0b
+                                r = cpu_set_add_all(&c->cpu_set, &set);
ddca0b
+                                if (r < 0)
ddca0b
+                                        return r;
ddca0b
 
ddca0b
                                 unit_write_settingf(u, flags, name, "%s=%s", name, str);
ddca0b
                         }
ddca0b
diff --git a/src/core/execute.c b/src/core/execute.c
ddca0b
index c42300a41e..22e5825905 100644
ddca0b
--- a/src/core/execute.c
ddca0b
+++ b/src/core/execute.c
ddca0b
@@ -2991,8 +2991,8 @@ static int exec_child(
ddca0b
                 }
ddca0b
         }
ddca0b
 
ddca0b
-        if (context->cpuset)
ddca0b
-                if (sched_setaffinity(0, CPU_ALLOC_SIZE(context->cpuset_ncpus), context->cpuset) < 0) {
ddca0b
+        if (context->cpu_set.set)
ddca0b
+                if (sched_setaffinity(0, context->cpu_set.allocated, context->cpu_set.set) < 0) {
ddca0b
                         *exit_status = EXIT_CPUAFFINITY;
ddca0b
                         return log_unit_error_errno(unit, errno, "Failed to set up CPU affinity: %m");
ddca0b
                 }
ddca0b
@@ -3694,7 +3694,7 @@ void exec_context_done(ExecContext *c) {
ddca0b
         c->temporary_filesystems = NULL;
ddca0b
         c->n_temporary_filesystems = 0;
ddca0b
 
ddca0b
-        c->cpuset = cpu_set_mfree(c->cpuset);
ddca0b
+        cpu_set_reset(&c->cpu_set);
ddca0b
 
ddca0b
         c->utmp_id = mfree(c->utmp_id);
ddca0b
         c->selinux_context = mfree(c->selinux_context);
ddca0b
@@ -4097,10 +4097,10 @@ void exec_context_dump(const ExecContext *c, FILE* f, const char *prefix) {
ddca0b
                         prefix, yes_no(c->cpu_sched_reset_on_fork));
ddca0b
         }
ddca0b
 
ddca0b
-        if (c->cpuset) {
ddca0b
+        if (c->cpu_set.set) {
ddca0b
                 fprintf(f, "%sCPUAffinity:", prefix);
ddca0b
-                for (i = 0; i < c->cpuset_ncpus; i++)
ddca0b
-                        if (CPU_ISSET_S(i, CPU_ALLOC_SIZE(c->cpuset_ncpus), c->cpuset))
ddca0b
+                for (i = 0; i < c->cpu_set.allocated * 8; i++)
ddca0b
+                        if (CPU_ISSET_S(i, c->cpu_set.allocated, c->cpu_set.set))
ddca0b
                                 fprintf(f, " %u", i);
ddca0b
                 fputs("\n", f);
ddca0b
         }
ddca0b
diff --git a/src/core/execute.h b/src/core/execute.h
ddca0b
index 8c91636adc..e1e7a494cd 100644
ddca0b
--- a/src/core/execute.h
ddca0b
+++ b/src/core/execute.h
ddca0b
@@ -14,6 +14,7 @@ typedef struct Manager Manager;
ddca0b
 #include <sys/capability.h>
ddca0b
 
ddca0b
 #include "cgroup-util.h"
ddca0b
+#include "cpu-set-util.h"
ddca0b
 #include "fdset.h"
ddca0b
 #include "list.h"
ddca0b
 #include "missing.h"
ddca0b
@@ -148,8 +149,7 @@ struct ExecContext {
ddca0b
         int cpu_sched_policy;
ddca0b
         int cpu_sched_priority;
ddca0b
 
ddca0b
-        cpu_set_t *cpuset;
ddca0b
-        unsigned cpuset_ncpus;
ddca0b
+        CPUSet cpu_set;
ddca0b
 
ddca0b
         ExecInput std_input;
ddca0b
         ExecOutput std_output;
ddca0b
diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c
ddca0b
index d9a5094aa0..34ae834188 100644
ddca0b
--- a/src/core/load-fragment.c
ddca0b
+++ b/src/core/load-fragment.c
ddca0b
@@ -1211,42 +1211,13 @@ int config_parse_exec_cpu_affinity(const char *unit,
ddca0b
                                    void *userdata) {
ddca0b
 
ddca0b
         ExecContext *c = data;
ddca0b
-        _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
ddca0b
-        int ncpus;
ddca0b
 
ddca0b
         assert(filename);
ddca0b
         assert(lvalue);
ddca0b
         assert(rvalue);
ddca0b
         assert(data);
ddca0b
 
ddca0b
-        ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue);
ddca0b
-        if (ncpus < 0)
ddca0b
-                return ncpus;
ddca0b
-
ddca0b
-        if (ncpus == 0) {
ddca0b
-                /* An empty assignment resets the CPU list */
ddca0b
-                c->cpuset = cpu_set_mfree(c->cpuset);
ddca0b
-                c->cpuset_ncpus = 0;
ddca0b
-                return 0;
ddca0b
-        }
ddca0b
-
ddca0b
-        if (!c->cpuset) {
ddca0b
-                c->cpuset = TAKE_PTR(cpuset);
ddca0b
-                c->cpuset_ncpus = (unsigned) ncpus;
ddca0b
-                return 0;
ddca0b
-        }
ddca0b
-
ddca0b
-        if (c->cpuset_ncpus < (unsigned) ncpus) {
ddca0b
-                CPU_OR_S(CPU_ALLOC_SIZE(c->cpuset_ncpus), cpuset, c->cpuset, cpuset);
ddca0b
-                CPU_FREE(c->cpuset);
ddca0b
-                c->cpuset = TAKE_PTR(cpuset);
ddca0b
-                c->cpuset_ncpus = (unsigned) ncpus;
ddca0b
-                return 0;
ddca0b
-        }
ddca0b
-
ddca0b
-        CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus), c->cpuset, c->cpuset, cpuset);
ddca0b
-
ddca0b
-        return 0;
ddca0b
+        return parse_cpu_set_extend(rvalue, &c->cpu_set, true, unit, filename, line, lvalue);
ddca0b
 }
ddca0b
 
ddca0b
 int config_parse_capability_set(
ddca0b
diff --git a/src/core/main.c b/src/core/main.c
ddca0b
index af7b26d6f1..e62b2756ee 100644
ddca0b
--- a/src/core/main.c
ddca0b
+++ b/src/core/main.c
ddca0b
@@ -537,16 +537,18 @@ static int config_parse_cpu_affinity2(
ddca0b
                 void *data,
ddca0b
                 void *userdata) {
ddca0b
 
ddca0b
-        _cleanup_cpu_free_ cpu_set_t *c = NULL;
ddca0b
-        int ncpus;
ddca0b
+        _cleanup_(cpu_set_reset) CPUSet c = {};
ddca0b
+        int r;
ddca0b
 
ddca0b
-        ncpus = parse_cpu_set_and_warn(rvalue, &c, unit, filename, line, lvalue);
ddca0b
-        if (ncpus < 0)
ddca0b
-                return ncpus;
ddca0b
+        r = parse_cpu_set_full(rvalue, &c, true, unit, filename, line, lvalue);
ddca0b
+        if (r < 0)
ddca0b
+                return r;
ddca0b
 
ddca0b
-        if (sched_setaffinity(0, CPU_ALLOC_SIZE(ncpus), c) < 0)
ddca0b
+        if (sched_setaffinity(0, c.allocated, c.set) < 0)
ddca0b
                 log_warning_errno(errno, "Failed to set CPU affinity: %m");
ddca0b
 
ddca0b
+        // FIXME: parsing and execution should be seperated.
ddca0b
+
ddca0b
         return 0;
ddca0b
 }
ddca0b
 
ddca0b
diff --git a/src/nspawn/nspawn-settings.c b/src/nspawn/nspawn-settings.c
ddca0b
index 62a3486952..21c24a1111 100644
ddca0b
--- a/src/nspawn/nspawn-settings.c
ddca0b
+++ b/src/nspawn/nspawn-settings.c
ddca0b
@@ -85,7 +85,7 @@ Settings* settings_free(Settings *s) {
ddca0b
         strv_free(s->syscall_blacklist);
ddca0b
         rlimit_free_all(s->rlimit);
ddca0b
         free(s->hostname);
ddca0b
-        s->cpuset = cpu_set_mfree(s->cpuset);
ddca0b
+        cpu_set_reset(&s->cpu_set);
ddca0b
 
ddca0b
         strv_free(s->network_interfaces);
ddca0b
         strv_free(s->network_macvlan);
ddca0b
@@ -687,41 +687,12 @@ int config_parse_cpu_affinity(
ddca0b
                 void *data,
ddca0b
                 void *userdata) {
ddca0b
 
ddca0b
-        _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
ddca0b
         Settings *settings = data;
ddca0b
-        int ncpus;
ddca0b
 
ddca0b
         assert(rvalue);
ddca0b
         assert(settings);
ddca0b
 
ddca0b
-        ncpus = parse_cpu_set_and_warn(rvalue, &cpuset, unit, filename, line, lvalue);
ddca0b
-        if (ncpus < 0)
ddca0b
-                return ncpus;
ddca0b
-
ddca0b
-        if (ncpus == 0) {
ddca0b
-                /* An empty assignment resets the CPU list */
ddca0b
-                settings->cpuset = cpu_set_mfree(settings->cpuset);
ddca0b
-                settings->cpuset_ncpus = 0;
ddca0b
-                return 0;
ddca0b
-        }
ddca0b
-
ddca0b
-        if (!settings->cpuset) {
ddca0b
-                settings->cpuset = TAKE_PTR(cpuset);
ddca0b
-                settings->cpuset_ncpus = (unsigned) ncpus;
ddca0b
-                return 0;
ddca0b
-        }
ddca0b
-
ddca0b
-        if (settings->cpuset_ncpus < (unsigned) ncpus) {
ddca0b
-                CPU_OR_S(CPU_ALLOC_SIZE(settings->cpuset_ncpus), cpuset, settings->cpuset, cpuset);
ddca0b
-                CPU_FREE(settings->cpuset);
ddca0b
-                settings->cpuset = TAKE_PTR(cpuset);
ddca0b
-                settings->cpuset_ncpus = (unsigned) ncpus;
ddca0b
-                return 0;
ddca0b
-        }
ddca0b
-
ddca0b
-        CPU_OR_S(CPU_ALLOC_SIZE((unsigned) ncpus), settings->cpuset, settings->cpuset, cpuset);
ddca0b
-
ddca0b
-        return 0;
ddca0b
+        return parse_cpu_set_extend(rvalue, &settings->cpu_set, true, unit, filename, line, lvalue);
ddca0b
 }
ddca0b
 
ddca0b
 DEFINE_CONFIG_PARSE_ENUM(config_parse_resolv_conf, resolv_conf_mode, ResolvConfMode, "Failed to parse resolv.conf mode");
ddca0b
diff --git a/src/nspawn/nspawn-settings.h b/src/nspawn/nspawn-settings.h
ddca0b
index d522f3cb36..da863ef11c 100644
ddca0b
--- a/src/nspawn/nspawn-settings.h
ddca0b
+++ b/src/nspawn/nspawn-settings.h
ddca0b
@@ -7,6 +7,7 @@
ddca0b
 #include "sd-id128.h"
ddca0b
 
ddca0b
 #include "conf-parser.h"
ddca0b
+#include "cpu-set-util.h"
ddca0b
 #include "macro.h"
ddca0b
 #include "nspawn-expose-ports.h"
ddca0b
 #include "nspawn-mount.h"
ddca0b
@@ -123,8 +124,7 @@ typedef struct Settings {
ddca0b
         int no_new_privileges;
ddca0b
         int oom_score_adjust;
ddca0b
         bool oom_score_adjust_set;
ddca0b
-        cpu_set_t *cpuset;
ddca0b
-        unsigned cpuset_ncpus;
ddca0b
+        CPUSet cpu_set;
ddca0b
         ResolvConfMode resolv_conf;
ddca0b
         LinkJournal link_journal;
ddca0b
         bool link_journal_try;
ddca0b
diff --git a/src/nspawn/nspawn.c b/src/nspawn/nspawn.c
ddca0b
index b40411dcd0..08255b5724 100644
ddca0b
--- a/src/nspawn/nspawn.c
ddca0b
+++ b/src/nspawn/nspawn.c
ddca0b
@@ -199,8 +199,7 @@ static struct rlimit *arg_rlimit[_RLIMIT_MAX] = {};
ddca0b
 static bool arg_no_new_privileges = false;
ddca0b
 static int arg_oom_score_adjust = 0;
ddca0b
 static bool arg_oom_score_adjust_set = false;
ddca0b
-static cpu_set_t *arg_cpuset = NULL;
ddca0b
-static unsigned arg_cpuset_ncpus = 0;
ddca0b
+static CPUSet arg_cpu_set = {};
ddca0b
 static ResolvConfMode arg_resolv_conf = RESOLV_CONF_AUTO;
ddca0b
 static TimezoneMode arg_timezone = TIMEZONE_AUTO;
ddca0b
 
ddca0b
@@ -1186,17 +1185,14 @@ static int parse_argv(int argc, char *argv[]) {
ddca0b
                         break;
ddca0b
 
ddca0b
                 case ARG_CPU_AFFINITY: {
ddca0b
-                        _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
ddca0b
+                        CPUSet cpuset;
ddca0b
 
ddca0b
                         r = parse_cpu_set(optarg, &cpuset);
ddca0b
                         if (r < 0)
ddca0b
-                                return log_error_errno(r, "Failed to parse CPU affinity mask: %s", optarg);
ddca0b
+                                return log_error_errno(r, "Failed to parse CPU affinity mask %s: %m", optarg);
ddca0b
 
ddca0b
-                        if (arg_cpuset)
ddca0b
-                                CPU_FREE(arg_cpuset);
ddca0b
-
ddca0b
-                        arg_cpuset = TAKE_PTR(cpuset);
ddca0b
-                        arg_cpuset_ncpus = r;
ddca0b
+                        cpu_set_reset(&arg_cpu_set);
ddca0b
+                        arg_cpu_set = cpuset;
ddca0b
                         arg_settings_mask |= SETTING_CPU_AFFINITY;
ddca0b
                         break;
ddca0b
                 }
ddca0b
@@ -2631,8 +2627,8 @@ static int inner_child(
ddca0b
                         return log_error_errno(r, "Failed to adjust OOM score: %m");
ddca0b
         }
ddca0b
 
ddca0b
-        if (arg_cpuset)
ddca0b
-                if (sched_setaffinity(0, CPU_ALLOC_SIZE(arg_cpuset_ncpus), arg_cpuset) < 0)
ddca0b
+        if (arg_cpu_set.set)
ddca0b
+                if (sched_setaffinity(0, arg_cpu_set.allocated, arg_cpu_set.set) < 0)
ddca0b
                         return log_error_errno(errno, "Failed to set CPU affinity: %m");
ddca0b
 
ddca0b
         r = drop_capabilities();
ddca0b
@@ -3494,15 +3490,14 @@ static int merge_settings(Settings *settings, const char *path) {
ddca0b
         }
ddca0b
 
ddca0b
         if ((arg_settings_mask & SETTING_CPU_AFFINITY) == 0 &&
ddca0b
-            settings->cpuset) {
ddca0b
+            settings->cpu_set.set) {
ddca0b
 
ddca0b
                 if (!arg_settings_trusted)
ddca0b
                         log_warning("Ignoring CPUAffinity= setting, file '%s' is not trusted.", path);
ddca0b
                 else {
ddca0b
-                        if (arg_cpuset)
ddca0b
-                                CPU_FREE(arg_cpuset);
ddca0b
-                        arg_cpuset = TAKE_PTR(settings->cpuset);
ddca0b
-                        arg_cpuset_ncpus = settings->cpuset_ncpus;
ddca0b
+                        cpu_set_reset(&arg_cpu_set);
ddca0b
+                        arg_cpu_set = settings->cpu_set;
ddca0b
+                        settings->cpu_set = (CPUSet) {};
ddca0b
                 }
ddca0b
         }
ddca0b
 
ddca0b
@@ -4600,7 +4595,7 @@ finish:
ddca0b
         rlimit_free_all(arg_rlimit);
ddca0b
         strv_free(arg_syscall_whitelist);
ddca0b
         strv_free(arg_syscall_blacklist);
ddca0b
-        arg_cpuset = cpu_set_mfree(arg_cpuset);
ddca0b
+        cpu_set_reset(&arg_cpu_set);
ddca0b
 
ddca0b
         return r < 0 ? EXIT_FAILURE : ret;
ddca0b
 }
ddca0b
diff --git a/src/shared/bus-unit-util.c b/src/shared/bus-unit-util.c
ddca0b
index 271cc054da..75b4aace84 100644
ddca0b
--- a/src/shared/bus-unit-util.c
ddca0b
+++ b/src/shared/bus-unit-util.c
ddca0b
@@ -932,13 +932,13 @@ static int bus_append_execute_property(sd_bus_message *m, const char *field, con
ddca0b
         }
ddca0b
 
ddca0b
         if (streq(field, "CPUAffinity")) {
ddca0b
-                _cleanup_cpu_free_ cpu_set_t *cpuset = NULL;
ddca0b
+                _cleanup_(cpu_set_reset) CPUSet cpuset = {};
ddca0b
 
ddca0b
                 r = parse_cpu_set(eq, &cpuset);
ddca0b
                 if (r < 0)
ddca0b
                         return log_error_errno(r, "Failed to parse %s value: %s", field, eq);
ddca0b
 
ddca0b
-                return bus_append_byte_array(m, field, cpuset, CPU_ALLOC_SIZE(r));
ddca0b
+                return bus_append_byte_array(m, field, cpuset.set, cpuset.allocated);
ddca0b
         }
ddca0b
 
ddca0b
         if (STR_IN_SET(field, "RestrictAddressFamilies", "SystemCallFilter")) {
ddca0b
diff --git a/src/test/test-cpu-set-util.c b/src/test/test-cpu-set-util.c
ddca0b
index ff5edb2a69..b9ec29af66 100644
ddca0b
--- a/src/test/test-cpu-set-util.c
ddca0b
+++ b/src/test/test-cpu-set-util.c
ddca0b
@@ -1,154 +1,171 @@
ddca0b
 /* SPDX-License-Identifier: LGPL-2.1+ */
ddca0b
 
ddca0b
+#include <errno.h>
ddca0b
+
ddca0b
 #include "alloc-util.h"
ddca0b
 #include "cpu-set-util.h"
ddca0b
 #include "macro.h"
ddca0b
 
ddca0b
 static void test_parse_cpu_set(void) {
ddca0b
-        cpu_set_t *c = NULL;
ddca0b
+        CPUSet c = {};
ddca0b
         _cleanup_free_ char *str = NULL;
ddca0b
-        int ncpus;
ddca0b
         int cpu;
ddca0b
 
ddca0b
         /* Simple range (from CPUAffinity example) */
ddca0b
-        ncpus = parse_cpu_set_and_warn("1 2", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus >= 1024);
ddca0b
-        assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
-        assert_se(CPU_ISSET_S(2, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
-        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 2);
ddca0b
-
ddca0b
-        assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus)));
ddca0b
+        assert_se(parse_cpu_set_full("1 2", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0);
ddca0b
+        assert_se(c.set);
ddca0b
+        assert_se(c.allocated >= sizeof(__cpu_mask) / 8);
ddca0b
+        assert_se(CPU_ISSET_S(1, c.allocated, c.set));
ddca0b
+        assert_se(CPU_ISSET_S(2, c.allocated, c.set));
ddca0b
+        assert_se(CPU_COUNT_S(c.allocated, c.set) == 2);
ddca0b
+
ddca0b
+        assert_se(str = cpu_set_to_string(&c);;
ddca0b
         log_info("cpu_set_to_string: %s", str);
ddca0b
         str = mfree(str);
ddca0b
-        c = cpu_set_mfree(c);
ddca0b
+        cpu_set_reset(&c);
ddca0b
 
ddca0b
         /* A more interesting range */
ddca0b
-        ncpus = parse_cpu_set_and_warn("0 1 2 3 8 9 10 11", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus >= 1024);
ddca0b
-        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
ddca0b
+        assert_se(parse_cpu_set_full("0 1 2 3 8 9 10 11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0);
ddca0b
+        assert_se(c.allocated >= sizeof(__cpu_mask) / 8);
ddca0b
+        assert_se(CPU_COUNT_S(c.allocated, c.set) == 8);
ddca0b
         for (cpu = 0; cpu < 4; cpu++)
ddca0b
-                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
+                assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
ddca0b
         for (cpu = 8; cpu < 12; cpu++)
ddca0b
-                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
-        assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus)));
ddca0b
+                assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
ddca0b
+        assert_se(str = cpu_set_to_string(&c);;
ddca0b
         log_info("cpu_set_to_string: %s", str);
ddca0b
         str = mfree(str);
ddca0b
-        c = cpu_set_mfree(c);
ddca0b
+        cpu_set_reset(&c);
ddca0b
 
ddca0b
         /* Quoted strings */
ddca0b
-        ncpus = parse_cpu_set_and_warn("8 '9' 10 \"11\"", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus >= 1024);
ddca0b
-        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 4);
ddca0b
+        assert_se(parse_cpu_set_full("8 '9' 10 \"11\"", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0);
ddca0b
+        assert_se(c.allocated >= sizeof(__cpu_mask) / 8);
ddca0b
+        assert_se(CPU_COUNT_S(c.allocated, c.set) == 4);
ddca0b
         for (cpu = 8; cpu < 12; cpu++)
ddca0b
-                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
-        assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus)));
ddca0b
+                assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
ddca0b
+        assert_se(str = cpu_set_to_string(&c);;
ddca0b
         log_info("cpu_set_to_string: %s", str);
ddca0b
         str = mfree(str);
ddca0b
-        c = cpu_set_mfree(c);
ddca0b
+        cpu_set_reset(&c);
ddca0b
 
ddca0b
         /* Use commas as separators */
ddca0b
-        ncpus = parse_cpu_set_and_warn("0,1,2,3 8,9,10,11", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus >= 1024);
ddca0b
-        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
ddca0b
+        assert_se(parse_cpu_set_full("0,1,2,3 8,9,10,11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0);
ddca0b
+        assert_se(c.allocated >= sizeof(__cpu_mask) / 8);
ddca0b
+        assert_se(CPU_COUNT_S(c.allocated, c.set) == 8);
ddca0b
         for (cpu = 0; cpu < 4; cpu++)
ddca0b
-                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
+                assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
ddca0b
         for (cpu = 8; cpu < 12; cpu++)
ddca0b
-                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
-        assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus)));
ddca0b
+                assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
ddca0b
+        assert_se(str = cpu_set_to_string(&c);;
ddca0b
         log_info("cpu_set_to_string: %s", str);
ddca0b
         str = mfree(str);
ddca0b
-        c = cpu_set_mfree(c);
ddca0b
+        cpu_set_reset(&c);
ddca0b
 
ddca0b
         /* Commas with spaces (and trailing comma, space) */
ddca0b
-        ncpus = parse_cpu_set_and_warn("0, 1, 2, 3, 4, 5, 6, 7, ", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus >= 1024);
ddca0b
-        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
ddca0b
+        assert_se(parse_cpu_set_full("0, 1, 2, 3, 4, 5, 6, 7, ", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0);
ddca0b
+        assert_se(c.allocated >= sizeof(__cpu_mask) / 8);
ddca0b
+        assert_se(CPU_COUNT_S(c.allocated, c.set) == 8);
ddca0b
         for (cpu = 0; cpu < 8; cpu++)
ddca0b
-                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
-        assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus)));
ddca0b
+                assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
ddca0b
+        assert_se(str = cpu_set_to_string(&c);;
ddca0b
         log_info("cpu_set_to_string: %s", str);
ddca0b
         str = mfree(str);
ddca0b
-        c = cpu_set_mfree(c);
ddca0b
+        cpu_set_reset(&c);
ddca0b
 
ddca0b
         /* Ranges */
ddca0b
-        ncpus = parse_cpu_set_and_warn("0-3,8-11", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus >= 1024);
ddca0b
-        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
ddca0b
+        assert_se(parse_cpu_set_full("0-3,8-11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0);
ddca0b
+        assert_se(c.allocated >= sizeof(__cpu_mask) / 8);
ddca0b
+        assert_se(CPU_COUNT_S(c.allocated, c.set) == 8);
ddca0b
         for (cpu = 0; cpu < 4; cpu++)
ddca0b
-                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
+                assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
ddca0b
         for (cpu = 8; cpu < 12; cpu++)
ddca0b
-                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
-        assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus)));
ddca0b
+                assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
ddca0b
+        assert_se(str = cpu_set_to_string(&c);;
ddca0b
         log_info("cpu_set_to_string: %s", str);
ddca0b
         str = mfree(str);
ddca0b
-        c = cpu_set_mfree(c);
ddca0b
+        cpu_set_reset(&c);
ddca0b
 
ddca0b
         /* Ranges with trailing comma, space */
ddca0b
-        ncpus = parse_cpu_set_and_warn("0-3  8-11, ", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus >= 1024);
ddca0b
-        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
ddca0b
+        assert_se(parse_cpu_set_full("0-3  8-11, ", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0);
ddca0b
+        assert_se(c.allocated >= sizeof(__cpu_mask) / 8);
ddca0b
+        assert_se(CPU_COUNT_S(c.allocated, c.set) == 8);
ddca0b
         for (cpu = 0; cpu < 4; cpu++)
ddca0b
-                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
+                assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
ddca0b
         for (cpu = 8; cpu < 12; cpu++)
ddca0b
-                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
-        assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus)));
ddca0b
+                assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
ddca0b
+        assert_se(str = cpu_set_to_string(&c);;
ddca0b
         log_info("cpu_set_to_string: %s", str);
ddca0b
         str = mfree(str);
ddca0b
-        c = cpu_set_mfree(c);
ddca0b
+        cpu_set_reset(&c);
ddca0b
 
ddca0b
         /* Negative range (returns empty cpu_set) */
ddca0b
-        ncpus = parse_cpu_set_and_warn("3-0", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus >= 1024);
ddca0b
-        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 0);
ddca0b
-        c = cpu_set_mfree(c);
ddca0b
+        assert_se(parse_cpu_set_full("3-0", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0);
ddca0b
+        assert_se(c.allocated >= sizeof(__cpu_mask) / 8);
ddca0b
+        assert_se(CPU_COUNT_S(c.allocated, c.set) == 0);
ddca0b
+        cpu_set_reset(&c);
ddca0b
 
ddca0b
         /* Overlapping ranges */
ddca0b
-        ncpus = parse_cpu_set_and_warn("0-7 4-11", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus >= 1024);
ddca0b
-        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 12);
ddca0b
+        assert_se(parse_cpu_set_full("0-7 4-11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0);
ddca0b
+        assert_se(c.allocated >= sizeof(__cpu_mask) / 8);
ddca0b
+        assert_se(CPU_COUNT_S(c.allocated, c.set) == 12);
ddca0b
         for (cpu = 0; cpu < 12; cpu++)
ddca0b
-                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
-        assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus)));
ddca0b
+                assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
ddca0b
+        assert_se(str = cpu_set_to_string(&c);;
ddca0b
         log_info("cpu_set_to_string: %s", str);
ddca0b
         str = mfree(str);
ddca0b
-        c = cpu_set_mfree(c);
ddca0b
+        cpu_set_reset(&c);
ddca0b
 
ddca0b
         /* Mix ranges and individual CPUs */
ddca0b
-        ncpus = parse_cpu_set_and_warn("0,1 4-11", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus >= 1024);
ddca0b
-        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 10);
ddca0b
-        assert_se(CPU_ISSET_S(0, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
-        assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
+        assert_se(parse_cpu_set_full("0,1 4-11", &c, true, NULL, "fake", 1, "CPUAffinity") >= 0);
ddca0b
+        assert_se(c.allocated >= sizeof(__cpu_mask) / 8);
ddca0b
+        assert_se(CPU_COUNT_S(c.allocated, c.set) == 10);
ddca0b
+        assert_se(CPU_ISSET_S(0, c.allocated, c.set));
ddca0b
+        assert_se(CPU_ISSET_S(1, c.allocated, c.set));
ddca0b
         for (cpu = 4; cpu < 12; cpu++)
ddca0b
-                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
ddca0b
-        assert_se(str = cpu_set_to_string(c, CPU_ALLOC_SIZE(ncpus)));
ddca0b
+                assert_se(CPU_ISSET_S(cpu, c.allocated, c.set));
ddca0b
+        assert_se(str = cpu_set_to_string(&c);;
ddca0b
         log_info("cpu_set_to_string: %s", str);
ddca0b
         str = mfree(str);
ddca0b
-        c = cpu_set_mfree(c);
ddca0b
+        cpu_set_reset(&c);
ddca0b
 
ddca0b
         /* Garbage */
ddca0b
-        ncpus = parse_cpu_set_and_warn("0 1 2 3 garbage", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus < 0);
ddca0b
-        assert_se(!c);
ddca0b
+        assert_se(parse_cpu_set_full("0 1 2 3 garbage", &c, true, NULL, "fake", 1, "CPUAffinity") == -EINVAL);
ddca0b
+        assert_se(!c.set);
ddca0b
+        assert_se(c.allocated == 0);
ddca0b
 
ddca0b
         /* Range with garbage */
ddca0b
-        ncpus = parse_cpu_set_and_warn("0-3 8-garbage", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus < 0);
ddca0b
-        assert_se(!c);
ddca0b
+        assert_se(parse_cpu_set_full("0-3 8-garbage", &c, true, NULL, "fake", 1, "CPUAffinity") == -EINVAL);
ddca0b
+        assert_se(!c.set);
ddca0b
+        assert_se(c.allocated == 0);
ddca0b
 
ddca0b
         /* Empty string */
ddca0b
-        c = NULL;
ddca0b
-        ncpus = parse_cpu_set_and_warn("", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus == 0);  /* empty string returns 0 */
ddca0b
-        assert_se(!c);
ddca0b
+        assert_se(parse_cpu_set_full("", &c, true, NULL, "fake", 1, "CPUAffinity") == 0);
ddca0b
+        assert_se(!c.set);                /* empty string returns NULL */
ddca0b
+        assert_se(c.allocated == 0);
ddca0b
 
ddca0b
         /* Runaway quoted string */
ddca0b
-        ncpus = parse_cpu_set_and_warn("0 1 2 3 \"4 5 6 7 ", &c, NULL, "fake", 1, "CPUAffinity");
ddca0b
-        assert_se(ncpus < 0);
ddca0b
-        assert_se(!c);
ddca0b
+        assert_se(parse_cpu_set_full("0 1 2 3 \"4 5 6 7 ", &c, true, NULL, "fake", 1, "CPUAffinity") == -EINVAL);
ddca0b
+        assert_se(!c.set);
ddca0b
+        assert_se(c.allocated == 0);
ddca0b
+
ddca0b
+        /* Maximum allocation */
ddca0b
+        assert_se(parse_cpu_set_full("8000-8191", &c, true, NULL, "fake", 1, "CPUAffinity") == 0);
ddca0b
+        assert_se(CPU_COUNT_S(c.allocated, c.set) == 192);
ddca0b
+        assert_se(str = cpu_set_to_string(&c);;
ddca0b
+        log_info("cpu_set_to_string: %s", str);
ddca0b
+        str = mfree(str);
ddca0b
+        cpu_set_reset(&c);
ddca0b
 }
ddca0b
 
ddca0b
 int main(int argc, char *argv[]) {
ddca0b
+        log_info("CPU_ALLOC_SIZE(1) = %zu", CPU_ALLOC_SIZE(1));
ddca0b
+        log_info("CPU_ALLOC_SIZE(9) = %zu", CPU_ALLOC_SIZE(9));
ddca0b
+        log_info("CPU_ALLOC_SIZE(64) = %zu", CPU_ALLOC_SIZE(64));
ddca0b
+        log_info("CPU_ALLOC_SIZE(65) = %zu", CPU_ALLOC_SIZE(65));
ddca0b
+        log_info("CPU_ALLOC_SIZE(1024) = %zu", CPU_ALLOC_SIZE(1024));
ddca0b
+        log_info("CPU_ALLOC_SIZE(1025) = %zu", CPU_ALLOC_SIZE(1025));
ddca0b
+        log_info("CPU_ALLOC_SIZE(8191) = %zu", CPU_ALLOC_SIZE(8191));
ddca0b
+
ddca0b
         test_parse_cpu_set();
ddca0b
 
ddca0b
         return 0;
ddca0b
diff --git a/src/test/test-sizeof.c b/src/test/test-sizeof.c
ddca0b
index 7a1e496ed2..396e68f35f 100644
ddca0b
--- a/src/test/test-sizeof.c
ddca0b
+++ b/src/test/test-sizeof.c
ddca0b
@@ -1,5 +1,6 @@
ddca0b
 /* SPDX-License-Identifier: LGPL-2.1+ */
ddca0b
 
ddca0b
+#include <sched.h>
ddca0b
 #include <stdio.h>
ddca0b
 #include <string.h>
ddca0b
 
ddca0b
@@ -64,6 +65,8 @@ int main(void) {
ddca0b
         info(uid_t);
ddca0b
         info(gid_t);
ddca0b
 
ddca0b
+        info(__cpu_mask);
ddca0b
+
ddca0b
         info(enum Enum);
ddca0b
         info(enum BigEnum);
ddca0b
         info(enum BigEnum2);