]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
cpu-set-util: Support ranges in parse_cpu_set_and_warn
authorFilipe Brandenburger <filbranden@google.com>
Fri, 25 Sep 2015 12:23:23 +0000 (05:23 -0700)
committerFilipe Brandenburger <filbranden@google.com>
Wed, 28 Oct 2015 00:56:26 +0000 (17:56 -0700)
Tested CPUAffinity ranges on both a service unit and in system.conf and
confirmed they work as expected (by inspecting /proc/PID/status, for the
main pid of the service and for pid 1).  Also mixed ranges with both
spaces, commas, trailing commas and spaces.

Added new tests to increase coverage of ranges and prevent regressions.

src/basic/cpu-set-util.c
src/test/test-util.c

index 441144d5cca7ba4af33db4f664456b7973d71d85..4950c66767baf9249c86b8c21f4e3f87e640f239 100644 (file)
@@ -72,14 +72,12 @@ int parse_cpu_set_and_warn(
 
         for (;;) {
                 _cleanup_free_ char *word = NULL;
-                unsigned cpu;
+                unsigned cpu, cpu_lower, cpu_upper;
                 int r;
 
                 r = extract_first_word(&rvalue, &word, WHITESPACE ",", EXTRACT_QUOTES);
-                if (r < 0) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
-                        return r;
-                }
+                if (r < 0)
+                        return log_syntax(unit, LOG_ERR, filename, line, r, "Invalid value for %s: %s", lvalue, whole_rvalue);
                 if (r == 0)
                         break;
 
@@ -89,13 +87,17 @@ int parse_cpu_set_and_warn(
                                 return log_oom();
                 }
 
-                r = safe_atou(word, &cpu);
-                if (r < 0 || cpu >= ncpus) {
-                        log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU affinity '%s'", rvalue);
-                        return -EINVAL;
-                }
-
-                CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
+                r = parse_range(word, &cpu_lower, &cpu_upper);
+                if (r < 0)
+                        return log_syntax(unit, LOG_ERR, filename, line, r, "Failed to parse CPU affinity '%s'", word);
+                if (cpu_lower >= ncpus || cpu_upper >= ncpus)
+                        return log_syntax(unit, LOG_ERR, filename, line, EINVAL, "CPU out of range '%s' ncpus is %u", word, ncpus);
+
+                if (cpu_lower > cpu_upper)
+                        log_syntax(unit, LOG_WARNING, filename, line, 0, "Range '%s' is invalid, %u > %u", word, cpu_lower, cpu_upper);
+                else
+                        for (cpu = cpu_lower; cpu <= cpu_upper; cpu++)
+                                CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
         }
 
         /* On success, sets *cpu_set and returns ncpus for the system. */
index 5d01d065236e52642c6f70d8e3db6998fdb47509..6a3bf20fe44ad49cf76ded9c4bfdaef1b866b71b 100644 (file)
@@ -1209,14 +1209,58 @@ static void test_parse_cpu_set(void) {
 
         /* Ranges */
         ncpus = parse_cpu_set_and_warn("0-3,8-11", &c, NULL, "fake", 1, "CPUAffinity");
-        assert_se(ncpus < 0);
-        assert_se(!c);
+        assert_se(ncpus >= 1024);
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
+        for (cpu = 0; cpu < 4; cpu++)
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+        for (cpu = 8; cpu < 12; cpu++)
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+        c = mfree(c);
+
+        /* Ranges with trailing comma, space */
+        ncpus = parse_cpu_set_and_warn("0-3  8-11, ", &c, NULL, "fake", 1, "CPUAffinity");
+        assert_se(ncpus >= 1024);
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
+        for (cpu = 0; cpu < 4; cpu++)
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+        for (cpu = 8; cpu < 12; cpu++)
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+        c = mfree(c);
+
+        /* Negative range (returns empty cpu_set) */
+        ncpus = parse_cpu_set_and_warn("3-0", &c, NULL, "fake", 1, "CPUAffinity");
+        assert_se(ncpus >= 1024);
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 0);
+        c = mfree(c);
+
+        /* Overlapping ranges */
+        ncpus = parse_cpu_set_and_warn("0-7 4-11", &c, NULL, "fake", 1, "CPUAffinity");
+        assert_se(ncpus >= 1024);
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 12);
+        for (cpu = 0; cpu < 12; cpu++)
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+        c = mfree(c);
+
+        /* Mix ranges and individual CPUs */
+        ncpus = parse_cpu_set_and_warn("0,1 4-11", &c, NULL, "fake", 1, "CPUAffinity");
+        assert_se(ncpus >= 1024);
+        assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 10);
+        assert_se(CPU_ISSET_S(0, CPU_ALLOC_SIZE(ncpus), c));
+        assert_se(CPU_ISSET_S(1, CPU_ALLOC_SIZE(ncpus), c));
+        for (cpu = 4; cpu < 12; cpu++)
+                assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
+        c = mfree(c);
 
         /* Garbage */
         ncpus = parse_cpu_set_and_warn("0 1 2 3 garbage", &c, NULL, "fake", 1, "CPUAffinity");
         assert_se(ncpus < 0);
         assert_se(!c);
 
+        /* Range with garbage */
+        ncpus = parse_cpu_set_and_warn("0-3 8-garbage", &c, NULL, "fake", 1, "CPUAffinity");
+        assert_se(ncpus < 0);
+        assert_se(!c);
+
         /* Empty string */
         c = NULL;
         ncpus = parse_cpu_set_and_warn("", &c, NULL, "fake", 1, "CPUAffinity");