/* SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include "cgroup.h" #include "manager.h" #include "rm-rf.h" #include "tests.h" #include "unit.h" TEST_RET(default_memory_low, .sd_booted = true) { _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; r = enter_cgroup_subroot(NULL); if (r == -ENOMEDIUM) return log_tests_skipped("cgroupfs not available"); _cleanup_free_ char *unit_dir = NULL; ASSERT_OK(get_testdata_dir("units", &unit_dir)); ASSERT_OK(setenv_unit_path(unit_dir)); assert_se(runtime_dir = setup_fake_runtime_dir()); r = manager_new(RUNTIME_SCOPE_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_OK(r); ASSERT_OK(manager_startup(m, NULL, NULL, NULL)); /* 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 * should have the value of 0, as it has MemoryLow explicitly set. * * ┌───────────┐ * │ dml.slice │ * └─────┬─────┘ * MemoryLow=100 * ┌───────────┴───────────┐ * │ dml-passthrough.slice │ * └───────────┬───────────┘ * ┌───────────────────────────────────┼───────────────────────────────────┐ * no new settings DefaultMemoryLow=15 MemoryLow=0 * ┌───────────────┴───────────────┐ ┌────────────────┴────────────────┐ ┌───────────────┴────────────────┐ * │ 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. * dml-discard-set-ml.service sets MemoryLow=15, and as such should have that override the * 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_OK(manager_load_startable_unit_or_warn(m, "dml.slice", NULL, &dml)); ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough.slice", NULL, &dml_passthrough)); assert_se(UNIT_GET_SLICE(dml_passthrough) == dml); ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough-empty.service", NULL, &dml_passthrough_empty)); assert_se(UNIT_GET_SLICE(dml_passthrough_empty) == dml_passthrough); ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-dml.service", NULL, &dml_passthrough_set_dml)); assert_se(UNIT_GET_SLICE(dml_passthrough_set_dml) == dml_passthrough); ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-passthrough-set-ml.service", NULL, &dml_passthrough_set_ml)); assert_se(UNIT_GET_SLICE(dml_passthrough_set_ml) == dml_passthrough); ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-override.slice", NULL, &dml_override)); assert_se(UNIT_GET_SLICE(dml_override) == dml); ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-override-empty.service", NULL, &dml_override_empty)); assert_se(UNIT_GET_SLICE(dml_override_empty) == dml_override); ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-discard.slice", NULL, &dml_discard)); assert_se(UNIT_GET_SLICE(dml_discard) == dml); ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-discard-empty.service", NULL, &dml_discard_empty)); assert_se(UNIT_GET_SLICE(dml_discard_empty) == dml_discard); ASSERT_OK(manager_load_startable_unit_or_warn(m, "dml-discard-set-ml.service", NULL, &dml_discard_set_ml)); assert_se(UNIT_GET_SLICE(dml_discard_set_ml) == dml_discard); assert_se(root = UNIT_GET_SLICE(dml)); assert_se(!UNIT_GET_SLICE(root)); 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); assert_se(unit_get_ancestor_memory_low(dml_passthrough_set_ml) == 0); 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; } DEFINE_TEST_MAIN(LOG_DEBUG);