[RHEL7,COMMIT] mm/memcg: close potential race of kmem unharge and reparent.

Submitted by Konstantin Khorenko on Aug. 14, 2019, 4:40 p.m.

Details

Message ID 201908141640.x7EGeUAS019997@finist-ce7.sw.ru
State New
Series "Series without cover letter"
Headers show

Commit Message

Konstantin Khorenko Aug. 14, 2019, 4:40 p.m.
The commit is pushed to "branch-rh7-3.10.0-957.27.2.vz7.107.x-ovz" and will appear at https://src.openvz.org/scm/ovz/vzkernel.git
after rh7-3.10.0-957.27.2.vz7.107.3
------>
commit 4a279f9156ef9dbbc637924e5a46397db3e7a42b
Author: Andrey Ryabinin <aryabinin@virtuozzo.com>
Date:   Wed Aug 14 19:40:29 2019 +0300

    mm/memcg: close potential race of kmem unharge and reparent.
    
    Make sure that we uncharge ->kmem first, so that mem_cgroup_reparent_charges()
    won't see false positive ->memory <= ->kmem.
    
    Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
    
    khorenko@: note, this is not only about false positive warning we faced in
    PSBM-97012, this is also about theoretical possibility of stopping reparenting
    too early due to the race.
---
 mm/memcontrol.c | 17 ++++++++++++-----
 1 file changed, 12 insertions(+), 5 deletions(-)

Patch hide | download patch | download mbox

diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 435fdcf8c141..464b6116c62e 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -3206,13 +3206,13 @@  static int try_charge(struct mem_cgroup *memcg, gfp_t gfp_mask, bool kmem_charge
 	 * successful charge implies full memory barrier.
 	 */
 	if (unlikely(memcg->is_offline)) {
-		page_counter_uncharge(&memcg->memory, batch);
-		if (do_swap_account)
-			page_counter_uncharge(&memcg->memsw, batch);
 		if (kmem_charge) {
 			WARN_ON_ONCE(1);
 			page_counter_uncharge(&memcg->kmem, nr_pages);
 		}
+		page_counter_uncharge(&memcg->memory, batch);
+		if (do_swap_account)
+			page_counter_uncharge(&memcg->memsw, batch);
 
 		goto bypass;
 	}
@@ -4380,6 +4380,13 @@  static void mem_cgroup_reparent_charges(struct mem_cgroup *memcg)
 		 * charge before adding to the LRU.
 		 */
 		mem = page_counter_read(&memcg->memory);
+
+		/*
+		 * Make sure that we won't see ->memory uncharge before ->kmem uncharge,
+		 * see uncharge_batch(), memcg_uncharge_kmem(). Pairing barrier provided
+		 * by page_counter_ucharge()->page_counter_cancel()->atomic_long_sub_return().
+		 */
+		smp_rmb();
 		kmem = page_counter_read(&memcg->kmem);
 	} while ((mem - kmem > 0) && time_before(jiffies, timeout));
 
@@ -7582,12 +7589,12 @@  static void uncharge_batch(struct mem_cgroup *memcg, unsigned long pgpgout,
 	unsigned long flags;
 
 	if (!mem_cgroup_is_root(memcg)) {
+		if (nr_kmem)
+			page_counter_uncharge(&memcg->kmem, nr_kmem);
 		if (nr_mem + nr_kmem)
 			page_counter_uncharge(&memcg->memory, nr_mem + nr_kmem);
 		if (nr_memsw + nr_kmem)
 			page_counter_uncharge(&memcg->memsw, nr_memsw + nr_kmem);
-		if (nr_kmem)
-			page_counter_uncharge(&memcg->kmem, nr_kmem);
 		if (nr_file)
 			page_counter_uncharge(&memcg->cache, nr_file - nr_shmem);