Skip to content

Commit

Permalink
Add a fallback logic for locating a usable cgroup for cgroups v2
Browse files Browse the repository at this point in the history
For cgroups v1 we already use /system.slice/benchexec-cgroup.service
as a fallback cgroup if it exists and we cannot use our current cgroup.
For cgroups v2 we do not yet have such a fallback.
However, implementing this makes it much easier for people
on systems without systemd (e.g., in a container)
to setup and configure cgroups for BenchExec, so let's do this.

Background discussion is in #1048,
from which also the suggested commands in the documentation are taken.

Thanks to @incaseoftrouble for reviewing the design of this.
  • Loading branch information
PhilippWendler committed Jun 3, 2024
1 parent 324bbc7 commit 29480b3
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 3 deletions.
32 changes: 29 additions & 3 deletions benchexec/cgroupsv2.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ def initialize():
# which might not be a good idea.
logging.debug("BenchExec was started in its own cgroup: %s", cgroup)

elif _create_systemd_scope_for_us():
# If we can create a systemd scope for us and move ourselves in it,
# we have a usable cgroup afterwards.
elif _create_systemd_scope_for_us() or _try_fallback_cgroup():
# If we can create a systemd scope for us or find a usable fallback cgroup
# and move ourselves in it, we have a usable cgroup afterwards.
cgroup = CgroupsV2.from_system()

else:
Expand Down Expand Up @@ -192,6 +192,32 @@ def _create_systemd_scope_for_us():
return False


def _try_fallback_cgroup():
"""
Attempt to locate a usable fallback cgroup.
If it is found this process is moved into it.
@return: a boolean indicating whether this succeeded
"""
# Attempt to use /benchexec cgroup.
# If it exists, some deliberately created it for us to use.
# The following does this by just faking a /proc/self/cgroup for this case.
cgroup = CgroupsV2.from_system(["0::/benchexec"])
if cgroup.path and not list(cgroup.get_all_tasks()):
logging.debug("Found existing cgroup /benchexec, using it as fallback.")

# Create cgroup for this execution of BenchExec.
cgroup = cgroup.create_fresh_child_cgroup(
cgroup.subsystems.keys(), prefix="benchexec_"
)
# Move ourselves to it such that for the rest of the code this looks as usual.
cgroup.add_task(os.getpid())

return True

return False


def _find_cgroup_mount():
"""
Return the mountpoint of the cgroupv2 unified hierarchy.
Expand Down
17 changes: 17 additions & 0 deletions doc/INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,23 @@ For both Podman and Docker this will work
if BenchExec is the main process inside the container,
otherwise you need to manually create an appropriate cgroup hierarchy inside the container,
i.e., one where BenchExec has its own separate cgroup.
You can either start BenchExec as the only process in a fresh cgroup
or create an empty cgroup named `/benchexec`,
which BenchExec will then automatically use.
This cgroup should have as many controllers enabled and delegated to sub-cgroups as possible,
for example like this:
```
mkdir -p /sys/fs/cgroup/benchexec
for controller in $(cat /sys/fs/cgroup/cgroup.controllers); do
echo "+$controller" > /sys/fs/cgroup/cgroup.subtree_control
done
for controller in $(cat /sys/fs/cgroup/benchexec/cgroup.controllers); do
echo "+$controller" > /sys/fs/cgroup/benchexec/cgroup.subtree_control
done
```
Note that if no other cgroups exist,
you first need to create a different child cgroup
for all existing processes in the container.

For systems with cgroups v1,
please use the following command line argument
Expand Down

0 comments on commit 29480b3

Please sign in to comment.