mirror of
https://github.com/systemd/systemd.git
synced 2024-11-24 10:43:35 +08:00
core: support percentage specifications on TasksMax=
This adds support for a TasksMax=40% syntax for specifying values relative to the system's configured maximum number of processes. This is useful in order to neatly subdivide the available room for tasks within containers.
This commit is contained in:
parent
bf3dd08a81
commit
83f8e80857
@ -327,15 +327,12 @@
|
||||
<term><varname>TasksMax=<replaceable>N</replaceable></varname></term>
|
||||
|
||||
<listitem>
|
||||
<para>Specify the maximum number of tasks that may be
|
||||
created in the unit. This ensures that the number of tasks
|
||||
accounted for the unit (see above) stays below a specific
|
||||
limit. If assigned the special value
|
||||
<literal>infinity</literal>, no tasks limit is applied. This
|
||||
controls the <literal>pids.max</literal> control group
|
||||
attribute. For details about this control group attribute,
|
||||
see <ulink
|
||||
url="https://www.kernel.org/doc/Documentation/cgroup-v1/pids.txt">pids.txt</ulink>.</para>
|
||||
<para>Specify the maximum number of tasks that may be created in the unit. This ensures that the number of
|
||||
tasks accounted for the unit (see above) stays below a specific limit. This either takes an absolute number
|
||||
of tasks or a percentage value that is taken relative to the configured maximum number of tasks on the
|
||||
system. If assigned the special value <literal>infinity</literal>, no tasks limit is applied. This controls
|
||||
the <literal>pids.max</literal> control group attribute. For details about this control group attribute, see
|
||||
<ulink url="https://www.kernel.org/doc/Documentation/cgroup-v1/pids.txt">pids.txt</ulink>.</para>
|
||||
|
||||
<para>Implies <literal>TasksAccounting=true</literal>. The
|
||||
system default for this setting may be controlled with
|
||||
|
@ -832,6 +832,61 @@ uint64_t physical_memory_scale(uint64_t v, uint64_t max) {
|
||||
return r;
|
||||
}
|
||||
|
||||
uint64_t system_tasks_max(void) {
|
||||
|
||||
#if SIZEOF_PID_T == 4
|
||||
#define TASKS_MAX ((uint64_t) (INT32_MAX-1))
|
||||
#elif SIZEOF_PID_T == 2
|
||||
#define TASKS_MAX ((uint64_t) (INT16_MAX-1))
|
||||
#else
|
||||
#error "Unknown pid_t size"
|
||||
#endif
|
||||
|
||||
_cleanup_free_ char *value = NULL, *root = NULL;
|
||||
uint64_t a = TASKS_MAX, b = TASKS_MAX;
|
||||
|
||||
/* Determine the maximum number of tasks that may run on this system. We check three sources to determine this
|
||||
* limit:
|
||||
*
|
||||
* a) the maximum value for the pid_t type
|
||||
* b) the cgroups pids_max attribute for the system
|
||||
* c) the kernel's configure maximum PID value
|
||||
*
|
||||
* And then pick the smallest of the three */
|
||||
|
||||
if (read_one_line_file("/proc/sys/kernel/pid_max", &value) >= 0)
|
||||
(void) safe_atou64(value, &a);
|
||||
|
||||
if (cg_get_root_path(&root) >= 0) {
|
||||
value = mfree(value);
|
||||
|
||||
if (cg_get_attribute("pids", root, "pids.max", &value) >= 0)
|
||||
(void) safe_atou64(value, &b);
|
||||
}
|
||||
|
||||
return MIN3(TASKS_MAX,
|
||||
a <= 0 ? TASKS_MAX : a,
|
||||
b <= 0 ? TASKS_MAX : b);
|
||||
}
|
||||
|
||||
uint64_t system_tasks_max_scale(uint64_t v, uint64_t max) {
|
||||
uint64_t t, m;
|
||||
|
||||
assert(max > 0);
|
||||
|
||||
/* Multiply the system's task value by the fraction v/max. Hence, if max==100 this calculates percentages
|
||||
* relative to the system's maximum number of tasks. Returns UINT64_MAX on overflow. */
|
||||
|
||||
t = system_tasks_max();
|
||||
assert(t > 0);
|
||||
|
||||
m = t * v;
|
||||
if (m / t != v) /* overflow? */
|
||||
return UINT64_MAX;
|
||||
|
||||
return m / max;
|
||||
}
|
||||
|
||||
int update_reboot_parameter_and_warn(const char *param) {
|
||||
int r;
|
||||
|
||||
|
@ -186,6 +186,9 @@ int namespace_enter(int pidns_fd, int mntns_fd, int netns_fd, int userns_fd, int
|
||||
uint64_t physical_memory(void);
|
||||
uint64_t physical_memory_scale(uint64_t v, uint64_t max);
|
||||
|
||||
uint64_t system_tasks_max(void);
|
||||
uint64_t system_tasks_max_scale(uint64_t v, uint64_t max);
|
||||
|
||||
int update_reboot_parameter_and_warn(const char *param);
|
||||
|
||||
int version(void);
|
||||
|
@ -1060,6 +1060,8 @@ int bus_cgroup_set_property(
|
||||
r = sd_bus_message_read(message, "t", &limit);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (limit <= 0)
|
||||
return sd_bus_error_set_errnof(error, EINVAL, "%s= is too small", name);
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
c->tasks_max = limit;
|
||||
@ -1071,6 +1073,26 @@ int bus_cgroup_set_property(
|
||||
unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu64, limit);
|
||||
}
|
||||
|
||||
return 1;
|
||||
} else if (streq(name, "TasksMaxScale")) {
|
||||
uint64_t limit;
|
||||
uint32_t raw;
|
||||
|
||||
r = sd_bus_message_read(message, "u", &raw);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
limit = system_tasks_max_scale(raw, UINT32_MAX);
|
||||
if (limit <= 0 || limit >= UINT64_MAX)
|
||||
return sd_bus_error_set_errnof(error, EINVAL, "%s= is out of range", name);
|
||||
|
||||
if (mode != UNIT_CHECK) {
|
||||
c->tasks_max = limit;
|
||||
unit_invalidate_cgroup(u, CGROUP_MASK_PIDS);
|
||||
unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu32 "%%",
|
||||
(uint32_t) (DIV_ROUND_UP((uint64_t) raw * 100U, (uint64_t) UINT32_MAX)));
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -2861,9 +2861,18 @@ int config_parse_tasks_max(
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = safe_atou64(rvalue, &u);
|
||||
if (r < 0 || u < 1) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
|
||||
r = parse_percent(rvalue);
|
||||
if (r < 0) {
|
||||
r = safe_atou64(rvalue, &u);
|
||||
if (r < 0) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, r, "Maximum tasks value '%s' invalid. Ignoring.", rvalue);
|
||||
return 0;
|
||||
}
|
||||
} else
|
||||
u = system_tasks_max_scale(r, 100U);
|
||||
|
||||
if (u <= 0 || u >= UINT64_MAX) {
|
||||
log_syntax(unit, LOG_ERR, filename, line, 0, "Maximum tasks value '%s' out of range. Ignoring.", rvalue);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -148,6 +148,26 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
|
||||
r = sd_bus_message_append(m, "sv", field, "t", bytes);
|
||||
goto finish;
|
||||
} else if (streq(field, "TasksMax")) {
|
||||
uint64_t t;
|
||||
|
||||
if (isempty(eq) || streq(eq, "infinity"))
|
||||
t = (uint64_t) -1;
|
||||
else {
|
||||
r = parse_percent(eq);
|
||||
if (r >= 0) {
|
||||
r = sd_bus_message_append(m, "sv", "TasksMaxScale", "u", (uint32_t) (((uint64_t) UINT32_MAX * r) / 100U));
|
||||
goto finish;
|
||||
} else {
|
||||
r = safe_atou64(eq, &t);
|
||||
if (r < 0)
|
||||
return log_error_errno(r, "Failed to parse maximum tasks specification %s", assignment);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
r = sd_bus_message_append(m, "sv", "TasksMax", "t", t);
|
||||
goto finish;
|
||||
}
|
||||
|
||||
r = sd_bus_message_append_basic(m, SD_BUS_TYPE_STRING, field);
|
||||
@ -191,21 +211,6 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen
|
||||
|
||||
r = sd_bus_message_append(m, "v", "b", r);
|
||||
|
||||
} else if (streq(field, "TasksMax")) {
|
||||
uint64_t n;
|
||||
|
||||
if (isempty(eq) || streq(eq, "infinity"))
|
||||
n = (uint64_t) -1;
|
||||
else {
|
||||
r = safe_atou64(eq, &n);
|
||||
if (r < 0) {
|
||||
log_error("Failed to parse maximum tasks specification %s", assignment);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
r = sd_bus_message_append(m, "v", "t", n);
|
||||
|
||||
} else if (STR_IN_SET(field, "CPUShares", "StartupCPUShares")) {
|
||||
uint64_t u;
|
||||
|
||||
|
@ -308,7 +308,43 @@ static void test_physical_memory_scale(void) {
|
||||
|
||||
/* overflow */
|
||||
assert_se(physical_memory_scale(UINT64_MAX/4, UINT64_MAX) == UINT64_MAX);
|
||||
}
|
||||
|
||||
static void test_system_tasks_max(void) {
|
||||
uint64_t t;
|
||||
|
||||
t = system_tasks_max();
|
||||
assert_se(t > 0);
|
||||
assert_se(t < UINT64_MAX);
|
||||
|
||||
log_info("Max tasks: %" PRIu64, t);
|
||||
}
|
||||
|
||||
static void test_system_tasks_max_scale(void) {
|
||||
uint64_t t;
|
||||
|
||||
t = system_tasks_max();
|
||||
|
||||
assert_se(system_tasks_max_scale(0, 100) == 0);
|
||||
assert_se(system_tasks_max_scale(100, 100) == t);
|
||||
|
||||
assert_se(system_tasks_max_scale(0, 1) == 0);
|
||||
assert_se(system_tasks_max_scale(1, 1) == t);
|
||||
assert_se(system_tasks_max_scale(2, 1) == 2*t);
|
||||
|
||||
assert_se(system_tasks_max_scale(0, 2) == 0);
|
||||
assert_se(system_tasks_max_scale(1, 2) == t/2);
|
||||
assert_se(system_tasks_max_scale(2, 2) == t);
|
||||
assert_se(system_tasks_max_scale(3, 2) == (3*t)/2);
|
||||
assert_se(system_tasks_max_scale(4, 2) == t*2);
|
||||
|
||||
assert_se(system_tasks_max_scale(0, UINT32_MAX) == 0);
|
||||
assert_se(system_tasks_max_scale((UINT32_MAX-1)/2, UINT32_MAX-1) == t/2);
|
||||
assert_se(system_tasks_max_scale(UINT32_MAX, UINT32_MAX) == t);
|
||||
|
||||
/* overflow */
|
||||
|
||||
assert_se(system_tasks_max_scale(UINT64_MAX/4, UINT64_MAX) == UINT64_MAX);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
@ -327,6 +363,8 @@ int main(int argc, char *argv[]) {
|
||||
test_raw_clone();
|
||||
test_physical_memory();
|
||||
test_physical_memory_scale();
|
||||
test_system_tasks_max();
|
||||
test_system_tasks_max_scale();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user