--- src/core/cgroup.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++-- src/core/manager.c | 7 ++++- src/core/manager.h | 3 ++ src/core/service.c | 2 +- src/shared/cgroup-util.c | 28 +++++++++++++---- src/shared/cgroup-util.h | 6 ++-- src/test/test-cgroup.c | 12 +++---- 7 files changed, 120 insertions(+), 19 deletions(-)
diff --git a/src/core/cgroup.c b/src/core/cgroup.c index 6474e08..a1d7d93 100644 --- a/src/core/cgroup.c +++ b/src/core/cgroup.c @@ -30,6 +30,8 @@ #define CGROUP_CPU_QUOTA_PERIOD_USEC ((usec_t) 100 * USEC_PER_MSEC) +static int cgroup_populated_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata); + void cgroup_context_init(CGroupContext *c) { assert(c); @@ -629,6 +631,7 @@ static const char *migrate_callback(CGroupControllerMask mask, void *userdata) { static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) { CGroupContext *c; int r; + int wd = -1; assert(u); @@ -655,10 +658,13 @@ static int unit_create_cgroups(Unit *u, CGroupControllerMask mask) { } /* First, create our own group */ - r = cg_create_everywhere(u->manager->cgroup_supported, mask, u->cgroup_path); + r = cg_create_everywhere(u->manager->cgroup_supported, mask, u->cgroup_path, u->manager->cgroup_populated_inotify_fd, &wd); if (r < 0) return log_error_errno(r, "Failed to create cgroup %s: %m", u->cgroup_path); + if (wd > 0 && hashmap_put(u->manager->cgroup_populated_by_wd, INT_TO_PTR(wd), u->cgroup_path) < 0) + inotify_rm_watch(u->manager->cgroup_populated_inotify_fd, wd); + /* Keep track that this is now realized */ u->cgroup_realized = true; u->cgroup_realized_mask = mask; @@ -893,6 +899,7 @@ pid_t unit_search_main_pid(Unit *u) { int manager_setup_cgroup(Manager *m) { _cleanup_free_ char *path = NULL; + _cleanup_free_ char *sane_behavior = NULL; int r; assert(m); @@ -944,7 +951,7 @@ int manager_setup_cgroup(Manager *m) { } /* 4. Make sure we are in the root cgroup */ - r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0); + r = cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, 0, -1, NULL); if (r < 0) return log_error_errno(r, "Failed to create root cgroup hierarchy: %m"); @@ -957,6 +964,23 @@ int manager_setup_cgroup(Manager *m) { /* 6. Always enable hierarchical support if it exists... */ cg_set_attribute("memory", "/", "memory.use_hierarchy", "1"); + + /* 7. Create inotify fd for cgroup.populated files, if + * supported on unified cgroups. Insane ones have + * cgroup.sane_behavior set to 0.*/ + r = cg_get_attribute(SYSTEMD_CGROUP_CONTROLLER, m->cgroup_root, "cgroup.sane_behavior", &sane_behavior); + if (r == -ENOENT) { + m->cgroup_populated_inotify_fd = inotify_init1(IN_NONBLOCK|IN_CLOEXEC); + if (m->cgroup_populated_inotify_fd < 0) + return log_error_errno(errno, "inotify_init1() failed: %m"); + + r = sd_event_add_io(m->event, &m->cgroup_populated_event_source, m->cgroup_populated_inotify_fd, EPOLLIN, cgroup_populated_dispatch_io, m); + if (r < 0) + return log_error_errno(errno, "Failed to create inotify event source: %m"); + /* TODO what priority to set? */ + (void) sd_event_source_set_description(m->cgroup_populated_event_source, "cgroup-populated"); + } + } /* 7. Figure out which controllers are supported */ @@ -975,10 +999,63 @@ void manager_shutdown_cgroup(Manager *m, bool delete) { m->pin_cgroupfs_fd = safe_close(m->pin_cgroupfs_fd); + m->cgroup_populated_inotify_fd = safe_close(m->cgroup_populated_inotify_fd); + hashmap_free(m->cgroup_populated_by_wd); + free(m->cgroup_root); m->cgroup_root = NULL; } +static int cgroup_populated_dispatch_io(sd_event_source *source, int fd, uint32_t revents, void *userdata) { + char *path; + _cleanup_free_ char *v = NULL; + Manager *m = userdata; + int r; + int populated = -1; + + assert(m); + assert(revents & (EPOLLPRI | EPOLLIN)); + + if (fd != m->cgroup_populated_inotify_fd) + return 0; + + for (;;) { + union inotify_event_buffer buffer; + struct inotify_event *e; + ssize_t l; + + l = read(fd, &buffer, sizeof(buffer)); + if (l < 0) { + if (errno == EAGAIN || errno == EINTR) + break; + + log_error_errno(errno, "Failed to read cgroup_populated inotify: %m"); + break; + } + + FOREACH_INOTIFY_EVENT(e, buffer, l) { + path = hashmap_get(m->cgroup_populated_by_wd, INT_TO_PTR(e->wd)); + if (!path) + continue; + + r = cg_get_attribute(SYSTEMD_CGROUP_CONTROLLER, path, "cgroup.populated", &v); + if (r < 0) + continue; + + r = safe_atoi(v, &populated); + if (r < 0) + continue; + + if (populated == 0) { + manager_notify_cgroup_empty(m, path); + log_info("manager_notify_cgroup_empty %s", path); + } + } + } + + return 0; +} + Unit* manager_get_unit_by_cgroup(Manager *m, const char *cgroup) { char *p; Unit *u; diff --git a/src/core/manager.c b/src/core/manager.c index b931b0d..57065ce 100644 --- a/src/core/manager.c +++ b/src/core/manager.c @@ -567,7 +567,7 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) { m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1; - m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->utab_inotify_fd = -1; + m->pin_cgroupfs_fd = m->notify_fd = m->signal_fd = m->time_change_fd = m->dev_autofs_fd = m->private_listen_fd = m->kdbus_fd = m->utab_inotify_fd = m->cgroup_populated_inotify_fd = -1; m->current_job_id = 1; /* start as id #1, so that we can leave #0 around as "null-like" value */ m->ask_password_inotify_fd = -1; @@ -594,6 +594,10 @@ int manager_new(ManagerRunningAs running_as, bool test_run, Manager **_m) { if (r < 0) goto fail; + r = hashmap_ensure_allocated(&m->cgroup_populated_by_wd, NULL); + if (r < 0) + goto fail; + r = hashmap_ensure_allocated(&m->watch_bus, &string_hash_ops); if (r < 0) goto fail; @@ -967,6 +971,7 @@ Manager* manager_free(Manager *m) { strv_free(m->environment); hashmap_free(m->cgroup_unit); + hashmap_free(m->cgroup_populated_by_wd); set_free_free(m->unit_path_cache); free(m->switch_root); diff --git a/src/core/manager.h b/src/core/manager.h index 4ef869d..9ad9cd9 100644 --- a/src/core/manager.h +++ b/src/core/manager.h @@ -215,6 +215,9 @@ struct Manager { /* Data specific to the cgroup subsystem */ Hashmap *cgroup_unit; + int cgroup_populated_inotify_fd; + sd_event_source *cgroup_populated_event_source; + Hashmap *cgroup_populated_by_wd; CGroupControllerMask cgroup_supported; char *cgroup_root; diff --git a/src/core/service.c b/src/core/service.c index 07347b9..3926569 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -1175,7 +1175,7 @@ static int service_spawn( if (is_control && UNIT(s)->cgroup_path) { path = strjoina(UNIT(s)->cgroup_path, "/control"); - cg_create(SYSTEMD_CGROUP_CONTROLLER, path); + cg_create(SYSTEMD_CGROUP_CONTROLLER, path, -1, NULL); } else path = UNIT(s)->cgroup_path; diff --git a/src/shared/cgroup-util.c b/src/shared/cgroup-util.c index 0d99c16..589ab2e 100644 --- a/src/shared/cgroup-util.c +++ b/src/shared/cgroup-util.c @@ -591,8 +591,9 @@ int cg_delete(const char *controller, const char *path) { return r == -ENOENT ? 0 : r; } -int cg_create(const char *controller, const char *path) { +int cg_create(const char *controller, const char *path, const int inotify_fd, int *wd) { _cleanup_free_ char *fs = NULL; + _cleanup_free_ char *populated = NULL; int r; r = cg_get_path_and_check(controller, path, NULL, &fs); @@ -611,15 +612,30 @@ int cg_create(const char *controller, const char *path) { return -errno; } + if (inotify_fd < 0 || !wd) + goto out; + + r = cg_get_path(controller, path, "cgroup.populated", &populated); + if (r < 0) { + log_warning_errno(r, "Failed to get 'cgroup.populated' file, ignoring: %m"); + goto out; + } + + *wd = inotify_add_watch(inotify_fd, populated, IN_MODIFY); + + if (*wd < 0) + log_warning_errno(errno, "Failed to add 'cgroup.populated' watch, ignoring: %m"); + +out: return 1; } -int cg_create_and_attach(const char *controller, const char *path, pid_t pid) { +int cg_create_and_attach(const char *controller, const char *path, pid_t pid, const int inotify_fd, int *wd) { int r, q; assert(pid >= 0); - r = cg_create(controller, path); + r = cg_create(controller, path, inotify_fd, wd); if (r < 0) return r; @@ -1745,7 +1761,7 @@ static const char mask_names[] = "memory\0" "devices\0"; -int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path) { +int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path, const int inotify_fd, int *wd) { CGroupControllerMask bit = 1; const char *n; int r; @@ -1755,14 +1771,14 @@ int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask ma * in all others */ /* First create the cgroup in our own hierarchy. */ - r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path); + r = cg_create(SYSTEMD_CGROUP_CONTROLLER, path, inotify_fd, wd); if (r < 0) return r; /* Then, do the same in the other hierarchies */ NULSTR_FOREACH(n, mask_names) { if (mask & bit) - cg_create(n, path); + cg_create(n, path, -1, NULL); else if (supported & bit) cg_trim(n, path, true); diff --git a/src/shared/cgroup-util.h b/src/shared/cgroup-util.h index cbf7201..2c5bdad 100644 --- a/src/shared/cgroup-util.h +++ b/src/shared/cgroup-util.h @@ -79,10 +79,10 @@ int cg_trim(const char *controller, const char *path, bool delete_root); int cg_rmdir(const char *controller, const char *path); int cg_delete(const char *controller, const char *path); -int cg_create(const char *controller, const char *path); +int cg_create(const char *controller, const char *path, const int inotify_fd, int *wd); int cg_attach(const char *controller, const char *path, pid_t pid); int cg_attach_fallback(const char *controller, const char *path, pid_t pid); -int cg_create_and_attach(const char *controller, const char *path, pid_t pid); +int cg_create_and_attach(const char *controller, const char *path, pid_t pid, const int inotify_fd, int *wd); int cg_set_attribute(const char *controller, const char *path, const char *attribute, const char *value); int cg_get_attribute(const char *controller, const char *path, const char *attribute, char **ret); @@ -128,7 +128,7 @@ int cg_slice_to_path(const char *unit, char **ret); typedef const char* (*cg_migrate_callback_t)(CGroupControllerMask mask, void *userdata); -int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path); +int cg_create_everywhere(CGroupControllerMask supported, CGroupControllerMask mask, const char *path, const int inotify_fd, int *wd); int cg_attach_everywhere(CGroupControllerMask supported, const char *path, pid_t pid, cg_migrate_callback_t callback, void *userdata); int cg_attach_many_everywhere(CGroupControllerMask supported, const char *path, Set* pids, cg_migrate_callback_t callback, void *userdata); int cg_migrate_everywhere(CGroupControllerMask supported, const char *from, const char *to, cg_migrate_callback_t callback, void *userdata); diff --git a/src/test/test-cgroup.c b/src/test/test-cgroup.c index 4be69a4..720d02e 100644 --- a/src/test/test-cgroup.c +++ b/src/test/test-cgroup.c @@ -30,11 +30,11 @@ int main(int argc, char*argv[]) { char *path; char *c, *p; - assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0); - assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a") == 0); - assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b") == 0); - assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-c") == 0); - assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0) == 0); + assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a", -1, NULL) == 0); + assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-a", -1, NULL) == 0); + assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b", -1, NULL) == 0); + assert_se(cg_create(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-c", -1, NULL) == 0); + assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b", 0, -1, NULL) == 0); assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0); assert_se(streq(path, "/test-b")); @@ -46,7 +46,7 @@ int main(int argc, char*argv[]) { assert_se(path_equal(path, "/test-a")); free(path); - assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-d", 0) == 0); + assert_se(cg_create_and_attach(SYSTEMD_CGROUP_CONTROLLER, "/test-b/test-d", 0, -1, NULL) == 0); assert_se(cg_pid_get_path(SYSTEMD_CGROUP_CONTROLLER, getpid(), &path) == 0); assert_se(path_equal(path, "/test-b/test-d")); -- 2.1.4 _______________________________________________ systemd-devel mailing list systemd-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/systemd-devel