2020-11-09 05:23:58 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
cgroup: Implement default propagation of MemoryLow with DefaultMemoryLow
In cgroup v2 we have protection tunables -- currently MemoryLow and
MemoryMin (there will be more in future for other resources, too). The
design of these protection tunables requires not only intermediate
cgroups to propagate protections, but also the units at the leaf of that
resource's operation to accept it (by setting MemoryLow or MemoryMin).
This makes sense from an low-level API design perspective, but it's a
good idea to also have a higher-level abstraction that can, by default,
propagate these resources to children recursively. In this patch, this
happens by having descendants set memory.low to N if their ancestor has
DefaultMemoryLow=N -- assuming they don't set a separate MemoryLow
value.
Any affected unit can opt out of this propagation by manually setting
`MemoryLow` to some value in its unit configuration. A unit can also
stop further propagation by setting `DefaultMemoryLow=` with no
argument. This removes further propagation in the subtree, but has no
effect on the unit itself (for that, use `MemoryLow=0`).
Our use case in production is simplifying the configuration of machines
which heavily rely on memory protection tunables, but currently require
tweaking a huge number of unit files to make that a reality. This
directive makes that significantly less fragile, and decreases the risk
of misconfiguration.
After this patch is merged, I will implement DefaultMemoryMin= using the
same principles.
2019-03-28 13:50:50 +01:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
|
|
|
#include "cgroup.h"
|
|
|
|
#include "manager.h"
|
|
|
|
#include "rm-rf.h"
|
|
|
|
#include "tests.h"
|
|
|
|
#include "unit.h"
|
|
|
|
|
|
|
|
static int test_default_memory_low(void) {
|
|
|
|
_cleanup_(rm_rf_physical_and_freep) char *runtime_dir = NULL;
|
|
|
|
_cleanup_(manager_freep) Manager *m = NULL;
|
|
|
|
Unit *root, *dml,
|
|
|
|
*dml_passthrough, *dml_passthrough_empty, *dml_passthrough_set_dml, *dml_passthrough_set_ml,
|
|
|
|
*dml_override, *dml_override_empty,
|
|
|
|
*dml_discard, *dml_discard_empty, *dml_discard_set_ml;
|
|
|
|
uint64_t dml_tree_default;
|
|
|
|
int r;
|
|
|
|
|
2019-11-08 16:57:25 +01:00
|
|
|
r = enter_cgroup_subroot(NULL);
|
cgroup: Implement default propagation of MemoryLow with DefaultMemoryLow
In cgroup v2 we have protection tunables -- currently MemoryLow and
MemoryMin (there will be more in future for other resources, too). The
design of these protection tunables requires not only intermediate
cgroups to propagate protections, but also the units at the leaf of that
resource's operation to accept it (by setting MemoryLow or MemoryMin).
This makes sense from an low-level API design perspective, but it's a
good idea to also have a higher-level abstraction that can, by default,
propagate these resources to children recursively. In this patch, this
happens by having descendants set memory.low to N if their ancestor has
DefaultMemoryLow=N -- assuming they don't set a separate MemoryLow
value.
Any affected unit can opt out of this propagation by manually setting
`MemoryLow` to some value in its unit configuration. A unit can also
stop further propagation by setting `DefaultMemoryLow=` with no
argument. This removes further propagation in the subtree, but has no
effect on the unit itself (for that, use `MemoryLow=0`).
Our use case in production is simplifying the configuration of machines
which heavily rely on memory protection tunables, but currently require
tweaking a huge number of unit files to make that a reality. This
directive makes that significantly less fragile, and decreases the risk
of misconfiguration.
After this patch is merged, I will implement DefaultMemoryMin= using the
same principles.
2019-03-28 13:50:50 +01:00
|
|
|
if (r == -ENOMEDIUM)
|
|
|
|
return log_tests_skipped("cgroupfs not available");
|
|
|
|
|
2019-12-06 21:51:59 +01:00
|
|
|
_cleanup_free_ char *unit_dir = NULL;
|
|
|
|
assert_se(get_testdata_dir("units", &unit_dir) >= 0);
|
|
|
|
assert_se(set_unit_path(unit_dir) >= 0);
|
cgroup: Implement default propagation of MemoryLow with DefaultMemoryLow
In cgroup v2 we have protection tunables -- currently MemoryLow and
MemoryMin (there will be more in future for other resources, too). The
design of these protection tunables requires not only intermediate
cgroups to propagate protections, but also the units at the leaf of that
resource's operation to accept it (by setting MemoryLow or MemoryMin).
This makes sense from an low-level API design perspective, but it's a
good idea to also have a higher-level abstraction that can, by default,
propagate these resources to children recursively. In this patch, this
happens by having descendants set memory.low to N if their ancestor has
DefaultMemoryLow=N -- assuming they don't set a separate MemoryLow
value.
Any affected unit can opt out of this propagation by manually setting
`MemoryLow` to some value in its unit configuration. A unit can also
stop further propagation by setting `DefaultMemoryLow=` with no
argument. This removes further propagation in the subtree, but has no
effect on the unit itself (for that, use `MemoryLow=0`).
Our use case in production is simplifying the configuration of machines
which heavily rely on memory protection tunables, but currently require
tweaking a huge number of unit files to make that a reality. This
directive makes that significantly less fragile, and decreases the risk
of misconfiguration.
After this patch is merged, I will implement DefaultMemoryMin= using the
same principles.
2019-03-28 13:50:50 +01:00
|
|
|
assert_se(runtime_dir = setup_fake_runtime_dir());
|
|
|
|
r = manager_new(UNIT_FILE_USER, MANAGER_TEST_RUN_BASIC, &m);
|
|
|
|
if (IN_SET(r, -EPERM, -EACCES)) {
|
|
|
|
log_error_errno(r, "manager_new: %m");
|
|
|
|
return log_tests_skipped("cannot create manager");
|
|
|
|
}
|
|
|
|
|
|
|
|
assert_se(r >= 0);
|
|
|
|
assert_se(manager_startup(m, NULL, NULL) >= 0);
|
|
|
|
|
|
|
|
/* dml.slice has DefaultMemoryLow=50. Beyond that, individual subhierarchies look like this:
|
|
|
|
*
|
|
|
|
* 1. dml-passthrough.slice sets MemoryLow=100. This should not affect its children, as only
|
|
|
|
* DefaultMemoryLow is propagated, not MemoryLow. As such, all leaf services should end up with
|
|
|
|
* memory.low as 50, inherited from dml.slice, *except* for dml-passthrough-set-ml.service, which
|
2019-05-03 14:40:11 +02:00
|
|
|
* should have the value of 0, as it has MemoryLow explicitly set.
|
cgroup: Implement default propagation of MemoryLow with DefaultMemoryLow
In cgroup v2 we have protection tunables -- currently MemoryLow and
MemoryMin (there will be more in future for other resources, too). The
design of these protection tunables requires not only intermediate
cgroups to propagate protections, but also the units at the leaf of that
resource's operation to accept it (by setting MemoryLow or MemoryMin).
This makes sense from an low-level API design perspective, but it's a
good idea to also have a higher-level abstraction that can, by default,
propagate these resources to children recursively. In this patch, this
happens by having descendants set memory.low to N if their ancestor has
DefaultMemoryLow=N -- assuming they don't set a separate MemoryLow
value.
Any affected unit can opt out of this propagation by manually setting
`MemoryLow` to some value in its unit configuration. A unit can also
stop further propagation by setting `DefaultMemoryLow=` with no
argument. This removes further propagation in the subtree, but has no
effect on the unit itself (for that, use `MemoryLow=0`).
Our use case in production is simplifying the configuration of machines
which heavily rely on memory protection tunables, but currently require
tweaking a huge number of unit files to make that a reality. This
directive makes that significantly less fragile, and decreases the risk
of misconfiguration.
After this patch is merged, I will implement DefaultMemoryMin= using the
same principles.
2019-03-28 13:50:50 +01:00
|
|
|
*
|
|
|
|
* ┌───────────┐
|
|
|
|
* │ dml.slice │
|
|
|
|
* └─────┬─────┘
|
|
|
|
* MemoryLow=100
|
|
|
|
* ┌───────────┴───────────┐
|
|
|
|
* │ dml-passthrough.slice │
|
|
|
|
* └───────────┬───────────┘
|
|
|
|
* ┌───────────────────────────────────┼───────────────────────────────────┐
|
2019-05-03 14:40:11 +02:00
|
|
|
* no new settings DefaultMemoryLow=15 MemoryLow=0
|
cgroup: Implement default propagation of MemoryLow with DefaultMemoryLow
In cgroup v2 we have protection tunables -- currently MemoryLow and
MemoryMin (there will be more in future for other resources, too). The
design of these protection tunables requires not only intermediate
cgroups to propagate protections, but also the units at the leaf of that
resource's operation to accept it (by setting MemoryLow or MemoryMin).
This makes sense from an low-level API design perspective, but it's a
good idea to also have a higher-level abstraction that can, by default,
propagate these resources to children recursively. In this patch, this
happens by having descendants set memory.low to N if their ancestor has
DefaultMemoryLow=N -- assuming they don't set a separate MemoryLow
value.
Any affected unit can opt out of this propagation by manually setting
`MemoryLow` to some value in its unit configuration. A unit can also
stop further propagation by setting `DefaultMemoryLow=` with no
argument. This removes further propagation in the subtree, but has no
effect on the unit itself (for that, use `MemoryLow=0`).
Our use case in production is simplifying the configuration of machines
which heavily rely on memory protection tunables, but currently require
tweaking a huge number of unit files to make that a reality. This
directive makes that significantly less fragile, and decreases the risk
of misconfiguration.
After this patch is merged, I will implement DefaultMemoryMin= using the
same principles.
2019-03-28 13:50:50 +01:00
|
|
|
* ┌───────────────┴───────────────┐ ┌────────────────┴────────────────┐ ┌───────────────┴────────────────┐
|
|
|
|
* │ dml-passthrough-empty.service │ │ dml-passthrough-set-dml.service │ │ dml-passthrough-set-ml.service │
|
|
|
|
* └───────────────────────────────┘ └─────────────────────────────────┘ └────────────────────────────────┘
|
|
|
|
*
|
|
|
|
* 2. dml-override.slice sets DefaultMemoryLow=10. As such, dml-override-empty.service should also
|
|
|
|
* end up with a memory.low of 10. dml-override.slice should still have a memory.low of 50.
|
|
|
|
*
|
|
|
|
* ┌───────────┐
|
|
|
|
* │ dml.slice │
|
|
|
|
* └─────┬─────┘
|
|
|
|
* DefaultMemoryLow=10
|
|
|
|
* ┌─────────┴──────────┐
|
|
|
|
* │ dml-override.slice │
|
|
|
|
* └─────────┬──────────┘
|
|
|
|
* no new settings
|
|
|
|
* ┌─────────────┴──────────────┐
|
|
|
|
* │ dml-override-empty.service │
|
|
|
|
* └────────────────────────────┘
|
|
|
|
*
|
|
|
|
* 3. dml-discard.slice sets DefaultMemoryLow= with no rvalue. As such,
|
|
|
|
* dml-discard-empty.service should end up with a value of 0.
|
2020-02-04 13:49:35 +01:00
|
|
|
* dml-discard-set-ml.service sets MemoryLow=15, and as such should have that override the
|
cgroup: Implement default propagation of MemoryLow with DefaultMemoryLow
In cgroup v2 we have protection tunables -- currently MemoryLow and
MemoryMin (there will be more in future for other resources, too). The
design of these protection tunables requires not only intermediate
cgroups to propagate protections, but also the units at the leaf of that
resource's operation to accept it (by setting MemoryLow or MemoryMin).
This makes sense from an low-level API design perspective, but it's a
good idea to also have a higher-level abstraction that can, by default,
propagate these resources to children recursively. In this patch, this
happens by having descendants set memory.low to N if their ancestor has
DefaultMemoryLow=N -- assuming they don't set a separate MemoryLow
value.
Any affected unit can opt out of this propagation by manually setting
`MemoryLow` to some value in its unit configuration. A unit can also
stop further propagation by setting `DefaultMemoryLow=` with no
argument. This removes further propagation in the subtree, but has no
effect on the unit itself (for that, use `MemoryLow=0`).
Our use case in production is simplifying the configuration of machines
which heavily rely on memory protection tunables, but currently require
tweaking a huge number of unit files to make that a reality. This
directive makes that significantly less fragile, and decreases the risk
of misconfiguration.
After this patch is merged, I will implement DefaultMemoryMin= using the
same principles.
2019-03-28 13:50:50 +01:00
|
|
|
* reset DefaultMemoryLow value. dml-discard.slice should still have an eventual memory.low of 50.
|
|
|
|
*
|
|
|
|
* ┌───────────┐
|
|
|
|
* │ dml.slice │
|
|
|
|
* └─────┬─────┘
|
|
|
|
* DefaultMemoryLow=
|
|
|
|
* ┌─────────┴─────────┐
|
|
|
|
* │ dml-discard.slice │
|
|
|
|
* └─────────┬─────────┘
|
|
|
|
* ┌──────────────┴───────────────┐
|
|
|
|
* no new settings MemoryLow=15
|
|
|
|
* ┌─────────────┴─────────────┐ ┌─────────────┴──────────────┐
|
|
|
|
* │ dml-discard-empty.service │ │ dml-discard-set-ml.service │
|
|
|
|
* └───────────────────────────┘ └────────────────────────────┘
|
|
|
|
*/
|
|
|
|
assert_se(manager_load_startable_unit_or_warn(m, "dml.slice", NULL, &dml) >= 0);
|
|
|
|
|
|
|
|
assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough.slice", NULL, &dml_passthrough) >= 0);
|
|
|
|
assert_se(UNIT_DEREF(dml_passthrough->slice) == dml);
|
|
|
|
assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-empty.service", NULL, &dml_passthrough_empty) >= 0);
|
|
|
|
assert_se(UNIT_DEREF(dml_passthrough_empty->slice) == dml_passthrough);
|
|
|
|
assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-dml.service", NULL, &dml_passthrough_set_dml) >= 0);
|
|
|
|
assert_se(UNIT_DEREF(dml_passthrough_set_dml->slice) == dml_passthrough);
|
|
|
|
assert_se(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-ml.service", NULL, &dml_passthrough_set_ml) >= 0);
|
|
|
|
assert_se(UNIT_DEREF(dml_passthrough_set_ml->slice) == dml_passthrough);
|
|
|
|
|
|
|
|
assert_se(manager_load_startable_unit_or_warn(m, "dml-override.slice", NULL, &dml_override) >= 0);
|
|
|
|
assert_se(UNIT_DEREF(dml_override->slice) == dml);
|
|
|
|
assert_se(manager_load_startable_unit_or_warn(m, "dml-override-empty.service", NULL, &dml_override_empty) >= 0);
|
|
|
|
assert_se(UNIT_DEREF(dml_override_empty->slice) == dml_override);
|
|
|
|
|
|
|
|
assert_se(manager_load_startable_unit_or_warn(m, "dml-discard.slice", NULL, &dml_discard) >= 0);
|
|
|
|
assert_se(UNIT_DEREF(dml_discard->slice) == dml);
|
|
|
|
assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-empty.service", NULL, &dml_discard_empty) >= 0);
|
|
|
|
assert_se(UNIT_DEREF(dml_discard_empty->slice) == dml_discard);
|
|
|
|
assert_se(manager_load_startable_unit_or_warn(m, "dml-discard-set-ml.service", NULL, &dml_discard_set_ml) >= 0);
|
|
|
|
assert_se(UNIT_DEREF(dml_discard_set_ml->slice) == dml_discard);
|
|
|
|
|
|
|
|
root = UNIT_DEREF(dml->slice);
|
|
|
|
assert_se(!UNIT_ISSET(root->slice));
|
|
|
|
|
|
|
|
assert_se(unit_get_ancestor_memory_low(root) == CGROUP_LIMIT_MIN);
|
|
|
|
|
|
|
|
assert_se(unit_get_ancestor_memory_low(dml) == CGROUP_LIMIT_MIN);
|
|
|
|
dml_tree_default = unit_get_cgroup_context(dml)->default_memory_low;
|
|
|
|
assert_se(dml_tree_default == 50);
|
|
|
|
|
|
|
|
assert_se(unit_get_ancestor_memory_low(dml_passthrough) == 100);
|
|
|
|
assert_se(unit_get_ancestor_memory_low(dml_passthrough_empty) == dml_tree_default);
|
|
|
|
assert_se(unit_get_ancestor_memory_low(dml_passthrough_set_dml) == 50);
|
2019-05-03 14:40:11 +02:00
|
|
|
assert_se(unit_get_ancestor_memory_low(dml_passthrough_set_ml) == 0);
|
cgroup: Implement default propagation of MemoryLow with DefaultMemoryLow
In cgroup v2 we have protection tunables -- currently MemoryLow and
MemoryMin (there will be more in future for other resources, too). The
design of these protection tunables requires not only intermediate
cgroups to propagate protections, but also the units at the leaf of that
resource's operation to accept it (by setting MemoryLow or MemoryMin).
This makes sense from an low-level API design perspective, but it's a
good idea to also have a higher-level abstraction that can, by default,
propagate these resources to children recursively. In this patch, this
happens by having descendants set memory.low to N if their ancestor has
DefaultMemoryLow=N -- assuming they don't set a separate MemoryLow
value.
Any affected unit can opt out of this propagation by manually setting
`MemoryLow` to some value in its unit configuration. A unit can also
stop further propagation by setting `DefaultMemoryLow=` with no
argument. This removes further propagation in the subtree, but has no
effect on the unit itself (for that, use `MemoryLow=0`).
Our use case in production is simplifying the configuration of machines
which heavily rely on memory protection tunables, but currently require
tweaking a huge number of unit files to make that a reality. This
directive makes that significantly less fragile, and decreases the risk
of misconfiguration.
After this patch is merged, I will implement DefaultMemoryMin= using the
same principles.
2019-03-28 13:50:50 +01:00
|
|
|
|
|
|
|
assert_se(unit_get_ancestor_memory_low(dml_override) == dml_tree_default);
|
|
|
|
assert_se(unit_get_ancestor_memory_low(dml_override_empty) == 10);
|
|
|
|
|
|
|
|
assert_se(unit_get_ancestor_memory_low(dml_discard) == dml_tree_default);
|
|
|
|
assert_se(unit_get_ancestor_memory_low(dml_discard_empty) == CGROUP_LIMIT_MIN);
|
|
|
|
assert_se(unit_get_ancestor_memory_low(dml_discard_set_ml) == 15);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int main(int argc, char* argv[]) {
|
|
|
|
int rc = EXIT_SUCCESS;
|
|
|
|
|
|
|
|
test_setup_logging(LOG_DEBUG);
|
|
|
|
|
|
|
|
TEST_REQ_RUNNING_SYSTEMD(rc = test_default_memory_low());
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|