Skip to content

Commit 1f47b61

Browse files
Vladimir Davydovtorvalds
Vladimir Davydov
authored andcommitted
mm: memcontrol: fix swap counter leak on swapout from offline cgroup
An offline memory cgroup might have anonymous memory or shmem left charged to it and no swap. Since only swap entries pin the id of an offline cgroup, such a cgroup will have no id and so an attempt to swapout its anon/shmem will not store memory cgroup info in the swap cgroup map. As a result, memcg->swap or memcg->memsw will never get uncharged from it and any of its ascendants. Fix this by always charging swapout to the first ancestor cgroup that hasn't released its id yet. [[email protected]: add comment to mem_cgroup_swapout] [[email protected]: use WARN_ON_ONCE() in mem_cgroup_id_get_online()] Link: http://lkml.kernel.org/r/20160803123445.GJ13263@esperanza Fixes: 73f576c ("mm: memcontrol: fix cgroup creation failure after many small jobs") Link: http://lkml.kernel.org/r/5336daa5c9a32e776067773d9da655d2dc126491.1470219853.git.vdavydov@virtuozzo.com Signed-off-by: Vladimir Davydov <[email protected]> Acked-by: Johannes Weiner <[email protected]> Acked-by: Michal Hocko <[email protected]> Cc: <[email protected]> [3.19+] Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 2f95ff9 commit 1f47b61

File tree

1 file changed

+38
-6
lines changed

1 file changed

+38
-6
lines changed

mm/memcontrol.c

+38-6
Original file line numberDiff line numberDiff line change
@@ -4082,6 +4082,24 @@ static void mem_cgroup_id_get(struct mem_cgroup *memcg)
40824082
atomic_inc(&memcg->id.ref);
40834083
}
40844084

4085+
static struct mem_cgroup *mem_cgroup_id_get_online(struct mem_cgroup *memcg)
4086+
{
4087+
while (!atomic_inc_not_zero(&memcg->id.ref)) {
4088+
/*
4089+
* The root cgroup cannot be destroyed, so it's refcount must
4090+
* always be >= 1.
4091+
*/
4092+
if (WARN_ON_ONCE(memcg == root_mem_cgroup)) {
4093+
VM_BUG_ON(1);
4094+
break;
4095+
}
4096+
memcg = parent_mem_cgroup(memcg);
4097+
if (!memcg)
4098+
memcg = root_mem_cgroup;
4099+
}
4100+
return memcg;
4101+
}
4102+
40854103
static void mem_cgroup_id_put(struct mem_cgroup *memcg)
40864104
{
40874105
if (atomic_dec_and_test(&memcg->id.ref)) {
@@ -5800,7 +5818,7 @@ subsys_initcall(mem_cgroup_init);
58005818
*/
58015819
void mem_cgroup_swapout(struct page *page, swp_entry_t entry)
58025820
{
5803-
struct mem_cgroup *memcg;
5821+
struct mem_cgroup *memcg, *swap_memcg;
58045822
unsigned short oldid;
58055823

58065824
VM_BUG_ON_PAGE(PageLRU(page), page);
@@ -5815,16 +5833,27 @@ void mem_cgroup_swapout(struct page *page, swp_entry_t entry)
58155833
if (!memcg)
58165834
return;
58175835

5818-
mem_cgroup_id_get(memcg);
5819-
oldid = swap_cgroup_record(entry, mem_cgroup_id(memcg));
5836+
/*
5837+
* In case the memcg owning these pages has been offlined and doesn't
5838+
* have an ID allocated to it anymore, charge the closest online
5839+
* ancestor for the swap instead and transfer the memory+swap charge.
5840+
*/
5841+
swap_memcg = mem_cgroup_id_get_online(memcg);
5842+
oldid = swap_cgroup_record(entry, mem_cgroup_id(swap_memcg));
58205843
VM_BUG_ON_PAGE(oldid, page);
5821-
mem_cgroup_swap_statistics(memcg, true);
5844+
mem_cgroup_swap_statistics(swap_memcg, true);
58225845

58235846
page->mem_cgroup = NULL;
58245847

58255848
if (!mem_cgroup_is_root(memcg))
58265849
page_counter_uncharge(&memcg->memory, 1);
58275850

5851+
if (memcg != swap_memcg) {
5852+
if (!mem_cgroup_is_root(swap_memcg))
5853+
page_counter_charge(&swap_memcg->memsw, 1);
5854+
page_counter_uncharge(&memcg->memsw, 1);
5855+
}
5856+
58285857
/*
58295858
* Interrupts should be disabled here because the caller holds the
58305859
* mapping->tree_lock lock which is taken with interrupts-off. It is
@@ -5863,11 +5892,14 @@ int mem_cgroup_try_charge_swap(struct page *page, swp_entry_t entry)
58635892
if (!memcg)
58645893
return 0;
58655894

5895+
memcg = mem_cgroup_id_get_online(memcg);
5896+
58665897
if (!mem_cgroup_is_root(memcg) &&
5867-
!page_counter_try_charge(&memcg->swap, 1, &counter))
5898+
!page_counter_try_charge(&memcg->swap, 1, &counter)) {
5899+
mem_cgroup_id_put(memcg);
58685900
return -ENOMEM;
5901+
}
58695902

5870-
mem_cgroup_id_get(memcg);
58715903
oldid = swap_cgroup_record(entry, mem_cgroup_id(memcg));
58725904
VM_BUG_ON_PAGE(oldid, page);
58735905
mem_cgroup_swap_statistics(memcg, true);

0 commit comments

Comments
 (0)