Conversation
FindCgroup() refused to work on a host that only mounts the unified
hierarchy, and CgroupV2 was a stub whose methods carried no receivers,
so the type never satisfied the Cgroup interface. Every validation test
that reads resource limits back out of the cgroup filesystem failed with
"cgroupv2 is not supported yet" on any current distribution.
Read the unified control files instead: memory.{max,low,swap.max},
cpu.max, cpuset.{cpus,mems}, pids.max, hugetlb.<size>.max and io.max.
memory.swap.max limits swap alone while the configuration field covers
memory plus swap, so the memory limit is added back. cpuset files fall
back to the effective set, which is where the unified hierarchy reports
an inherited value.
Values the unified hierarchy does not carry are left unset rather than
guessed, so that a caller can tell them apart from a real zero. That
covers swappiness, disableOOMKiller, useHierarchy, kernel, kernelTCP
and the block IO weights, and also cpu.shares, which runtimes map onto
cpu.weight through a lossy conversion the runtime-spec does not define.
The devices and network controllers have no unified counterpart at all:
device access is enforced by an eBPF program that cannot be read back,
and net_cls and net_prio were never ported, so both report that rather
than a missing file.
Version() lets a caller ask which hierarchy it is talking to.
Relates to opencontainers#807.
Signed-off-by: Daniel Golle <daniel@makrotopia.org>
The cgroup test bundles ask for memory.swappiness, memory.disableOOMKiller, memory.kernel, memory.kernelTCP, blockIO.leafWeight and the block IO weights on every host. None of those exist in the unified hierarchy, and the create operation requires a runtime that cannot apply a property to generate an error and not create the container. A correct cgroup v2 only runtime therefore fails these tests by doing exactly what the spec asks of it. Set those fields only where the hierarchy can carry them. cpu.shares goes the same way: runtimes map it onto cpu.weight with a lossy conversion the runtime-spec does not define, so the configured value cannot be recovered and asserting on it would test a convention rather than the spec. Compare the remaining fields with a helper that reports a value neither side carries as a diagnostic instead of a failure, so that one unsupported field no longer hides the checks that follow it: the block IO and cpu validators used to return early and silently drop every later assertion. linux_cgroups_relative_cpus grows the same behaviour by using the shared CPU validator its siblings already use rather than its own copy of it. Signed-off-by: Daniel Golle <daniel@makrotopia.org>
linux.resources.network and linux.resources.devices have no counterpart in the unified hierarchy at all. net_cls and net_prio were never ported, and device access is enforced by an eBPF program that cannot be read back from the filesystem, so there is nothing for these tests to compare the configuration against. Skip the four tests where the host runs a unified hierarchy, the way the suite already skips tests that do not apply to the platform it runs on. Signed-off-by: Daniel Golle <daniel@makrotopia.org>
Both delete tests build the cgroup directory out of a literal /sys/fs/cgroup/pids. On a unified hierarchy that directory never exists, so delete_only_create_resources fails when it tries to populate it and delete_resources passes without testing anything: the path it expects the runtime to have removed was never there. Ask the Cgroup implementation where the cgroup lives instead, which also picks up the real mount point rather than assuming one. Move a process with cgroup.procs, which both hierarchies provide, rather than with the cgroup v1 only tasks file. Fixes opencontainers#807 Signed-off-by: Daniel Golle <daniel@makrotopia.org>
The unified hierarchy applies the same device rules cgroup v1 did, but it publishes no list to read them back from, so linux_cgroups_devices has nothing to compare against and is skipped there. The rules are still in force; only this test's way of looking at them is gone. Look at them from inside the container instead. The three rules the test already configures name no device that the bundle puts in the container, so add a node the first of them allows and one that only the generator's leading deny-all covers. The rules themselves are untouched, which leaves the cgroup v1 path reading exactly what it read before. runtimetest works out what the list permits by applying the entries in the listed order, as a runtime is required to, and compares that with what opening the node does. Only EPERM means the device controller refused, and it is reported before the driver behind the node is consulted, so nothing has to be loaded for the answer to be meaningful. Devices a runtime must supply are left alone, since runtimes add rules of their own for those. Signed-off-by: Daniel Golle <daniel@makrotopia.org>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This builds on #812 and should be read after it. It is that branch plus one commit,
validation: check the device rules by trying them, which is the only part that is new here; the four commits below it are #812's and will disappear from this diff once that merges.linux_cgroups_devicesreads the configured rules back out ofdevices.list, which is a cgroup v1 interface. #812 skips the test on a unified hierarchy for that reason, and the wording there is careful to say that the restrictions are still applied and still enforced, by an attachedBPF_CGROUP_DEVICEprogram in the runtimes that implement it. Only the window the test looks through is gone, not the property it is checking, so skipping it is a poor final answer. This replaces the skip with a check that works on both hierarchies.The three rules the test already configures name no device that the bundle puts in the container, so nothing about them can be observed from the inside. Two device nodes fix that:
/dev/allowedat c 10:229, which the first of those rules permits, and/dev/deniedat c 10:230, which only the generator's leading deny-all covers. The rules themselves are untouched, so the cgroup v1 path reads exactly what it read before, andlinux.devicesentries do not become cgroup rules in any runtime I checked. On a unified hierarchyruntimetestworks out what the list permits by applying the entries in the listed order, which is the one requirement the spec states about this field, and compares that with what opening each node actually does. Only EPERM means the device controller refused, and it comes back before the driver behind the node is consulted, so nothing has to be loaded for the answer to be meaningful. Devices a runtime is required to supply are skipped, because runtimes add rules of their own for those and they say nothing about the configured list.Verified as root on a cgroup v2 host against two runtimes, both reporting
okfor the allowed node and the denied node: crun 1.29.1, and uxc, the OpenWrt runtime whose cgroup v2 device support is an eBPF emulation of the cgroup v1 semantics. Two independent implementations agreeing is what makes me think the test measures the property rather than one runtime's quirk. It has not been run against runc, because the machine with the privileges to test device enforcement does not have runc on it, so I want to be explicit about that rather than leave it implied.The reason I am reasonably confident about runc anyway, and the reason the fixture uses these particular numbers, is that runc appends its own allow rules after the ones from
config.json:The comment on
AllowedDevicesconcedes that this is "at the very least 'questionable' (if not outright wrong) according to the runtime-spec" and that users "cannot disable this behaviour". Those appended rules are eleven allows:c *:* mandb *:* m, which grant mknod alone, then c 1:3, c 1:8, c 1:7, c 5:0, c 1:5, c 1:9, c 136:*, c 5:2 and c 10:200. None of them grants read access to c 10:229 or c 10:230, so neither node is re-allowed behind the configured list, and the wildcardmrules mean creating both nodes still works under the leading deny-all. A fixture that denied one of the default devices instead would have failed against runc for reasons that are runc's rather than the test's, which is what these numbers avoid. If a reviewer with runc to hand finds otherwise I would very much like to know.cmd/runtimetest/main_test.gocovers the rule evaluation itself: wildcards, type mismatches, a deny that does not mention read, and a later entry overriding an earlier one.The device-ordering question this raises is now opencontainers/runtime-spec#1321.