Commit Graph

589 Commits

Author SHA1 Message Date
Kir Kolyshkin 4f8ccc5ff5 libct/cg/sd/v2: call initPath from Path
Sometimes Path() is called before m.path is initialized (in particular,
this happens from (*linuxContainer).newInitConfig), so we do need to
make sure to call initPath.

This fixes the following integration tests (for cgroup v2 + systemd case,
currently not enabled -- to be enabled by further commits):

 *  runc run (blkio weight)
 * runc run (cgroupv2 mount inside container)

Fixes: ff692f289b ("Fix cgroup2 mount for rootless case")
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-07 22:05:56 -07:00
Kir Kolyshkin c7f847ed3a libct/cg/sd: use global dbus connection
Using per cgroup manager dbus connection instances means
that every cgroup manager instance gets a new connection,
and those connections are never closed, ultimately resulting
in file descriptors limit being hit.

Revert back to using a single global dbus connection for everything,
without changing the callers.

NOTE that it is assumed a runtime can't use both root and rootless
dbus at the same time. If this happens, we panic.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-06 12:37:55 -07:00
Kir Kolyshkin 99c5c504ea libct/cg/sd: introduce and use getManagerProperty
Commit 47ef9a104f forgot to wrap GetManagerProperty("ControlGroup")
into retryOnDisconnect. Since there's one other user of
GetManagerProperty, add getManagerProperty wrapper and use it.

Fixes: 47ef9a104f

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-06 12:37:55 -07:00
Kir Kolyshkin 524abc59f4 freezer: add delay after freeze
I hate to keep adding those kludges, but lately TestFreeze (and
TestSystemdFreeze) from libcontainer/integration fails a lot. The
failure comes and goes, and is probably this is caused by a slow host
allocated for the test, and a slow VM on top of it.

To remediate, add a small sleep on every 25th iteration in between
asking the kernel to freeze and checking its status.

In the worst case scenario (failure to freeze), this adds about 0.4 ms
(40 x 10 us) to the duration of the call.

It is hard to measure how this affects CI as GHA plays a roulette when
allocating a node to run the test on, but it seems to help. With
additional debug info, I saw somewhat frequent "frozen after 24 retries"
or "frozen after 49 retries", meaning it succeeded right after the added
sleep.

While at it, rewrite/improve the comments.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-05-06 10:51:46 -07:00
Akihiro Suda c061bc78aa Merge pull request #2906 from kolyshkin/set-allow-nil 2021-04-30 21:01:15 +09:00
Aleksa Sarai 016717a550 merge branch 'pr-2926'
Sebastiaan van Stijn (1):
  cgroups/systemd: replace deprecated dbus functions

LGTMs: AkihiroSuda cyphar
Closes #2926
2021-04-30 18:14:00 +10:00
Akihiro Suda 8f85f4f1f2 Merge pull request #2921 from kolyshkin/faster-openfile 2021-04-30 17:06:37 +09:00
Kir Kolyshkin abf12ce0db libc/cg: improve Manager docs
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-29 15:30:12 -07:00
Kir Kolyshkin 3f65946756 libct/cg: make Set accept configs.Resources
A cgroup manager's Set method sets cgroup resources, but historically
it was accepting configs.Cgroups.

Refactor it to accept resources only. This is an improvement from the
API point of view, as the method can not change cgroup configuration
(such as path to the cgroup etc), it can only set (modify) its
resources/limits.

This also lays the foundation for complicated resource updates, as now
Set has two sets of resources -- the one that was previously specified
during cgroup manager creation (or the previous Set), and the one passed
in the argument, so it could deduce the difference between these. This
is a long term goal though.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-29 15:24:19 -07:00
Kir Kolyshkin af0710a0f8 libct/cg/sd/v2: fix Set argument
For some reason, systemd cgroup v2 driver's Set is not using its
container argument when generating systemd unit properties.

This bug is not detected by our update tests as we run a new binary
every time and thus a new instance of a cgroup manager.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-29 15:12:56 -07:00
Kir Kolyshkin 850b2c47b2 libct/cg/fscommon.OpenFile: speed up ro case
Commit 88e8350de2, among the other things, replaced filepath.Join with
securejoin.SecureJoin for both reads and writes to cgroupfs.

Commits e76ac1c054 and 31f0f5b7e0 switched more code to use
fscommon.ReadFile (and thus securejoin). Commit 0228226e6d introduced
fscommon.OpenFile (which uses securejoin as the fallback if openat2(2)
is not available, which is the case for older kernels), and commit
c95e69007c switched most of cgroup/fs[2] code to use it.

As a result, fs.GetStats() method became noticeable slower, mostly due
to securejoin calling os.Lstat and filepath.Clean.

Using securejoin as a security measure for cgroupfs files is
not well justified, as cgroupfs do not contain symlinks, and none of the
code using it have uncleaned paths. In particular, fs/fs2/systemd
managers do check and sanitize their paths.

This commit modifies the code to not use securejoin. Instead, it checks
that the opened file is indeed on cgroupfs.

Using BenchmarkGetStats on a CentOS 8 VM, I see the following
improvement:

Before:
> BenchmarkGetStats-8               8376            625135 ns/op

After:
> BenchmarkGetStats-8   	   12226	    485015 ns/op

An intermediate version, with no fstatfs to check fstype:
> BenchmarkGetStats-8              13162            452281 ns/op

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-29 14:55:02 -07:00
Sebastiaan van Stijn 71a8aee846 cgroups/systemd: replace deprecated dbus functions
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-04-29 22:07:02 +02:00
Kir Kolyshkin 47ef9a104f libct/cg/sd: retry on dbus disconnect
Instead of reconnecting to dbus after some failed operations, and
returning an error (so a caller has to retry), reconnect AND retry
in place for all such operations.

This should fix issues caused by a stale dbus connection after e.g.
a dbus daemon restart.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 16:19:49 -07:00
Shiming Zhang 6122bc8beb Privatize NewUserSystemDbus
Signed-off-by: Shiming Zhang <wzshiming@foxmail.com>
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 16:16:27 -07:00
Shiming Zhang 15fee9899f libct/cg/sd: add renew dbus connection
[@kolyshkin: doc nits, use dbus.ErrClosed and isDbusError]

Signed-off-by: Shiming Zhang <wzshiming@foxmail.com>
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 16:16:05 -07:00
Kir Kolyshkin bacfc2c2f9 libct/cg/sd: add isDbusError
Generalize isUnitExists as isDbusError, and use errors.As while at it
(which can handle wrapped errors as well).

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 16:14:04 -07:00
Shiming Zhang cdbed6f02f libct/cg/sd: add dbus manager
[@kolyshkin: documentation nits]

Signed-off-by: Shiming Zhang <wzshiming@foxmail.com>
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 16:02:39 -07:00
Kir Kolyshkin 9efd8466ab libct/cg/fscommon.OpenFile: reverse checks order
In case openat2() is not available, it does not make sense to calculate
relpath (and check if path has /sys/fs/cgroup prefix).

Reverse the order of checks to not do that in case openat2 is not
available.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 09:51:31 -07:00
Kir Kolyshkin 0bee5e0b01 libct/cg/fs: add GetStats benchmark
On my CentOS 8 VM it shows:

> BenchmarkGetStats-8   	    8376	    625135 ns/op

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-27 09:51:20 -07:00
Kir Kolyshkin fcd7fe85e1 libct/cg/fs/freezer: make sure to thaw on failure
Function (*FreezerGroup).Set has a few paths where in can return an
error. In any case, if an error is returned, we failed to freeze,
and we need to thaw to avoid leaving the cgroup in a stuck state.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-20 15:57:59 -07:00
Sebastiaan van Stijn 73f22e7f1a libcontainer/cgroups/systemd: replace use of deprecated dbus.New()
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-04-20 14:07:23 +02:00
Akihiro Suda ba257d2de8 Merge pull request #2873 from kolyshkin/mem-root-cgroup2
libct/cg/fs2.GetStats() improvements
2021-04-19 13:59:14 +09:00
Odin Ugedal ee3b563de7 Add cfs throttle stats to cgroup v2
Signed-off-by: Odin Ugedal <odin@uged.al>
2021-04-16 13:41:22 +02:00
Kir Kolyshkin ef9922c26c libct/cg: don't return OOMKillCount error when rootless
Commit 5d0ffbf9c8 added OOM kill count checking and better container
start/run/exec error reporting in case we hit OOM.

It also introduced warnings like these:

> level=warning msg="unable to get oom kill count" error="openat2
> /sys/fs/cgroup/user.slice/user-1000.slice/user@1000.service/test_hello/memory.events:
> no such file or directory"

In case of rootless containers, unless cgroup is delegated or systemd is
used, runc can not create a cgroup and thus it fails to get OOM kill
count. This is expected, and the warning should not be shown in this
case.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-14 17:57:00 -07:00
Kir Kolyshkin 5cdd9022a9 libct/cg/fs[2]: fix comments about m.rootless
For fs, commit fc620fdf81 made rootless field private,
and for fs2, it was always private, and yet comments in both
mention it as m.Rootless.

Fix it.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-14 17:44:22 -07:00
Sebastiaan van Stijn f28a8cc28c ebpf: replace deprecated prog.Attach/prog.Detach
Caught by golangci-lint when enabling golint:

    libcontainer/cgroups/ebpf/ebpf.go:35:12: SA1019: prog.Attach is deprecated: use link.RawAttachProgram instead. (staticcheck)
        if err := prog.Attach(dirFD, ebpf.AttachCGroupDevice, unix.BPF_F_ALLOW_MULTI); err != nil {
                  ^
    libcontainer/cgroups/ebpf/ebpf.go:39:13: SA1019: prog.Detach is deprecated: use link.RawDetachProgram instead. (staticcheck)
            if err := prog.Detach(dirFD, ebpf.AttachCGroupDevice, unix.BPF_F_ALLOW_MULTI); err != nil {
                      ^

Worth noting that we currently call prog.Detach() with unix.BPF_F_ALLOW_MULTI;
https://github.com/golang/sys/blob/22da62e12c0cd9c1da93581e1113ca4d82a5be14/unix/zerrors_linux.go#L178

    BPF_F_ALLOW_MULTI = 0x2

Looking at the source code for prog.Detach(); https://github.com/cilium/ebpf/blob/v0.4.0/prog.go#L579-L581,
this would _always_ produce an error:

    if flags != 0 {
        return errors.New("flags must be zero")
    }

Note that the flags parameter is not used (except for that validation)

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-04-13 12:27:39 +02:00
Qiang Huang 2d38476c96 Merge pull request #2840 from kolyshkin/ignore-kmem
Ignore kernel memory settings
2021-04-13 09:44:14 +08:00
Kir Kolyshkin 52390d6804 Ignore kernel memory settings
This is somewhat radical approach to deal with kernel memory.

Per-cgroup kernel memory limiting was always problematic. A few
examples:

 - older kernels had bugs and were even oopsing sometimes (best example
   is RHEL7 kernel);
 - kernel is unable to reclaim the kernel memory so once the limit is
   hit a cgroup is toasted;
 - some kernel memory allocations don't allow failing.

In addition to that,

 - users don't have a clue about how to set kernel memory limits
   (as the concept is much more complicated than e.g. [user] memory);
 - different kernels might have different kernel memory usage,
   which is sort of unexpected;
 - cgroup v2 do not have a [dedicated] kmem limit knob, and thus
   runc silently ignores kernel memory limits for v2;
 - kernel v5.4 made cgroup v1 kmem.limit obsoleted (see
   https://github.com/torvalds/linux/commit/0158115f702b).

In view of all this, and as the runtime-spec lists memory.kernel
and memory.kernelTCP as OPTIONAL, let's ignore kernel memory
limits (for cgroup v1, same as we're already doing for v2).

This should result in less bugs and better user experience.

The only bad side effect from it might be that stat can show kernel
memory usage as 0 (since the accounting is not enabled).

[v2: add a warning in specconv that limits are ignored]

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-04-12 12:18:11 -07:00
Akihiro Suda 6023d635d7 Merge pull request #2844 from kolyshkin/sd-cg-docs 2021-04-13 04:12:34 +09:00
Sebastiaan van Stijn 4316df8b53 libcontainer/system: move userns utilities to separate package
Moving these utilities to a separate package, so that consumers of this
package don't have to pull in the whole "system" package.

Looking at uses of these utilities (outside of runc itself);

`RunningInUserNS()` is used by [various external consumers][1],
so adding a "Deprecated" alias for this.

[1]: https://grep.app/search?current=2&q=.RunningInUserNS

Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
2021-04-04 22:42:03 +02:00
Kir Kolyshkin 0f8d2b6bed libct/cg/fs2.Stat: don't look for available controllers
Some controllers might still have stats available even if they are
disabled (this is definitely so for cpu.stat -- see earlier commit).

Some stat methods might implement sensible fallbacks (see previous
commit for statPids.

In the view of all that, it makes sense to not check if a particular
controller is available, but rather ignore ENOENT from it.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-03-31 12:16:14 -07:00
Kir Kolyshkin 85416b8742 libct/cg/fs2.statPids: fall back directly
When getting pids stats, instead of checking whether the pids controller
is available, let's use a fall back function in case pids.current does
not exist. This simplifies the logic in fs2.GetStats.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-03-31 12:03:12 -07:00
Kir Kolyshkin 10f9a982ac libct/cg/fs2/getPidsWithoutController: optimize
It is inefficient to create an associative map for the whole purpose of
counting the number of elements in it, especially if the elements are
all unique. It uses more CPU than necessary and creates some work for
the garbage collector.

The file we read contains PIDs and newlines, and the easiest/fastest way
to get the number of PIDs is just to count the newlines.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-03-31 11:55:07 -07:00
Kir Kolyshkin 6121f8b69f libct/cg/fs2.Stat: always call statCpu
Giuseppe found out that cpu.stat for a cgroup is available even if
the cpu controller is not enabled for it. So, let's call statCpu
regradress, and ignore ENOENT.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-03-31 11:40:28 -07:00
Kir Kolyshkin 9455395b06 libct/cg/fs2/memory.Stat: add usage for root cgroup
There is no memory.{current,max} for the root node in cgroup v2, and
thus stats for "/sys/fs/cgroup" return an error.

The same thing works with cgroup v1 (as there are
memory.{usage,limit}_in_bytes files in the root node).

Emulate stats for /sys/fs/cgroup by getting numbers from
/proc/self/meminfo.

NOTE that both memory.current (in cgroup v2) and memory.usage_in_bytes
(in cgroup v1) include page cache etc into the number, so we do the same
when calculating memory usage (as opposed to number reported by "free",
which excludes page cache and buffers).

[v2: check for cgroup files first, as future kernels might add it]
[v3: don't subtract cache from mem_used, simplifying the logic]

[Initially, I wanted to avoid parsing yet another /proc file and
instead mock some numbers using data from memory.stat but was
unable to come up with formulae that make sense.]

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-03-31 11:14:49 -07:00
Kir Kolyshkin a9c47fe70f libct/cg/fs[2]/getMemoryData[V2]: optimize
Existing code ignores ENOENT error in case we're reading data from
controls that might not be enabled. While this is correct, the code
can be improved:

1. Check name != "" instead of moduleName != "memory", as these checks
   are equivalent but the new one is faster.

2. It does not make sense to ignore subsequent errors -- if the control
   is not available, we won't hit this codepath.

3. Add a comment explaining why we ignore the error.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-03-31 11:14:49 -07:00
Kir Kolyshkin b99ca25ad0 libct/cg/fs2/memory: fix swap reporting
cgroup v1 reports combined mem+swap in stats.MemoryStats.SwapUsage.
In cgroup v2, swap is separate.

For the sake of compatibility, make v2 report mem+swap as well.
This also includes Limit.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-03-31 11:14:49 -07:00
Akihiro Suda 5df79d5c3d Merge pull request #2820 from dqminh/io-cgroup2-fallback
fs2: fallback to setting io.weight if io.bfq.weight
2021-03-26 18:58:51 +09:00
Kir Kolyshkin f585cec7dc libct/cg/v2: always enable TasksAccounting
This unconditionally enables TasksAccounting for systemd unified (v2)
cgroup driver, making it work the same way as the legacy (v1) driver.

Practically, it is probably a no-op since DefaultTasksAccounting is
usually true.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-03-09 20:17:21 -08:00
Daniel Dao 8c7ece1e6d fs2: fallback to setting io.weight if io.bfq.weight
if bfq is not loaded, then io.bfq.weight is not available. io.weight
should always be available and is the next best equivalent thing.

Signed-off-by: Daniel Dao <dqminh89@gmail.com>
2021-03-05 13:55:36 +00:00
Akihiro Suda d56a9c67ac Merge pull request #2812 from kolyshkin/memory-tight 2021-02-26 10:52:17 +09:00
Aleksa Sarai c153261830 merge branch 'pr-2786'
Daniel Dao (1):
  Do not convert blkio weight value using blkio->io conversion scheme

LGTMs: @kolyshkin @AkihiroSuda @cyphar
Closes #2786
2021-02-24 18:29:27 +11:00
Daniel Dao c3ffd2ef81 Do not convert blkio weight value using blkio->io conversion scheme
bfq weight controller (i.e. io.bfq.weight if present) is still using the
same bfq weight scheme (i.e 1->1000, see [1].) Unfortunately the
documentation for this was wrong, and only fixed recently [2].

Therefore, if we map blkio weight to io.bfq.weight, there's no need to
do any conversion. Otherwise, we will try to write invalid value which
results in error such as:

```
time="2021-02-03T14:55:30Z" level=error msg="container_linux.go:367: starting container process caused: process_linux.go:495: container init caused: process_linux.go:458: setting cgroup config for procHooks process caused: failed to write \"7475\": write /sys/fs/cgroup/runc-cgroups-integration-test/test-cgroup/io.bfq.weight: numerical result out of range"
```

[1] https://github.com/torvalds/linux/blob/master/Documentation/block/bfq-iosched.rst
[2] https://github.com/torvalds/linux/commit/65752aef0a407e1ef17ec78a7fc31ba4e0b360f9

Signed-off-by: Daniel Dao <dqminh89@gmail.com>
2021-02-23 19:46:16 -08:00
Kir Kolyshkin 5d0ffbf9c8 runc start/run: report OOM
In some cases, container init fails to start because it is killed by
the kernel OOM killer. The errors returned by runc in such cases are
semi-random and rather cryptic. Below are a few examples.

On cgroup v1 + systemd cgroup driver:

> process_linux.go:348: copying bootstrap data to pipe caused: write init-p: broken pipe

> process_linux.go:352: getting the final child's pid from pipe caused: EOF

On cgroup v2:

> process_linux.go:495: container init caused: read init-p: connection reset by peer

> process_linux.go:484: writing syncT 'resume' caused: write init-p: broken pipe

This commits adds the OOM method to cgroup managers, which tells whether
the container was OOM-killed. In case that has happened, the original error
is discarded (unless --debug is set), and the new OOM error is reported
instead:

> ERRO[0000] container_linux.go:367: starting container process caused: container init was OOM-killed (memory limit too low?)

Also, fix the rootless test cases that are failing because they expect
an error in the first line, and we have an additional warning now:

> unable to get oom kill count" error="no directory specified for memory.oom_control

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-02-23 16:15:33 -08:00
Kir Kolyshkin 7e137b9044 libct/cg/fs2/hugetlb: use fscommon.GetValueByKey
This makes the code simpler and more future-proof, in case
any more values will appear in hugetlb.*.events.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-02-23 16:11:55 -08:00
Kir Kolyshkin 9fa65f6607 libct/cg/fscommon: add GetValueByKey
Generalize the libct/getValueFromCgroup() as fscommon.GetValueByKey(),
and document it.

No changes other than using fscommon.ParseUint to convert the value.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-02-23 16:11:55 -08:00
Kir Kolyshkin 494f900e91 libct/cg/fscommon: rename/facelift GetCgroupParamKeyValue
1. This is the only function in the package with Get prefix
   that does not read a file (but parses a string). Rename
   accordingly, and convert the callers.

	GetCgroupParamKeyValue -> ParseKeyValue

2. Use strings.Split rather than strings.Fields. Split by a space
   is 2x faster, plus we can limit the splitting. The downside is
   we have to strip a newline in one of the callers.

3. Improve the doc and the code flow.

4. Fix a test case with invalid data (spaces at BOL).

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-02-23 16:11:55 -08:00
Kir Kolyshkin 1880d2fc05 libct/cg/fs/memory: handle EBUSY
EBUSY when trying to set memory limit may mean the new limit is too low
(lower than the current usage, and the kernel can't do anything).
Provide a more specific error for such case.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-02-23 16:11:55 -08:00
Kir Kolyshkin 27fd3fc3ce libct/cg/fs: setMemoryAndSwap: refactor
1. Factor out setMemory and setSwap
2. Pass cgroup.Resources (rather than cgroup) to setMemoryAndSwap().
3. Merge the duplicated "set memory, set swap" case.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-02-23 16:11:55 -08:00
Kir Kolyshkin 3cced523a5 libct/cg/fs/memory: optimize Set
Currently, we read and parse 5 different files while we only need 1.

Use GetCgroupParamUint() directly to get current limit.

While at it, remove the workaround previously needed for the unit test,
and make it a bit more verbose.

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2021-02-23 16:11:55 -08:00