Merge pull request #31127 from poettering/cgroup-runtime2
core: split out cgroup specific state fields from Unit → CGroupRuntime
This commit is contained in:
+90
-67
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
@@ -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
File diff suppressed because it is too large
Load Diff
+98
-1
@@ -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
@@ -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)) {
|
||||
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
@@ -79,6 +79,7 @@ struct Mount {
|
||||
CGroupContext cgroup_context;
|
||||
|
||||
ExecRuntime *exec_runtime;
|
||||
CGroupRuntime *cgroup_runtime;
|
||||
|
||||
MountState state, deserialized_state;
|
||||
|
||||
|
||||
+5
-1
@@ -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"
|
||||
|
||||
@@ -21,6 +21,7 @@ struct Scope {
|
||||
|
||||
CGroupContext cgroup_context;
|
||||
KillContext kill_context;
|
||||
CGroupRuntime *cgroup_runtime;
|
||||
|
||||
ScopeState state, deserialized_state;
|
||||
ScopeResult result;
|
||||
|
||||
+8
-3
@@ -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"
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -11,6 +11,8 @@ struct Slice {
|
||||
SliceState state, deserialized_state;
|
||||
|
||||
CGroupContext cgroup_context;
|
||||
|
||||
CGroupRuntime *cgroup_runtime;
|
||||
};
|
||||
|
||||
extern const UnitVTable slice_vtable;
|
||||
|
||||
+4
-1
@@ -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"
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -70,6 +70,7 @@ struct Swap {
|
||||
CGroupContext cgroup_context;
|
||||
|
||||
ExecRuntime *exec_runtime;
|
||||
CGroupRuntime *cgroup_runtime;
|
||||
|
||||
SwapState state, deserialized_state;
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
@@ -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("-------");
|
||||
|
||||
Reference in New Issue
Block a user