Merge pull request #13512 from msekletar/freezer
core: introduce support for cgroup freezer
This commit is contained in:
+95
-1
@@ -16,7 +16,9 @@
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "fs-util.h"
|
||||
#include "io-util.h"
|
||||
#include "limits-util.h"
|
||||
#include "nulstr-util.h"
|
||||
#include "parse-util.h"
|
||||
#include "path-util.h"
|
||||
#include "process-util.h"
|
||||
@@ -2661,6 +2663,16 @@ void unit_add_to_cgroup_empty_queue(Unit *u) {
|
||||
log_debug_errno(r, "Failed to enable cgroup empty event source: %m");
|
||||
}
|
||||
|
||||
static void unit_remove_from_cgroup_empty_queue(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
if (!u->in_cgroup_empty_queue)
|
||||
return;
|
||||
|
||||
LIST_REMOVE(cgroup_empty_queue, u->manager->cgroup_empty_queue, u);
|
||||
u->in_cgroup_empty_queue = false;
|
||||
}
|
||||
|
||||
int unit_check_oom(Unit *u) {
|
||||
_cleanup_free_ char *oom_kill = NULL;
|
||||
bool increased;
|
||||
@@ -2761,6 +2773,41 @@ static void unit_add_to_cgroup_oom_queue(Unit *u) {
|
||||
log_error_errno(r, "Failed to enable cgroup oom event source: %m");
|
||||
}
|
||||
|
||||
static int unit_check_cgroup_events(Unit *u) {
|
||||
char *values[2] = {};
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
r = cg_get_keyed_attribute_graceful(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.events",
|
||||
STRV_MAKE("populated", "frozen"), values);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
/* The cgroup.events notifications can be merged together so act as we saw the given state for the
|
||||
* first time. The functions we call to handle given state are idempotent, which makes them
|
||||
* effectively remember the previous state. */
|
||||
if (values[0]) {
|
||||
if (streq(values[0], "1"))
|
||||
unit_remove_from_cgroup_empty_queue(u);
|
||||
else
|
||||
unit_add_to_cgroup_empty_queue(u);
|
||||
}
|
||||
|
||||
/* Disregard freezer state changes due to operations not initiated by us */
|
||||
if (values[1] && IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING)) {
|
||||
if (streq(values[1], "0"))
|
||||
unit_thawed(u);
|
||||
else
|
||||
unit_frozen(u);
|
||||
}
|
||||
|
||||
free(values[0]);
|
||||
free(values[1]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
|
||||
Manager *m = userdata;
|
||||
|
||||
@@ -2797,7 +2844,7 @@ static int on_cgroup_inotify_event(sd_event_source *s, int fd, uint32_t revents,
|
||||
|
||||
u = hashmap_get(m->cgroup_control_inotify_wd_unit, INT_TO_PTR(e->wd));
|
||||
if (u)
|
||||
unit_add_to_cgroup_empty_queue(u);
|
||||
unit_check_cgroup_events(u);
|
||||
|
||||
u = hashmap_get(m->cgroup_memory_inotify_wd_unit, INT_TO_PTR(e->wd));
|
||||
if (u)
|
||||
@@ -3550,6 +3597,46 @@ int compare_job_priority(const void *a, const void *b) {
|
||||
return strcmp(x->unit->id, y->unit->id);
|
||||
}
|
||||
|
||||
int unit_cgroup_freezer_action(Unit *u, FreezerAction action) {
|
||||
_cleanup_free_ char *path = NULL;
|
||||
FreezerState target, kernel = _FREEZER_STATE_INVALID;
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
|
||||
|
||||
if (!u->cgroup_realized)
|
||||
return -EBUSY;
|
||||
|
||||
target = action == FREEZER_FREEZE ? FREEZER_FROZEN : FREEZER_RUNNING;
|
||||
|
||||
r = unit_freezer_state_kernel(u, &kernel);
|
||||
if (r < 0)
|
||||
log_unit_debug_errno(u, r, "Failed to obtain cgroup freezer state: %m");
|
||||
|
||||
if (target == kernel) {
|
||||
u->freezer_state = target;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, "cgroup.freeze", &path);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
log_unit_debug(u, "%s unit.", action == FREEZER_FREEZE ? "Freezing" : "Thawing");
|
||||
|
||||
if (action == FREEZER_FREEZE)
|
||||
u->freezer_state = FREEZER_FREEZING;
|
||||
else
|
||||
u->freezer_state = FREEZER_THAWING;
|
||||
|
||||
r = write_string_file(path, one_zero(action == FREEZER_FREEZE), WRITE_STRING_FILE_DISABLE_BUFFER);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char* const cgroup_device_policy_table[_CGROUP_DEVICE_POLICY_MAX] = {
|
||||
[CGROUP_DEVICE_POLICY_AUTO] = "auto",
|
||||
[CGROUP_DEVICE_POLICY_CLOSED] = "closed",
|
||||
@@ -3585,3 +3672,10 @@ int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name) {
|
||||
}
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(cgroup_device_policy, CGroupDevicePolicy);
|
||||
|
||||
static const char* const freezer_action_table[_FREEZER_ACTION_MAX] = {
|
||||
[FREEZER_FREEZE] = "freeze",
|
||||
[FREEZER_THAW] = "thaw",
|
||||
};
|
||||
|
||||
DEFINE_STRING_TABLE_LOOKUP(freezer_action, FreezerAction);
|
||||
|
||||
@@ -47,6 +47,14 @@ typedef enum CGroupDevicePolicy {
|
||||
_CGROUP_DEVICE_POLICY_INVALID = -1
|
||||
} CGroupDevicePolicy;
|
||||
|
||||
typedef enum FreezerAction {
|
||||
FREEZER_FREEZE,
|
||||
FREEZER_THAW,
|
||||
|
||||
_FREEZER_ACTION_MAX,
|
||||
_FREEZER_ACTION_INVALID = -1,
|
||||
} FreezerAction;
|
||||
|
||||
struct CGroupDeviceAllow {
|
||||
LIST_FIELDS(CGroupDeviceAllow, device_allow);
|
||||
char *path;
|
||||
@@ -274,3 +282,7 @@ bool unit_cgroup_delegate(Unit *u);
|
||||
int compare_job_priority(const void *a, const void *b);
|
||||
|
||||
int unit_get_cpuset(Unit *u, CPUSet *cpus, const char *name);
|
||||
int unit_cgroup_freezer_action(Unit *u, FreezerAction action);
|
||||
|
||||
const char* freezer_action_to_string(FreezerAction a) _const_;
|
||||
FreezerAction freezer_action_from_string(const char *s) _pure_;
|
||||
|
||||
@@ -620,6 +620,14 @@ static int method_clean_unit(sd_bus_message *message, void *userdata, sd_bus_err
|
||||
return method_generic_unit_operation(message, userdata, error, bus_unit_method_clean, GENERIC_UNIT_LOAD|GENERIC_UNIT_VALIDATE_LOADED);
|
||||
}
|
||||
|
||||
static int method_freeze_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
return method_generic_unit_operation(message, userdata, error, bus_unit_method_freeze, 0);
|
||||
}
|
||||
|
||||
static int method_thaw_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
return method_generic_unit_operation(message, userdata, error, bus_unit_method_thaw, 0);
|
||||
}
|
||||
|
||||
static int method_reset_failed_unit(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
/* Don't load the unit (because unloaded units can't be in failed state), and don't insist on the
|
||||
* unit to be loaded properly (since a failed unit might have its unit file disappeared) */
|
||||
@@ -2584,6 +2592,18 @@ const sd_bus_vtable bus_manager_vtable[] = {
|
||||
NULL,,
|
||||
method_clean_unit,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_NAMES("FreezeUnit",
|
||||
"s",
|
||||
SD_BUS_PARAM(name),
|
||||
NULL,,
|
||||
method_freeze_unit,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_NAMES("ThawUnit",
|
||||
"s",
|
||||
SD_BUS_PARAM(name),
|
||||
NULL,,
|
||||
method_thaw_unit,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD_WITH_NAMES("ResetFailedUnit",
|
||||
"s",
|
||||
SD_BUS_PARAM(name),
|
||||
|
||||
@@ -46,12 +46,14 @@ static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_job_mode, job_mode, JobMode);
|
||||
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_emergency_action, emergency_action, EmergencyAction);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_description, "s", Unit, unit_description);
|
||||
static BUS_DEFINE_PROPERTY_GET2(property_get_active_state, "s", Unit, unit_active_state, unit_active_state_to_string);
|
||||
static BUS_DEFINE_PROPERTY_GET2(property_get_freezer_state, "s", Unit, unit_freezer_state, freezer_state_to_string);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_sub_state, "s", Unit, unit_sub_state_to_string);
|
||||
static BUS_DEFINE_PROPERTY_GET2(property_get_unit_file_state, "s", Unit, unit_get_unit_file_state, unit_file_state_to_string);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_can_reload, "b", Unit, unit_can_reload);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_can_start, "b", Unit, unit_can_start_refuse_manual);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_can_stop, "b", Unit, unit_can_stop_refuse_manual);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_can_isolate, "b", Unit, unit_can_isolate_refuse_manual);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_can_freeze, "b", Unit, unit_can_freeze);
|
||||
static BUS_DEFINE_PROPERTY_GET(property_get_need_daemon_reload, "b", Unit, unit_need_daemon_reload);
|
||||
static BUS_DEFINE_PROPERTY_GET_GLOBAL(property_get_empty_strv, "as", 0);
|
||||
|
||||
@@ -724,6 +726,79 @@ int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error
|
||||
return sd_bus_reply_method_return(message, NULL);
|
||||
}
|
||||
|
||||
static int bus_unit_method_freezer_generic(sd_bus_message *message, void *userdata, sd_bus_error *error, FreezerAction action) {
|
||||
const char* perm;
|
||||
int (*method)(Unit*);
|
||||
Unit *u = userdata;
|
||||
bool reply_no_delay = false;
|
||||
int r;
|
||||
|
||||
assert(message);
|
||||
assert(u);
|
||||
assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
|
||||
|
||||
if (action == FREEZER_FREEZE) {
|
||||
perm = "stop";
|
||||
method = unit_freeze;
|
||||
} else {
|
||||
perm = "start";
|
||||
method = unit_thaw;
|
||||
}
|
||||
|
||||
r = mac_selinux_unit_access_check(u, message, perm, error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
r = bus_verify_manage_units_async_full(
|
||||
u,
|
||||
perm,
|
||||
CAP_SYS_ADMIN,
|
||||
N_("Authentication is required to freeze or thaw the processes of '$(unit)' unit."),
|
||||
true,
|
||||
message,
|
||||
error);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
return 1; /* No authorization for now, but the async polkit stuff will call us again when it has it */
|
||||
|
||||
r = method(u);
|
||||
if (r == -EOPNOTSUPP)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_NOT_SUPPORTED, "Unit '%s' does not support freezing.", u->id);
|
||||
if (r == -EBUSY)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_UNIT_BUSY, "Unit has a pending job.");
|
||||
if (r == -EHOSTDOWN)
|
||||
return sd_bus_error_setf(error, BUS_ERROR_UNIT_INACTIVE, "Unit is inactive.");
|
||||
if (r == -EALREADY)
|
||||
return sd_bus_error_setf(error, SD_BUS_ERROR_FAILED, "Previously requested freezer operation for unit '%s' is still in progress.", u->id);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (r == 0)
|
||||
reply_no_delay = true;
|
||||
|
||||
assert(!u->pending_freezer_message);
|
||||
|
||||
r = sd_bus_message_new_method_return(message, &u->pending_freezer_message);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (reply_no_delay) {
|
||||
r = bus_unit_send_pending_freezer_message(u);
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
return bus_unit_method_freezer_generic(message, userdata, error, FREEZER_THAW);
|
||||
}
|
||||
|
||||
int bus_unit_method_freeze(sd_bus_message *message, void *userdata, sd_bus_error *error) {
|
||||
return bus_unit_method_freezer_generic(message, userdata, error, FREEZER_FREEZE);
|
||||
}
|
||||
|
||||
static int property_get_refs(
|
||||
sd_bus *bus,
|
||||
const char *path,
|
||||
@@ -793,6 +868,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
|
||||
SD_BUS_PROPERTY("Description", "s", property_get_description, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("LoadState", "s", property_get_load_state, offsetof(Unit, load_state), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("ActiveState", "s", property_get_active_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("FreezerState", "s", property_get_freezer_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("SubState", "s", property_get_sub_state, 0, SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("FragmentPath", "s", NULL, offsetof(Unit, fragment_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("SourcePath", "s", NULL, offsetof(Unit, source_path), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@@ -809,6 +885,7 @@ const sd_bus_vtable bus_unit_vtable[] = {
|
||||
SD_BUS_PROPERTY("CanReload", "b", property_get_can_reload, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("CanIsolate", "b", property_get_can_isolate, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("CanClean", "as", property_get_can_clean, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("CanFreeze", "b", property_get_can_freeze, 0, SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("Job", "(uo)", property_get_job, offsetof(Unit, job), SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE),
|
||||
SD_BUS_PROPERTY("StopWhenUnneeded", "b", bus_property_get_bool, offsetof(Unit, stop_when_unneeded), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
SD_BUS_PROPERTY("RefuseManualStart", "b", bus_property_get_bool, offsetof(Unit, refuse_manual_start), SD_BUS_VTABLE_PROPERTY_CONST),
|
||||
@@ -940,6 +1017,16 @@ const sd_bus_vtable bus_unit_vtable[] = {
|
||||
NULL,,
|
||||
bus_unit_method_clean,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("Freeze",
|
||||
NULL,
|
||||
NULL,
|
||||
bus_unit_method_freeze,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
SD_BUS_METHOD("Thaw",
|
||||
NULL,
|
||||
NULL,
|
||||
bus_unit_method_thaw,
|
||||
SD_BUS_VTABLE_UNPRIVILEGED),
|
||||
|
||||
/* For dependency types we don't support anymore always return an empty array */
|
||||
SD_BUS_PROPERTY("RequiresOverridable", "as", property_get_empty_strv, 0, SD_BUS_VTABLE_HIDDEN),
|
||||
@@ -1566,6 +1653,23 @@ void bus_unit_send_pending_change_signal(Unit *u, bool including_new) {
|
||||
bus_unit_send_change_signal(u);
|
||||
}
|
||||
|
||||
int bus_unit_send_pending_freezer_message(Unit *u) {
|
||||
int r;
|
||||
|
||||
assert(u);
|
||||
|
||||
if (!u->pending_freezer_message)
|
||||
return 0;
|
||||
|
||||
r = sd_bus_send(NULL, u->pending_freezer_message, NULL);
|
||||
if (r < 0)
|
||||
log_warning_errno(r, "Failed to send queued message, ignoring: %m");
|
||||
|
||||
u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int send_removed_signal(sd_bus *bus, void *userdata) {
|
||||
_cleanup_(sd_bus_message_unrefp) sd_bus_message *m = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
|
||||
@@ -11,6 +11,7 @@ extern const sd_bus_vtable bus_unit_cgroup_vtable[];
|
||||
|
||||
void bus_unit_send_change_signal(Unit *u);
|
||||
void bus_unit_send_pending_change_signal(Unit *u, bool including_new);
|
||||
int bus_unit_send_pending_freezer_message(Unit *u);
|
||||
void bus_unit_send_removed_signal(Unit *u);
|
||||
|
||||
int bus_unit_method_start_generic(sd_bus_message *message, Unit *u, JobType job_type, bool reload_if_possible, sd_bus_error *error);
|
||||
@@ -25,6 +26,8 @@ int bus_unit_method_attach_processes(sd_bus_message *message, void *userdata, sd
|
||||
int bus_unit_method_ref(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_unit_method_unref(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_unit_method_clean(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_unit_method_freeze(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
int bus_unit_method_thaw(sd_bus_message *message, void *userdata, sd_bus_error *error);
|
||||
|
||||
typedef enum BusUnitQueueFlags {
|
||||
BUS_UNIT_QUEUE_RELOAD_IF_POSSIBLE = 1 << 0,
|
||||
|
||||
+6
-1
@@ -960,10 +960,15 @@ static void destroy_bus(Manager *m, sd_bus **bus) {
|
||||
if (j->bus_track && sd_bus_track_get_bus(j->bus_track) == *bus)
|
||||
j->bus_track = sd_bus_track_unref(j->bus_track);
|
||||
|
||||
HASHMAP_FOREACH(u, m->units, i)
|
||||
HASHMAP_FOREACH(u, m->units, i) {
|
||||
if (u->bus_track && sd_bus_track_get_bus(u->bus_track) == *bus)
|
||||
u->bus_track = sd_bus_track_unref(u->bus_track);
|
||||
|
||||
/* Get rid of pending freezer messages on this bus */
|
||||
if (u->pending_freezer_message && sd_bus_message_get_bus(u->pending_freezer_message) == *bus)
|
||||
u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
|
||||
}
|
||||
|
||||
/* Get rid of queued message on this bus */
|
||||
if (m->pending_reload_message && sd_bus_message_get_bus(m->pending_reload_message) == *bus)
|
||||
m->pending_reload_message = sd_bus_message_unref(m->pending_reload_message);
|
||||
|
||||
@@ -635,6 +635,9 @@ const UnitVTable scope_vtable = {
|
||||
|
||||
.kill = scope_kill,
|
||||
|
||||
.freeze = unit_freeze_vtable_common,
|
||||
.thaw = unit_thaw_vtable_common,
|
||||
|
||||
.get_timeout = scope_get_timeout,
|
||||
|
||||
.serialize = scope_serialize,
|
||||
|
||||
@@ -7,17 +7,8 @@
|
||||
|
||||
int mac_selinux_generic_access_check(sd_bus_message *message, const char *path, const char *permission, sd_bus_error *error);
|
||||
|
||||
#if HAVE_SELINUX
|
||||
|
||||
#define mac_selinux_access_check(message, permission, error) \
|
||||
mac_selinux_generic_access_check((message), NULL, (permission), (error))
|
||||
|
||||
#define mac_selinux_unit_access_check(unit, message, permission, error) \
|
||||
mac_selinux_generic_access_check((message), unit_label_path(unit), (permission), (error))
|
||||
|
||||
#else
|
||||
|
||||
#define mac_selinux_access_check(message, permission, error) 0
|
||||
#define mac_selinux_unit_access_check(unit, message, permission, error) 0
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4458,6 +4458,9 @@ const UnitVTable service_vtable = {
|
||||
.clean = service_clean,
|
||||
.can_clean = service_can_clean,
|
||||
|
||||
.freeze = unit_freeze_vtable_common,
|
||||
.thaw = unit_thaw_vtable_common,
|
||||
|
||||
.serialize = service_serialize,
|
||||
.deserialize_item = service_deserialize_item,
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
#include "alloc-util.h"
|
||||
#include "dbus-slice.h"
|
||||
#include "dbus-unit.h"
|
||||
#include "fd-util.h"
|
||||
#include "log.h"
|
||||
#include "serialize.h"
|
||||
#include "slice.h"
|
||||
@@ -347,6 +348,82 @@ static void slice_enumerate_perpetual(Manager *m) {
|
||||
(void) slice_make_perpetual(m, SPECIAL_SYSTEM_SLICE, NULL);
|
||||
}
|
||||
|
||||
static bool slice_freezer_action_supported_by_children(Unit *s) {
|
||||
Unit *member;
|
||||
void *v;
|
||||
Iterator i;
|
||||
|
||||
assert(s);
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE], i) {
|
||||
int r;
|
||||
|
||||
if (UNIT_DEREF(member->slice) != s)
|
||||
continue;
|
||||
|
||||
if (member->type == UNIT_SLICE) {
|
||||
r = slice_freezer_action_supported_by_children(member);
|
||||
if (!r)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!UNIT_VTABLE(member)->freeze)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int slice_freezer_action(Unit *s, FreezerAction action) {
|
||||
Unit *member;
|
||||
void *v;
|
||||
Iterator i;
|
||||
int r;
|
||||
|
||||
assert(s);
|
||||
assert(IN_SET(action, FREEZER_FREEZE, FREEZER_THAW));
|
||||
|
||||
if (!slice_freezer_action_supported_by_children(s))
|
||||
return log_unit_warning(s, "Requested freezer operation is not supported by all children of the slice");
|
||||
|
||||
HASHMAP_FOREACH_KEY(v, member, s->dependencies[UNIT_BEFORE], i) {
|
||||
if (UNIT_DEREF(member->slice) != s)
|
||||
continue;
|
||||
|
||||
if (action == FREEZER_FREEZE)
|
||||
r = UNIT_VTABLE(member)->freeze(member);
|
||||
else
|
||||
r = UNIT_VTABLE(member)->thaw(member);
|
||||
|
||||
if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
r = unit_cgroup_freezer_action(s, action);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int slice_freeze(Unit *s) {
|
||||
assert(s);
|
||||
|
||||
return slice_freezer_action(s, FREEZER_FREEZE);
|
||||
}
|
||||
|
||||
static int slice_thaw(Unit *s) {
|
||||
assert(s);
|
||||
|
||||
return slice_freezer_action(s, FREEZER_THAW);
|
||||
}
|
||||
|
||||
static bool slice_can_freeze(Unit *s) {
|
||||
assert(s);
|
||||
|
||||
return slice_freezer_action_supported_by_children(s);
|
||||
}
|
||||
|
||||
const UnitVTable slice_vtable = {
|
||||
.object_size = sizeof(Slice),
|
||||
.cgroup_context_offset = offsetof(Slice, cgroup_context),
|
||||
@@ -371,6 +448,10 @@ const UnitVTable slice_vtable = {
|
||||
|
||||
.kill = slice_kill,
|
||||
|
||||
.freeze = slice_freeze,
|
||||
.thaw = slice_thaw,
|
||||
.can_freeze = slice_can_freeze,
|
||||
|
||||
.serialize = slice_serialize,
|
||||
.deserialize_item = slice_deserialize_item,
|
||||
|
||||
|
||||
+123
@@ -628,6 +628,7 @@ void unit_free(Unit *u) {
|
||||
sd_bus_slot_unref(u->match_bus_slot);
|
||||
sd_bus_track_unref(u->bus_track);
|
||||
u->deserialized_refs = strv_free(u->deserialized_refs);
|
||||
u->pending_freezer_message = sd_bus_message_unref(u->pending_freezer_message);
|
||||
|
||||
unit_free_requires_mounts_for(u);
|
||||
|
||||
@@ -737,6 +738,38 @@ void unit_free(Unit *u) {
|
||||
free(u);
|
||||
}
|
||||
|
||||
FreezerState unit_freezer_state(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
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);
|
||||
|
||||
@@ -1846,6 +1879,7 @@ int unit_start(Unit *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);
|
||||
|
||||
return UNIT_VTABLE(u)->start(u);
|
||||
}
|
||||
@@ -1898,6 +1932,7 @@ int unit_stop(Unit *u) {
|
||||
return -EBADR;
|
||||
|
||||
unit_add_to_dbus_queue(u);
|
||||
unit_cgroup_freezer_action(u, FREEZER_THAW);
|
||||
|
||||
return UNIT_VTABLE(u)->stop(u);
|
||||
}
|
||||
@@ -1954,6 +1989,8 @@ int unit_reload(Unit *u) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
unit_cgroup_freezer_action(u, FREEZER_THAW);
|
||||
|
||||
return UNIT_VTABLE(u)->reload(u);
|
||||
}
|
||||
|
||||
@@ -3497,6 +3534,8 @@ int unit_serialize(Unit *u, FILE *f, FDSet *fds, bool serialize_jobs) {
|
||||
if (!sd_id128_is_null(u->invocation_id))
|
||||
(void) serialize_item_format(f, "invocation-id", SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(u->invocation_id));
|
||||
|
||||
(void) serialize_item_format(f, "freezer-state", "%s", freezer_state_to_string(unit_freezer_state(u)));
|
||||
|
||||
bus_track_serialize(u->bus_track, f, "ref");
|
||||
|
||||
for (m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) {
|
||||
@@ -3805,6 +3844,16 @@ int unit_deserialize(Unit *u, FILE *f, FDSet *fds) {
|
||||
log_unit_warning_errno(u, r, "Failed to set invocation ID for unit: %m");
|
||||
}
|
||||
|
||||
continue;
|
||||
} else if (streq(l, "freezer-state")) {
|
||||
FreezerState s;
|
||||
|
||||
s = freezer_state_from_string(v);
|
||||
if (s < 0)
|
||||
log_unit_debug(u, "Failed to deserialize freezer-state '%s', ignoring.", v);
|
||||
else
|
||||
u->freezer_state = s;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -6076,6 +6125,80 @@ int unit_can_clean(Unit *u, ExecCleanMask *ret) {
|
||||
return UNIT_VTABLE(u)->can_clean(u, ret);
|
||||
}
|
||||
|
||||
bool unit_can_freeze(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
if (UNIT_VTABLE(u)->can_freeze)
|
||||
return UNIT_VTABLE(u)->can_freeze(u);
|
||||
|
||||
return UNIT_VTABLE(u)->freeze;
|
||||
}
|
||||
|
||||
void unit_frozen(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
u->freezer_state = FREEZER_FROZEN;
|
||||
|
||||
bus_unit_send_pending_freezer_message(u);
|
||||
}
|
||||
|
||||
void unit_thawed(Unit *u) {
|
||||
assert(u);
|
||||
|
||||
u->freezer_state = FREEZER_RUNNING;
|
||||
|
||||
bus_unit_send_pending_freezer_message(u);
|
||||
}
|
||||
|
||||
static 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())
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
if (u->job)
|
||||
return -EBUSY;
|
||||
|
||||
if (u->load_state != UNIT_LOADED)
|
||||
return -EHOSTDOWN;
|
||||
|
||||
s = unit_active_state(u);
|
||||
if (s != UNIT_ACTIVE)
|
||||
return -EHOSTDOWN;
|
||||
|
||||
if (IN_SET(u->freezer_state, FREEZER_FREEZING, FREEZER_THAWING))
|
||||
return -EALREADY;
|
||||
|
||||
r = method(u);
|
||||
if (r <= 0)
|
||||
return r;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static const char* const collect_mode_table[_COLLECT_MODE_MAX] = {
|
||||
[COLLECT_INACTIVE] = "inactive",
|
||||
[COLLECT_INACTIVE_OR_FAILED] = "inactive-or-failed",
|
||||
|
||||
@@ -114,6 +114,9 @@ typedef struct Unit {
|
||||
UnitLoadState load_state;
|
||||
Unit *merged_into;
|
||||
|
||||
FreezerState freezer_state;
|
||||
sd_bus_message *pending_freezer_message;
|
||||
|
||||
char *id; /* One name is special because we use it for identification. Points to an entry in the names set */
|
||||
char *instance;
|
||||
|
||||
@@ -483,6 +486,11 @@ typedef struct UnitVTable {
|
||||
/* Clear out the various runtime/state/cache/logs/configuration data */
|
||||
int (*clean)(Unit *u, ExecCleanMask m);
|
||||
|
||||
/* Freeze the unit */
|
||||
int (*freeze)(Unit *u);
|
||||
int (*thaw)(Unit *u);
|
||||
bool (*can_freeze)(Unit *u);
|
||||
|
||||
/* Return which kind of data can be cleaned */
|
||||
int (*can_clean)(Unit *u, ExecCleanMask *ret);
|
||||
|
||||
@@ -695,6 +703,8 @@ const char *unit_status_string(Unit *u) _pure_;
|
||||
bool unit_has_name(const Unit *u, const char *name);
|
||||
|
||||
UnitActiveState unit_active_state(Unit *u);
|
||||
FreezerState unit_freezer_state(Unit *u);
|
||||
int unit_freezer_state_kernel(Unit *u, FreezerState *ret);
|
||||
|
||||
const char* unit_sub_state_to_string(Unit *u);
|
||||
|
||||
@@ -878,6 +888,16 @@ void unit_destroy_runtime_directory(Unit *u, const ExecContext *context);
|
||||
int unit_clean(Unit *u, ExecCleanMask mask);
|
||||
int unit_can_clean(Unit *u, ExecCleanMask *ret_mask);
|
||||
|
||||
bool unit_can_freeze(Unit *u);
|
||||
int unit_freeze(Unit *u);
|
||||
void unit_frozen(Unit *u);
|
||||
|
||||
int unit_thaw(Unit *u);
|
||||
void unit_thawed(Unit *u);
|
||||
|
||||
int unit_freeze_vtable_common(Unit *u);
|
||||
int unit_thaw_vtable_common(Unit *u);
|
||||
|
||||
/* Macros which append UNIT= or USER_UNIT= to the message */
|
||||
|
||||
#define log_unit_full(unit, level, error, ...) \
|
||||
|
||||
Reference in New Issue
Block a user