cgroup-util: drop handcrafted cg_is_empty(), always check cgroup.events populated field (#37624)

This commit is contained in:
Yu Watanabe
2025-05-27 10:41:56 +09:00
committed by GitHub
8 changed files with 16 additions and 121 deletions

View File

@@ -198,47 +198,6 @@ int cg_read_pidref(FILE *f, PidRef *ret, CGroupFlags flags) {
}
}
int cg_read_event(
const char *controller,
const char *path,
const char *event,
char **ret) {
_cleanup_free_ char *events = NULL, *content = NULL;
int r;
r = cg_get_path(controller, path, "cgroup.events", &events);
if (r < 0)
return r;
r = read_full_virtual_file(events, &content, NULL);
if (r < 0)
return r;
for (const char *p = content;;) {
_cleanup_free_ char *line = NULL, *key = NULL;
const char *q;
r = extract_first_word(&p, &line, "\n", 0);
if (r < 0)
return r;
if (r == 0)
return -ENOENT;
q = line;
r = extract_first_word(&q, &key, " ", 0);
if (r < 0)
return r;
if (r == 0)
return -EINVAL;
if (!streq(key, event))
continue;
return strdup_to(ret, q);
}
}
bool cg_kill_supported(void) {
static thread_local int supported = -1;
@@ -792,81 +751,25 @@ int cg_pidref_get_path(const char *controller, const PidRef *pidref, char **ret_
}
int cg_is_empty(const char *controller, const char *path) {
_cleanup_fclose_ FILE *f = NULL;
pid_t pid;
_cleanup_free_ char *t = NULL;
int r;
/* Check if the cgroup hierarchy under 'path' is empty. On cgroup v2 it's exposed via the "populated"
* attribute of "cgroup.events". */
assert(path);
r = cg_enumerate_processes(controller, path, &f);
/* The root cgroup is always populated */
if (empty_or_root(path))
return false;
r = cg_get_keyed_attribute(SYSTEMD_CGROUP_CONTROLLER, path, "cgroup.events", STRV_MAKE("populated"), &t);
if (r == -ENOENT)
return true;
if (r < 0)
return r;
r = cg_read_pid(f, &pid, CGROUP_DONT_SKIP_UNMAPPED);
if (r < 0)
return r;
return r == 0;
}
int cg_is_empty_recursive(const char *controller, const char *path) {
int r;
assert(path);
/* The root cgroup is always populated */
if (controller && empty_or_root(path))
return false;
r = cg_unified_controller(controller);
if (r < 0)
return r;
if (r > 0) {
_cleanup_free_ char *t = NULL;
/* On the unified hierarchy we can check empty state
* via the "populated" attribute of "cgroup.events". */
r = cg_read_event(controller, path, "populated", &t);
if (r == -ENOENT)
return true;
if (r < 0)
return r;
return streq(t, "0");
} else {
_cleanup_closedir_ DIR *d = NULL;
char *fn;
r = cg_is_empty(controller, path);
if (r <= 0)
return r;
r = cg_enumerate_subgroups(controller, path, &d);
if (r == -ENOENT)
return true;
if (r < 0)
return r;
while ((r = cg_read_subgroup(d, &fn)) > 0) {
_cleanup_free_ char *p = NULL;
p = path_join(path, fn);
free(fn);
if (!p)
return -ENOMEM;
r = cg_is_empty_recursive(controller, p);
if (r <= 0)
return r;
}
if (r < 0)
return r;
return true;
}
return streq(t, "0");
}
int cg_split_spec(const char *spec, char **ret_controller, char **ret_path) {

View File

@@ -188,7 +188,6 @@ typedef enum CGroupFlags {
int cg_enumerate_processes(const char *controller, const char *path, FILE **ret);
int cg_read_pid(FILE *f, pid_t *ret, CGroupFlags flags);
int cg_read_pidref(FILE *f, PidRef *ret, CGroupFlags flags);
int cg_read_event(const char *controller, const char *path, const char *event, char **ret);
int cg_enumerate_subgroups(const char *controller, const char *path, DIR **ret);
int cg_read_subgroup(DIR *d, char **ret);
@@ -233,7 +232,6 @@ int cg_get_xattr_bool(const char *path, const char *name);
int cg_remove_xattr(const char *path, const char *name);
int cg_is_empty(const char *controller, const char *path);
int cg_is_empty_recursive(const char *controller, const char *path);
int cg_get_root_path(char **path);

View File

@@ -2760,7 +2760,7 @@ int unit_cgroup_is_empty(Unit *u) {
if (!crt->cgroup_path)
return -EOWNERDEAD;
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, crt->cgroup_path);
r = cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, crt->cgroup_path);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to determine whether cgroup %s is empty: %m", empty_to_root(crt->cgroup_path));
return r;
@@ -3626,8 +3626,6 @@ static int unit_get_cpu_usage_raw(const Unit *u, const CGroupRuntime *crt, nsec_
uint64_t us;
r = cg_get_keyed_attribute("cpu", crt->cgroup_path, "cpu.stat", STRV_MAKE("usage_usec"), &val);
if (IN_SET(r, -ENOENT, -ENXIO))
return -ENODATA;
if (r < 0)
return r;
@@ -4082,8 +4080,6 @@ static int unit_cgroup_freezer_kernel_state(Unit *u, FreezerState *ret) {
"cgroup.events",
STRV_MAKE("frozen"),
&val);
if (IN_SET(r, -ENOENT, -ENXIO))
return -ENODATA;
if (r < 0)
return r;

View File

@@ -1976,7 +1976,7 @@ static int cgroup_good(Service *s) {
if (!s->cgroup_runtime || !s->cgroup_runtime->cgroup_path)
return 0;
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_runtime->cgroup_path);
r = cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_runtime->cgroup_path);
if (r < 0)
return r;

View File

@@ -457,7 +457,7 @@ static int show_unit_cgroup(
/* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
if (cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
return 0;
show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, prefix, c, &leader, leader > 0, get_output_flags());

View File

@@ -436,7 +436,7 @@ static int show_unit_cgroup(sd_bus *bus, const char *unit, pid_t leader) {
/* Fallback for older systemd versions where the GetUnitProcesses() call is not yet available */
if (cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
if (cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, cgroup) != 0 && leader <= 0)
return 0;
show_cgroup_and_extra(SYSTEMD_CGROUP_CONTROLLER, cgroup, "\t\t ", c, &leader, leader > 0, get_output_flags());

View File

@@ -251,7 +251,7 @@ int show_cgroup_by_path(
if (!k)
return -ENOMEM;
if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty_recursive(NULL, k) > 0)
if (!(flags & OUTPUT_SHOW_ALL) && cg_is_empty(NULL, k) > 0)
continue;
if (!shown_pids) {

View File

@@ -106,9 +106,7 @@ TEST(cg_create) {
free(path);
ASSERT_OK_POSITIVE(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, test_a));
ASSERT_OK_POSITIVE(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, test_b));
ASSERT_OK_POSITIVE(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, test_a));
ASSERT_OK_ZERO(cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, test_b));
ASSERT_OK_ZERO(cg_is_empty(SYSTEMD_CGROUP_CONTROLLER, test_b));
ASSERT_OK_ZERO(cg_kill_recursive(test_a, 0, 0, NULL, NULL, NULL));
ASSERT_OK_POSITIVE(cg_kill_recursive(test_b, 0, 0, NULL, NULL, NULL));