assert(u);
crt = unit_get_cgroup_runtime(u);
-
if (crt && streq_ptr(crt->cgroup_path, path))
return 0;
return 1;
}
+int unit_get_cgroup_path_with_fallback(const Unit *u, char **ret) {
+ assert(u);
+ assert(ret);
+
+ const CGroupRuntime *crt = unit_get_cgroup_runtime(u);
+ if (!crt || !crt->cgroup_path)
+ return unit_default_cgroup_path(u, ret);
+
+ return strdup_to_full(ret, crt->cgroup_path); /* returns 1 -> cgroup_path is alive */
+}
+
int unit_watch_cgroup(Unit *u) {
_cleanup_free_ char *events = NULL;
int r;
return 0;
}
-int unit_pick_cgroup_path(Unit *u) {
- _cleanup_free_ char *path = NULL;
- int r;
-
- assert(u);
-
- if (!UNIT_HAS_CGROUP_CONTEXT(u))
- return -EINVAL;
-
- CGroupRuntime *crt = unit_setup_cgroup_runtime(u);
- if (!crt)
- return -ENOMEM;
- if (crt->cgroup_path)
- return 0;
-
- r = unit_default_cgroup_path(u, &path);
- if (r < 0)
- return log_unit_error_errno(u, r, "Failed to generate default cgroup path: %m");
-
- r = unit_set_cgroup_path(u, path);
- if (r == -EEXIST)
- return log_unit_error_errno(u, r, "Control group %s exists already.", empty_to_root(path));
- if (r < 0)
- return log_unit_error_errno(u, r, "Failed to set unit's control group path to %s: %m", empty_to_root(path));
-
- return 0;
-}
-
static int unit_update_cgroup(
Unit *u,
CGroupMask target_mask,
CGroupMask enable_mask,
ManagerState state) {
- bool created;
- _cleanup_free_ char *cgroup_full_path = NULL;
+ _cleanup_free_ char *cgroup = NULL, *cgroup_full_path = NULL;
+ bool set_path, created;
int r;
assert(u);
if (u->freezer_state != FREEZER_RUNNING)
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EBUSY), "Cannot realize cgroup for frozen unit.");
- /* Figure out our cgroup path */
- r = unit_pick_cgroup_path(u);
+ r = unit_get_cgroup_path_with_fallback(u, &cgroup);
if (r < 0)
- return r;
-
- CGroupRuntime *crt = ASSERT_PTR(unit_get_cgroup_runtime(u));
+ return log_unit_error_errno(u, r, "Failed to get cgroup path: %m");
+ set_path = r == 0;
/* First, create our own group */
- r = cg_create(crt->cgroup_path);
+ r = cg_create(cgroup);
if (r < 0)
- return log_unit_error_errno(u, r, "Failed to create cgroup %s: %m", empty_to_root(crt->cgroup_path));
+ return log_unit_error_errno(u, r, "Failed to create cgroup %s: %m", empty_to_root(cgroup));
created = r;
+ if (set_path) {
+ r = unit_set_cgroup_path(u, cgroup);
+ if (r == -EEXIST)
+ return log_unit_error_errno(u, r, "Picked control group '%s' as default, but it's in use already.", empty_to_root(cgroup));
+ if (r < 0)
+ return log_unit_error_errno(u, r, "Failed to set unit's control group path to '%s': %m", empty_to_root(cgroup));
+ assert(r > 0);
+ }
+
+ CGroupRuntime *crt = ASSERT_PTR(unit_get_cgroup_runtime(u));
+
uint64_t cgroup_id = 0;
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, crt->cgroup_path, NULL, &cgroup_full_path);
if (r == 0) {
(void) unit_watch_cgroup_memory(u);
/* For v2 we preserve enabled controllers in delegated units, adjust others, */
- if (created || !crt->cgroup_realized || !unit_cgroup_delegate(u)) {
+ if (created || !unit_cgroup_delegate(u)) {
CGroupMask result_mask = 0;
/* Enable all controllers we need */
}
/* Keep track that this is now realized */
- crt->cgroup_realized = true;
crt->cgroup_realized_mask = target_mask;
/* Set attributes */
if (!unit_cgroup_delegate(u))
return -ENOMEDIUM;
- r = unit_pick_cgroup_path(u);
- if (r < 0)
- return r;
-
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (!crt || !crt->cgroup_path)
return -EOWNERDEAD;
* enabled through cgroup.subtree_control, and since the BPF pseudo-controllers don't show up there, they
* simply don't matter. */
- return crt->cgroup_realized &&
+ return crt->cgroup_path &&
((crt->cgroup_realized_mask ^ target_mask) & CGROUP_MASK_V1) == 0 &&
((crt->cgroup_enabled_mask ^ enable_mask) & CGROUP_MASK_V2) == 0 &&
crt->cgroup_invalidated_mask == 0;
* Unlike unit_has_mask_realized, we don't care what was enabled, only that anything we want to remove is
* already removed. */
- return !crt->cgroup_realized ||
+ return !crt->cgroup_path ||
(FLAGS_SET(crt->cgroup_realized_mask, target_mask & CGROUP_MASK_V1) &&
FLAGS_SET(crt->cgroup_enabled_mask, enable_mask & CGROUP_MASK_V2));
}
* Unlike unit_has_mask_realized, we don't care about the controllers that are not present, only that anything
* we want to add is already added. */
- return crt->cgroup_realized &&
+ return crt->cgroup_path &&
((crt->cgroup_realized_mask | target_mask) & CGROUP_MASK_V1) == (crt->cgroup_realized_mask & CGROUP_MASK_V1) &&
((crt->cgroup_enabled_mask | enable_mask) & CGROUP_MASK_V2) == (crt->cgroup_enabled_mask & CGROUP_MASK_V2);
}
/* The cgroup for this unit might not actually be fully realised yet, in which case it isn't
* holding any controllers open anyway. */
- if (!rt->cgroup_realized)
+ if (!rt->cgroup_path)
continue;
/* We must disable those below us first in order to release the controller. */
/* We only enqueue siblings if they were realized once at least, in the main
* hierarchy. */
crt = unit_get_cgroup_runtime(m);
- if (!crt || !crt->cgroup_realized)
+ if (!crt || !crt->cgroup_path)
continue;
/* If the unit doesn't need any new controllers and has current ones
assert(crt == unit_get_cgroup_runtime(u));
assert(!crt->cgroup_path);
- crt->cgroup_realized = false;
crt->cgroup_realized_mask = 0;
crt->cgroup_enabled_mask = 0;
.ipv6_deny_map_fd = -EBADF,
.cgroup_invalidated_mask = _CGROUP_MASK_ALL,
+
+ .deserialized_cgroup_realized = -1,
};
unit_reset_cpu_accounting(/* unit = */ NULL, crt);
if (crt->cgroup_id != 0)
(void) serialize_item_format(f, "cgroup-id", "%" PRIu64, crt->cgroup_id);
- (void) serialize_bool(f, "cgroup-realized", crt->cgroup_realized);
(void) serialize_cgroup_mask(f, "cgroup-realized-mask", crt->cgroup_realized_mask);
(void) serialize_cgroup_mask(f, "cgroup-enabled-mask", crt->cgroup_enabled_mask);
(void) serialize_cgroup_mask(f, "cgroup-invalidated-mask", crt->cgroup_invalidated_mask);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", value);
- (void) unit_watch_cgroup(u);
- (void) unit_watch_cgroup_memory(u);
return 1;
}
if (MATCH_DESERIALIZE_IMMEDIATE(u, "cgroup-id", key, value, safe_atou64, cgroup_id))
return 1;
- if (MATCH_DESERIALIZE(u, "cgroup-realized", key, value, parse_boolean, cgroup_realized))
+ if (MATCH_DESERIALIZE_IMMEDIATE(u, "cgroup-realized", key, value, parse_tristate, deserialized_cgroup_realized))
return 1;
if (MATCH_DESERIALIZE_IMMEDIATE(u, "cgroup-realized-mask", key, value, cg_mask_from_string, cgroup_realized_mask))
/* Let's make sure that everything that is deserialized also gets any potential new cgroup settings
* applied after we are done. For that we invalidate anything already realized, so that we can
* realize it again. */
- CGroupRuntime *crt;
- crt = unit_get_cgroup_runtime(u);
- if (crt && crt->cgroup_realized) {
- unit_invalidate_cgroup(u, _CGROUP_MASK_ALL);
- unit_invalidate_cgroup_bpf(u);
+ CGroupRuntime *crt = unit_get_cgroup_runtime(u);
+ if (crt && crt->cgroup_path) {
+ /* Since v258, CGroupRuntime.cgroup_path is coupled with cgroup realized state, which however
+ * wasn't the case in prior versions with the realized state tracked in a discrete field.
+ * Patch cgroup_realized == 0 back to no cgroup_path here hence. */
+ if (crt->deserialized_cgroup_realized == 0)
+ unit_release_cgroup(u, /* drop_cgroup_runtime = */ false);
+ else {
+ unit_invalidate_cgroup(u, _CGROUP_MASK_ALL);
+ unit_invalidate_cgroup_bpf(u);
+ }
}
return 0;
fprintf(f,
"%s\tSlice: %s\n"
- "%s\tCGroup: %s\n"
- "%s\tCGroup realized: %s\n",
+ "%s\tCGroup: %s\n",
prefix, strna(unit_slice_name(u)),
- prefix, strna(crt ? crt->cgroup_path : NULL),
- prefix, yes_no(crt ? crt->cgroup_realized : false));
+ prefix, strna(crt ? crt->cgroup_path : NULL));
if (crt && crt->cgroup_realized_mask != 0) {
_cleanup_free_ char *s = NULL;