diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/basic/cgroup-util.c | 8 | ||||
-rw-r--r-- | src/basic/cgroup-util.h | 2 | ||||
-rw-r--r-- | src/cgtop/cgtop.c | 107 | ||||
-rw-r--r-- | src/core/cgroup.c | 49 | ||||
-rw-r--r-- | src/core/cgroup.h | 4 | ||||
-rw-r--r-- | src/core/dbus-cgroup.c | 41 | ||||
-rw-r--r-- | src/core/dbus-unit.c | 25 | ||||
-rw-r--r-- | src/core/load-fragment-gperf.gperf.m4 | 2 | ||||
-rw-r--r-- | src/core/load-fragment.c | 37 | ||||
-rw-r--r-- | src/core/load-fragment.h | 1 | ||||
-rw-r--r-- | src/core/main.c | 3 | ||||
-rw-r--r-- | src/core/manager.h | 1 | ||||
-rw-r--r-- | src/core/unit.c | 1 | ||||
-rw-r--r-- | src/shared/bus-util.c | 29 | ||||
-rw-r--r-- | src/systemctl/systemctl.c | 92 |
15 files changed, 332 insertions, 70 deletions
diff --git a/src/basic/cgroup-util.c b/src/basic/cgroup-util.c index 94a25585b2..6099dc8721 100644 --- a/src/basic/cgroup-util.c +++ b/src/basic/cgroup-util.c @@ -2018,9 +2018,10 @@ int cg_mask_supported(CGroupMask *ret) { mask |= CGROUP_CONTROLLER_TO_MASK(v); } - /* Currently, we only support the memory controller in - * the unified hierarchy, mask everything else off. */ - mask &= CGROUP_MASK_MEMORY; + /* Currently, we only support the memory and pids + * controller in the unified hierarchy, mask + * everything else off. */ + mask &= CGROUP_MASK_MEMORY | CGROUP_MASK_PIDS; } else { CGroupController c; @@ -2212,6 +2213,7 @@ static const char *cgroup_controller_table[_CGROUP_CONTROLLER_MAX] = { [CGROUP_CONTROLLER_BLKIO] = "blkio", [CGROUP_CONTROLLER_MEMORY] = "memory", [CGROUP_CONTROLLER_DEVICES] = "devices", + [CGROUP_CONTROLLER_PIDS] = "pids", }; DEFINE_STRING_TABLE_LOOKUP(cgroup_controller, CGroupController); diff --git a/src/basic/cgroup-util.h b/src/basic/cgroup-util.h index 16d439fd9d..7e1b4f9c72 100644 --- a/src/basic/cgroup-util.h +++ b/src/basic/cgroup-util.h @@ -35,6 +35,7 @@ typedef enum CGroupController { CGROUP_CONTROLLER_BLKIO, CGROUP_CONTROLLER_MEMORY, CGROUP_CONTROLLER_DEVICES, + CGROUP_CONTROLLER_PIDS, _CGROUP_CONTROLLER_MAX, _CGROUP_CONTROLLER_INVALID = -1, } CGroupController; @@ -48,6 +49,7 @@ typedef enum CGroupMask { CGROUP_MASK_BLKIO = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_BLKIO), CGROUP_MASK_MEMORY = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_MEMORY), CGROUP_MASK_DEVICES = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_DEVICES), + CGROUP_MASK_PIDS = CGROUP_CONTROLLER_TO_MASK(CGROUP_CONTROLLER_PIDS), _CGROUP_MASK_ALL = CGROUP_CONTROLLER_TO_MASK(_CGROUP_CONTROLLER_MAX) - 1 } CGroupMask; diff --git a/src/cgtop/cgtop.c b/src/cgtop/cgtop.c index 1c94bea31a..7a0399d8f2 100644 --- a/src/cgtop/cgtop.c +++ b/src/cgtop/cgtop.c @@ -45,7 +45,7 @@ typedef struct Group { bool memory_valid:1; bool io_valid:1; - unsigned n_tasks; + uint64_t n_tasks; unsigned cpu_iteration; nsec_t cpu_usage; @@ -65,7 +65,12 @@ static unsigned arg_iterations = (unsigned) -1; static bool arg_batch = false; static bool arg_raw = false; static usec_t arg_delay = 1*USEC_PER_SEC; -static bool arg_kernel_threads = false; + +enum { + COUNT_PIDS, + COUNT_USERSPACE_PROCESSES, + COUNT_ALL_PROCESSES, +} arg_count = COUNT_PIDS; static bool arg_recursive = true; static enum { @@ -73,7 +78,7 @@ static enum { ORDER_TASKS, ORDER_CPU, ORDER_MEMORY, - ORDER_IO + ORDER_IO, } arg_order = ORDER_CPU; static enum { @@ -153,7 +158,7 @@ static int process( } } - if (streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { + if (streq(controller, SYSTEMD_CGROUP_CONTROLLER) && IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES)) { _cleanup_fclose_ FILE *f = NULL; pid_t pid; @@ -166,7 +171,7 @@ static int process( g->n_tasks = 0; while (cg_read_pid(f, &pid) > 0) { - if (!arg_kernel_threads && is_kernel_thread(pid) > 0) + if (arg_count == COUNT_USERSPACE_PROCESSES && is_kernel_thread(pid) > 0) continue; g->n_tasks++; @@ -175,6 +180,26 @@ static int process( if (g->n_tasks > 0) g->n_tasks_valid = true; + } else if (streq(controller, "pids") && arg_count == COUNT_PIDS) { + _cleanup_free_ char *p = NULL, *v = NULL; + + r = cg_get_path(controller, path, "pids.current", &p); + if (r < 0) + return r; + + r = read_one_line_file(p, &v); + if (r == -ENOENT) + return 0; + if (r < 0) + return r; + + r = safe_atou64(v, &g->n_tasks); + if (r < 0) + return r; + + if (g->n_tasks > 0) + g->n_tasks_valid = true; + } else if (streq(controller, "cpuacct") && cg_unified() <= 0) { _cleanup_free_ char *p = NULL, *v = NULL; uint64_t new_usage; @@ -371,6 +396,7 @@ static int refresh_one( return r; if (arg_recursive && + IN_SET(arg_count, COUNT_ALL_PROCESSES, COUNT_USERSPACE_PROCESSES) && child && child->n_tasks_valid && streq(controller, SYSTEMD_CGROUP_CONTROLLER)) { @@ -409,6 +435,9 @@ static int refresh(const char *root, Hashmap *a, Hashmap *b, unsigned iteration) r = refresh_one("blkio", root, a, b, iteration, 0, NULL); if (r < 0) return r; + r = refresh_one("pids", root, a, b, iteration, 0, NULL); + if (r < 0) + return r; return 0; } @@ -549,7 +578,7 @@ static void display(Hashmap *a) { printf("%s%-*s%s %s%7s%s %s%s%s %s%8s%s %s%8s%s %s%8s%s\n\n", arg_order == ORDER_PATH ? ON : "", path_columns, "Control Group", arg_order == ORDER_PATH ? OFF : "", - arg_order == ORDER_TASKS ? ON : "", "Tasks", + arg_order == ORDER_TASKS ? ON : "", arg_count == COUNT_PIDS ? "Tasks" : arg_count == COUNT_USERSPACE_PROCESSES ? "Procs" : "Proc+", arg_order == ORDER_TASKS ? OFF : "", arg_order == ORDER_CPU ? ON : "", buffer, arg_order == ORDER_CPU ? OFF : "", @@ -576,7 +605,7 @@ static void display(Hashmap *a) { printf("%-*s", path_columns, ellipsized ?: path); if (g->n_tasks_valid) - printf(" %7u", g->n_tasks); + printf(" %7" PRIu64, g->n_tasks); else fputs(" -", stdout); @@ -602,15 +631,16 @@ static void help(void) { " -h --help Show this help\n" " --version Show package version\n" " -p --order=path Order by path\n" - " -t --order=tasks Order by number of tasks\n" + " -t --order=tasks Order by number of tasks/processes\n" " -c --order=cpu Order by CPU load (default)\n" " -m --order=memory Order by memory load\n" " -i --order=io Order by IO load\n" " -r --raw Provide raw (not human-readable) numbers\n" " --cpu=percentage Show CPU usage as percentage (default)\n" " --cpu=time Show CPU usage as time\n" - " -k Include kernel threads in task count\n" - " --recursive=BOOL Sum up task count recursively\n" + " -P Count userspace processes instead of tasks (excl. kernel)\n" + " -k Count all processes instead of tasks (incl. kernel)\n" + " --recursive=BOOL Sum up process count recursively\n" " -d --delay=DELAY Delay between updates\n" " -n --iterations=N Run for N iterations before exiting\n" " -b --batch Run in batch mode, accepting no input\n" @@ -642,12 +672,13 @@ static int parse_argv(int argc, char *argv[]) { {} }; + bool recursive_unset = false; int c, r; assert(argc >= 1); assert(argv); - while ((c = getopt_long(argc, argv, "hptcmin:brd:k", options, NULL)) >= 0) + while ((c = getopt_long(argc, argv, "hptcmin:brd:kP", options, NULL)) >= 0) switch (c) { @@ -748,7 +779,11 @@ static int parse_argv(int argc, char *argv[]) { break; case 'k': - arg_kernel_threads = true; + arg_count = COUNT_ALL_PROCESSES; + break; + + case 'P': + arg_count = COUNT_USERSPACE_PROCESSES; break; case ARG_RECURSIVE: @@ -759,6 +794,7 @@ static int parse_argv(int argc, char *argv[]) { } arg_recursive = r; + recursive_unset = r == 0; break; case '?': @@ -773,9 +809,23 @@ static int parse_argv(int argc, char *argv[]) { return -EINVAL; } + if (recursive_unset && arg_count == COUNT_PIDS) { + log_error("Non-recursive counting is only supported when counting processes, not tasks. Use -P or -k."); + return -EINVAL; + } + return 1; } +static const char* counting_what(void) { + if (arg_count == COUNT_PIDS) + return "tasks"; + else if (arg_count == COUNT_ALL_PROCESSES) + return "all processes (incl. kernel)"; + else + return "userspace processes (excl. kernel)"; +} + int main(int argc, char *argv[]) { int r; Hashmap *a = NULL, *b = NULL; @@ -783,10 +833,19 @@ int main(int argc, char *argv[]) { usec_t last_refresh = 0; bool quit = false, immediate_refresh = false; _cleanup_free_ char *root = NULL; + CGroupMask mask; log_parse_environment(); log_open(); + r = cg_mask_supported(&mask); + if (r < 0) { + log_error_errno(r, "Failed to determine supported controllers: %m"); + goto finish; + } + + arg_count = (mask & CGROUP_MASK_PIDS) ? COUNT_PIDS : COUNT_USERSPACE_PROCESSES; + r = parse_argv(argc, argv); if (r <= 0) goto finish; @@ -899,15 +958,26 @@ int main(int argc, char *argv[]) { break; case 'k': - arg_kernel_threads = !arg_kernel_threads; - fprintf(stdout, "\nCounting kernel threads: %s.", yes_no(arg_kernel_threads)); + arg_count = arg_count != COUNT_ALL_PROCESSES ? COUNT_ALL_PROCESSES : COUNT_PIDS; + fprintf(stdout, "\nCounting: %s.", counting_what()); + fflush(stdout); + sleep(1); + break; + + case 'P': + arg_count = arg_count != COUNT_USERSPACE_PROCESSES ? COUNT_USERSPACE_PROCESSES : COUNT_PIDS; + fprintf(stdout, "\nCounting: %s.", counting_what()); fflush(stdout); sleep(1); break; case 'r': - arg_recursive = !arg_recursive; - fprintf(stdout, "\nRecursive task counting: %s", yes_no(arg_recursive)); + if (arg_count == COUNT_PIDS) + fprintf(stdout, "\n\aCannot toggle recursive counting, not available in task counting mode."); + else { + arg_recursive = !arg_recursive; + fprintf(stdout, "\nRecursive process counting: %s", yes_no(arg_recursive)); + } fflush(stdout); sleep(1); break; @@ -939,9 +1009,10 @@ int main(int argc, char *argv[]) { case '?': case 'h': fprintf(stdout, - "\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n" + "\t<" ON "p" OFF "> By path; <" ON "t" OFF "> By tasks/procs; <" ON "c" OFF "> By CPU; <" ON "m" OFF "> By memory; <" ON "i" OFF "> By I/O\n" "\t<" ON "+" OFF "> Inc. delay; <" ON "-" OFF "> Dec. delay; <" ON "%%" OFF "> Toggle time; <" ON "SPACE" OFF "> Refresh\n" - "\t<" ON "k" OFF "> Count kernel threads; <" ON "r" OFF "> Count recursively; <" ON "q" OFF "> Quit"); + "\t<" ON "P" OFF "> Toggle count userspace processes; <" ON "k" OFF "> Toggle count all processes\n" + "\t<" ON "r" OFF "> Count processes recursively; <" ON "q" OFF "> Quit"); fflush(stdout); sleep(3); break; diff --git a/src/core/cgroup.c b/src/core/cgroup.c index baa7cc5488..48000d4e6e 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -22,10 +22,11 @@ #include <fcntl.h> #include <fnmatch.h> -#include "process-util.h" +#include "cgroup-util.h" #include "path-util.h" +#include "process-util.h" #include "special.h" -#include "cgroup-util.h" + #include "cgroup.h" #define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC) @@ -41,6 +42,7 @@ void cgroup_context_init(CGroupContext *c) { c->memory_limit = (uint64_t) -1; c->blockio_weight = (unsigned long) -1; c->startup_blockio_weight = (unsigned long) -1; + c->tasks_max = (uint64_t) -1; c->cpu_quota_per_sec_usec = USEC_INFINITY; } @@ -106,6 +108,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { "%sBlockIOWeight=%lu\n" "%sStartupBlockIOWeight=%lu\n" "%sMemoryLimit=%" PRIu64 "\n" + "%sTasksMax=%" PRIu64 "\n" "%sDevicePolicy=%s\n" "%sDelegate=%s\n", prefix, yes_no(c->cpu_accounting), @@ -117,6 +120,7 @@ void cgroup_context_dump(CGroupContext *c, FILE* f, const char *prefix) { prefix, c->blockio_weight, prefix, c->startup_blockio_weight, prefix, c->memory_limit, + prefix, c->tasks_max, prefix, cgroup_device_policy_to_string(c->device_policy), prefix, yes_no(c->delegate)); @@ -466,6 +470,21 @@ void cgroup_context_apply(CGroupContext *c, CGroupMask mask, const char *path, M log_debug("Ignoring device %s while writing cgroup attribute.", a->path); } } + + if ((mask & CGROUP_MASK_PIDS) && !is_root) { + + if (c->tasks_max != (uint64_t) -1) { + char buf[DECIMAL_STR_MAX(uint64_t) + 2]; + + sprintf(buf, "%" PRIu64 "\n", c->tasks_max); + r = cg_set_attribute("pids", path, "pids.max", buf); + } else + r = cg_set_attribute("pids", path, "pids.max", "max"); + + if (r < 0) + log_full_errno(IN_SET(r, -ENOENT, -EROFS) ? LOG_DEBUG : LOG_WARNING, r, + "Failed to set pids.max on %s: %m", path); + } } CGroupMask cgroup_context_get_mask(CGroupContext *c) { @@ -494,6 +513,10 @@ CGroupMask cgroup_context_get_mask(CGroupContext *c) { c->device_policy != CGROUP_AUTO) mask |= CGROUP_MASK_DEVICES; + if (c->tasks_accounting || + c->tasks_max != (uint64_t) -1) + mask |= CGROUP_MASK_PIDS; + return mask; } @@ -1459,6 +1482,28 @@ int unit_get_memory_current(Unit *u, uint64_t *ret) { return safe_atou64(v, ret); } +int unit_get_tasks_current(Unit *u, uint64_t *ret) { + _cleanup_free_ char *v = NULL; + int r; + + assert(u); + assert(ret); + + if (!u->cgroup_path) + return -ENODATA; + + if ((u->cgroup_realized_mask & CGROUP_MASK_PIDS) == 0) + return -ENODATA; + + r = cg_get_attribute("pids", u->cgroup_path, "pids.current", &v); + if (r == -ENOENT) + return -ENODATA; + if (r < 0) + return r; + + return safe_atou64(v, ret); +} + static int unit_get_cpu_usage_raw(Unit *u, nsec_t *ret) { _cleanup_free_ char *v = NULL; uint64_t ns; diff --git a/src/core/cgroup.h b/src/core/cgroup.h index 438f5bf50f..3ba09d56a4 100644 --- a/src/core/cgroup.h +++ b/src/core/cgroup.h @@ -72,6 +72,7 @@ struct CGroupContext { bool cpu_accounting; bool blockio_accounting; bool memory_accounting; + bool tasks_accounting; unsigned long cpu_shares; unsigned long startup_cpu_shares; @@ -88,6 +89,8 @@ struct CGroupContext { LIST_HEAD(CGroupDeviceAllow, device_allow); bool delegate; + + uint64_t tasks_max; }; #include "unit.h" @@ -137,6 +140,7 @@ int unit_search_main_pid(Unit *u, pid_t *ret); int unit_watch_all_pids(Unit *u); int unit_get_memory_current(Unit *u, uint64_t *ret); +int unit_get_tasks_current(Unit *u, uint64_t *ret); int unit_get_cpu_usage(Unit *u, nsec_t *ret); int unit_reset_cpu_usage(Unit *u); diff --git a/src/core/dbus-cgroup.c b/src/core/dbus-cgroup.c index ba2f4e53b9..94de92c3dd 100644 --- a/src/core/dbus-cgroup.c +++ b/src/core/dbus-cgroup.c @@ -168,6 +168,8 @@ const sd_bus_vtable bus_cgroup_vtable[] = { SD_BUS_PROPERTY("MemoryLimit", "t", NULL, offsetof(CGroupContext, memory_limit), 0), SD_BUS_PROPERTY("DevicePolicy", "s", property_get_cgroup_device_policy, offsetof(CGroupContext, device_policy), 0), SD_BUS_PROPERTY("DeviceAllow", "a(ss)", property_get_device_allow, 0, 0), + SD_BUS_PROPERTY("TasksAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, tasks_accounting), 0), + SD_BUS_PROPERTY("TasksMax", "t", NULL, offsetof(CGroupContext, tasks_max), 0), SD_BUS_VTABLE_END }; @@ -551,7 +553,11 @@ int bus_cgroup_set_property( if (mode != UNIT_CHECK) { c->memory_limit = limit; u->cgroup_realized_mask &= ~CGROUP_MASK_MEMORY; - unit_write_drop_in_private_format(u, mode, name, "%s=%" PRIu64, name, limit); + + if (limit == (uint64_t) -1) + unit_write_drop_in_private(u, mode, name, "MemoryLimit=infinity"); + else + unit_write_drop_in_private_format(u, mode, name, "MemoryLimit=%" PRIu64, limit); } return 1; @@ -667,6 +673,39 @@ int bus_cgroup_set_property( return 1; + } else if (streq(name, "TasksAccounting")) { + int b; + + r = sd_bus_message_read(message, "b", &b); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->tasks_accounting = b; + u->cgroup_realized_mask &= ~CGROUP_MASK_PIDS; + unit_write_drop_in_private(u, mode, name, b ? "TasksAccounting=yes" : "TasksAccounting=no"); + } + + return 1; + + } else if (streq(name, "TasksMax")) { + uint64_t limit; + + r = sd_bus_message_read(message, "t", &limit); + if (r < 0) + return r; + + if (mode != UNIT_CHECK) { + c->tasks_max = limit; + u->cgroup_realized_mask &= ~CGROUP_MASK_PIDS; + + if (limit == (uint64_t) -1) + unit_write_drop_in_private(u, mode, name, "TasksMax=infinity"); + else + unit_write_drop_in_private_format(u, mode, name, "TasksMax=%" PRIu64, limit); + } + + return 1; } if (u->transient && u->load_state == UNIT_STUB) { diff --git a/src/core/dbus-unit.c b/src/core/dbus-unit.c index f9275ed935..f7e9795928 100644 --- a/src/core/dbus-unit.c +++ b/src/core/dbus-unit.c @@ -736,6 +736,30 @@ static int property_get_current_memory( return sd_bus_message_append(reply, "t", sz); } +static int property_get_current_tasks( + sd_bus *bus, + const char *path, + const char *interface, + const char *property, + sd_bus_message *reply, + void *userdata, + sd_bus_error *error) { + + uint64_t cn = (uint64_t) -1; + Unit *u = userdata; + int r; + + assert(bus); + assert(reply); + assert(u); + + r = unit_get_tasks_current(u, &cn); + if (r < 0 && r != -ENODATA) + log_unit_warning_errno(u, r, "Failed to get pids.current attribute: %m"); + + return sd_bus_message_append(reply, "t", cn); +} + static int property_get_cpu_usage( sd_bus *bus, const char *path, @@ -796,6 +820,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = { SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0), SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0), SD_BUS_PROPERTY("CPUUsageNSec", "t", property_get_cpu_usage, 0, 0), + SD_BUS_PROPERTY("TasksCurrent", "t", property_get_current_tasks, 0, 0), SD_BUS_VTABLE_END }; diff --git a/src/core/load-fragment-gperf.gperf.m4 b/src/core/load-fragment-gperf.gperf.m4 index edd55b9e45..e056fd863c 100644 --- a/src/core/load-fragment-gperf.gperf.m4 +++ b/src/core/load-fragment-gperf.gperf.m4 @@ -124,6 +124,8 @@ $1.StartupBlockIOWeight, config_parse_blockio_weight, 0, $1.BlockIODeviceWeight, config_parse_blockio_device_weight, 0, offsetof($1, cgroup_context) $1.BlockIOReadBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context) $1.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0, offsetof($1, cgroup_context) +$1.TasksAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.tasks_accounting) +$1.TasksMax, config_parse_tasks_max, 0, offsetof($1, cgroup_context) $1.Delegate, config_parse_bool, 0, offsetof($1, cgroup_context.delegate)' )m4_dnl Unit.Description, config_parse_unit_string_printf, 0, offsetof(Unit, description) diff --git a/src/core/load-fragment.c b/src/core/load-fragment.c index 3b02cd8085..07f2770559 100644 --- a/src/core/load-fragment.c +++ b/src/core/load-fragment.c @@ -2686,7 +2686,7 @@ int config_parse_memory_limit( off_t bytes; int r; - if (isempty(rvalue)) { + if (isempty(rvalue) || streq(rvalue, "infinity")) { c->memory_limit = (uint64_t) -1; return 0; } @@ -2694,9 +2694,8 @@ int config_parse_memory_limit( assert_cc(sizeof(uint64_t) == sizeof(off_t)); r = parse_size(rvalue, 1024, &bytes); - if (r < 0) { - log_syntax(unit, LOG_ERR, filename, line, EINVAL, - "Memory limit '%s' invalid. Ignoring.", rvalue); + if (r < 0 || bytes < 1) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Memory limit '%s' invalid. Ignoring.", rvalue); return 0; } @@ -2704,6 +2703,36 @@ int config_parse_memory_limit( return 0; } +int config_parse_tasks_max( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + CGroupContext *c = data; + uint64_t u; + int r; + + if (isempty(rvalue) || streq(rvalue, "infinity")) { + c->tasks_max = (uint64_t) -1; + return 0; + } + + r = safe_atou64(rvalue, &u); + if (r < 0 || u < 1) { + log_syntax(unit, LOG_ERR, filename, line, EINVAL, "Maximum tasks value '%s' invalid. Ignoring.", rvalue); + return 0; + } + + return 0; +} + int config_parse_device_allow( const char *unit, const char *filename, diff --git a/src/core/load-fragment.h b/src/core/load-fragment.h index 1d128716c4..638b343a6e 100644 --- a/src/core/load-fragment.h +++ b/src/core/load-fragment.h @@ -84,6 +84,7 @@ int config_parse_environ(const char *unit, const char *filename, unsigned line, int config_parse_unit_slice(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_cpu_shares(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_memory_limit(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); +int config_parse_tasks_max(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_device_policy(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_device_allow(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); int config_parse_blockio_weight(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata); diff --git a/src/core/main.c b/src/core/main.c index c9657505c3..200fe740da 100644 --- a/src/core/main.c +++ b/src/core/main.c @@ -114,6 +114,7 @@ static FILE* arg_serialization = NULL; static bool arg_default_cpu_accounting = false; static bool arg_default_blockio_accounting = false; static bool arg_default_memory_accounting = false; +static bool arg_default_tasks_accounting = false; static void nop_handler(int sig) {} @@ -676,6 +677,7 @@ static int parse_config_file(void) { { "Manager", "DefaultCPUAccounting", config_parse_bool, 0, &arg_default_cpu_accounting }, { "Manager", "DefaultBlockIOAccounting", config_parse_bool, 0, &arg_default_blockio_accounting }, { "Manager", "DefaultMemoryAccounting", config_parse_bool, 0, &arg_default_memory_accounting }, + { "Manager", "DefaultTasksAccounting", config_parse_bool, 0, &arg_default_tasks_accounting }, {} }; @@ -704,6 +706,7 @@ static void manager_set_defaults(Manager *m) { m->default_cpu_accounting = arg_default_cpu_accounting; m->default_blockio_accounting = arg_default_blockio_accounting; m->default_memory_accounting = arg_default_memory_accounting; + m->default_tasks_accounting = arg_default_tasks_accounting; manager_set_default_rlimits(m, arg_default_rlimit); manager_environment_add(m, NULL, arg_default_environment); diff --git a/src/core/manager.h b/src/core/manager.h index 9956cb7700..78a0e50a33 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -256,6 +256,7 @@ struct Manager { bool default_cpu_accounting; bool default_memory_accounting; bool default_blockio_accounting; + bool default_tasks_accounting; usec_t default_timer_accuracy_usec; diff --git a/src/core/unit.c b/src/core/unit.c index e40ea24be8..2ebfb09a7a 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -125,6 +125,7 @@ static void unit_init(Unit *u) { cc->cpu_accounting = u->manager->default_cpu_accounting; cc->blockio_accounting = u->manager->default_blockio_accounting; cc->memory_accounting = u->manager->default_memory_accounting; + cc->tasks_accounting = u->manager->default_tasks_accounting; } ec = unit_get_exec_context(u); diff --git a/src/shared/bus-util.c b/src/shared/bus-util.c index 36c44227c5..0ca1bbfb21 100644 --- a/src/shared/bus-util.c +++ b/src/shared/bus-util.c @@ -1421,7 +1421,7 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen return bus_log_create_error(r); if (STR_IN_SET(field, - "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", + "CPUAccounting", "MemoryAccounting", "BlockIOAccounting", "TasksAccounting", "SendSIGHUP", "SendSIGKILL", "WakeSystem", "DefaultDependencies", "IgnoreSIGPIPE", "TTYVHangup", "TTYReset", "RemainAfterExit")) { @@ -1436,14 +1436,33 @@ int bus_append_unit_property_assignment(sd_bus_message *m, const char *assignmen } else if (streq(field, "MemoryLimit")) { off_t bytes; - r = parse_size(eq, 1024, &bytes); - if (r < 0) { - log_error("Failed to parse bytes specification %s", assignment); - return -EINVAL; + if (isempty(eq) || streq(eq, "infinity")) + bytes = (uint64_t) -1; + else { + r = parse_size(eq, 1024, &bytes); + if (r < 0) { + log_error("Failed to parse bytes specification %s", assignment); + return -EINVAL; + } } r = sd_bus_message_append(m, "v", "t", (uint64_t) bytes); + } 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", "BlockIOWeight")) { uint64_t u; diff --git a/src/systemctl/systemctl.c b/src/systemctl/systemctl.c index 40b366c7a5..10835b81bf 100644 --- a/src/systemctl/systemctl.c +++ b/src/systemctl/systemctl.c @@ -20,59 +20,60 @@ along with systemd; If not, see <http://www.gnu.org/licenses/>. ***/ -#include <sys/reboot.h> -#include <linux/reboot.h> -#include <stdio.h> +#include <errno.h> +#include <fcntl.h> #include <getopt.h> +#include <linux/reboot.h> #include <locale.h> #include <stdbool.h> +#include <stddef.h> +#include <stdio.h> #include <string.h> -#include <errno.h> -#include <unistd.h> -#include <fcntl.h> +#include <sys/reboot.h> #include <sys/socket.h> -#include <stddef.h> +#include <unistd.h> +#include "sd-bus.h" #include "sd-daemon.h" #include "sd-login.h" -#include "sd-bus.h" -#include "log.h" -#include "util.h" -#include "macro.h" -#include "set.h" -#include "utmp-wtmp.h" -#include "special.h" -#include "initreq.h" -#include "path-util.h" -#include "strv.h" + +#include "build.h" +#include "bus-common-errors.h" +#include "bus-error.h" +#include "bus-message.h" +#include "bus-util.h" #include "cgroup-show.h" #include "cgroup-util.h" -#include "list.h" -#include "path-lookup.h" -#include "exit-status.h" -#include "build.h" -#include "unit-name.h" -#include "pager.h" -#include "spawn-ask-password-agent.h" -#include "spawn-polkit-agent.h" -#include "install.h" -#include "logs-show.h" -#include "socket-util.h" -#include "fileio.h" #include "copy.h" -#include "env-util.h" -#include "bus-util.h" -#include "bus-message.h" -#include "bus-error.h" -#include "bus-common-errors.h" -#include "mkdir.h" #include "dropin.h" #include "efivars.h" +#include "env-util.h" +#include "exit-status.h" +#include "fileio.h" #include "formats-util.h" -#include "process-util.h" -#include "terminal-util.h" #include "hostname-util.h" +#include "initreq.h" +#include "install.h" +#include "list.h" +#include "log.h" +#include "logs-show.h" +#include "macro.h" +#include "mkdir.h" +#include "pager.h" +#include "path-lookup.h" +#include "path-util.h" +#include "process-util.h" +#include "set.h" #include "signal-util.h" +#include "socket-util.h" +#include "spawn-ask-password-agent.h" +#include "spawn-polkit-agent.h" +#include "special.h" +#include "strv.h" +#include "terminal-util.h" +#include "unit-name.h" +#include "util.h" +#include "utmp-wtmp.h" static char **arg_types = NULL; static char **arg_states = NULL; @@ -3285,6 +3286,8 @@ typedef struct UnitStatusInfo { uint64_t memory_current; uint64_t memory_limit; uint64_t cpu_usage_nsec; + uint64_t tasks_current; + uint64_t tasks_max; LIST_HEAD(ExecStatusInfo, exec); } UnitStatusInfo; @@ -3543,6 +3546,15 @@ static void print_status_info( if (i->status_errno > 0) printf(" Error: %i (%s)\n", i->status_errno, strerror(i->status_errno)); + if (i->tasks_current != (uint64_t) -1) { + printf(" Tasks: %" PRIu64, i->tasks_current); + + if (i->tasks_max != (uint64_t) -1) + printf(" (limit: %" PRIi64 ")\n", i->tasks_max); + else + printf("\n"); + } + if (i->memory_current != (uint64_t) -1) { char buf[FORMAT_BYTES_MAX]; @@ -3776,6 +3788,10 @@ static int status_property(const char *name, sd_bus_message *m, UnitStatusInfo * i->memory_current = u; else if (streq(name, "MemoryLimit")) i->memory_limit = u; + else if (streq(name, "TasksCurrent")) + i->tasks_current = u; + else if (streq(name, "TasksMax")) + i->tasks_max = u; else if (streq(name, "CPUUsageNSec")) i->cpu_usage_nsec = u; @@ -4252,6 +4268,8 @@ static int show_one( .memory_current = (uint64_t) -1, .memory_limit = (uint64_t) -1, .cpu_usage_nsec = (uint64_t) -1, + .tasks_current = (uint64_t) -1, + .tasks_max = (uint64_t) -1, }; ExecStatusInfo *p; int r; |