}
}
+bool unit_maybe_release_cgroup(Unit *u) {
+ int r;
+
+ assert(u);
+
+ if (!u->cgroup_path)
+ return true;
+
+ /* Don't release the cgroup if there are still processes under it. If we get notified later when all the
+ * processes exit (e.g. the processes were in D-state and exited after the unit was marked as failed)
+ * we need the cgroup paths to continue to be tracked by the manager so they can be looked up and cleaned
+ * up later. */
+ r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
+ if (r < 0)
+ log_unit_debug_errno(u, r, "Error checking if the cgroup is recursively empty, ignoring: %m");
+ else if (r == 1) {
+ unit_release_cgroup(u);
+ return true;
+ }
+
+ return false;
+}
+
void unit_prune_cgroup(Unit *u) {
int r;
bool is_root_slice;
if (is_root_slice)
return;
- unit_release_cgroup(u);
+ if (!unit_maybe_release_cgroup(u)) /* Returns true if the cgroup was released */
+ return;
u->cgroup_realized = false;
u->cgroup_realized_mask = 0;
int unit_pick_cgroup_path(Unit *u);
int unit_realize_cgroup(Unit *u);
-void unit_release_cgroup(Unit *u);
void unit_prune_cgroup(Unit *u);
int unit_watch_cgroup(Unit *u);
int unit_watch_cgroup_memory(Unit *u);
+void unit_release_cgroup(Unit *u);
+/* Releases the cgroup only if it is recursively empty.
+ * Returns true if the cgroup was released, false otherwise. */
+bool unit_maybe_release_cgroup(Unit *u);
+
void unit_add_to_cgroup_empty_queue(Unit *u);
int unit_check_oomd_kill(Unit *u);
int unit_check_oom(Unit *u);
if (IN_SET(s->state, SCOPE_RUNNING, SCOPE_ABANDONED, SCOPE_STOP_SIGTERM, SCOPE_STOP_SIGKILL))
scope_enter_dead(s, SCOPE_SUCCESS);
+
+ /* If the cgroup empty notification comes when the unit is not active, we must have failed to clean
+ * up the cgroup earlier and should do it now. */
+ if (IN_SET(s->state, SCOPE_DEAD, SCOPE_FAILED))
+ unit_prune_cgroup(u);
}
static void scope_sigchld_event(Unit *u, pid_t pid, int code, int status) {
break;
+ /* If the cgroup empty notification comes when the unit is not active, we must have failed to clean
+ * up the cgroup earlier and should do it now. */
+ case SERVICE_DEAD:
+ case SERVICE_FAILED:
+ unit_prune_cgroup(u);
+ break;
+
default:
;
}