Merge pull request #10567 from cdown/disable_controller

cgroup: Add DisableControllers= directive to disable controller in subtree
This commit is contained in:
Lennart Poettering 2018-12-03 18:58:29 +01:00 committed by GitHub
commit a365325e04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 342 additions and 49 deletions

View file

@ -736,6 +736,27 @@
</listitem>
</varlistentry>
<varlistentry>
<term><varname>DisableControllers=</varname></term>
<listitem>
<para>Disables controllers from being enabled for a unit's children. If a controller listed is already in use
in its subtree, the controller will be removed from the subtree. This can be used to avoid child units being
able to implicitly or explicitly enable a controller. Defaults to not disabling any controllers.</para>
<para>It may not be possible to successfully disable a controller if the unit or any child of the unit in
question delegates controllers to its children, as any delegated subtree of the cgroup hierarchy is unmanaged
by systemd.</para>
<para>Multiple controllers may be specified, separated by spaces. You may also pass
<varname>DisableControllers=</varname> multiple times, in which case each new instance adds another controller
to disable. Passing <varname>DisableControllers=</varname> by itself with no controller name present resets
the disabled controller list.</para>
<para>Valid controllers are <option>cpu</option>, <option>cpuacct</option>, <option>io</option>,
<option>blkio</option>, <option>memory</option>, <option>devices</option>, and <option>pids</option>.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

View file

@ -375,6 +375,26 @@ int cgroup_add_device_allow(CGroupContext *c, const char *dev, const char *mode)
return 0;
}
static void cgroup_xattr_apply(Unit *u) {
char ids[SD_ID128_STRING_MAX];
int r;
assert(u);
if (!MANAGER_IS_SYSTEM(u->manager))
return;
if (sd_id128_is_null(u->invocation_id))
return;
r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
"trusted.invocation_id",
sd_id128_to_string(u->invocation_id, ids), 32,
0);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", u->cgroup_path);
}
static int lookup_block_device(const char *p, dev_t *ret) {
struct stat st = {};
int r;
@ -1326,7 +1346,7 @@ CGroupMask unit_get_own_mask(Unit *u) {
if (!c)
return 0;
return cgroup_context_get_mask(c) | unit_get_bpf_mask(u) | unit_get_delegate_mask(u);
return (cgroup_context_get_mask(c) | unit_get_bpf_mask(u) | unit_get_delegate_mask(u)) & ~unit_get_ancestor_disable_mask(u);
}
CGroupMask unit_get_delegate_mask(Unit *u) {
@ -1396,6 +1416,31 @@ CGroupMask unit_get_siblings_mask(Unit *u) {
return unit_get_subtree_mask(u); /* we are the top-level slice */
}
CGroupMask unit_get_disable_mask(Unit *u) {
CGroupContext *c;
c = unit_get_cgroup_context(u);
if (!c)
return 0;
return c->disable_controllers;
}
CGroupMask unit_get_ancestor_disable_mask(Unit *u) {
CGroupMask mask;
assert(u);
mask = unit_get_disable_mask(u);
/* Returns the mask of controllers which are marked as forcibly
* disabled in any ancestor unit or the unit in question. */
if (UNIT_ISSET(u->slice))
mask |= unit_get_ancestor_disable_mask(UNIT_DEREF(u->slice));
return mask;
}
CGroupMask unit_get_subtree_mask(Unit *u) {
/* Returns the mask of this subtree, meaning of the group
@ -1416,6 +1461,7 @@ CGroupMask unit_get_target_mask(Unit *u) {
mask = unit_get_own_mask(u) | unit_get_members_mask(u) | unit_get_siblings_mask(u);
mask &= u->manager->cgroup_supported;
mask &= ~unit_get_ancestor_disable_mask(u);
return mask;
}
@ -1430,6 +1476,7 @@ CGroupMask unit_get_enable_mask(Unit *u) {
mask = unit_get_members_mask(u);
mask &= u->manager->cgroup_supported;
mask &= ~unit_get_ancestor_disable_mask(u);
return mask;
}
@ -1597,7 +1644,8 @@ int unit_pick_cgroup_path(Unit *u) {
static int unit_create_cgroup(
Unit *u,
CGroupMask target_mask,
CGroupMask enable_mask) {
CGroupMask enable_mask,
ManagerState state) {
bool created;
int r;
@ -1665,6 +1713,10 @@ static int unit_create_cgroup(
log_unit_warning_errno(u, r, "Failed to migrate cgroup from to %s, ignoring: %m", u->cgroup_path);
}
/* Set attributes */
cgroup_context_apply(u, target_mask, state);
cgroup_xattr_apply(u);
return 0;
}
@ -1806,26 +1858,6 @@ int unit_attach_pids_to_cgroup(Unit *u, Set *pids, const char *suffix_path) {
return r;
}
static void cgroup_xattr_apply(Unit *u) {
char ids[SD_ID128_STRING_MAX];
int r;
assert(u);
if (!MANAGER_IS_SYSTEM(u->manager))
return;
if (sd_id128_is_null(u->invocation_id))
return;
r = cg_set_xattr(SYSTEMD_CGROUP_CONTROLLER, u->cgroup_path,
"trusted.invocation_id",
sd_id128_to_string(u->invocation_id, ids), 32,
0);
if (r < 0)
log_unit_debug_errno(u, r, "Failed to set invocation ID on control group %s, ignoring: %m", u->cgroup_path);
}
static bool unit_has_mask_realized(
Unit *u,
CGroupMask target_mask,
@ -1854,6 +1886,40 @@ static bool unit_has_mask_realized(
u->cgroup_invalidated_mask == 0;
}
static bool unit_has_mask_disables_realized(
Unit *u,
CGroupMask target_mask,
CGroupMask enable_mask) {
assert(u);
/* Returns true if all controllers which should be disabled are indeed disabled.
*
* Unlike unit_has_mask_realized, we don't care what was enabled, only that anything we want to remove is
* already removed. */
return !u->cgroup_realized ||
(FLAGS_SET(u->cgroup_realized_mask, target_mask & CGROUP_MASK_V1) &&
FLAGS_SET(u->cgroup_enabled_mask, enable_mask & CGROUP_MASK_V2));
}
static bool unit_has_mask_enables_realized(
Unit *u,
CGroupMask target_mask,
CGroupMask enable_mask) {
assert(u);
/* Returns true if all controllers which should be enabled are indeed enabled.
*
* Unlike unit_has_mask_realized, we don't care about the controllers that are not present, only that anything
* we want to add is already added. */
return u->cgroup_realized &&
((u->cgroup_realized_mask | target_mask) & CGROUP_MASK_V1) == (u->cgroup_realized_mask & CGROUP_MASK_V1) &&
((u->cgroup_enabled_mask | enable_mask) & CGROUP_MASK_V2) == (u->cgroup_enabled_mask & CGROUP_MASK_V2);
}
void unit_add_to_cgroup_realize_queue(Unit *u) {
assert(u);
@ -1874,10 +1940,127 @@ static void unit_remove_from_cgroup_realize_queue(Unit *u) {
u->in_cgroup_realize_queue = false;
}
/* Controllers can only be enabled breadth-first, from the root of the
* hierarchy downwards to the unit in question. */
static int unit_realize_cgroup_now_enable(Unit *u, ManagerState state) {
CGroupMask target_mask, enable_mask, new_target_mask, new_enable_mask;
int r;
assert(u);
/* First go deal with this unit's parent, or we won't be able to enable
* any new controllers at this layer. */
if (UNIT_ISSET(u->slice)) {
r = unit_realize_cgroup_now_enable(UNIT_DEREF(u->slice), state);
if (r < 0)
return r;
}
target_mask = unit_get_target_mask(u);
enable_mask = unit_get_enable_mask(u);
/* We can only enable in this direction, don't try to disable anything.
*/
if (unit_has_mask_enables_realized(u, target_mask, enable_mask))
return 0;
new_target_mask = u->cgroup_realized_mask | target_mask;
new_enable_mask = u->cgroup_enabled_mask | enable_mask;
return unit_create_cgroup(u, new_target_mask, new_enable_mask, state);
}
/* Controllers can only be disabled depth-first, from the leaves of the
* hierarchy upwards to the unit in question. */
static int unit_realize_cgroup_now_disable(Unit *u, ManagerState state) {
Iterator i;
Unit *m;
void *v;
assert(u);
if (u->type != UNIT_SLICE)
return 0;
HASHMAP_FOREACH_KEY(v, m, u->dependencies[UNIT_BEFORE], i) {
CGroupMask target_mask, enable_mask, new_target_mask, new_enable_mask;
int r;
if (UNIT_DEREF(m->slice) != u)
continue;
/* The cgroup for this unit might not actually be fully
* realised yet, in which case it isn't holding any controllers
* open anyway. */
if (!m->cgroup_path)
continue;
/* We must disable those below us first in order to release the
* controller. */
if (m->type == UNIT_SLICE)
(void) unit_realize_cgroup_now_disable(m, state);
target_mask = unit_get_target_mask(m);
enable_mask = unit_get_enable_mask(m);
/* We can only disable in this direction, don't try to enable
* anything. */
if (unit_has_mask_disables_realized(m, target_mask, enable_mask))
continue;
new_target_mask = m->cgroup_realized_mask & target_mask;
new_enable_mask = m->cgroup_enabled_mask & enable_mask;
r = unit_create_cgroup(m, new_target_mask, new_enable_mask, state);
if (r < 0)
return r;
}
return 0;
}
/* Check if necessary controllers and attributes for a unit are in place.
*
* If so, do nothing.
* If not, create paths, move processes over, and set attributes.
* - If so, do nothing.
* - If not, create paths, move processes over, and set attributes.
*
* Controllers can only be *enabled* in a breadth-first way, and *disabled* in
* a depth-first way. As such the process looks like this:
*
* Suppose we have a cgroup hierarchy which looks like this:
*
* root
* / \
* / \
* / \
* a b
* / \ / \
* / \ / \
* c d e f
* / \ / \ / \ / \
* h i j k l m n o
*
* 1. We want to realise cgroup "d" now.
* 2. cgroup "a" has DisableControllers=cpu in the associated unit.
* 3. cgroup "k" just started requesting the memory controller.
*
* To make this work we must do the following in order:
*
* 1. Disable CPU controller in k, j
* 2. Disable CPU controller in d
* 3. Enable memory controller in root
* 4. Enable memory controller in a
* 5. Enable memory controller in d
* 6. Enable memory controller in k
*
* Notice that we need to touch j in one direction, but not the other. We also
* don't go beyond d when disabling -- it's up to "a" to get realized if it
* wants to disable further. The basic rules are therefore:
*
* - If you're disabling something, you need to realise all of the cgroups from
* your recursive descendants to the root. This starts from the leaves.
* - If you're enabling something, you need to realise from the root cgroup
* downwards, but you don't need to iterate your recursive descendants.
*
* Returns 0 on success and < 0 on failure. */
static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
@ -1894,22 +2077,23 @@ static int unit_realize_cgroup_now(Unit *u, ManagerState state) {
if (unit_has_mask_realized(u, target_mask, enable_mask))
return 0;
/* First, realize parents */
/* Disable controllers below us, if there are any */
r = unit_realize_cgroup_now_disable(u, state);
if (r < 0)
return r;
/* Enable controllers above us, if there are any */
if (UNIT_ISSET(u->slice)) {
r = unit_realize_cgroup_now(UNIT_DEREF(u->slice), state);
r = unit_realize_cgroup_now_enable(UNIT_DEREF(u->slice), state);
if (r < 0)
return r;
}
/* And then do the real work */
r = unit_create_cgroup(u, target_mask, enable_mask);
/* Now actually deal with the cgroup we were trying to realise and set attributes */
r = unit_create_cgroup(u, target_mask, enable_mask, state);
if (r < 0)
return r;
/* Finally, apply the necessary attributes. */
cgroup_context_apply(u, target_mask, state);
cgroup_xattr_apply(u);
/* Now, reset the invalidation mask */
u->cgroup_invalidated_mask = 0;
return 0;

View file

@ -118,6 +118,8 @@ struct CGroupContext {
bool delegate;
CGroupMask delegate_controllers;
CGroupMask disable_controllers;
};
/* Used when querying IP accounting data */
@ -151,6 +153,9 @@ CGroupMask unit_get_delegate_mask(Unit *u);
CGroupMask unit_get_members_mask(Unit *u);
CGroupMask unit_get_siblings_mask(Unit *u);
CGroupMask unit_get_subtree_mask(Unit *u);
CGroupMask unit_get_disable_mask(Unit *u);
CGroupMask unit_get_ancestor_disable_mask(Unit *u);
CGroupMask unit_get_target_mask(Unit *u);
CGroupMask unit_get_enable_mask(Unit *u);

View file

@ -17,6 +17,38 @@
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_cgroup_device_policy, cgroup_device_policy, CGroupDevicePolicy);
static int property_get_cgroup_mask(
sd_bus *bus,
const char *path,
const char *interface,
const char *property,
sd_bus_message *reply,
void *userdata,
sd_bus_error *error) {
CGroupMask *mask = userdata;
CGroupController ctrl;
int r;
assert(bus);
assert(reply);
r = sd_bus_message_open_container(reply, 'a', "s");
if (r < 0)
return r;
for (ctrl = 0; ctrl < _CGROUP_CONTROLLER_MAX; ctrl++) {
if ((*mask & CGROUP_CONTROLLER_TO_MASK(ctrl)) == 0)
continue;
r = sd_bus_message_append(reply, "s", cgroup_controller_to_string(ctrl));
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
}
static int property_get_delegate_controllers(
sd_bus *bus,
const char *path,
@ -27,8 +59,6 @@ static int property_get_delegate_controllers(
sd_bus_error *error) {
CGroupContext *c = userdata;
CGroupController cc;
int r;
assert(bus);
assert(reply);
@ -37,20 +67,7 @@ static int property_get_delegate_controllers(
if (!c->delegate)
return sd_bus_message_append(reply, "as", 0);
r = sd_bus_message_open_container(reply, 'a', "s");
if (r < 0)
return r;
for (cc = 0; cc < _CGROUP_CONTROLLER_MAX; cc++) {
if ((c->delegate_controllers & CGROUP_CONTROLLER_TO_MASK(cc)) == 0)
continue;
r = sd_bus_message_append(reply, "s", cgroup_controller_to_string(cc));
if (r < 0)
return r;
}
return sd_bus_message_close_container(reply);
return property_get_cgroup_mask(bus, path, interface, property, reply, &c->delegate_controllers, error);
}
static int property_get_io_device_weight(
@ -342,6 +359,7 @@ const sd_bus_vtable bus_cgroup_vtable[] = {
SD_BUS_PROPERTY("IPAccounting", "b", bus_property_get_bool, offsetof(CGroupContext, ip_accounting), 0),
SD_BUS_PROPERTY("IPAddressAllow", "a(iayu)", property_get_ip_address_access, offsetof(CGroupContext, ip_address_allow), 0),
SD_BUS_PROPERTY("IPAddressDeny", "a(iayu)", property_get_ip_address_access, offsetof(CGroupContext, ip_address_deny), 0),
SD_BUS_PROPERTY("DisableControllers", "as", property_get_cgroup_mask, offsetof(CGroupContext, disable_controllers), 0),
SD_BUS_VTABLE_END
};

View file

@ -192,6 +192,7 @@ $1.BlockIOWriteBandwidth, config_parse_blockio_bandwidth, 0,
$1.TasksAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.tasks_accounting)
$1.TasksMax, config_parse_tasks_max, 0, offsetof($1, cgroup_context.tasks_max)
$1.Delegate, config_parse_delegate, 0, offsetof($1, cgroup_context)
$1.DisableControllers, config_parse_disable_controllers, 0, offsetof($1, cgroup_context)
$1.IPAccounting, config_parse_bool, 0, offsetof($1, cgroup_context.ip_accounting)
$1.IPAddressAllow, config_parse_ip_address_access, 0, offsetof($1, cgroup_context.ip_address_allow)
$1.IPAddressDeny, config_parse_ip_address_access, 0, offsetof($1, cgroup_context.ip_address_deny)

View file

@ -4323,6 +4323,41 @@ int config_parse_exit_status(
return 0;
}
int config_parse_disable_controllers(
const char *unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
int r;
CGroupContext *c = data;
CGroupMask disabled_mask;
/* 1. If empty, make all controllers eligible for use again.
* 2. If non-empty, merge all listed controllers, space separated. */
if (isempty(rvalue)) {
c->disable_controllers = 0;
return 0;
}
r = cg_mask_from_string(rvalue, &disabled_mask);
if (r < 0 || disabled_mask <= 0) {
log_syntax(unit, LOG_ERR, filename, line, r, "Invalid cgroup string: %s, ignoring", rvalue);
return 0;
}
c->disable_controllers |= disabled_mask;
return 0;
}
#define FOLLOW_MAX 8
static int open_follow(char **filename, FILE **_f, Set *names, char **_final) {

View file

@ -105,6 +105,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_log_extra_fields);
CONFIG_PARSER_PROTOTYPE(config_parse_collect_mode);
CONFIG_PARSER_PROTOTYPE(config_parse_pid_file);
CONFIG_PARSER_PROTOTYPE(config_parse_exit_status);
CONFIG_PARSER_PROTOTYPE(config_parse_disable_controllers);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, GPERF_LEN_TYPE length);

View file

@ -30,7 +30,7 @@ static void log_cgroup_mask(CGroupMask got, CGroupMask expected) {
static int test_cgroup_mask(void) {
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
_cleanup_(manager_freep) Manager *m = NULL;
Unit *son, *daughter, *parent, *root, *grandchild, *parent_deep;
Unit *son, *daughter, *parent, *root, *grandchild, *parent_deep, *nomem_parent, *nomem_leaf;
int r;
CGroupMask cpu_accounting_mask = get_cpu_accounting_mask();
@ -68,11 +68,15 @@ static int test_cgroup_mask(void) {
assert_se(manager_load_startable_unit_or_warn(m, "daughter.service", NULL, &daughter) >= 0);
assert_se(manager_load_startable_unit_or_warn(m, "grandchild.service", NULL, &grandchild) >= 0);
assert_se(manager_load_startable_unit_or_warn(m, "parent-deep.slice", NULL, &parent_deep) >= 0);
assert_se(manager_load_startable_unit_or_warn(m, "nomem.slice", NULL, &nomem_parent) >= 0);
assert_se(manager_load_startable_unit_or_warn(m, "nomemleaf.service", NULL, &nomem_leaf) >= 0);
assert_se(UNIT_DEREF(son->slice) == parent);
assert_se(UNIT_DEREF(daughter->slice) == parent);
assert_se(UNIT_DEREF(parent_deep->slice) == parent);
assert_se(UNIT_DEREF(grandchild->slice) == parent_deep);
assert_se(UNIT_DEREF(nomem_leaf->slice) == nomem_parent);
root = UNIT_DEREF(parent->slice);
assert_se(UNIT_DEREF(nomem_parent->slice) == root);
/* Verify per-unit cgroups settings. */
ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(son), CGROUP_MASK_CPU);
@ -80,6 +84,8 @@ static int test_cgroup_mask(void) {
ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(grandchild), 0);
ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(parent_deep), CGROUP_MASK_MEMORY);
ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(parent), (CGROUP_MASK_IO | CGROUP_MASK_BLKIO));
ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(nomem_parent), 0);
ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(nomem_leaf), (CGROUP_MASK_IO | CGROUP_MASK_BLKIO));
ASSERT_CGROUP_MASK_JOINED(unit_get_own_mask(root), 0);
/* Verify aggregation of member masks */
@ -88,6 +94,8 @@ static int test_cgroup_mask(void) {
ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(grandchild), 0);
ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(parent_deep), 0);
ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(parent), (CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_MEMORY));
ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(nomem_parent), (CGROUP_MASK_IO | CGROUP_MASK_BLKIO));
ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(nomem_leaf), 0);
ASSERT_CGROUP_MASK_JOINED(unit_get_members_mask(root), (CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
/* Verify aggregation of sibling masks. */
@ -96,6 +104,8 @@ static int test_cgroup_mask(void) {
ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(grandchild), 0);
ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(parent_deep), (CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_MEMORY));
ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(parent), (CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(nomem_parent), (CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(nomem_leaf), (CGROUP_MASK_IO | CGROUP_MASK_BLKIO));
ASSERT_CGROUP_MASK_JOINED(unit_get_siblings_mask(root), (CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY));
/* Verify aggregation of target masks. */
@ -104,6 +114,8 @@ static int test_cgroup_mask(void) {
ASSERT_CGROUP_MASK(unit_get_target_mask(grandchild), 0);
ASSERT_CGROUP_MASK(unit_get_target_mask(parent_deep), (CGROUP_MASK_EXTEND_JOINED(CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_MEMORY) & m->cgroup_supported));
ASSERT_CGROUP_MASK(unit_get_target_mask(parent), (CGROUP_MASK_EXTEND_JOINED(CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported));
ASSERT_CGROUP_MASK(unit_get_target_mask(nomem_parent), (CGROUP_MASK_EXTEND_JOINED(CGROUP_MASK_CPU | CGROUP_MASK_CPUACCT | CGROUP_MASK_IO | CGROUP_MASK_BLKIO) & m->cgroup_supported));
ASSERT_CGROUP_MASK(unit_get_target_mask(nomem_leaf), (CGROUP_MASK_EXTEND_JOINED(CGROUP_MASK_IO | CGROUP_MASK_BLKIO) & m->cgroup_supported));
ASSERT_CGROUP_MASK(unit_get_target_mask(root), (CGROUP_MASK_EXTEND_JOINED(CGROUP_MASK_CPU | cpu_accounting_mask | CGROUP_MASK_IO | CGROUP_MASK_BLKIO | CGROUP_MASK_MEMORY) & m->cgroup_supported));
return 0;

View file

@ -18,6 +18,8 @@ test_data_files = '''
hwdb/10-bad.hwdb
journal-data/journal-1.txt
journal-data/journal-2.txt
nomem.slice
nomemleaf.service
parent-deep.slice
parent.slice
sched_idle_bad.service

5
test/nomem.slice Normal file
View file

@ -0,0 +1,5 @@
[Unit]
Description=Nomem Parent Slice
[Slice]
DisableControllers=memory

9
test/nomemleaf.service Normal file
View file

@ -0,0 +1,9 @@
[Unit]
Description=Nomem Leaf Service
[Service]
Slice=nomem.slice
Type=oneshot
ExecStart=/bin/true
IOWeight=200
MemoryAccounting=true