return u->freezer_state;
}
-int unit_freezer_state_kernel(Unit *u, FreezerState *ret) {
- char *values[1] = {};
- int r;
-
- assert(u);
-
- r = cg_get_keyed_attribute(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events",
- STRV_MAKE("frozen"), values);
- if (r < 0)
- return r;
-
- r = _FREEZER_STATE_INVALID;
-
- if (values[0]) {
- if (streq(values[0], "0"))
- r = FREEZER_RUNNING;
- else if (streq(values[0], "1"))
- r = FREEZER_FROZEN;
- }
-
- free(values[0]);
- *ret = r;
-
- return 0;
-}
-
UnitActiveState unit_active_state(Unit *u) {
assert(u);
return unit_start(following, details);
}
+ /* Check to make sure the unit isn't frozen */
+ if (u->freezer_state != FREEZER_RUNNING)
+ return -EDEADLK;
+
/* Check our ability to start early so that failure conditions don't cause us to enter a busy loop. */
if (UNIT_VTABLE(u)->can_start) {
r = UNIT_VTABLE(u)->can_start(u);
* waits for a holdoff timer to elapse before it will start again. */
unit_add_to_dbus_queue(u);
- unit_cgroup_freezer_action(u, FREEZER_THAW);
if (!u->activation_details) /* Older details object wins */
u->activation_details = activation_details_ref(details);
* -EBADR: This unit type does not support stopping.
* -EALREADY: Unit is already stopped.
* -EAGAIN: An operation is already in progress. Retry later.
+ * -EDEADLK: Unit is frozen
*/
int unit_stop(Unit *u) {
UnitActiveState state;
return unit_stop(following);
}
+ /* Check to make sure the unit isn't frozen */
+ if (u->freezer_state != FREEZER_RUNNING)
+ return -EDEADLK;
+
if (!UNIT_VTABLE(u)->stop)
return -EBADR;
unit_add_to_dbus_queue(u);
- unit_cgroup_freezer_action(u, FREEZER_THAW);
return UNIT_VTABLE(u)->stop(u);
}
* -EBADR: This unit type does not support reloading.
* -ENOEXEC: Unit is not started.
* -EAGAIN: An operation is already in progress. Retry later.
+ * -EDEADLK: Unit is frozen.
*/
int unit_reload(Unit *u) {
UnitActiveState state;
return unit_reload(following);
}
+ /* Check to make sure the unit isn't frozen */
+ if (u->freezer_state != FREEZER_RUNNING)
+ return -EDEADLK;
+
unit_add_to_dbus_queue(u);
if (!UNIT_VTABLE(u)->reload) {
return 0;
}
- unit_cgroup_freezer_action(u, FREEZER_THAW);
-
return UNIT_VTABLE(u)->reload(u);
}
return unit_can_isolate(u) && !u->refuse_manual_start;
}
+void unit_next_freezer_state(Unit *u, FreezerAction action, FreezerState *ret, FreezerState *ret_target) {
+ Unit *slice;
+ FreezerState curr, parent, next, tgt;
+
+ assert(u);
+ assert(IN_SET(action, FREEZER_FREEZE, FREEZER_PARENT_FREEZE,
+ FREEZER_THAW, FREEZER_PARENT_THAW));
+ assert(ret);
+ assert(ret_target);
+
+ /* This function determintes the correct freezer state transitions for a unit
+ * given the action being requested. It returns the next state, and also the "target",
+ * which is either FREEZER_FROZEN or FREEZER_RUNNING, depending on what actual state we
+ * ultimately want to achieve. */
+
+ curr = u->freezer_state;
+ slice = UNIT_GET_SLICE(u);
+ if (slice)
+ parent = slice->freezer_state;
+ else
+ parent = FREEZER_RUNNING;
+
+ if (action == FREEZER_FREEZE) {
+ /* We always "promote" a freeze initiated by parent into a normal freeze */
+ if (IN_SET(curr, FREEZER_FROZEN, FREEZER_FROZEN_BY_PARENT))
+ next = FREEZER_FROZEN;
+ else
+ next = FREEZER_FREEZING;
+ } else if (action == FREEZER_THAW) {
+ /* Thawing is the most complicated operation here, because we can't thaw a unit
+ * if its parent is frozen. So we instead "demote" a normal freeze into a freeze
+ * initiated by parent if the parent is frozen */
+ if (IN_SET(curr, FREEZER_RUNNING, FREEZER_THAWING, FREEZER_FREEZING_BY_PARENT, FREEZER_FROZEN_BY_PARENT))
+ next = curr;
+ else if (curr == FREEZER_FREEZING) {
+ if (IN_SET(parent, FREEZER_RUNNING, FREEZER_THAWING))
+ next = FREEZER_THAWING;
+ else
+ next = FREEZER_FREEZING_BY_PARENT;
+ } else {
+ assert(curr == FREEZER_FROZEN);
+ if (IN_SET(parent, FREEZER_RUNNING, FREEZER_THAWING))
+ next = FREEZER_THAWING;
+ else
+ next = FREEZER_FROZEN_BY_PARENT;
+ }
+ } else if (action == FREEZER_PARENT_FREEZE) {
+ /* We need to avoid accidentally demoting units frozen manually */
+ if (IN_SET(curr, FREEZER_FREEZING, FREEZER_FROZEN, FREEZER_FROZEN_BY_PARENT))
+ next = curr;
+ else
+ next = FREEZER_FREEZING_BY_PARENT;
+ } else {
+ assert(action == FREEZER_PARENT_THAW);
+
+ /* We don't want to thaw units from a parent if they were frozen
+ * manually, so for such units this action is a no-op */
+ if (IN_SET(curr, FREEZER_RUNNING, FREEZER_FREEZING, FREEZER_FROZEN))
+ next = curr;
+ else
+ next = FREEZER_THAWING;
+ }
+
+ tgt = freezer_state_finish(next);
+ if (tgt == FREEZER_FROZEN_BY_PARENT)
+ tgt = FREEZER_FROZEN;
+ assert(IN_SET(tgt, FREEZER_RUNNING, FREEZER_FROZEN));
+
+ *ret = next;
+ *ret_target = tgt;
+}
+
bool unit_can_freeze(Unit *u) {
assert(u);
+ if (unit_has_name(u, SPECIAL_ROOT_SLICE) || unit_has_name(u, SPECIAL_INIT_SCOPE))
+ return false;
+
if (UNIT_VTABLE(u)->can_freeze)
return UNIT_VTABLE(u)->can_freeze(u);
- return UNIT_VTABLE(u)->freeze;
+ return UNIT_VTABLE(u)->freezer_action;
}
void unit_frozen(Unit *u) {
assert(u);
- u->freezer_state = FREEZER_FROZEN;
+ u->freezer_state = u->freezer_state == FREEZER_FREEZING_BY_PARENT
+ ? FREEZER_FROZEN_BY_PARENT
+ : FREEZER_FROZEN;
+
+ log_unit_debug(u, "Unit now %s.", freezer_state_to_string(u->freezer_state));
bus_unit_send_pending_freezer_message(u, false);
}
u->freezer_state = FREEZER_RUNNING;
+ log_unit_debug(u, "Unit thawed.");
+
bus_unit_send_pending_freezer_message(u, false);
}
-static int unit_freezer_action(Unit *u, FreezerAction action) {
+int unit_freezer_action(Unit *u, FreezerAction action) {
UnitActiveState s;
- int (*method)(Unit*);
int r;
assert(u);
assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
- method = action == FREEZER_FREEZE ? UNIT_VTABLE(u)->freeze : UNIT_VTABLE(u)->thaw;
- if (!method || !cg_freezer_supported())
+ if (!cg_freezer_supported() || !unit_can_freeze(u))
return -EOPNOTSUPP;
if (u->job)
if (s != UNIT_ACTIVE)
return -EHOSTDOWN;
- if ((IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING) && action == FREEZER_FREEZE) ||
- (u->freezer_state == FREEZER_THAWING && action == FREEZER_THAW))
+ if (action == FREEZER_FREEZE && IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_FREEZING_BY_PARENT))
+ return -EALREADY;
+ if (action == FREEZER_THAW && u->freezer_state == FREEZER_THAWING)
return -EALREADY;
+ if (action == FREEZER_THAW && IN_SET(u->freezer_state, FREEZER_FREEZING_BY_PARENT, FREEZER_FROZEN_BY_PARENT))
+ return -ECHILD;
- r = method(u);
+ r = UNIT_VTABLE(u)->freezer_action(u, action);
if (r <= 0)
return r;
- assert(IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING));
-
+ assert(IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_FREEZING_BY_PARENT, FREEZER_THAWING));
return 1;
}
-int unit_freeze(Unit *u) {
- return unit_freezer_action(u, FREEZER_FREEZE);
-}
-
-int unit_thaw(Unit *u) {
- return unit_freezer_action(u, FREEZER_THAW);
-}
-
-/* Wrappers around low-level cgroup freezer operations common for service and scope units */
-int unit_freeze_vtable_common(Unit *u) {
- return unit_cgroup_freezer_action(u, FREEZER_FREEZE);
-}
-
-int unit_thaw_vtable_common(Unit *u) {
- return unit_cgroup_freezer_action(u, FREEZER_THAW);
-}
-
Condition *unit_find_failed_condition(Unit *u) {
Condition *failed_trigger = NULL;
bool has_succeeded_trigger = false;