Skip to content

[BUG] cgroup_reader misses cgroup v1 and non-root cgroupns=private paths #604

Description

@bburda

Bug report

cgroup_reader in ros2_medkit_linux_introspection reports container limits
only for one of the three common cgroup layouts. In the other two it either
fails completely or silently returns nothing.

Both problems are in
src/ros2_medkit_discovery_plugins/ros2_medkit_linux_introspection/src/linux_utils/cgroup_reader.cpp.

1. cgroup v1 is not handled.

read_cgroup_path() only matches the cgroup v2 line format:

if (line.rfind("0::", 0) == 0) {
  return line.substr(3);
}

On a pure cgroup v1 host every line has the form <id>:<controllers>:<path>, so
no line matches, the function returns an empty string, and read_cgroup_info()
gives up.

2. The filesystem path is built for cgroupns=host only.

auto cgroup_fs_path = root + "/sys/fs/cgroup" + cgroup_path;

This assumes /proc/self/cgroup reports a path like /docker/<id>, which is the
case with cgroupns=host. With cgroupns=private, which is the default in
current Docker, the container sees its own cgroup mounted directly at
/sys/fs/cgroup and the reported path is usually /. The concatenation then
gives /sys/fs/cgroup//cpu.max, which happens to resolve. Any private namespace
with a path other than / builds a path that does not exist, and the limits are
reported as absent.

Steps to reproduce

  1. Run the gateway in a container started with --cgroupns=private and a cgroup
    path other than /.
  2. Query the x-medkit-procfs data for the process.
  3. Compare the reported CPU and memory limits with the ones set on the container.

Expected behavior

Container CPU and memory limits are reported on cgroup v1 and v2, and under both
cgroupns=host and cgroupns=private.

Actual behavior

On cgroup v1 the read fails and no limits are reported. Under cgroupns=private
with a non-root path the limit files are not found and the limits are reported as
absent, which is indistinguishable from an unlimited container.

Environment

  • ros2_medkit version: 0.6.0
  • ROS 2 distro: all
  • OS: any Linux host; the v1 case needs a host without unified cgroups

Additional information

Suggested fix:

  • Try the bare path /sys/fs/cgroup/<file> first, and fall back to the joined
    path only if that misses. That covers both namespace modes without detecting
    which one is in use.
  • Parse the v1 line format as well, and read cpu.cfs_quota_us divided by
    cpu.cfs_period_us for the CPU limit and memory.limit_in_bytes for memory.
  • Reporting "no limit" when the file could not be read hides the failure. It
    would be better to distinguish "unlimited" from "could not read".

Note that sched_getaffinity() is the only source that reflects --cpuset-cpus.
A quota read alone does not see it, so a caller that wants the effective CPU
budget needs both.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions