diff --git a/src/basic/unit-def.c b/src/basic/unit-def.c index 04b096cd19..a2b2f9d6f0 100644 --- a/src/basic/unit-def.c +++ b/src/basic/unit-def.c @@ -144,6 +144,16 @@ FreezerState freezer_state_finish(FreezerState state) { return freezer_state_finish_table[state]; } +FreezerState freezer_state_objective(FreezerState state) { + FreezerState objective; + + objective = freezer_state_finish(state); + if (objective == FREEZER_FROZEN_BY_PARENT) + objective = FREEZER_FROZEN; + + return objective; +} + static const char* const unit_marker_table[_UNIT_MARKER_MAX] = { [UNIT_MARKER_NEEDS_RELOAD] = "needs-reload", [UNIT_MARKER_NEEDS_RESTART] = "needs-restart", diff --git a/src/basic/unit-def.h b/src/basic/unit-def.h index e0b1c6730d..7ae086c0b9 100644 --- a/src/basic/unit-def.h +++ b/src/basic/unit-def.h @@ -325,6 +325,7 @@ UnitActiveState unit_active_state_from_string(const char *s) _pure_; const char* freezer_state_to_string(FreezerState i) _const_; FreezerState freezer_state_from_string(const char *s) _pure_; FreezerState freezer_state_finish(FreezerState i) _const_; +FreezerState freezer_state_objective(FreezerState state) _const_; const char* unit_marker_to_string(UnitMarker m) _const_; UnitMarker unit_marker_from_string(const char *s) _pure_; diff --git a/src/core/service.c b/src/core/service.c index c8cda06099..09276fc4ee 100644 --- a/src/core/service.c +++ b/src/core/service.c @@ -414,7 +414,7 @@ static void service_extend_timeout(Service *s, usec_t extend_timeout_usec) { static void service_reset_watchdog(Service *s) { assert(s); - if (freezer_state_finish(UNIT(s)->freezer_state) != FREEZER_RUNNING) { + if (freezer_state_objective(UNIT(s)->freezer_state) != FREEZER_RUNNING) { log_unit_debug(UNIT(s), "Service is currently %s, skipping resetting watchdog.", freezer_state_to_string(UNIT(s)->freezer_state)); return; @@ -1425,7 +1425,7 @@ static int service_coldplug(Unit *u) { (void) unit_setup_exec_runtime(u); if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY, SERVICE_REFRESH_EXTENSIONS, SERVICE_MOUNTING) && - freezer_state_finish(u->freezer_state) == FREEZER_RUNNING) + freezer_state_objective(u->freezer_state) == FREEZER_RUNNING) service_start_watchdog(s); if (UNIT_ISSET(s->accept_socket)) { @@ -5643,18 +5643,33 @@ int service_determine_exec_selinux_label(Service *s, char **ret) { return 0; } -static int service_cgroup_freezer_action(Unit *u, FreezerAction action) { +static int service_freezer_action(Unit *u, FreezerAction action) { Service *s = ASSERT_PTR(SERVICE(u)); + FreezerState old_objective, new_objective; int r; + old_objective = freezer_state_objective(u->freezer_state); + r = unit_cgroup_freezer_action(u, action); - if (r <= 0) + if (r < 0) return r; - if (action == FREEZER_FREEZE) - service_stop_watchdog(s); - else if (action == FREEZER_THAW) - service_reset_watchdog(s); + new_objective = freezer_state_objective(u->freezer_state); + + /* Note that we cannot trivially check the retval of unit_cgroup_freezer_action() here, since + * that signals whether the operation is ongoing from *kernel's PoV*. If the freeze operation + * is aborted, the frozen attribute of the cgroup would never have been flipped in kernel, + * and unit_cgroup_freezer_action() will happily return 0, yet the watchdog still needs to be reset; + * vice versa. */ + + if (old_objective != new_objective) { + if (new_objective == FREEZER_FROZEN) + service_stop_watchdog(s); + else if (new_objective == FREEZER_RUNNING) + service_reset_watchdog(s); + else + assert_not_reached(); + } return r; } @@ -5796,7 +5811,7 @@ const UnitVTable service_vtable = { .live_mount = service_live_mount, .can_live_mount = service_can_live_mount, - .freezer_action = service_cgroup_freezer_action, + .freezer_action = service_freezer_action, .serialize = service_serialize, .deserialize_item = service_deserialize_item, diff --git a/src/core/unit.c b/src/core/unit.c index a4ddf4d91e..da02a252e5 100644 --- a/src/core/unit.c +++ b/src/core/unit.c @@ -6438,9 +6438,7 @@ void unit_next_freezer_state(Unit *u, FreezerAction action, FreezerState *ret_ne assert_not_reached(); } - objective = freezer_state_finish(next); - if (objective == FREEZER_FROZEN_BY_PARENT) - objective = FREEZER_FROZEN; + objective = freezer_state_objective(next); assert(IN_SET(objective, FREEZER_RUNNING, FREEZER_FROZEN)); *ret_next = next;