spiegel_podman/pkg
Matthew Heon ebacfbd091 podman: fix memleak caused by renaming and not deleting
the exit file

If the container exit code needs to be retained, it cannot be retained
in tmpfs, because libpod runs in a memcg itself so it can't leave
traces with a daemon-less design.

This wasn't a memleak detectable by kmemleak for example. The kernel
never lost track of the memory and there was no erroneous refcounting
either. The reference count dependencies however are not easy to track
because when a refcount is increased, there's no way to tell who's
still holding the reference. In this case it was a single page of
tmpfs pagecache holding a refcount that kept pinned a whole hierarchy
of dying memcg, slab kmem, cgropups, unrechable kernfs nodes and the
respective dentries and inodes. Such a problem wouldn't happen if the
exit file was stored in a regular filesystem because the pagecache
could be reclaimed in such case under memory pressure. The tmpfs page
can be swapped out, but that's not enough to release the memcg with
CONFIG_MEMCG_SWAP_ENABLED=y.

No amount of more aggressive kernel slab shrinking could have solved
this. Not even assigning slab kmem of dying cgroups to alive cgroup
would fully solve this. The only way to free the memory of a dying
cgroup when a struct page still references it, would be to loop over
all "struct page" in the kernel to find which one is associated with
the dying cgroup which is a O(N) operation (where N is the number of
pages and can reach billions). Linking all the tmpfs pages to the
memcg would cost less during memcg offlining, but it would waste lots
of memory and CPU globally. So this can't be optimized in the kernel.

A cronjob running this command can act as workaround and will allow
all slab cache to be released, not just the single tmpfs pages.

    rm -f /run/libpod/exits/*

This patch solved the memleak with a reproducer, booting with
cgroup.memory=nokmem and with selinux disabled. The reason memcg kmem
and selinux were disabled for testing of this fix, is because kmem
greatly decreases the kernel effectiveness in reusing partial slab
objects. cgroup.memory=nokmem is strongly recommended at least for
workstation usage. selinux needs to be further analyzed because it
causes further slab allocations.

The upstream podman commit used for testing is
1fe2965e4f (v1.4.4).

The upstream kernel commit used for testing is
f16fea666898dbdd7812ce94068c76da3e3fcf1e (v5.2-rc6).

Reported-by: Michele Baldessari <michele@redhat.com>
Signed-off-by: Andrea Arcangeli <aarcange@redhat.com>

<Applied with small tweaks to comments>
Signed-off-by: Matthew Heon <matthew.heon@pm.me>
2019-07-31 17:28:42 -04:00
..
adapter podman: fix memleak caused by renaming and not deleting 2019-07-31 17:28:42 -04:00
annotations Update cri-o annotations 2019-04-01 11:10:54 +02:00
apparmor golangci-lint round #3 2019-07-21 14:22:39 -05:00
cgroups golangci-lint round #3 2019-07-21 14:22:39 -05:00
channelwriter account for varlink calls that dont use more 2019-07-10 08:50:05 -05:00
criu Add helper function to read out CRIU version 2018-10-23 12:52:03 +02:00
ctime Re-add int64 casts for ctime 2019-07-23 15:43:40 -04:00
errorhandling first pass of corrections for golangci-lint 2019-07-10 15:52:17 -05:00
firewall golangci-lint round #3 2019-07-21 14:22:39 -05:00
hooks Improved hooks monitoring 2019-07-25 09:52:45 -04:00
inspect Begin to break up pkg/inspect 2019-06-03 15:54:53 -04:00
kubeutils Remove dependency on kubernetes 2018-03-27 21:55:33 +00:00
logs first pass of corrections for golangci-lint 2019-07-10 15:52:17 -05:00
lookup pkg/lookup: Return ID-only pointers on ErrNo*Entries 2018-12-04 14:46:43 -08:00
namespaces podman: support --userns=ns|container 2019-07-25 23:04:55 +02:00
netns golangci-lint pass number 2 2019-07-11 09:13:06 -05:00
registrar Update registrar unit tests to match them of cri-o 2019-04-04 08:53:32 +02:00
registries Use the logical registry location instead of the physical one in (podman info) 2019-06-14 15:46:17 +02:00
resolvconf Use host's resolv.conf if no network namespace enabled 2018-11-27 15:28:09 -05:00
rootless golangci-lint round #3 2019-07-21 14:22:39 -05:00
spec podman: support --userns=ns|container 2019-07-25 23:04:55 +02:00
sysinfo golangci-lint round #3 2019-07-21 14:22:39 -05:00
systemdgen Do not hardcode podman binary location in generate systemd. 2019-07-07 11:20:41 +03:00
tracing trivial cleanups from golang 2019-07-03 15:41:33 -05:00
trust trivial cleanups from golang 2019-07-03 15:41:33 -05:00
util golangci-lint phase 4 2019-07-22 15:44:04 -05:00
varlinkapi refactor to reduce duplicated error parsing 2019-07-23 16:49:04 -04:00