Merge pull request #31127 from poettering/cgroup-runtime2

core: split out cgroup specific state fields from Unit → CGroupRuntime
This commit is contained in:
Luca Boccassi
2024-02-16 23:38:53 +00:00
committed by GitHub
29 changed files with 1370 additions and 781 deletions
+5 -6
View File
@@ -148,6 +148,11 @@ Features:
* varlink: extend varlink IDL macros to include documentation strings
* Introduce a CGroupRef structure, inspired by PidRef. Should contain cgroup
path, cgroup id, and cgroup fd. Use it to continously pin all v2 cgroups via
a cgroup_ref field in the CGroupRuntime structure. Eventually switch things
over to do all cgroupfs access only via that structure's fd.
* Get rid of the symlinks in /run/systemd/units/* and exclusively use cgroupfs
xattrs to convey info about invocation ids, logging settings and so on.
support for cgroupfs xattrs in the "trusted." namespace was added in linux
@@ -334,12 +339,6 @@ Features:
/var/lib/sysexts/ which can be used to place only DDIs that shall be used as
sysext
* in pid1: move out all cgroup state settings from Unit into a new object
CGroupState or so which is allocated when we realize the unit into a cgroup,
and then remains referenced by it. The new object should also carry an fd to
the realized cgroup, to pin it (and later execute all cgroup operations over,
once we drop cgroupv1 compat).
* Varlinkification of the following command line tools, to open them up to
other programs via IPC:
- bootctl
+90 -67
View File
@@ -196,19 +196,26 @@ static int bpf_firewall_compile_bpf(
_cleanup_(bpf_program_freep) BPFProgram *p = NULL;
int accounting_map_fd, r;
bool access_enabled;
CGroupRuntime *crt;
assert(u);
assert(ret);
crt = unit_get_cgroup_runtime(u);
if (!crt) {
*ret = NULL;
return 0;
}
accounting_map_fd = is_ingress ?
u->ip_accounting_ingress_map_fd :
u->ip_accounting_egress_map_fd;
crt->ip_accounting_ingress_map_fd :
crt->ip_accounting_egress_map_fd;
access_enabled =
u->ipv4_allow_map_fd >= 0 ||
u->ipv6_allow_map_fd >= 0 ||
u->ipv4_deny_map_fd >= 0 ||
u->ipv6_deny_map_fd >= 0 ||
crt->ipv4_allow_map_fd >= 0 ||
crt->ipv6_allow_map_fd >= 0 ||
crt->ipv4_deny_map_fd >= 0 ||
crt->ipv6_deny_map_fd >= 0 ||
ip_allow_any ||
ip_deny_any;
@@ -234,26 +241,26 @@ static int bpf_firewall_compile_bpf(
* - Otherwise, access will be granted
*/
if (u->ipv4_deny_map_fd >= 0) {
r = add_lookup_instructions(p, u->ipv4_deny_map_fd, ETH_P_IP, is_ingress, ACCESS_DENIED);
if (crt->ipv4_deny_map_fd >= 0) {
r = add_lookup_instructions(p, crt->ipv4_deny_map_fd, ETH_P_IP, is_ingress, ACCESS_DENIED);
if (r < 0)
return r;
}
if (u->ipv6_deny_map_fd >= 0) {
r = add_lookup_instructions(p, u->ipv6_deny_map_fd, ETH_P_IPV6, is_ingress, ACCESS_DENIED);
if (crt->ipv6_deny_map_fd >= 0) {
r = add_lookup_instructions(p, crt->ipv6_deny_map_fd, ETH_P_IPV6, is_ingress, ACCESS_DENIED);
if (r < 0)
return r;
}
if (u->ipv4_allow_map_fd >= 0) {
r = add_lookup_instructions(p, u->ipv4_allow_map_fd, ETH_P_IP, is_ingress, ACCESS_ALLOWED);
if (crt->ipv4_allow_map_fd >= 0) {
r = add_lookup_instructions(p, crt->ipv4_allow_map_fd, ETH_P_IP, is_ingress, ACCESS_ALLOWED);
if (r < 0)
return r;
}
if (u->ipv6_allow_map_fd >= 0) {
r = add_lookup_instructions(p, u->ipv6_allow_map_fd, ETH_P_IPV6, is_ingress, ACCESS_ALLOWED);
if (crt->ipv6_allow_map_fd >= 0) {
r = add_lookup_instructions(p, crt->ipv6_allow_map_fd, ETH_P_IPV6, is_ingress, ACCESS_ALLOWED);
if (r < 0)
return r;
}
@@ -495,37 +502,36 @@ static int bpf_firewall_prepare_access_maps(
return 0;
}
static int bpf_firewall_prepare_accounting_maps(Unit *u, bool enabled, int *fd_ingress, int *fd_egress) {
static int bpf_firewall_prepare_accounting_maps(Unit *u, bool enabled, CGroupRuntime *crt) {
int r;
assert(u);
assert(fd_ingress);
assert(fd_egress);
assert(crt);
if (enabled) {
if (*fd_ingress < 0) {
if (crt->ip_accounting_ingress_map_fd < 0) {
char *name = strjoina("I_", u->id);
r = bpf_map_new(name, BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(uint64_t), 2, 0);
if (r < 0)
return r;
*fd_ingress = r;
crt->ip_accounting_ingress_map_fd = r;
}
if (*fd_egress < 0) {
if (crt->ip_accounting_egress_map_fd < 0) {
char *name = strjoina("E_", u->id);
r = bpf_map_new(name, BPF_MAP_TYPE_ARRAY, sizeof(int), sizeof(uint64_t), 2, 0);
if (r < 0)
return r;
*fd_egress = r;
crt->ip_accounting_egress_map_fd = r;
}
} else {
*fd_ingress = safe_close(*fd_ingress);
*fd_egress = safe_close(*fd_egress);
crt->ip_accounting_ingress_map_fd = safe_close(crt->ip_accounting_ingress_map_fd);
crt->ip_accounting_egress_map_fd = safe_close(crt->ip_accounting_egress_map_fd);
zero(u->ip_accounting_extra);
zero(crt->ip_accounting_extra);
}
return 0;
@@ -535,6 +541,7 @@ int bpf_firewall_compile(Unit *u) {
const char *ingress_name = NULL, *egress_name = NULL;
bool ip_allow_any = false, ip_deny_any = false;
CGroupContext *cc;
CGroupRuntime *crt;
int r, supported;
assert(u);
@@ -543,6 +550,10 @@ int bpf_firewall_compile(Unit *u) {
if (!cc)
return -EINVAL;
crt = unit_setup_cgroup_runtime(u);
if (!crt)
return -ENOMEM;
supported = bpf_firewall_supported();
if (supported < 0)
return supported;
@@ -569,14 +580,14 @@ int bpf_firewall_compile(Unit *u) {
* but we reuse the accounting maps. That way the firewall in effect always maps to the actual
* configuration, but we don't flush out the accounting unnecessarily */
u->ip_bpf_ingress = bpf_program_free(u->ip_bpf_ingress);
u->ip_bpf_egress = bpf_program_free(u->ip_bpf_egress);
crt->ip_bpf_ingress = bpf_program_free(crt->ip_bpf_ingress);
crt->ip_bpf_egress = bpf_program_free(crt->ip_bpf_egress);
u->ipv4_allow_map_fd = safe_close(u->ipv4_allow_map_fd);
u->ipv4_deny_map_fd = safe_close(u->ipv4_deny_map_fd);
crt->ipv4_allow_map_fd = safe_close(crt->ipv4_allow_map_fd);
crt->ipv4_deny_map_fd = safe_close(crt->ipv4_deny_map_fd);
u->ipv6_allow_map_fd = safe_close(u->ipv6_allow_map_fd);
u->ipv6_deny_map_fd = safe_close(u->ipv6_deny_map_fd);
crt->ipv6_allow_map_fd = safe_close(crt->ipv6_allow_map_fd);
crt->ipv6_deny_map_fd = safe_close(crt->ipv6_deny_map_fd);
if (u->type != UNIT_SLICE) {
/* In inner nodes we only do accounting, we do not actually bother with access control. However, leaf
@@ -585,24 +596,24 @@ int bpf_firewall_compile(Unit *u) {
* means that all configure IP access rules *will* take effect on processes, even though we never
* compile them for inner nodes. */
r = bpf_firewall_prepare_access_maps(u, ACCESS_ALLOWED, &u->ipv4_allow_map_fd, &u->ipv6_allow_map_fd, &ip_allow_any);
r = bpf_firewall_prepare_access_maps(u, ACCESS_ALLOWED, &crt->ipv4_allow_map_fd, &crt->ipv6_allow_map_fd, &ip_allow_any);
if (r < 0)
return log_unit_error_errno(u, r, "bpf-firewall: Preparation of BPF allow maps failed: %m");
r = bpf_firewall_prepare_access_maps(u, ACCESS_DENIED, &u->ipv4_deny_map_fd, &u->ipv6_deny_map_fd, &ip_deny_any);
r = bpf_firewall_prepare_access_maps(u, ACCESS_DENIED, &crt->ipv4_deny_map_fd, &crt->ipv6_deny_map_fd, &ip_deny_any);
if (r < 0)
return log_unit_error_errno(u, r, "bpf-firewall: Preparation of BPF deny maps failed: %m");
}
r = bpf_firewall_prepare_accounting_maps(u, cc->ip_accounting, &u->ip_accounting_ingress_map_fd, &u->ip_accounting_egress_map_fd);
r = bpf_firewall_prepare_accounting_maps(u, cc->ip_accounting, crt);
if (r < 0)
return log_unit_error_errno(u, r, "bpf-firewall: Preparation of BPF accounting maps failed: %m");
r = bpf_firewall_compile_bpf(u, ingress_name, true, &u->ip_bpf_ingress, ip_allow_any, ip_deny_any);
r = bpf_firewall_compile_bpf(u, ingress_name, true, &crt->ip_bpf_ingress, ip_allow_any, ip_deny_any);
if (r < 0)
return log_unit_error_errno(u, r, "bpf-firewall: Compilation of ingress BPF program failed: %m");
r = bpf_firewall_compile_bpf(u, egress_name, false, &u->ip_bpf_egress, ip_allow_any, ip_deny_any);
r = bpf_firewall_compile_bpf(u, egress_name, false, &crt->ip_bpf_egress, ip_allow_any, ip_deny_any);
if (r < 0)
return log_unit_error_errno(u, r, "bpf-firewall: Compilation of egress BPF program failed: %m");
@@ -634,6 +645,7 @@ static int load_bpf_progs_from_fs_to_set(Unit *u, char **filter_paths, Set **set
int bpf_firewall_load_custom(Unit *u) {
CGroupContext *cc;
CGroupRuntime *crt;
int r, supported;
assert(u);
@@ -641,6 +653,9 @@ int bpf_firewall_load_custom(Unit *u) {
cc = unit_get_cgroup_context(u);
if (!cc)
return 0;
crt = unit_get_cgroup_runtime(u);
if (!crt)
return 0;
if (!(cc->ip_filters_ingress || cc->ip_filters_egress))
return 0;
@@ -653,10 +668,10 @@ int bpf_firewall_load_custom(Unit *u) {
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"bpf-firewall: BPF_F_ALLOW_MULTI not supported, cannot attach custom BPF programs.");
r = load_bpf_progs_from_fs_to_set(u, cc->ip_filters_ingress, &u->ip_bpf_custom_ingress);
r = load_bpf_progs_from_fs_to_set(u, cc->ip_filters_ingress, &crt->ip_bpf_custom_ingress);
if (r < 0)
return r;
r = load_bpf_progs_from_fs_to_set(u, cc->ip_filters_egress, &u->ip_bpf_custom_egress);
r = load_bpf_progs_from_fs_to_set(u, cc->ip_filters_egress, &crt->ip_bpf_custom_egress);
if (r < 0)
return r;
@@ -686,6 +701,7 @@ int bpf_firewall_install(Unit *u) {
_cleanup_(bpf_program_freep) BPFProgram *ip_bpf_ingress_uninstall = NULL, *ip_bpf_egress_uninstall = NULL;
_cleanup_free_ char *path = NULL;
CGroupContext *cc;
CGroupRuntime *crt;
int r, supported;
uint32_t flags;
@@ -694,9 +710,12 @@ int bpf_firewall_install(Unit *u) {
cc = unit_get_cgroup_context(u);
if (!cc)
return -EINVAL;
if (!u->cgroup_path)
crt = unit_get_cgroup_runtime(u);
if (!crt)
return -EINVAL;
if (!u->cgroup_realized)
if (!crt->cgroup_path)
return -EINVAL;
if (!crt->cgroup_realized)
return -EINVAL;
supported = bpf_firewall_supported();
@@ -709,11 +728,11 @@ int bpf_firewall_install(Unit *u) {
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"bpf-firewall: BPF_F_ALLOW_MULTI not supported, not doing BPF firewall on slice units.");
if (supported != BPF_FIREWALL_SUPPORTED_WITH_MULTI &&
(!set_isempty(u->ip_bpf_custom_ingress) || !set_isempty(u->ip_bpf_custom_egress)))
(!set_isempty(crt->ip_bpf_custom_ingress) || !set_isempty(crt->ip_bpf_custom_egress)))
return log_unit_debug_errno(u, SYNTHETIC_ERRNO(EOPNOTSUPP),
"bpf-firewall: BPF_F_ALLOW_MULTI not supported, cannot attach custom BPF programs.");
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &path);
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, crt->cgroup_path, NULL, &path);
if (r < 0)
return log_unit_error_errno(u, r, "bpf-firewall: Failed to determine cgroup path: %m");
@@ -724,44 +743,44 @@ int bpf_firewall_install(Unit *u) {
* after attaching the new programs, so that there's no time window where neither program is
* attached. (There will be a program where both are attached, but that's OK, since this is a
* security feature where we rather want to lock down too much than too little */
ip_bpf_egress_uninstall = TAKE_PTR(u->ip_bpf_egress_installed);
ip_bpf_ingress_uninstall = TAKE_PTR(u->ip_bpf_ingress_installed);
ip_bpf_egress_uninstall = TAKE_PTR(crt->ip_bpf_egress_installed);
ip_bpf_ingress_uninstall = TAKE_PTR(crt->ip_bpf_ingress_installed);
} else {
/* If we don't have BPF_F_ALLOW_MULTI then unref the old BPF programs (which will implicitly
* detach them) right before attaching the new program, to minimize the time window when we
* don't account for IP traffic. */
u->ip_bpf_egress_installed = bpf_program_free(u->ip_bpf_egress_installed);
u->ip_bpf_ingress_installed = bpf_program_free(u->ip_bpf_ingress_installed);
crt->ip_bpf_egress_installed = bpf_program_free(crt->ip_bpf_egress_installed);
crt->ip_bpf_ingress_installed = bpf_program_free(crt->ip_bpf_ingress_installed);
}
if (u->ip_bpf_egress) {
r = bpf_program_cgroup_attach(u->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path, flags);
if (crt->ip_bpf_egress) {
r = bpf_program_cgroup_attach(crt->ip_bpf_egress, BPF_CGROUP_INET_EGRESS, path, flags);
if (r < 0)
return log_unit_error_errno(u, r,
"bpf-firewall: Attaching egress BPF program to cgroup %s failed: %m", path);
/* Remember that this BPF program is installed now. */
u->ip_bpf_egress_installed = TAKE_PTR(u->ip_bpf_egress);
crt->ip_bpf_egress_installed = TAKE_PTR(crt->ip_bpf_egress);
}
if (u->ip_bpf_ingress) {
r = bpf_program_cgroup_attach(u->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path, flags);
if (crt->ip_bpf_ingress) {
r = bpf_program_cgroup_attach(crt->ip_bpf_ingress, BPF_CGROUP_INET_INGRESS, path, flags);
if (r < 0)
return log_unit_error_errno(u, r,
"bpf-firewall: Attaching ingress BPF program to cgroup %s failed: %m", path);
u->ip_bpf_ingress_installed = TAKE_PTR(u->ip_bpf_ingress);
crt->ip_bpf_ingress_installed = TAKE_PTR(crt->ip_bpf_ingress);
}
/* And now, definitely get rid of the old programs, and detach them */
ip_bpf_egress_uninstall = bpf_program_free(ip_bpf_egress_uninstall);
ip_bpf_ingress_uninstall = bpf_program_free(ip_bpf_ingress_uninstall);
r = attach_custom_bpf_progs(u, path, BPF_CGROUP_INET_EGRESS, &u->ip_bpf_custom_egress, &u->ip_bpf_custom_egress_installed);
r = attach_custom_bpf_progs(u, path, BPF_CGROUP_INET_EGRESS, &crt->ip_bpf_custom_egress, &crt->ip_bpf_custom_egress_installed);
if (r < 0)
return r;
r = attach_custom_bpf_progs(u, path, BPF_CGROUP_INET_INGRESS, &u->ip_bpf_custom_ingress, &u->ip_bpf_custom_ingress_installed);
r = attach_custom_bpf_progs(u, path, BPF_CGROUP_INET_INGRESS, &crt->ip_bpf_custom_ingress, &crt->ip_bpf_custom_ingress_installed);
if (r < 0)
return r;
@@ -954,21 +973,25 @@ void emit_bpf_firewall_warning(Unit *u) {
void bpf_firewall_close(Unit *u) {
assert(u);
u->ip_accounting_ingress_map_fd = safe_close(u->ip_accounting_ingress_map_fd);
u->ip_accounting_egress_map_fd = safe_close(u->ip_accounting_egress_map_fd);
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (!crt)
return;
u->ipv4_allow_map_fd = safe_close(u->ipv4_allow_map_fd);
u->ipv6_allow_map_fd = safe_close(u->ipv6_allow_map_fd);
u->ipv4_deny_map_fd = safe_close(u->ipv4_deny_map_fd);
u->ipv6_deny_map_fd = safe_close(u->ipv6_deny_map_fd);
crt->ip_accounting_ingress_map_fd = safe_close(crt->ip_accounting_ingress_map_fd);
crt->ip_accounting_egress_map_fd = safe_close(crt->ip_accounting_egress_map_fd);
u->ip_bpf_ingress = bpf_program_free(u->ip_bpf_ingress);
u->ip_bpf_ingress_installed = bpf_program_free(u->ip_bpf_ingress_installed);
u->ip_bpf_egress = bpf_program_free(u->ip_bpf_egress);
u->ip_bpf_egress_installed = bpf_program_free(u->ip_bpf_egress_installed);
crt->ipv4_allow_map_fd = safe_close(crt->ipv4_allow_map_fd);
crt->ipv6_allow_map_fd = safe_close(crt->ipv6_allow_map_fd);
crt->ipv4_deny_map_fd = safe_close(crt->ipv4_deny_map_fd);
crt->ipv6_deny_map_fd = safe_close(crt->ipv6_deny_map_fd);
u->ip_bpf_custom_ingress = set_free(u->ip_bpf_custom_ingress);
u->ip_bpf_custom_egress = set_free(u->ip_bpf_custom_egress);
u->ip_bpf_custom_ingress_installed = set_free(u->ip_bpf_custom_ingress_installed);
u->ip_bpf_custom_egress_installed = set_free(u->ip_bpf_custom_egress_installed);
crt->ip_bpf_ingress = bpf_program_free(crt->ip_bpf_ingress);
crt->ip_bpf_ingress_installed = bpf_program_free(crt->ip_bpf_ingress_installed);
crt->ip_bpf_egress = bpf_program_free(crt->ip_bpf_egress);
crt->ip_bpf_egress_installed = bpf_program_free(crt->ip_bpf_egress_installed);
crt->ip_bpf_custom_ingress = set_free(crt->ip_bpf_custom_ingress);
crt->ip_bpf_custom_egress = set_free(crt->ip_bpf_custom_egress);
crt->ip_bpf_custom_ingress_installed = set_free(crt->ip_bpf_custom_ingress_installed);
crt->ip_bpf_custom_egress_installed = set_free(crt->ip_bpf_custom_egress_installed);
}
+14 -3
View File
@@ -81,6 +81,7 @@ static int bpf_foreign_prepare(
Unit *u,
enum bpf_attach_type attach_type,
const char *bpffs_path) {
_cleanup_(bpf_program_freep) BPFProgram *prog = NULL;
_cleanup_free_ BPFForeignKey *key = NULL;
uint32_t prog_id;
@@ -101,6 +102,11 @@ static int bpf_foreign_prepare(
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
"bpf-foreign: Path in BPF filesystem is expected.");
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (!crt)
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
"Failed to get control group runtime object.");
r = bpf_program_new_from_bpffs_path(bpffs_path, &prog);
if (r < 0)
return log_unit_error_errno(u, r, "bpf-foreign: Failed to create foreign BPF program: %m");
@@ -114,7 +120,7 @@ static int bpf_foreign_prepare(
return log_unit_error_errno(u, r,
"bpf-foreign: Failed to create foreign BPF program key from path '%s': %m", bpffs_path);
r = hashmap_ensure_put(&u->bpf_foreign_by_key, &bpf_foreign_by_key_hash_ops, key, prog);
r = hashmap_ensure_put(&crt->bpf_foreign_by_key, &bpf_foreign_by_key_hash_ops, key, prog);
if (r == -EEXIST) {
log_unit_warning_errno(u, r, "bpf-foreign: Foreign BPF program already exists, ignoring: %m");
return 0;
@@ -131,6 +137,7 @@ static int bpf_foreign_prepare(
int bpf_foreign_install(Unit *u) {
_cleanup_free_ char *cgroup_path = NULL;
CGroupContext *cc;
CGroupRuntime *crt;
int r, ret = 0;
assert(u);
@@ -139,7 +146,11 @@ int bpf_foreign_install(Unit *u) {
if (!cc)
return 0;
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
crt = unit_get_cgroup_runtime(u);
if (!crt)
return 0;
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, crt->cgroup_path, NULL, &cgroup_path);
if (r < 0)
return log_unit_error_errno(u, r, "bpf-foreign: Failed to get cgroup path: %m");
@@ -149,6 +160,6 @@ int bpf_foreign_install(Unit *u) {
ret = r;
}
r = attach_programs(u, cgroup_path, u->bpf_foreign_by_key, BPF_F_ALLOW_MULTI);
r = attach_programs(u, cgroup_path, crt->bpf_foreign_by_key, BPF_F_ALLOW_MULTI);
return ret < 0 ? ret : r;
}
+10 -4
View File
@@ -209,7 +209,9 @@ int bpf_restrict_fs_update(const Set *filesystems, uint64_t cgroup_id, int outer
return 0;
}
int bpf_restrict_fs_cleanup(const Unit *u) {
int bpf_restrict_fs_cleanup(Unit *u) {
CGroupRuntime *crt;
assert(u);
assert(u->manager);
@@ -220,14 +222,18 @@ int bpf_restrict_fs_cleanup(const Unit *u) {
if (!u->manager->restrict_fs)
return 0;
if (u->cgroup_id == 0)
crt = unit_get_cgroup_runtime(u);
if (!crt)
return 0;
if (crt->cgroup_id == 0)
return 0;
int fd = sym_bpf_map__fd(u->manager->restrict_fs->maps.cgroup_hash);
if (fd < 0)
return log_unit_error_errno(u, errno, "bpf-restrict-fs: Failed to get BPF map fd: %m");
if (sym_bpf_map_delete_elem(fd, &u->cgroup_id) != 0 && errno != ENOENT)
if (sym_bpf_map_delete_elem(fd, &crt->cgroup_id) != 0 && errno != ENOENT)
return log_unit_debug_errno(u, errno, "bpf-restrict-fs: Failed to delete cgroup entry from LSM BPF map: %m");
return 0;
@@ -259,7 +265,7 @@ int bpf_restrict_fs_update(const Set *filesystems, uint64_t cgroup_id, int outer
return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "bpf-restrict-fs: Failed to restrict filesystems using LSM BPF: %m");
}
int bpf_restrict_fs_cleanup(const Unit *u) {
int bpf_restrict_fs_cleanup(Unit *u) {
return 0;
}
+1 -1
View File
@@ -17,7 +17,7 @@ typedef struct restrict_fs_bpf restrict_fs_bpf;
bool bpf_restrict_fs_supported(bool initialize);
int bpf_restrict_fs_setup(Manager *m);
int bpf_restrict_fs_update(const Set *filesystems, uint64_t cgroup_id, int outer_map_fd, bool allow_list);
int bpf_restrict_fs_cleanup(const Unit *u);
int bpf_restrict_fs_cleanup(Unit *u);
int bpf_restrict_fs_map_fd(Unit *u);
void bpf_restrict_fs_destroy(struct restrict_fs_bpf *prog);
int bpf_restrict_fs_parse_filesystem(const char *name, Set **filesystems, FilesystemParseFlags flags, const char *unit, const char *filename, unsigned line);
+38 -17
View File
@@ -103,13 +103,18 @@ static int restrict_ifaces_install_impl(Unit *u) {
_cleanup_free_ char *cgroup_path = NULL;
_cleanup_close_ int cgroup_fd = -EBADF;
CGroupContext *cc;
CGroupRuntime *crt;
int r;
cc = unit_get_cgroup_context(u);
if (!cc)
return 0;
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
crt = unit_get_cgroup_runtime(u);
if (!crt)
return 0;
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, crt->cgroup_path, NULL, &cgroup_path);
if (r < 0)
return log_unit_error_errno(u, r, "restrict-interfaces: Failed to get cgroup path: %m");
@@ -137,30 +142,42 @@ static int restrict_ifaces_install_impl(Unit *u) {
if (r != 0)
return log_unit_error_errno(u, r, "restrict-interfaces: Failed to create egress cgroup link: %m");
u->restrict_ifaces_ingress_bpf_link = TAKE_PTR(ingress_link);
u->restrict_ifaces_egress_bpf_link = TAKE_PTR(egress_link);
crt->restrict_ifaces_ingress_bpf_link = TAKE_PTR(ingress_link);
crt->restrict_ifaces_egress_bpf_link = TAKE_PTR(egress_link);
return 0;
}
int bpf_restrict_ifaces_install(Unit *u) {
int r;
r = restrict_ifaces_install_impl(u);
fdset_close(u->initial_restric_ifaces_link_fds);
return r;
}
int bpf_restrict_ifaces_serialize(Unit *u, FILE *f, FDSet *fds) {
CGroupRuntime *crt;
int r;
assert(u);
r = bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_ingress_bpf_link);
crt = unit_get_cgroup_runtime(u);
if (!crt)
return 0;
r = restrict_ifaces_install_impl(u);
fdset_close(crt->initial_restric_ifaces_link_fds);
return r;
}
int bpf_restrict_ifaces_serialize(Unit *u, FILE *f, FDSet *fds) {
CGroupRuntime *crt;
int r;
assert(u);
crt = unit_get_cgroup_runtime(u);
if (!crt)
return 0;
r = bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", crt->restrict_ifaces_ingress_bpf_link);
if (r < 0)
return r;
return bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", u->restrict_ifaces_egress_bpf_link);
return bpf_serialize_link(f, fds, "restrict-ifaces-bpf-fd", crt->restrict_ifaces_egress_bpf_link);
}
int bpf_restrict_ifaces_add_initial_link_fd(Unit *u, int fd) {
@@ -168,13 +185,17 @@ int bpf_restrict_ifaces_add_initial_link_fd(Unit *u, int fd) {
assert(u);
if (!u->initial_restric_ifaces_link_fds) {
u->initial_restric_ifaces_link_fds = fdset_new();
if (!u->initial_restric_ifaces_link_fds)
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (!crt)
return -EINVAL;
if (!crt->initial_restric_ifaces_link_fds) {
crt->initial_restric_ifaces_link_fds = fdset_new();
if (!crt->initial_restric_ifaces_link_fds)
return log_oom();
}
r = fdset_put(u->initial_restric_ifaces_link_fds, fd);
r = fdset_put(crt->initial_restric_ifaces_link_fds, fd);
if (r < 0)
return log_unit_error_errno(u, r,
"restrict-interfaces: Failed to put restrict-ifaces-bpf-fd %d to restored fdset: %m", fd);
+30 -13
View File
@@ -139,13 +139,18 @@ int bpf_socket_bind_add_initial_link_fd(Unit *u, int fd) {
assert(u);
if (!u->initial_socket_bind_link_fds) {
u->initial_socket_bind_link_fds = fdset_new();
if (!u->initial_socket_bind_link_fds)
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (!crt)
return log_unit_error_errno(u, SYNTHETIC_ERRNO(EINVAL),
"Failed to get control group runtime object.");
if (!crt->initial_socket_bind_link_fds) {
crt->initial_socket_bind_link_fds = fdset_new();
if (!crt->initial_socket_bind_link_fds)
return log_oom();
}
r = fdset_put(u->initial_socket_bind_link_fds, fd);
r = fdset_put(crt->initial_socket_bind_link_fds, fd);
if (r < 0)
return log_unit_error_errno(u, r, "bpf-socket-bind: Failed to put BPF fd %d to initial fdset", fd);
@@ -158,6 +163,7 @@ static int socket_bind_install_impl(Unit *u) {
_cleanup_free_ char *cgroup_path = NULL;
_cleanup_close_ int cgroup_fd = -EBADF;
CGroupContext *cc;
CGroupRuntime *crt;
int r;
assert(u);
@@ -166,7 +172,11 @@ static int socket_bind_install_impl(Unit *u) {
if (!cc)
return 0;
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, NULL, &cgroup_path);
crt = unit_get_cgroup_runtime(u);
if (!crt)
return 0;
r = cg_get_path(SYSTEMD_CGROUP_CONTROLLER, crt->cgroup_path, NULL, &cgroup_path);
if (r < 0)
return log_unit_error_errno(u, r, "bpf-socket-bind: Failed to get cgroup path: %m");
@@ -193,35 +203,42 @@ static int socket_bind_install_impl(Unit *u) {
return log_unit_error_errno(u, r, "bpf-socket-bind: Failed to link '%s' cgroup-bpf program: %m",
sym_bpf_program__name(obj->progs.sd_bind6));
u->ipv4_socket_bind_link = TAKE_PTR(ipv4);
u->ipv6_socket_bind_link = TAKE_PTR(ipv6);
crt->ipv4_socket_bind_link = TAKE_PTR(ipv4);
crt->ipv6_socket_bind_link = TAKE_PTR(ipv6);
return 0;
}
int bpf_socket_bind_install(Unit *u) {
CGroupRuntime *crt;
int r;
assert(u);
r = socket_bind_install_impl(u);
if (r == -ENOMEM)
return r;
crt = unit_get_cgroup_runtime(u);
if (!crt)
return 0;
fdset_close(u->initial_socket_bind_link_fds);
r = socket_bind_install_impl(u);
fdset_close(crt->initial_socket_bind_link_fds);
return r;
}
int bpf_socket_bind_serialize(Unit *u, FILE *f, FDSet *fds) {
CGroupRuntime *crt;
int r;
assert(u);
r = bpf_serialize_link(f, fds, "ipv4-socket-bind-bpf-link", u->ipv4_socket_bind_link);
crt = unit_get_cgroup_runtime(u);
if (!crt)
return 0;
r = bpf_serialize_link(f, fds, "ipv4-socket-bind-bpf-link", crt->ipv4_socket_bind_link);
if (r < 0)
return r;
return bpf_serialize_link(f, fds, "ipv6-socket-bind-bpf-link", u->ipv6_socket_bind_link);
return bpf_serialize_link(f, fds, "ipv6-socket-bind-bpf-link", crt->ipv6_socket_bind_link);
}
#else /* ! BPF_FRAMEWORK */
+858 -251
View File
File diff suppressed because it is too large Load Diff
+98 -1
View File
@@ -3,6 +3,9 @@
#include <stdbool.h>
#include "sd-event.h"
#include "bpf-program.h"
#include "bpf-restrict-fs.h"
#include "cgroup-util.h"
#include "cpu-set-util.h"
@@ -35,6 +38,7 @@ typedef struct CGroupBlockIODeviceWeight CGroupBlockIODeviceWeight;
typedef struct CGroupBlockIODeviceBandwidth CGroupBlockIODeviceBandwidth;
typedef struct CGroupBPFForeignProgram CGroupBPFForeignProgram;
typedef struct CGroupSocketBindItem CGroupSocketBindItem;
typedef struct CGroupRuntime CGroupRuntime;
typedef enum CGroupDevicePolicy {
/* When devices listed, will allow those, plus built-in ones, if none are listed will allow
@@ -131,7 +135,9 @@ typedef enum CGroupPressureWatch {
_CGROUP_PRESSURE_WATCH_INVALID = -EINVAL,
} CGroupPressureWatch;
/* When adding members make sure to update cgroup_context_copy() accordingly */
/* The user-supplied cgroup-related configuration options. This remains mostly immutable while the service
* manager is running (except for an occasional SetProperty() configuration change), outside of reload
* cycles. When adding members make sure to update cgroup_context_copy() accordingly. */
struct CGroupContext {
bool cpu_accounting;
bool io_accounting;
@@ -288,6 +294,86 @@ typedef enum CGroupLimitType {
_CGROUP_LIMIT_INVALID = -EINVAL,
} CGroupLimitType;
/* The dynamic, regular updated information about a unit that as a realized cgroup. This is only allocated when a unit is first realized */
typedef struct CGroupRuntime {
/* Where the cpu.stat or cpuacct.usage was at the time the unit was started */
nsec_t cpu_usage_base;
nsec_t cpu_usage_last; /* the most recently read value */
/* Most recently read value of memory accounting metrics */
uint64_t memory_accounting_last[_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST + 1];
/* The current counter of OOM kills initiated by systemd-oomd */
uint64_t managed_oom_kill_last;
/* The current counter of the oom_kill field in the memory.events cgroup attribute */
uint64_t oom_kill_last;
/* Where the io.stat data was at the time the unit was started */
uint64_t io_accounting_base[_CGROUP_IO_ACCOUNTING_METRIC_MAX];
uint64_t io_accounting_last[_CGROUP_IO_ACCOUNTING_METRIC_MAX]; /* the most recently read value */
/* Counterparts in the cgroup filesystem */
char *cgroup_path;
uint64_t cgroup_id;
CGroupMask cgroup_realized_mask; /* In which hierarchies does this unit's cgroup exist? (only relevant on cgroup v1) */
CGroupMask cgroup_enabled_mask; /* Which controllers are enabled (or more correctly: enabled for the children) for this unit's cgroup? (only relevant on cgroup v2) */
CGroupMask cgroup_invalidated_mask; /* A mask specifying controllers which shall be considered invalidated, and require re-realization */
CGroupMask cgroup_members_mask; /* A cache for the controllers required by all children of this cgroup (only relevant for slice units) */
/* Inotify watch descriptors for watching cgroup.events and memory.events on cgroupv2 */
int cgroup_control_inotify_wd;
int cgroup_memory_inotify_wd;
/* Device Controller BPF program */
BPFProgram *bpf_device_control_installed;
/* IP BPF Firewalling/accounting */
int ip_accounting_ingress_map_fd;
int ip_accounting_egress_map_fd;
uint64_t ip_accounting_extra[_CGROUP_IP_ACCOUNTING_METRIC_MAX];
int ipv4_allow_map_fd;
int ipv6_allow_map_fd;
int ipv4_deny_map_fd;
int ipv6_deny_map_fd;
BPFProgram *ip_bpf_ingress, *ip_bpf_ingress_installed;
BPFProgram *ip_bpf_egress, *ip_bpf_egress_installed;
Set *ip_bpf_custom_ingress;
Set *ip_bpf_custom_ingress_installed;
Set *ip_bpf_custom_egress;
Set *ip_bpf_custom_egress_installed;
/* BPF programs managed (e.g. loaded to kernel) by an entity external to systemd,
* attached to unit cgroup by provided program fd and attach type. */
Hashmap *bpf_foreign_by_key;
FDSet *initial_socket_bind_link_fds;
#if BPF_FRAMEWORK
/* BPF links to BPF programs attached to cgroup/bind{4|6} hooks and
* responsible for allowing or denying a unit to bind(2) to a socket
* address. */
struct bpf_link *ipv4_socket_bind_link;
struct bpf_link *ipv6_socket_bind_link;
#endif
FDSet *initial_restric_ifaces_link_fds;
#if BPF_FRAMEWORK
struct bpf_link *restrict_ifaces_ingress_bpf_link;
struct bpf_link *restrict_ifaces_egress_bpf_link;
#endif
bool cgroup_realized:1;
bool cgroup_members_mask_valid:1;
/* Reset cgroup accounting next time we fork something off */
bool reset_accounting:1;
/* Whether we warned about clamping the CPU quota period */
bool warned_clamping_cpu_quota_period:1;
} CGroupRuntime;
typedef struct Unit Unit;
typedef struct Manager Manager;
typedef enum ManagerState ManagerState;
@@ -360,6 +446,7 @@ int unit_watch_cgroup(Unit *u);
int unit_watch_cgroup_memory(Unit *u);
void unit_add_to_cgroup_realize_queue(Unit *u);
int unit_cgroup_is_empty(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. */
@@ -435,6 +522,16 @@ bool unit_cgroup_delegate(Unit *u);
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_;
CGroupRuntime *cgroup_runtime_new(void);
CGroupRuntime *cgroup_runtime_free(CGroupRuntime *crt);
DEFINE_TRIVIAL_CLEANUP_FUNC(CGroupRuntime*, cgroup_runtime_free);
int cgroup_runtime_serialize(Unit *u, FILE *f, FDSet *fds);
int cgroup_runtime_deserialize_one(Unit *u, const char *key, const char *value, FDSet *fds);
const char* cgroup_pressure_watch_to_string(CGroupPressureWatch a) _const_;
CGroupPressureWatch cgroup_pressure_watch_from_string(const char *s) _pure_;
+11 -2
View File
@@ -69,6 +69,10 @@ static int build_managed_oom_json_array_element(Unit *u, const char *property, J
if (!c)
return -EINVAL;
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (!crt)
return -EINVAL;
if (UNIT_IS_INACTIVE_OR_FAILED(unit_active_state(u)))
/* systemd-oomd should always treat inactive units as though they didn't enable any action since they
* should not have a valid cgroup */
@@ -83,19 +87,24 @@ static int build_managed_oom_json_array_element(Unit *u, const char *property, J
return json_build(ret_v, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("mode", JSON_BUILD_STRING(mode)),
JSON_BUILD_PAIR("path", JSON_BUILD_STRING(u->cgroup_path)),
JSON_BUILD_PAIR("path", JSON_BUILD_STRING(crt->cgroup_path)),
JSON_BUILD_PAIR("property", JSON_BUILD_STRING(property)),
JSON_BUILD_PAIR_CONDITION(use_limit, "limit", JSON_BUILD_UNSIGNED(c->moom_mem_pressure_limit))));
}
int manager_varlink_send_managed_oom_update(Unit *u) {
_cleanup_(json_variant_unrefp) JsonVariant *arr = NULL, *v = NULL;
CGroupRuntime *crt;
CGroupContext *c;
int r;
assert(u);
if (!UNIT_VTABLE(u)->can_set_managed_oom || !u->manager || !u->cgroup_path)
if (!UNIT_VTABLE(u)->can_set_managed_oom || !u->manager)
return 0;
crt = unit_get_cgroup_runtime(u);
if (!crt || !crt->cgroup_path)
return 0;
if (MANAGER_IS_SYSTEM(u->manager)) {
+6 -2
View File
@@ -1300,7 +1300,9 @@ int bus_cgroup_set_property(
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
c->cpu_quota_per_sec_usec = u64;
u->warned_clamping_cpu_quota_period = false;
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (crt)
crt->warned_clamping_cpu_quota_period = false;
unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
if (c->cpu_quota_per_sec_usec == USEC_INFINITY)
@@ -1324,7 +1326,9 @@ int bus_cgroup_set_property(
if (!UNIT_WRITE_FLAGS_NOOP(flags)) {
c->cpu_quota_period_usec = u64;
u->warned_clamping_cpu_quota_period = false;
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (crt)
crt->warned_clamping_cpu_quota_period = false;
unit_invalidate_cgroup(u, CGROUP_MASK_CPU);
if (c->cpu_quota_period_usec == USEC_INFINITY)
unit_write_setting(u, flags, "CPUQuotaPeriodSec", "CPUQuotaPeriodSec=");
+27 -5
View File
@@ -1216,12 +1216,32 @@ static int property_get_cgroup(
* indicates the root cgroup, which we report as "/". c) all
* other cases we report as-is. */
if (u->cgroup_path)
t = empty_to_root(u->cgroup_path);
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (crt && crt->cgroup_path)
t = empty_to_root(crt->cgroup_path);
return sd_bus_message_append(reply, "s", t);
}
static int property_get_cgroup_id(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
Unit *u = ASSERT_PTR(userdata);
assert(bus);
assert(reply);
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
return sd_bus_message_append(reply, "t", crt ? crt->cgroup_id : 0);
}
static int append_process(sd_bus_message *reply, const char *p, PidRef *pid, Set *pids) {
_cleanup_free_ char *buf = NULL, *cmdline = NULL;
int r;
@@ -1350,8 +1370,10 @@ int bus_unit_method_get_processes(sd_bus_message *message, void *userdata, sd_bu
if (r < 0)
return r;
if (u->cgroup_path) {
r = append_cgroup(reply, u->cgroup_path, pids);
CGroupRuntime *crt;
crt = unit_get_cgroup_runtime(u);
if (crt && crt->cgroup_path) {
r = append_cgroup(reply, crt->cgroup_path, pids);
if (r < 0)
return r;
}
@@ -1558,7 +1580,7 @@ const sd_bus_vtable bus_unit_cgroup_vtable[] = {
SD_BUS_VTABLE_START(0),
SD_BUS_PROPERTY("Slice", "s", property_get_slice, 0, 0),
SD_BUS_PROPERTY("ControlGroup", "s", property_get_cgroup, 0, 0),
SD_BUS_PROPERTY("ControlGroupId", "t", NULL, offsetof(Unit, cgroup_id), 0),
SD_BUS_PROPERTY("ControlGroupId", "t", property_get_cgroup_id, 0, 0),
SD_BUS_PROPERTY("MemoryCurrent", "t", property_get_current_memory, 0, 0),
SD_BUS_PROPERTY("MemoryPeak", "t", property_get_memory_accounting, 0, 0),
SD_BUS_PROPERTY("MemorySwapCurrent", "t", property_get_memory_accounting, 0, 0),
+8 -2
View File
@@ -240,6 +240,7 @@ static void mount_done(Unit *u) {
mount_parameters_done(&m->parameters_fragment);
m->exec_runtime = exec_runtime_free(m->exec_runtime);
exec_command_done_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
m->control_command = NULL;
@@ -815,8 +816,10 @@ static int mount_coldplug(Unit *u) {
return r;
}
if (!IN_SET(m->deserialized_state, MOUNT_DEAD, MOUNT_FAILED))
if (!IN_SET(m->deserialized_state, MOUNT_DEAD, MOUNT_FAILED)) {
(void) unit_setup_exec_runtime(u);
(void) unit_setup_cgroup_runtime(u);
}
mount_set_state(m, m->deserialized_state);
return 0;
@@ -1332,7 +1335,9 @@ static void mount_cycle_clear(Mount *m) {
m->result = MOUNT_SUCCESS;
m->reload_result = MOUNT_SUCCESS;
exec_command_reset_status_array(m->exec_command, _MOUNT_EXEC_COMMAND_MAX);
UNIT(m)->reset_accounting = true;
if (m->cgroup_runtime)
m->cgroup_runtime->reset_accounting = true;
}
static int mount_start(Unit *u) {
@@ -2448,6 +2453,7 @@ const UnitVTable mount_vtable = {
.cgroup_context_offset = offsetof(Mount, cgroup_context),
.kill_context_offset = offsetof(Mount, kill_context),
.exec_runtime_offset = offsetof(Mount, exec_runtime),
.cgroup_runtime_offset = offsetof(Mount, cgroup_runtime),
.sections =
"Unit\0"
+1
View File
@@ -79,6 +79,7 @@ struct Mount {
CGroupContext cgroup_context;
ExecRuntime *exec_runtime;
CGroupRuntime *cgroup_runtime;
MountState state, deserialized_state;
+5 -1
View File
@@ -353,6 +353,9 @@ static int scope_enter_start_chown(Scope *s) {
assert(s);
assert(s->user);
if (!s->cgroup_runtime)
return -EINVAL;
r = scope_arm_timer(s, /* relative= */ true, u->manager->defaults.timeout_start_usec);
if (r < 0)
return r;
@@ -385,7 +388,7 @@ static int scope_enter_start_chown(Scope *s) {
}
}
r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path, uid, gid);
r = cg_set_access(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_runtime->cgroup_path, uid, gid);
if (r < 0) {
log_unit_error_errno(UNIT(s), r, "Failed to adjust control group access: %m");
_exit(EXIT_CGROUP);
@@ -776,6 +779,7 @@ const UnitVTable scope_vtable = {
.object_size = sizeof(Scope),
.cgroup_context_offset = offsetof(Scope, cgroup_context),
.kill_context_offset = offsetof(Scope, kill_context),
.cgroup_runtime_offset = offsetof(Scope, cgroup_runtime),
.sections =
"Unit\0"
+1
View File
@@ -21,6 +21,7 @@ struct Scope {
CGroupContext cgroup_context;
KillContext kill_context;
CGroupRuntime *cgroup_runtime;
ScopeState state, deserialized_state;
ScopeResult result;
+8 -3
View File
@@ -460,6 +460,7 @@ static void service_done(Unit *u) {
s->status_text = mfree(s->status_text);
s->exec_runtime = exec_runtime_free(s->exec_runtime);
exec_command_free_array(s->exec_command, _SERVICE_EXEC_COMMAND_MAX);
s->control_command = NULL;
s->main_command = NULL;
@@ -1345,6 +1346,7 @@ static int service_coldplug(Unit *u) {
SERVICE_DEAD_RESOURCES_PINNED)) {
(void) unit_enqueue_rewatch_pids(u);
(void) unit_setup_exec_runtime(u);
(void) unit_setup_cgroup_runtime(u);
}
if (IN_SET(s->deserialized_state, SERVICE_START_POST, SERVICE_RUNNING, SERVICE_RELOAD, SERVICE_RELOAD_SIGNAL, SERVICE_RELOAD_NOTIFY))
@@ -1885,10 +1887,10 @@ static int cgroup_good(Service *s) {
/* Returns 0 if the cgroup is empty or doesn't exist, > 0 if it is exists and is populated, < 0 if we can't
* figure it out */
if (!UNIT(s)->cgroup_path)
if (!s->cgroup_runtime || !s->cgroup_runtime->cgroup_path)
return 0;
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, UNIT(s)->cgroup_path);
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, s->cgroup_runtime->cgroup_path);
if (r < 0)
return r;
@@ -2773,7 +2775,9 @@ static int service_start(Unit *u) {
s->flush_n_restarts = false;
}
u->reset_accounting = true;
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (crt)
crt->reset_accounting = true;
service_enter_condition(s);
return 1;
@@ -5149,6 +5153,7 @@ const UnitVTable service_vtable = {
.cgroup_context_offset = offsetof(Service, cgroup_context),
.kill_context_offset = offsetof(Service, kill_context),
.exec_runtime_offset = offsetof(Service, exec_runtime),
.cgroup_runtime_offset = offsetof(Service, cgroup_runtime),
.sections =
"Unit\0"
+2
View File
@@ -168,6 +168,8 @@ struct Service {
/* Runtime data of the execution context */
ExecRuntime *exec_runtime;
CGroupRuntime *cgroup_runtime;
PidRef main_pid, control_pid;
/* if we are a socket activated service instance, store information of the connection/peer/socket */
+1
View File
@@ -399,6 +399,7 @@ static int slice_freezer_action(Unit *s, FreezerAction action) {
const UnitVTable slice_vtable = {
.object_size = sizeof(Slice),
.cgroup_context_offset = offsetof(Slice, cgroup_context),
.cgroup_runtime_offset = offsetof(Slice, cgroup_runtime),
.sections =
"Unit\0"
+2
View File
@@ -11,6 +11,8 @@ struct Slice {
SliceState state, deserialized_state;
CGroupContext cgroup_context;
CGroupRuntime *cgroup_runtime;
};
extern const UnitVTable slice_vtable;
+4 -1
View File
@@ -167,6 +167,7 @@ static void socket_done(Unit *u) {
s->peers_by_address = set_free(s->peers_by_address);
s->exec_runtime = exec_runtime_free(s->exec_runtime);
exec_command_free_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
s->control_command = NULL;
@@ -2473,7 +2474,8 @@ static int socket_start(Unit *u) {
s->result = SOCKET_SUCCESS;
exec_command_reset_status_list_array(s->exec_command, _SOCKET_EXEC_COMMAND_MAX);
u->reset_accounting = true;
if (s->cgroup_runtime)
s->cgroup_runtime->reset_accounting = true;
socket_enter_start_pre(s);
return 1;
@@ -3528,6 +3530,7 @@ const UnitVTable socket_vtable = {
.cgroup_context_offset = offsetof(Socket, cgroup_context),
.kill_context_offset = offsetof(Socket, kill_context),
.exec_runtime_offset = offsetof(Socket, exec_runtime),
.cgroup_runtime_offset = offsetof(Socket, cgroup_runtime),
.sections =
"Unit\0"
+1
View File
@@ -92,6 +92,7 @@ struct Socket {
CGroupContext cgroup_context;
ExecRuntime *exec_runtime;
CGroupRuntime *cgroup_runtime;
/* For Accept=no sockets refers to the one service we'll
* activate. For Accept=yes sockets is either NULL, or filled
+8 -2
View File
@@ -168,6 +168,7 @@ static void swap_done(Unit *u) {
s->parameters_fragment.options = mfree(s->parameters_fragment.options);
s->exec_runtime = exec_runtime_free(s->exec_runtime);
exec_command_done_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX);
s->control_command = NULL;
@@ -564,8 +565,10 @@ static int swap_coldplug(Unit *u) {
return r;
}
if (!IN_SET(new_state, SWAP_DEAD, SWAP_FAILED))
if (!IN_SET(new_state, SWAP_DEAD, SWAP_FAILED)) {
(void) unit_setup_exec_runtime(u);
(void) unit_setup_cgroup_runtime(u);
}
swap_set_state(s, new_state);
return 0;
@@ -854,7 +857,9 @@ static void swap_cycle_clear(Swap *s) {
s->result = SWAP_SUCCESS;
exec_command_reset_status_array(s->exec_command, _SWAP_EXEC_COMMAND_MAX);
UNIT(s)->reset_accounting = true;
if (s->cgroup_runtime)
s->cgroup_runtime->reset_accounting = true;
}
static int swap_start(Unit *u) {
@@ -1589,6 +1594,7 @@ const UnitVTable swap_vtable = {
.cgroup_context_offset = offsetof(Swap, cgroup_context),
.kill_context_offset = offsetof(Swap, kill_context),
.exec_runtime_offset = offsetof(Swap, exec_runtime),
.cgroup_runtime_offset = offsetof(Swap, cgroup_runtime),
.sections =
"Unit\0"
+1
View File
@@ -70,6 +70,7 @@ struct Swap {
CGroupContext cgroup_context;
ExecRuntime *exec_runtime;
CGroupRuntime *cgroup_runtime;
SwapState state, deserialized_state;
+7 -4
View File
@@ -87,13 +87,14 @@ static void bad_specifier(const Unit *u, char specifier) {
static int specifier_cgroup(char specifier, const void *data, const char *root, const void *userdata, char **ret) {
const Unit *u = ASSERT_PTR(userdata);
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
bad_specifier(u, specifier);
if (u->cgroup_path) {
if (crt && crt->cgroup_path) {
char *n;
n = strdup(u->cgroup_path);
n = strdup(crt->cgroup_path);
if (!n)
return -ENOMEM;
@@ -126,8 +127,10 @@ static int specifier_cgroup_slice(char specifier, const void *data, const char *
slice = UNIT_GET_SLICE(u);
if (slice) {
if (slice->cgroup_path)
n = strdup(slice->cgroup_path);
CGroupRuntime *crt = unit_get_cgroup_runtime(slice);
if (crt && crt->cgroup_path)
n = strdup(crt->cgroup_path);
else
return unit_default_cgroup_path(slice, ret);
} else
+20 -228
View File
@@ -13,23 +13,6 @@
#include "unit-serialize.h"
#include "user-util.h"
static int serialize_cgroup_mask(FILE *f, const char *key, CGroupMask mask) {
_cleanup_free_ char *s = NULL;
int r;
assert(f);
assert(key);
if (mask == 0)
return 0;
r = cg_mask_to_string(mask, &s);
if (r < 0)
return log_error_errno(r, "Failed to format cgroup mask: %m");
return serialize_item(f, key, s);
}
/* Make sure out values fit in the bitfield. */
assert_cc(_UNIT_MARKER_MAX <= sizeof(((Unit){}).markers) * 8);
@@ -69,40 +52,6 @@ static int deserialize_markers(Unit *u, const char *value) {
}
}
static const char* const ip_accounting_metric_field_table[_CGROUP_IP_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IP_INGRESS_BYTES] = "ip-accounting-ingress-bytes",
[CGROUP_IP_INGRESS_PACKETS] = "ip-accounting-ingress-packets",
[CGROUP_IP_EGRESS_BYTES] = "ip-accounting-egress-bytes",
[CGROUP_IP_EGRESS_PACKETS] = "ip-accounting-egress-packets",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(ip_accounting_metric_field, CGroupIPAccountingMetric);
static const char* const io_accounting_metric_field_base_table[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-base",
[CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-base",
[CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-base",
[CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-base",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(io_accounting_metric_field_base, CGroupIOAccountingMetric);
static const char* const io_accounting_metric_field_last_table[_CGROUP_IO_ACCOUNTING_METRIC_MAX] = {
[CGROUP_IO_READ_BYTES] = "io-accounting-read-bytes-last",
[CGROUP_IO_WRITE_BYTES] = "io-accounting-write-bytes-last",
[CGROUP_IO_READ_OPERATIONS] = "io-accounting-read-operations-last",
[CGROUP_IO_WRITE_OPERATIONS] = "io-accounting-write-operations-last",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(io_accounting_metric_field_last, CGroupIOAccountingMetric);
static const char* const memory_accounting_metric_field_last_table[_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST + 1] = {
[CGROUP_MEMORY_PEAK] = "memory-accounting-peak",
[CGROUP_MEMORY_SWAP_PEAK] = "memory-accounting-swap-peak",
};
DEFINE_PRIVATE_STRING_TABLE_LOOKUP(memory_accounting_metric_field_last, CGroupMemoryAccountingMetric);
int unit_serialize_state(Unit *u, FILE *f, FDSet *fds, bool switching_root) {
int r;
@@ -158,48 +107,7 @@ int unit_serialize_state(Unit *u, FILE *f, FDSet *fds, bool switching_root) {
(void) serialize_bool(f, "exported-log-rate-limit-interval", u->exported_log_ratelimit_interval);
(void) serialize_bool(f, "exported-log-rate-limit-burst", u->exported_log_ratelimit_burst);
(void) serialize_item_format(f, "cpu-usage-base", "%" PRIu64, u->cpu_usage_base);
if (u->cpu_usage_last != NSEC_INFINITY)
(void) serialize_item_format(f, "cpu-usage-last", "%" PRIu64, u->cpu_usage_last);
if (u->managed_oom_kill_last > 0)
(void) serialize_item_format(f, "managed-oom-kill-last", "%" PRIu64, u->managed_oom_kill_last);
if (u->oom_kill_last > 0)
(void) serialize_item_format(f, "oom-kill-last", "%" PRIu64, u->oom_kill_last);
for (CGroupIOAccountingMetric im = 0; im < _CGROUP_IO_ACCOUNTING_METRIC_MAX; im++) {
(void) serialize_item_format(f, io_accounting_metric_field_base_to_string(im), "%" PRIu64, u->io_accounting_base[im]);
if (u->io_accounting_last[im] != UINT64_MAX)
(void) serialize_item_format(f, io_accounting_metric_field_last_to_string(im), "%" PRIu64, u->io_accounting_last[im]);
}
for (CGroupMemoryAccountingMetric metric = 0; metric <= _CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST; metric++) {
uint64_t v;
r = unit_get_memory_accounting(u, metric, &v);
if (r >= 0)
(void) serialize_item_format(f, memory_accounting_metric_field_last_to_string(metric), "%" PRIu64, v);
}
if (u->cgroup_path)
(void) serialize_item(f, "cgroup", u->cgroup_path);
(void) serialize_bool(f, "cgroup-realized", u->cgroup_realized);
(void) serialize_cgroup_mask(f, "cgroup-realized-mask", u->cgroup_realized_mask);
(void) serialize_cgroup_mask(f, "cgroup-enabled-mask", u->cgroup_enabled_mask);
(void) serialize_cgroup_mask(f, "cgroup-invalidated-mask", u->cgroup_invalidated_mask);
(void) bpf_socket_bind_serialize(u, f, fds);
(void) bpf_program_serialize_attachment(f, fds, "ip-bpf-ingress-installed", u->ip_bpf_ingress_installed);
(void) bpf_program_serialize_attachment(f, fds, "ip-bpf-egress-installed", u->ip_bpf_egress_installed);
(void) bpf_program_serialize_attachment(f, fds, "bpf-device-control-installed", u->bpf_device_control_installed);
(void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-ingress-installed", u->ip_bpf_custom_ingress_installed);
(void) bpf_program_serialize_attachment_set(f, fds, "ip-bpf-custom-egress-installed", u->ip_bpf_custom_egress_installed);
(void) bpf_restrict_ifaces_serialize(u, f, fds);
(void) cgroup_runtime_serialize(u, f, fds);
if (uid_is_valid(u->ref_uid))
(void) serialize_item_format(f, "ref-uid", UID_FMT, u->ref_uid);
@@ -214,14 +122,6 @@ int unit_serialize_state(Unit *u, FILE *f, FDSet *fds, bool switching_root) {
bus_track_serialize(u->bus_track, f, "ref");
for (CGroupIPAccountingMetric m = 0; m < _CGROUP_IP_ACCOUNTING_METRIC_MAX; m++) {
uint64_t v;
r = unit_get_ip_accounting(u, m, &v);
if (r >= 0)
(void) serialize_item_format(f, ip_accounting_metric_field_to_string(m), "%" PRIu64, v);
}
if (!switching_root) {
if (u->job) {
fputs("job\n", f);
@@ -297,7 +197,6 @@ int unit_deserialize_state(Unit *u, FILE *f, FDSet *fds) {
for (;;) {
_cleanup_free_ char *l = NULL;
ssize_t m;
size_t k;
char *v;
@@ -380,76 +279,7 @@ int unit_deserialize_state(Unit *u, FILE *f, FDSet *fds) {
else if (MATCH_DESERIALIZE("exported-log-rate-limit-burst", l, v, parse_boolean, u->exported_log_ratelimit_burst))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("cpu-usage-base", l, v, safe_atou64, u->cpu_usage_base) ||
MATCH_DESERIALIZE_IMMEDIATE("cpuacct-usage-base", l, v, safe_atou64, u->cpu_usage_base))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("cpu-usage-last", l, v, safe_atou64, u->cpu_usage_last))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("managed-oom-kill-last", l, v, safe_atou64, u->managed_oom_kill_last))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("oom-kill-last", l, v, safe_atou64, u->oom_kill_last))
continue;
else if (streq(l, "cgroup")) {
r = unit_set_cgroup_path(u, v);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set cgroup path %s, ignoring: %m", v);
(void) unit_watch_cgroup(u);
(void) unit_watch_cgroup_memory(u);
continue;
} else if (MATCH_DESERIALIZE("cgroup-realized", l, v, parse_boolean, u->cgroup_realized))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-realized-mask", l, v, cg_mask_from_string, u->cgroup_realized_mask))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-enabled-mask", l, v, cg_mask_from_string, u->cgroup_enabled_mask))
continue;
else if (MATCH_DESERIALIZE_IMMEDIATE("cgroup-invalidated-mask", l, v, cg_mask_from_string, u->cgroup_invalidated_mask))
continue;
else if (STR_IN_SET(l, "ipv4-socket-bind-bpf-link-fd", "ipv6-socket-bind-bpf-link-fd")) {
int fd;
fd = deserialize_fd(fds, v);
if (fd >= 0)
(void) bpf_socket_bind_add_initial_link_fd(u, fd);
continue;
} else if (streq(l, "ip-bpf-ingress-installed")) {
(void) bpf_program_deserialize_attachment(v, fds, &u->ip_bpf_ingress_installed);
continue;
} else if (streq(l, "ip-bpf-egress-installed")) {
(void) bpf_program_deserialize_attachment(v, fds, &u->ip_bpf_egress_installed);
continue;
} else if (streq(l, "bpf-device-control-installed")) {
(void) bpf_program_deserialize_attachment(v, fds, &u->bpf_device_control_installed);
continue;
} else if (streq(l, "ip-bpf-custom-ingress-installed")) {
(void) bpf_program_deserialize_attachment_set(v, fds, &u->ip_bpf_custom_ingress_installed);
continue;
} else if (streq(l, "ip-bpf-custom-egress-installed")) {
(void) bpf_program_deserialize_attachment_set(v, fds, &u->ip_bpf_custom_egress_installed);
continue;
} else if (streq(l, "restrict-ifaces-bpf-fd")) {
int fd;
fd = deserialize_fd(fds, v);
if (fd >= 0)
(void) bpf_restrict_ifaces_add_initial_link_fd(u, fd);
continue;
} else if (streq(l, "ref-uid")) {
else if (streq(l, "ref-uid")) {
uid_t uid;
r = parse_uid(v, &uid);
@@ -499,55 +329,6 @@ int unit_deserialize_state(Unit *u, FILE *f, FDSet *fds) {
continue;
}
m = memory_accounting_metric_field_last_from_string(l);
if (m >= 0) {
uint64_t c;
r = safe_atou64(v, &c);
if (r < 0)
log_unit_debug(u, "Failed to parse memory accounting last value %s, ignoring.", v);
else
u->memory_accounting_last[m] = c;
continue;
}
/* Check if this is an IP accounting metric serialization field */
m = ip_accounting_metric_field_from_string(l);
if (m >= 0) {
uint64_t c;
r = safe_atou64(v, &c);
if (r < 0)
log_unit_debug(u, "Failed to parse IP accounting value %s, ignoring.", v);
else
u->ip_accounting_extra[m] = c;
continue;
}
m = io_accounting_metric_field_base_from_string(l);
if (m >= 0) {
uint64_t c;
r = safe_atou64(v, &c);
if (r < 0)
log_unit_debug(u, "Failed to parse IO accounting base value %s, ignoring.", v);
else
u->io_accounting_base[m] = c;
continue;
}
m = io_accounting_metric_field_last_from_string(l);
if (m >= 0) {
uint64_t c;
r = safe_atou64(v, &c);
if (r < 0)
log_unit_debug(u, "Failed to parse IO accounting last value %s, ignoring.", v);
else
u->io_accounting_last[m] = c;
continue;
}
r = exec_shared_runtime_deserialize_compat(u, l, v, fds);
if (r < 0) {
log_unit_warning(u, "Failed to deserialize runtime parameter '%s', ignoring.", l);
@@ -556,6 +337,13 @@ int unit_deserialize_state(Unit *u, FILE *f, FDSet *fds) {
/* Returns positive if key was handled by the call */
continue;
r = cgroup_runtime_deserialize_one(u, l, v, fds);
if (r < 0) {
log_unit_warning(u, "Failed to deserialize cgroup runtime parameter '%s, ignoring.", l);
continue;
} else if (r > 0)
continue; /* was handled */
if (UNIT_VTABLE(u)->deserialize_item) {
r = UNIT_VTABLE(u)->deserialize_item(u, l, v, fds);
if (r < 0)
@@ -574,7 +362,9 @@ int unit_deserialize_state(Unit *u, FILE *f, FDSet *fds) {
/* 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. */
if (u->cgroup_realized) {
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);
}
@@ -707,23 +497,25 @@ void unit_dump(Unit *u, FILE *f, const char *prefix) {
}
if (UNIT_HAS_CGROUP_CONTEXT(u)) {
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
fprintf(f,
"%s\tSlice: %s\n"
"%s\tCGroup: %s\n"
"%s\tCGroup realized: %s\n",
prefix, strna(unit_slice_name(u)),
prefix, strna(u->cgroup_path),
prefix, yes_no(u->cgroup_realized));
prefix, strna(crt ? crt->cgroup_path : NULL),
prefix, yes_no(crt ? crt->cgroup_realized : false));
if (u->cgroup_realized_mask != 0) {
if (crt && crt->cgroup_realized_mask != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(u->cgroup_realized_mask, &s);
(void) cg_mask_to_string(crt->cgroup_realized_mask, &s);
fprintf(f, "%s\tCGroup realized mask: %s\n", prefix, strnull(s));
}
if (u->cgroup_enabled_mask != 0) {
if (crt && crt->cgroup_enabled_mask != 0) {
_cleanup_free_ char *s = NULL;
(void) cg_mask_to_string(u->cgroup_enabled_mask, &s);
(void) cg_mask_to_string(crt->cgroup_enabled_mask, &s);
fprintf(f, "%s\tCGroup enabled mask: %s\n", prefix, strnull(s));
}
+95 -81
View File
@@ -109,29 +109,13 @@ Unit* unit_new(Manager *m, size_t size) {
u->unit_file_preset = -1;
u->on_failure_job_mode = JOB_REPLACE;
u->on_success_job_mode = JOB_FAIL;
u->cgroup_control_inotify_wd = -1;
u->cgroup_memory_inotify_wd = -1;
u->job_timeout = USEC_INFINITY;
u->job_running_timeout = USEC_INFINITY;
u->ref_uid = UID_INVALID;
u->ref_gid = GID_INVALID;
u->cpu_usage_last = NSEC_INFINITY;
unit_reset_memory_accounting_last(u);
unit_reset_io_accounting_last(u);
u->cgroup_invalidated_mask |= CGROUP_MASK_BPF_FIREWALL;
u->failure_action_exit_status = u->success_action_exit_status = -1;
u->ip_accounting_ingress_map_fd = -EBADF;
u->ip_accounting_egress_map_fd = -EBADF;
u->ipv4_allow_map_fd = -EBADF;
u->ipv6_allow_map_fd = -EBADF;
u->ipv4_deny_map_fd = -EBADF;
u->ipv6_deny_map_fd = -EBADF;
u->last_section_private = -1;
u->start_ratelimit = (const RateLimit) {
@@ -139,7 +123,13 @@ Unit* unit_new(Manager *m, size_t size) {
m->defaults.start_limit_burst,
};
u->auto_start_stop_ratelimit = (const RateLimit) { .interval = 10 * USEC_PER_SEC, .burst = 16 };
u->auto_start_stop_ratelimit = (const RateLimit) {
.interval = 10 * USEC_PER_SEC,
.burst = 16
};
unit_reset_memory_accounting_last(u);
unit_reset_io_accounting_last(u);
return u;
}
@@ -490,16 +480,11 @@ bool unit_may_gc(Unit *u) {
if (unit_success_failure_handler_has_jobs(u))
return false;
if (u->cgroup_path) {
/* If the unit has a cgroup, then check whether there's anything in it. If so, we should stay
* around. Units with active processes should never be collected. */
r = cg_is_empty_recursive(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to determine whether cgroup %s is empty: %m", empty_to_root(u->cgroup_path));
if (r <= 0)
return false;
}
/* If the unit has a cgroup, then check whether there's anything in it. If so, we should stay
* around. Units with active processes should never be collected. */
r = unit_cgroup_is_empty(u);
if (r <= 0 && r != -ENXIO)
return false; /* ENXIO means: currently not realized */
if (!UNIT_VTABLE(u)->may_gc)
return true;
@@ -804,12 +789,6 @@ Unit* unit_free(Unit *u) {
if (u->on_console)
manager_unref_console(u->manager);
fdset_free(u->initial_socket_bind_link_fds);
#if BPF_FRAMEWORK
bpf_link_free(u->ipv4_socket_bind_link);
bpf_link_free(u->ipv6_socket_bind_link);
#endif
unit_release_cgroup(u);
if (!MANAGER_IS_RELOADING(u->manager))
@@ -866,16 +845,6 @@ Unit* unit_free(Unit *u) {
bpf_firewall_close(u);
hashmap_free(u->bpf_foreign_by_key);
bpf_program_free(u->bpf_device_control_installed);
#if BPF_FRAMEWORK
bpf_link_free(u->restrict_ifaces_ingress_bpf_link);
bpf_link_free(u->restrict_ifaces_egress_bpf_link);
#endif
fdset_free(u->initial_restric_ifaces_link_fds);
condition_free_list(u->conditions);
condition_free_list(u->asserts);
@@ -2966,7 +2935,8 @@ int unit_enqueue_rewatch_pids(Unit *u) {
assert(u);
if (!u->cgroup_path)
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (!crt || !crt->cgroup_path)
return -ENOENT;
r = cg_unified_controller(SYSTEMD_CGROUP_CONTROLLER);
@@ -3452,8 +3422,11 @@ int unit_set_slice(Unit *u, Unit *slice) {
return 0;
/* Disallow slice changes if @u is already bound to cgroups */
if (UNIT_GET_SLICE(u) && u->cgroup_realized)
return -EBUSY;
if (UNIT_GET_SLICE(u)) {
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (crt && crt->cgroup_realized)
return -EBUSY;
}
/* Remove any slices assigned prior; we should only have one UNIT_IN_SLICE dependency */
if (UNIT_GET_SLICE(u))
@@ -4093,31 +4066,35 @@ int unit_kill(
/* Note: if we shall enqueue rather than kill we won't do this via the cgroup mechanism, since it
* doesn't really make much sense (and given that enqueued values are a relatively expensive
* resource, and we shouldn't allow us to be subjects for such allocation sprees) */
if (IN_SET(who, KILL_ALL, KILL_ALL_FAIL) && u->cgroup_path && code == SI_USER) {
_cleanup_set_free_ Set *pid_set = NULL;
if (IN_SET(who, KILL_ALL, KILL_ALL_FAIL) && code == SI_USER) {
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
/* Exclude the main/control pids from being killed via the cgroup */
r = unit_pid_set(u, &pid_set);
if (r < 0)
return log_oom();
if (crt && crt->cgroup_path) {
_cleanup_set_free_ Set *pid_set = NULL;
r = cg_kill_recursive(u->cgroup_path, signo, 0, pid_set, kill_common_log, u);
if (r < 0 && !IN_SET(r, -ESRCH, -ENOENT)) {
if (ret >= 0)
sd_bus_error_set_errnof(
ret_error, r,
"Failed to send signal SIG%s to auxiliary processes: %m",
/* Exclude the main/control pids from being killed via the cgroup */
r = unit_pid_set(u, &pid_set);
if (r < 0)
return log_oom();
r = cg_kill_recursive(crt->cgroup_path, signo, 0, pid_set, kill_common_log, u);
if (r < 0 && !IN_SET(r, -ESRCH, -ENOENT)) {
if (ret >= 0)
sd_bus_error_set_errnof(
ret_error, r,
"Failed to send signal SIG%s to auxiliary processes: %m",
signal_to_string(signo));
log_unit_warning_errno(
u, r,
"Failed to send signal SIG%s to auxiliary processes on client request: %m",
signal_to_string(signo));
log_unit_warning_errno(
u, r,
"Failed to send signal SIG%s to auxiliary processes on client request: %m",
signal_to_string(signo));
RET_GATHER(ret, r);
}
RET_GATHER(ret, r);
killed = killed || r >= 0;
}
killed = killed || r >= 0;
}
/* If the "fail" versions of the operation are requested, then complain if the set of processes we killed is empty */
@@ -4377,7 +4354,7 @@ ExecContext *unit_get_exec_context(const Unit *u) {
return (ExecContext*) ((uint8_t*) u + offset);
}
KillContext *unit_get_kill_context(Unit *u) {
KillContext *unit_get_kill_context(const Unit *u) {
size_t offset;
assert(u);
@@ -4391,7 +4368,7 @@ KillContext *unit_get_kill_context(Unit *u) {
return (KillContext*) ((uint8_t*) u + offset);
}
CGroupContext *unit_get_cgroup_context(Unit *u) {
CGroupContext *unit_get_cgroup_context(const Unit *u) {
size_t offset;
if (u->type < 0)
@@ -4404,7 +4381,7 @@ CGroupContext *unit_get_cgroup_context(Unit *u) {
return (CGroupContext*) ((uint8_t*) u + offset);
}
ExecRuntime *unit_get_exec_runtime(Unit *u) {
ExecRuntime *unit_get_exec_runtime(const Unit *u) {
size_t offset;
if (u->type < 0)
@@ -4417,6 +4394,19 @@ ExecRuntime *unit_get_exec_runtime(Unit *u) {
return *(ExecRuntime**) ((uint8_t*) u + offset);
}
CGroupRuntime *unit_get_cgroup_runtime(const Unit *u) {
size_t offset;
if (u->type < 0)
return NULL;
offset = UNIT_VTABLE(u)->cgroup_runtime_offset;
if (offset <= 0)
return NULL;
return *(CGroupRuntime**) ((uint8_t*) u + offset);
}
static const char* unit_drop_in_dir(Unit *u, UnitWriteFlags flags) {
assert(u);
@@ -4810,7 +4800,8 @@ int unit_kill_context(Unit *u, KillOperation k) {
r = unit_kill_context_one(u, unit_control_pid(u), "control", /* is_alien = */ false, sig, send_sighup, log_func);
wait_for_exit = wait_for_exit || r > 0;
if (u->cgroup_path &&
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (crt && crt->cgroup_path &&
(c->kill_mode == KILL_CONTROL_GROUP || (c->kill_mode == KILL_MIXED && k == KILL_KILL))) {
_cleanup_set_free_ Set *pid_set = NULL;
@@ -4820,14 +4811,14 @@ int unit_kill_context(Unit *u, KillOperation k) {
return r;
r = cg_kill_recursive(
u->cgroup_path,
crt->cgroup_path,
sig,
CGROUP_SIGCONT|CGROUP_IGNORE_SELF,
pid_set,
log_func, u);
if (r < 0) {
if (!IN_SET(r, -EAGAIN, -ESRCH, -ENOENT))
log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", empty_to_root(u->cgroup_path));
log_unit_warning_errno(u, r, "Failed to kill control group %s, ignoring: %m", empty_to_root(crt->cgroup_path));
} else if (r > 0) {
@@ -4848,7 +4839,7 @@ int unit_kill_context(Unit *u, KillOperation k) {
return r;
(void) cg_kill_recursive(
u->cgroup_path,
crt->cgroup_path,
SIGHUP,
CGROUP_IGNORE_SELF,
pid_set,
@@ -4996,6 +4987,21 @@ int unit_setup_exec_runtime(Unit *u) {
return r;
}
CGroupRuntime *unit_setup_cgroup_runtime(Unit *u) {
size_t offset;
assert(u);
offset = UNIT_VTABLE(u)->cgroup_runtime_offset;
assert(offset > 0);
CGroupRuntime **rt = (CGroupRuntime**) ((uint8_t*) u + offset);
if (*rt)
return *rt;
return (*rt = cgroup_runtime_new());
}
bool unit_type_supported(UnitType t) {
static int8_t cache[_UNIT_TYPE_MAX] = {}; /* -1: disabled, 1: enabled: 0: don't know */
int r;
@@ -5339,7 +5345,8 @@ int unit_set_exec_params(Unit *u, ExecParameters *p) {
SET_FLAG(p->flags, EXEC_PASS_LOG_UNIT|EXEC_CHOWN_DIRECTORIES, MANAGER_IS_SYSTEM(u->manager));
/* Copy parameters from unit */
p->cgroup_path = u->cgroup_path;
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
p->cgroup_path = crt ? crt->cgroup_path : NULL;
SET_FLAG(p->flags, EXEC_CGROUP_DELEGATE, unit_cgroup_delegate(u));
p->received_credentials_directory = u->manager->received_credentials_directory;
@@ -5359,7 +5366,7 @@ int unit_set_exec_params(Unit *u, ExecParameters *p) {
p->user_lookup_fd = u->manager->user_lookup_fds[1];
p->cgroup_id = u->cgroup_id;
p->cgroup_id = crt ? crt->cgroup_id : 0;
p->invocation_id = u->invocation_id;
sd_id128_to_string(p->invocation_id, p->invocation_id_string);
p->unit_id = strdup(u->id);
@@ -5381,6 +5388,10 @@ int unit_fork_helper_process(Unit *u, const char *name, PidRef *ret) {
(void) unit_realize_cgroup(u);
CGroupRuntime *crt = unit_setup_cgroup_runtime(u);
if (!crt)
return -ENOMEM;
r = safe_fork(name, FORK_REOPEN_LOG|FORK_DEATHSIG_SIGTERM, &pid);
if (r < 0)
return r;
@@ -5403,10 +5414,10 @@ int unit_fork_helper_process(Unit *u, const char *name, PidRef *ret) {
(void) default_signals(SIGNALS_CRASH_HANDLER, SIGNALS_IGNORE);
(void) ignore_signals(SIGPIPE);
if (u->cgroup_path) {
r = cg_attach_everywhere(u->manager->cgroup_supported, u->cgroup_path, 0, NULL, NULL);
if (crt->cgroup_path) {
r = cg_attach_everywhere(u->manager->cgroup_supported, crt->cgroup_path, 0, NULL, NULL);
if (r < 0) {
log_unit_error_errno(u, r, "Failed to join unit cgroup %s: %m", empty_to_root(u->cgroup_path));
log_unit_error_errno(u, r, "Failed to join unit cgroup %s: %m", empty_to_root(crt->cgroup_path));
_exit(EXIT_CGROUP);
}
}
@@ -5801,9 +5812,10 @@ int unit_prepare_exec(Unit *u) {
(void) unit_realize_cgroup(u);
if (u->reset_accounting) {
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (crt->reset_accounting) {
(void) unit_reset_accounting(u);
u->reset_accounting = false;
crt->reset_accounting = false;
}
unit_export_state_files(u);
@@ -5863,11 +5875,13 @@ int unit_warn_leftover_processes(Unit *u, cg_kill_log_func_t log_func) {
(void) unit_pick_cgroup_path(u);
if (!u->cgroup_path)
CGroupRuntime *crt = unit_get_cgroup_runtime(u);
if (!crt || !crt->cgroup_path)
return 0;
return cg_kill_recursive(
u->cgroup_path,
crt->cgroup_path,
/* sig= */ 0,
/* flags= */ 0,
/* set= */ NULL,
+13 -83
View File
@@ -208,6 +208,7 @@ struct UnitRef {
LIST_FIELDS(UnitRef, refs_by_target);
};
/* The generic, dynamic definition of the unit */
typedef struct Unit {
Manager *manager;
@@ -370,74 +371,6 @@ typedef struct Unit {
UnitFileState unit_file_state;
PresetAction unit_file_preset;
/* Where the cpu.stat or cpuacct.usage was at the time the unit was started */
nsec_t cpu_usage_base;
nsec_t cpu_usage_last; /* the most recently read value */
/* Most recently read value of memory accounting metrics */
uint64_t memory_accounting_last[_CGROUP_MEMORY_ACCOUNTING_METRIC_CACHED_LAST + 1];
/* The current counter of OOM kills initiated by systemd-oomd */
uint64_t managed_oom_kill_last;
/* The current counter of the oom_kill field in the memory.events cgroup attribute */
uint64_t oom_kill_last;
/* Where the io.stat data was at the time the unit was started */
uint64_t io_accounting_base[_CGROUP_IO_ACCOUNTING_METRIC_MAX];
uint64_t io_accounting_last[_CGROUP_IO_ACCOUNTING_METRIC_MAX]; /* the most recently read value */
/* Counterparts in the cgroup filesystem */
char *cgroup_path;
uint64_t cgroup_id;
CGroupMask cgroup_realized_mask; /* In which hierarchies does this unit's cgroup exist? (only relevant on cgroup v1) */
CGroupMask cgroup_enabled_mask; /* Which controllers are enabled (or more correctly: enabled for the children) for this unit's cgroup? (only relevant on cgroup v2) */
CGroupMask cgroup_invalidated_mask; /* A mask specifying controllers which shall be considered invalidated, and require re-realization */
CGroupMask cgroup_members_mask; /* A cache for the controllers required by all children of this cgroup (only relevant for slice units) */
/* Inotify watch descriptors for watching cgroup.events and memory.events on cgroupv2 */
int cgroup_control_inotify_wd;
int cgroup_memory_inotify_wd;
/* Device Controller BPF program */
BPFProgram *bpf_device_control_installed;
/* IP BPF Firewalling/accounting */
int ip_accounting_ingress_map_fd;
int ip_accounting_egress_map_fd;
uint64_t ip_accounting_extra[_CGROUP_IP_ACCOUNTING_METRIC_MAX];
int ipv4_allow_map_fd;
int ipv6_allow_map_fd;
int ipv4_deny_map_fd;
int ipv6_deny_map_fd;
BPFProgram *ip_bpf_ingress, *ip_bpf_ingress_installed;
BPFProgram *ip_bpf_egress, *ip_bpf_egress_installed;
Set *ip_bpf_custom_ingress;
Set *ip_bpf_custom_ingress_installed;
Set *ip_bpf_custom_egress;
Set *ip_bpf_custom_egress_installed;
/* BPF programs managed (e.g. loaded to kernel) by an entity external to systemd,
* attached to unit cgroup by provided program fd and attach type. */
Hashmap *bpf_foreign_by_key;
FDSet *initial_socket_bind_link_fds;
#if BPF_FRAMEWORK
/* BPF links to BPF programs attached to cgroup/bind{4|6} hooks and
* responsible for allowing or denying a unit to bind(2) to a socket
* address. */
struct bpf_link *ipv4_socket_bind_link;
struct bpf_link *ipv6_socket_bind_link;
#endif
FDSet *initial_restric_ifaces_link_fds;
#if BPF_FRAMEWORK
struct bpf_link *restrict_ifaces_ingress_bpf_link;
struct bpf_link *restrict_ifaces_egress_bpf_link;
#endif
/* Low-priority event source which is used to remove watched PIDs that have gone away, and subscribe to any new
* ones which might have appeared. */
sd_event_source *rewatch_pids_event_source;
@@ -508,12 +441,6 @@ typedef struct Unit {
bool in_audit:1;
bool on_console:1;
bool cgroup_realized:1;
bool cgroup_members_mask_valid:1;
/* Reset cgroup accounting next time we fork something off */
bool reset_accounting:1;
bool start_limit_hit:1;
/* Did we already invoke unit_coldplug() for this unit? */
@@ -529,9 +456,6 @@ typedef struct Unit {
bool exported_log_ratelimit_interval:1;
bool exported_log_ratelimit_burst:1;
/* Whether we warned about clamping the CPU quota period */
bool warned_clamping_cpu_quota_period:1;
/* When writing transient unit files, stores which section we stored last. If < 0, we didn't write any yet. If
* == 0 we are in the [Unit] section, if > 0 we are in the unit type-specific section. */
signed int last_section_private:2;
@@ -577,6 +501,7 @@ static inline bool UNIT_WRITE_FLAGS_NOOP(UnitWriteFlags flags) {
#include "kill.h"
/* The static const, immutable data about a specific unit type */
typedef struct UnitVTable {
/* How much memory does an object of this unit type need */
size_t object_size;
@@ -593,11 +518,14 @@ typedef struct UnitVTable {
* KillContext is found, if the unit type has that */
size_t kill_context_offset;
/* If greater than 0, the offset into the object where the
* pointer to ExecSharedRuntime is found, if the unit type has
* that */
/* If greater than 0, the offset into the object where the pointer to ExecRuntime is found, if
* the unit type has that */
size_t exec_runtime_offset;
/* If greater than 0, the offset into the object where the pointer to CGroupRuntime is found, if the
* unit type has that */
size_t cgroup_runtime_offset;
/* The name of the configuration file section with the private settings of this unit */
const char *private_section;
@@ -993,12 +921,14 @@ void unit_ref_unset(UnitRef *ref);
int unit_patch_contexts(Unit *u);
ExecContext *unit_get_exec_context(const Unit *u) _pure_;
KillContext *unit_get_kill_context(Unit *u) _pure_;
CGroupContext *unit_get_cgroup_context(Unit *u) _pure_;
KillContext *unit_get_kill_context(const Unit *u) _pure_;
CGroupContext *unit_get_cgroup_context(const Unit *u) _pure_;
ExecRuntime *unit_get_exec_runtime(Unit *u) _pure_;
ExecRuntime *unit_get_exec_runtime(const Unit *u) _pure_;
CGroupRuntime *unit_get_cgroup_runtime(const Unit *u) _pure_;
int unit_setup_exec_runtime(Unit *u);
CGroupRuntime *unit_setup_cgroup_runtime(Unit *u);
const char* unit_escape_setting(const char *s, UnitWriteFlags flags, char **buf);
char* unit_concat_strv(char **l, UnitWriteFlags flags);
+5 -4
View File
@@ -155,10 +155,11 @@ int main(int argc, char *argv[]) {
return log_tests_skipped("Kernel doesn't support the necessary bpf bits (masked out via seccomp?)");
assert_se(r >= 0);
assert_se(u->ip_bpf_ingress);
assert_se(u->ip_bpf_egress);
CGroupRuntime *crt = ASSERT_PTR(unit_get_cgroup_runtime(u));
assert_se(crt->ip_bpf_ingress);
assert_se(crt->ip_bpf_egress);
r = bpf_program_load_kernel(u->ip_bpf_ingress, log_buf, ELEMENTSOF(log_buf));
r = bpf_program_load_kernel(crt->ip_bpf_ingress, log_buf, ELEMENTSOF(log_buf));
log_notice("log:");
log_notice("-------");
@@ -167,7 +168,7 @@ int main(int argc, char *argv[]) {
assert_se(r >= 0);
r = bpf_program_load_kernel(u->ip_bpf_egress, log_buf, ELEMENTSOF(log_buf));
r = bpf_program_load_kernel(crt->ip_bpf_egress, log_buf, ELEMENTSOF(log_buf));
log_notice("log:");
log_notice("-------");