aboutsummaryrefslogtreecommitdiffstats
path: root/mm
diff options
context:
space:
mode:
authorVladimir Davydov <vdavydov@virtuozzo.com>2016-08-11 18:33:00 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2016-08-11 19:58:13 -0400
commit1f47b61fb4077936465dcde872a4e5cc4fe708da (patch)
treecc16f86163ea1f672d4264ad9168a01359315437 /mm
parent2f95ff90b99600f53df4a0aa652322d349d67957 (diff)
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. [hannes@cmpxchg.org: add comment to mem_cgroup_swapout] [vdavydov@virtuozzo.com: use WARN_ON_ONCE() in mem_cgroup_id_get_online()] Link: http://lkml.kernel.org/r/20160803123445.GJ13263@esperanza Fixes: 73f576c04b941 ("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 <vdavydov@virtuozzo.com> Acked-by: Johannes Weiner <hannes@cmpxchg.org> Acked-by: Michal Hocko <mhocko@suse.com> Cc: <stable@vger.kernel.org> [3.19+] Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm')
-rw-r--r--mm/memcontrol.c44
1 files changed, 38 insertions, 6 deletions
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index e74d7080ec9e..791b00ca4e8b 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -4082,6 +4082,24 @@ static void mem_cgroup_id_get(struct mem_cgroup *memcg)
4082 atomic_inc(&memcg->id.ref); 4082 atomic_inc(&memcg->id.ref);
4083} 4083}
4084 4084
4085static 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
4085static void mem_cgroup_id_put(struct mem_cgroup *memcg) 4103static void mem_cgroup_id_put(struct mem_cgroup *memcg)
4086{ 4104{
4087 if (atomic_dec_and_test(&memcg->id.ref)) { 4105 if (atomic_dec_and_test(&memcg->id.ref)) {
@@ -5800,7 +5818,7 @@ subsys_initcall(mem_cgroup_init);
5800 */ 5818 */
5801void mem_cgroup_swapout(struct page *page, swp_entry_t entry) 5819void mem_cgroup_swapout(struct page *page, swp_entry_t entry)
5802{ 5820{
5803 struct mem_cgroup *memcg; 5821 struct mem_cgroup *memcg, *swap_memcg;
5804 unsigned short oldid; 5822 unsigned short oldid;
5805 5823
5806 VM_BUG_ON_PAGE(PageLRU(page), page); 5824 VM_BUG_ON_PAGE(PageLRU(page), page);
@@ -5815,16 +5833,27 @@ void mem_cgroup_swapout(struct page *page, swp_entry_t entry)
5815 if (!memcg) 5833 if (!memcg)
5816 return; 5834 return;
5817 5835
5818 mem_cgroup_id_get(memcg); 5836 /*
5819 oldid = swap_cgroup_record(entry, mem_cgroup_id(memcg)); 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));
5820 VM_BUG_ON_PAGE(oldid, page); 5843 VM_BUG_ON_PAGE(oldid, page);
5821 mem_cgroup_swap_statistics(memcg, true); 5844 mem_cgroup_swap_statistics(swap_memcg, true);
5822 5845
5823 page->mem_cgroup = NULL; 5846 page->mem_cgroup = NULL;
5824 5847
5825 if (!mem_cgroup_is_root(memcg)) 5848 if (!mem_cgroup_is_root(memcg))
5826 page_counter_uncharge(&memcg->memory, 1); 5849 page_counter_uncharge(&memcg->memory, 1);
5827 5850
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
5828 /* 5857 /*
5829 * Interrupts should be disabled here because the caller holds the 5858 * Interrupts should be disabled here because the caller holds the
5830 * mapping->tree_lock lock which is taken with interrupts-off. It is 5859 * 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)
5863 if (!memcg) 5892 if (!memcg)
5864 return 0; 5893 return 0;
5865 5894
5895 memcg = mem_cgroup_id_get_online(memcg);
5896
5866 if (!mem_cgroup_is_root(memcg) && 5897 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);
5868 return -ENOMEM; 5900 return -ENOMEM;
5901 }
5869 5902
5870 mem_cgroup_id_get(memcg);
5871 oldid = swap_cgroup_record(entry, mem_cgroup_id(memcg)); 5903 oldid = swap_cgroup_record(entry, mem_cgroup_id(memcg));
5872 VM_BUG_ON_PAGE(oldid, page); 5904 VM_BUG_ON_PAGE(oldid, page);
5873 mem_cgroup_swap_statistics(memcg, true); 5905 mem_cgroup_swap_statistics(memcg, true);