mirror of
https://github.com/systemd/systemd-stable.git
synced 2025-02-02 09:47:03 +03:00
Merge pull request #1699 from filbranden/cpuaffinity9
cpu-set-util: Support ranges in parse_cpu_set_and_warn (v4)
This commit is contained in:
commit
e3d285e6c0
@ -109,8 +109,10 @@
|
||||
<term><varname>CPUAffinity=</varname></term>
|
||||
|
||||
<listitem><para>Configures the initial CPU affinity for the
|
||||
init process. Takes a space-separated list of CPU
|
||||
indices.</para></listitem>
|
||||
init process. Takes a list of CPU indices or ranges separated
|
||||
by either whitespace or commas. CPU ranges are specified by
|
||||
the lower and upper CPU indices separated by a
|
||||
dash.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
|
@ -217,8 +217,10 @@
|
||||
<term><varname>CPUAffinity=</varname></term>
|
||||
|
||||
<listitem><para>Controls the CPU affinity of the executed
|
||||
processes. Takes a space-separated list of CPU indices. This
|
||||
option may be specified more than once in which case the
|
||||
processes. Takes a list of CPU indices or ranges separated by
|
||||
either whitespace or commas. CPU ranges are specified by the
|
||||
lower and upper CPU indices separated by a dash.
|
||||
This option may be specified more than once in which case the
|
||||
specified CPU affinity masks are merged. If the empty string
|
||||
is assigned, the mask is reset, all assignments prior to this
|
||||
will have no effect. See
|
||||
|
@ -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;
|
||||
}
|
||||
r = extract_first_word(&rvalue, &word, WHITESPACE ",", EXTRACT_QUOTES);
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
CPU_SET_S(cpu, CPU_ALLOC_SIZE(ncpus), c);
|
||||
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. */
|
||||
|
@ -19,6 +19,8 @@
|
||||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include "alloc-util.h"
|
||||
#include "extract-word.h"
|
||||
#include "parse-util.h"
|
||||
#include "string-util.h"
|
||||
#include "util.h"
|
||||
@ -207,6 +209,43 @@ int parse_size(const char *t, uint64_t base, uint64_t *size) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int parse_range(const char *t, unsigned *lower, unsigned *upper) {
|
||||
_cleanup_free_ char *word = NULL;
|
||||
unsigned l, u;
|
||||
int r;
|
||||
|
||||
assert(lower);
|
||||
assert(upper);
|
||||
|
||||
/* Extract the lower bound. */
|
||||
r = extract_first_word(&t, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return -EINVAL;
|
||||
|
||||
r = safe_atou(word, &l);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* Check for the upper bound and extract it if needed */
|
||||
if (!t)
|
||||
/* Single number with no dashes. */
|
||||
u = l;
|
||||
else if (!*t)
|
||||
/* Trailing dash is an error. */
|
||||
return -EINVAL;
|
||||
else {
|
||||
r = safe_atou(t, &u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
*lower = l;
|
||||
*upper = u;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char *format_bytes(char *buf, size_t l, uint64_t t) {
|
||||
unsigned i;
|
||||
|
||||
|
@ -33,6 +33,7 @@ int parse_pid(const char *s, pid_t* ret_pid);
|
||||
int parse_mode(const char *s, mode_t *ret);
|
||||
|
||||
int parse_size(const char *t, uint64_t base, uint64_t *size);
|
||||
int parse_range(const char *t, unsigned *lower, unsigned *upper);
|
||||
|
||||
#define FORMAT_BYTES_MAX 8
|
||||
char *format_bytes(char *buf, size_t l, uint64_t t);
|
||||
|
@ -979,6 +979,185 @@ static void test_parse_size(void) {
|
||||
assert_se(parse_size("-10B 20K", 1024, &bytes) == -ERANGE);
|
||||
}
|
||||
|
||||
static void test_parse_range(void) {
|
||||
unsigned lower, upper;
|
||||
|
||||
/* Successful cases */
|
||||
assert_se(parse_range("111", &lower, &upper) == 0);
|
||||
assert_se(lower == 111);
|
||||
assert_se(upper == 111);
|
||||
|
||||
assert_se(parse_range("111-123", &lower, &upper) == 0);
|
||||
assert_se(lower == 111);
|
||||
assert_se(upper == 123);
|
||||
|
||||
assert_se(parse_range("123-111", &lower, &upper) == 0);
|
||||
assert_se(lower == 123);
|
||||
assert_se(upper == 111);
|
||||
|
||||
assert_se(parse_range("123-123", &lower, &upper) == 0);
|
||||
assert_se(lower == 123);
|
||||
assert_se(upper == 123);
|
||||
|
||||
assert_se(parse_range("0", &lower, &upper) == 0);
|
||||
assert_se(lower == 0);
|
||||
assert_se(upper == 0);
|
||||
|
||||
assert_se(parse_range("0-15", &lower, &upper) == 0);
|
||||
assert_se(lower == 0);
|
||||
assert_se(upper == 15);
|
||||
|
||||
assert_se(parse_range("15-0", &lower, &upper) == 0);
|
||||
assert_se(lower == 15);
|
||||
assert_se(upper == 0);
|
||||
|
||||
assert_se(parse_range("128-65535", &lower, &upper) == 0);
|
||||
assert_se(lower == 128);
|
||||
assert_se(upper == 65535);
|
||||
|
||||
assert_se(parse_range("1024-4294967295", &lower, &upper) == 0);
|
||||
assert_se(lower == 1024);
|
||||
assert_se(upper == 4294967295);
|
||||
|
||||
/* Leading whitespace is acceptable */
|
||||
assert_se(parse_range(" 111", &lower, &upper) == 0);
|
||||
assert_se(lower == 111);
|
||||
assert_se(upper == 111);
|
||||
|
||||
assert_se(parse_range(" 111-123", &lower, &upper) == 0);
|
||||
assert_se(lower == 111);
|
||||
assert_se(upper == 123);
|
||||
|
||||
assert_se(parse_range("111- 123", &lower, &upper) == 0);
|
||||
assert_se(lower == 111);
|
||||
assert_se(upper == 123);
|
||||
|
||||
assert_se(parse_range("\t111-\t123", &lower, &upper) == 0);
|
||||
assert_se(lower == 111);
|
||||
assert_se(upper == 123);
|
||||
|
||||
assert_se(parse_range(" \t 111- \t 123", &lower, &upper) == 0);
|
||||
assert_se(lower == 111);
|
||||
assert_se(upper == 123);
|
||||
|
||||
/* Error cases, make sure they fail as expected */
|
||||
lower = upper = 9999;
|
||||
assert_se(parse_range("111garbage", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("garbage111", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("garbage", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111-123garbage", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111garbage-123", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
/* Empty string */
|
||||
lower = upper = 9999;
|
||||
assert_se(parse_range("", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
/* 111--123 will pass -123 to safe_atou which returns -ERANGE for negative */
|
||||
assert_se(parse_range("111--123", &lower, &upper) == -ERANGE);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("-111-123", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111-123-", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111.4-123", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111-123.4", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111,4-123", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111-123,4", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
/* Error on trailing dash */
|
||||
assert_se(parse_range("111-", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111-123-", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111--", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111- ", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
/* Whitespace is not a separator */
|
||||
assert_se(parse_range("111 123", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111\t123", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111 \t 123", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
/* Trailing whitespace is invalid (from safe_atou) */
|
||||
assert_se(parse_range("111 ", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111-123 ", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111 -123", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111 -123 ", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111\t-123\t", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
assert_se(parse_range("111 \t -123 \t ", &lower, &upper) == -EINVAL);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
|
||||
/* Out of the "unsigned" range, this is 1<<64 */
|
||||
assert_se(parse_range("0-18446744073709551616", &lower, &upper) == -ERANGE);
|
||||
assert_se(lower == 9999);
|
||||
assert_se(upper == 9999);
|
||||
}
|
||||
|
||||
static void test_parse_cpu_set(void) {
|
||||
cpu_set_t *c = NULL;
|
||||
int ncpus;
|
||||
@ -1012,19 +1191,76 @@ static void test_parse_cpu_set(void) {
|
||||
|
||||
/* Use commas as separators */
|
||||
ncpus = parse_cpu_set_and_warn("0,1,2,3 8,9,10,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);
|
||||
|
||||
/* Commas with spaces (and trailing comma, space) */
|
||||
ncpus = parse_cpu_set_and_warn("0, 1, 2, 3, 4, 5, 6, 7, ", &c, NULL, "fake", 1, "CPUAffinity");
|
||||
assert_se(ncpus >= 1024);
|
||||
assert_se(CPU_COUNT_S(CPU_ALLOC_SIZE(ncpus), c) == 8);
|
||||
for (cpu = 0; cpu < 8; cpu++)
|
||||
assert_se(CPU_ISSET_S(cpu, CPU_ALLOC_SIZE(ncpus), c));
|
||||
c = mfree(c);
|
||||
|
||||
/* 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");
|
||||
@ -2365,6 +2601,7 @@ int main(int argc, char *argv[]) {
|
||||
test_u64log2();
|
||||
test_protect_errno();
|
||||
test_parse_size();
|
||||
test_parse_range();
|
||||
test_parse_cpu_set();
|
||||
test_config_parse_iec_uint64();
|
||||
test_strextend();
|
||||
|
Loading…
x
Reference in New Issue
Block a user