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