1
1
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:
Lennart Poettering 2015-10-28 13:06:56 +01:00
commit e3d285e6c0
6 changed files with 303 additions and 20 deletions

View File

@ -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>

View File

@ -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

View 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;
}
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. */

View File

@ -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;

View File

@ -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);

View File

@ -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();