From: Luca Boccassi Date: Fri, 29 May 2026 10:23:23 +0000 (+0100) Subject: udev: run workers in sibling cgroup and use cgroup.kill X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=60686a33c3030e8eb60cd1d10ac44bc8987f3627;p=thirdparty%2Fsystemd.git udev: run workers in sibling cgroup and use cgroup.kill Since a1f4fd387603673a79a84ca4e5ce25b439b85fe6 udev processes run in an 'udev' subcgroup, to avoid killing control processes when clearing workers. But the main process is still in the same cgroup, so the atomic cgroup.kill cannot be used. If the main subcgroup exists, try to create a sibling 'workers' cgroup, and use it for workers processes, and use cgroup.kill if available. This is especially useful as rules can spawn arbitrary programs/scripts and TasksMax= is set to unlimited. --- diff --git a/src/udev/udev-manager.c b/src/udev/udev-manager.c index 44d2ee64003..ba23882a911 100644 --- a/src/udev/udev-manager.c +++ b/src/udev/udev-manager.c @@ -6,6 +6,7 @@ #include "sd-varlink.h" +#include "cgroup-setup.h" #include "cgroup-util.h" #include "common-signal.h" #include "daemon-util.h" @@ -21,6 +22,7 @@ #include "io-util.h" #include "list.h" #include "notify-recv.h" +#include "path-util.h" #include "pidref.h" #include "prioq.h" #include "process-util.h" @@ -226,7 +228,7 @@ Manager* manager_free(Manager *manager) { sd_event_source_unref(manager->kill_workers_event); sd_event_unref(manager->event); - free(manager->cgroup); + free(manager->workers_cgroup); return mfree(manager); } @@ -586,6 +588,14 @@ static int worker_spawn(Manager *manager, Event *event) { .manager_pid = manager_pid, }; + if (manager->workers_cgroup) { + r = cg_attach(manager->workers_cgroup, /* pid= */ 0); + if (r < 0) { + log_error_errno(r, "Failed to move worker into cgroup '%s': %m", manager->workers_cgroup); + _exit(EXIT_FAILURE); + } + } + if (setenv("NOTIFY_SOCKET", manager->worker_notify_socket_path, /* overwrite= */ true) < 0) { log_error_errno(errno, "Failed to set $NOTIFY_SOCKET: %m"); _exit(EXIT_FAILURE); @@ -1350,9 +1360,10 @@ static int on_post(sd_event_source *s, void *userdata) { if (!hashmap_isempty(manager->workers)) return 0; /* There still exist idle workers. */ - if (manager->cgroup && set_isempty(manager->synthesize_change_child_event_sources)) - /* cleanup possible left-over processes in our cgroup */ - (void) cg_kill(manager->cgroup, SIGKILL, CGROUP_IGNORE_SELF, /* killed_pids= */ NULL, /* log_kill= */ NULL, /* userdata= */ NULL); + if (manager->workers_cgroup && set_isempty(manager->synthesize_change_child_event_sources)) + /* cleanup possible left-over processes in the workers cgroup */ + if (cg_kill_kernel_sigkill(manager->workers_cgroup) == -EOPNOTSUPP) + (void) cg_kill(manager->workers_cgroup, SIGKILL, CGROUP_IGNORE_SELF, /* killed_pids= */ NULL, /* log_kill= */ NULL, /* userdata= */ NULL); return 0; } @@ -1480,12 +1491,30 @@ int manager_main(Manager *manager) { assert(manager); _cleanup_free_ char *cgroup = NULL; - r = cg_pid_get_path(0, &cgroup); + r = cg_pid_get_path(/* pid= */ 0, &cgroup); if (r < 0) log_debug_errno(r, "Failed to get cgroup, ignoring: %m"); else if (endswith(cgroup, "/udev")) { /* If we are in a subcgroup /udev/ we assume it was delegated to us */ log_debug("Running in delegated subcgroup '%s'.", cgroup); - manager->cgroup = TAKE_PTR(cgroup); + + /* Try to create a sibling 'workers' cgroup and spawn all workers inside it, so that we can + * use cgroup.kill to atomically clear all workers. */ + _cleanup_free_ char *workers_cgroup = NULL; + r = path_extract_directory(cgroup, &workers_cgroup); + if (r < 0) + return log_warning_errno(r, "Failed to extract parent of cgroup '%s': %m", cgroup); + + if (!path_extend(&workers_cgroup, "workers")) + return log_oom(); + + r = cg_create(workers_cgroup); + if (r < 0) + log_warning_errno(r, "Failed to create workers cgroup '%s', ignoring: %m", workers_cgroup); + else { + log_debug("Running workers in delegated subcgroup '%s'.", workers_cgroup); + manager->workers_cgroup = TAKE_PTR(workers_cgroup); + } + } r = manager_setup_event(manager); diff --git a/src/udev/udev-manager.h b/src/udev/udev-manager.h index 2eaaf5b0c3d..c1b98c52d1d 100644 --- a/src/udev/udev-manager.h +++ b/src/udev/udev-manager.h @@ -39,7 +39,7 @@ typedef struct Manager { Hashmap *workers; LIST_HEAD(Event, events); Event *last_event; - char *cgroup; + char *workers_cgroup; UdevRules *rules; Hashmap *properties;