diff --git a/block/blk-cgroup.c b/block/blk-cgroup.c
index 37e6cc91d576..058393e7665a 100644
--- a/block/blk-cgroup.c
+++ b/block/blk-cgroup.c
@@ -1200,7 +1200,7 @@ static int blkcg_print_stat(struct seq_file *sf, void *v)
 	if (!seq_css(sf)->parent)
 		blkcg_fill_root_iostats();
 	else
-		cgroup_rstat_flush(blkcg->css.cgroup);
+		cgroup_rstat_flush_relaxed(blkcg->css.cgroup, true);
 
 	rcu_read_lock();
 	hlist_for_each_entry_rcu(blkg, &blkcg->blkg_list, blkcg_node) {
diff --git a/include/linux/cgroup.h b/include/linux/cgroup.h
index 2150ca60394b..ff65bc100ca5 100644
--- a/include/linux/cgroup.h
+++ b/include/linux/cgroup.h
@@ -691,6 +691,7 @@ void cgroup_rstat_updated(struct cgroup *cgrp, int cpu);
 void cgroup_rstat_flush(struct cgroup *cgrp);
 void cgroup_rstat_flush_hold(struct cgroup *cgrp);
 void cgroup_rstat_flush_release(struct cgroup *cgrp);
+int cgroup_rstat_flush_relaxed(struct cgroup *cgrp, bool wait_for_flush);
 
 /*
  * Basic resource stats.
diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h
index 030d34e9d117..7e24c5e1327f 100644
--- a/include/linux/memcontrol.h
+++ b/include/linux/memcontrol.h
@@ -1026,6 +1026,7 @@ unsigned long lruvec_page_state_local(struct lruvec *lruvec,
 				      enum node_stat_item idx);
 
 void mem_cgroup_flush_stats(struct mem_cgroup *memcg);
+void mem_cgroup_flush_stats_relaxed(struct mem_cgroup *memcg);
 void mem_cgroup_flush_stats_ratelimited(struct mem_cgroup *memcg);
 
 void __mod_lruvec_kmem_state(void *p, enum node_stat_item idx, int val);
diff --git a/kernel/cgroup/rstat.c b/kernel/cgroup/rstat.c
index a06b45272411..80a4b949138f 100644
--- a/kernel/cgroup/rstat.c
+++ b/kernel/cgroup/rstat.c
@@ -11,6 +11,9 @@
 
 static DEFINE_SPINLOCK(cgroup_rstat_lock);
 static DEFINE_PER_CPU(raw_spinlock_t, cgroup_rstat_cpu_lock);
+static struct cgroup *cgrp_rstat_ongoing_flusher = NULL;
+static struct task_struct *cgrp_rstat_ongoing_flusher_ID = NULL;
+static DEFINE_MUTEX(cgrp_rstat_ongoing_flusher_serialize);
 
 static void cgroup_base_stat_flush(struct cgroup *cgrp, int cpu);
 
@@ -299,6 +302,68 @@ static inline void __cgroup_rstat_unlock(struct cgroup *cgrp, int cpu_in_loop)
 	spin_unlock_irq(&cgroup_rstat_lock);
 }
 
+static inline bool cgroup_is_root(struct cgroup *cgrp)
+{
+	return cgroup_parent(cgrp) == NULL;
+}
+
+/**
+ * cgroup_rstat_trylock_flusher - Trylock that checks for on ongoing flusher
+ * @cgrp: target cgroup
+ * @strict: always lock and ignore/skip ongoing flusher checks
+ *
+ * Function return value follow trylock semantics. Returning true when lock is
+ * obtained. Returning false when not locked and it detected flushing can be
+ * skipped as another ongoing flusher is taking care of the flush.
+ *
+ * For callers that depend on flush completing before returning a strict option
+ * is provided.
+ */
+static bool cgroup_rstat_trylock_flusher(struct cgroup *cgrp, bool strict)
+{
+	struct cgroup *ongoing;
+
+	if (strict)
+		goto lock;
+
+	/*
+	 * Check if ongoing flusher is already taking care of this.  Descendant
+	 * check is necessary due to cgroup v1 supporting multiple root's.
+	 */
+	ongoing = READ_ONCE(cgrp_rstat_ongoing_flusher);
+	if (ongoing && cgroup_is_descendant(cgrp, ongoing))
+		return false;
+
+	/* Grab right to be ongoing flusher */
+	if (!ongoing && cgroup_is_root(cgrp)) {
+		struct cgroup *old;
+
+		old = cmpxchg(&cgrp_rstat_ongoing_flusher, NULL, cgrp);
+		if (old) {
+			/* Lost race for being ongoing flusher */
+			if (cgroup_is_descendant(cgrp, old))
+				return false;
+		}
+		/* Due to lock yield combined with strict mode record ID */
+		WRITE_ONCE(cgrp_rstat_ongoing_flusher_ID, current);
+	}
+lock:
+	__cgroup_rstat_lock(cgrp, -1);
+
+	return true;
+}
+
+static void cgroup_rstat_unlock_flusher(struct cgroup *cgrp)
+{
+	if (cgrp == READ_ONCE(cgrp_rstat_ongoing_flusher) &&
+	    READ_ONCE(cgrp_rstat_ongoing_flusher_ID) == current) {
+		WRITE_ONCE(cgrp_rstat_ongoing_flusher_ID, NULL);
+		WRITE_ONCE(cgrp_rstat_ongoing_flusher, NULL);
+	}
+
+	__cgroup_rstat_unlock(cgrp, -1);
+}
+
 /* see cgroup_rstat_flush() */
 static void cgroup_rstat_flush_locked(struct cgroup *cgrp)
 	__releases(&cgroup_rstat_lock) __acquires(&cgroup_rstat_lock)
@@ -333,6 +398,19 @@ static void cgroup_rstat_flush_locked(struct cgroup *cgrp)
 	}
 }
 
+static int __cgroup_rstat_flush(struct cgroup *cgrp, bool strict)
+{
+	might_sleep();
+
+	if (!cgroup_rstat_trylock_flusher(cgrp, strict))
+		return false;
+
+	cgroup_rstat_flush_locked(cgrp);
+	cgroup_rstat_unlock_flusher(cgrp);
+
+	return true;
+}
+
 /**
  * cgroup_rstat_flush - flush stats in @cgrp's subtree
  * @cgrp: target cgroup
@@ -348,11 +426,49 @@ static void cgroup_rstat_flush_locked(struct cgroup *cgrp)
  */
 __bpf_kfunc void cgroup_rstat_flush(struct cgroup *cgrp)
 {
-	might_sleep();
+	__cgroup_rstat_flush(cgrp, true);
+}
 
-	__cgroup_rstat_lock(cgrp, -1);
-	cgroup_rstat_flush_locked(cgrp);
-	__cgroup_rstat_unlock(cgrp, -1);
+int cgroup_rstat_flush_relaxed(struct cgroup *cgrp, bool wait_for_flush)
+{
+	bool flushed = __cgroup_rstat_flush(cgrp, false);
+
+	if (!flushed && wait_for_flush) {
+		/*
+		 * Reaching here we know an ongoing flusher is running, that
+		 * will take care of flushing for us, but for caller to read
+		 * accurate stats we want to wait for this ongoing flusher.
+		 *
+		 * TODO: When lock becomes mutex and no-yielding this code can
+		 * be simplifed as we can just sleep on the mutex lock.
+		 */
+		struct task_struct *id, *cur_id;
+		u64 timeout;
+
+		id = READ_ONCE(cgrp_rstat_ongoing_flusher_ID);
+		timeout = jiffies_64 + msecs_to_jiffies(50);
+
+		if (!id)
+			return false;
+
+		cond_resched();
+		/* We might get lucky and flush already completed */
+		cur_id = READ_ONCE(cgrp_rstat_ongoing_flusher_ID);
+
+		/* Due to lock yield, make sure "id" flusher completes */
+		while (cur_id == id && time_before64(jiffies_64, timeout)) {
+			cond_resched();
+			/* Use mutex to reduce stress on global lock */
+			mutex_lock(&cgrp_rstat_ongoing_flusher_serialize);
+			__cgroup_rstat_lock(cgrp, -1);
+			/* Get lock with ongoing can happen due to yielding */
+			cur_id = READ_ONCE(cgrp_rstat_ongoing_flusher_ID);
+			__cgroup_rstat_unlock(cgrp, -1);
+			mutex_unlock(&cgrp_rstat_ongoing_flusher_serialize);
+		}
+	}
+
+	return flushed;
 }
 
 /**
@@ -368,8 +484,11 @@ void cgroup_rstat_flush_hold(struct cgroup *cgrp)
 	__acquires(&cgroup_rstat_lock)
 {
 	might_sleep();
-	__cgroup_rstat_lock(cgrp, -1);
-	cgroup_rstat_flush_locked(cgrp);
+
+	if (cgroup_rstat_trylock_flusher(cgrp, false))
+		cgroup_rstat_flush_locked(cgrp);
+	else
+		__cgroup_rstat_lock(cgrp, -1);
 }
 
 /**
@@ -379,7 +498,7 @@ void cgroup_rstat_flush_hold(struct cgroup *cgrp)
 void cgroup_rstat_flush_release(struct cgroup *cgrp)
 	__releases(&cgroup_rstat_lock)
 {
-	__cgroup_rstat_unlock(cgrp, -1);
+	cgroup_rstat_unlock_flusher(cgrp);
 }
 
 int cgroup_rstat_init(struct cgroup *cgrp)
diff --git a/mm/memcontrol.c b/mm/memcontrol.c
index 71fe2a95b8bd..6694f7a859b5 100644
--- a/mm/memcontrol.c
+++ b/mm/memcontrol.c
@@ -871,12 +871,26 @@ static inline void memcg_rstat_updated(struct mem_cgroup *memcg, int val)
 	}
 }
 
-static void do_flush_stats(struct mem_cgroup *memcg)
+static void do_flush_stats(struct mem_cgroup *memcg, bool wait_for_flush)
 {
-	if (mem_cgroup_is_root(memcg))
-		WRITE_ONCE(flush_last_time, jiffies_64);
+	bool flushed = cgroup_rstat_flush_relaxed(memcg->css.cgroup,
+						  wait_for_flush);
 
-	cgroup_rstat_flush(memcg->css.cgroup);
+	if (mem_cgroup_is_root(memcg) && flushed)
+		    WRITE_ONCE(flush_last_time, jiffies_64);
+}
+
+static void __mem_cgroup_flush_stats(struct mem_cgroup *memcg,
+				     bool wait_for_flush)
+{
+	if (mem_cgroup_disabled())
+		return;
+
+	if (!memcg)
+		memcg = root_mem_cgroup;
+
+	if (memcg_vmstats_needs_flush(memcg->vmstats))
+		do_flush_stats(memcg, wait_for_flush);
 }
 
 /*
@@ -890,21 +904,19 @@ static void do_flush_stats(struct mem_cgroup *memcg)
  */
 void mem_cgroup_flush_stats(struct mem_cgroup *memcg)
 {
-	if (mem_cgroup_disabled())
-		return;
-
-	if (!memcg)
-		memcg = root_mem_cgroup;
+	__mem_cgroup_flush_stats(memcg, true);
+}
 
-	if (memcg_vmstats_needs_flush(memcg->vmstats))
-		do_flush_stats(memcg);
+void mem_cgroup_flush_stats_relaxed(struct mem_cgroup *memcg)
+{
+	__mem_cgroup_flush_stats(memcg, false);
 }
 
 void mem_cgroup_flush_stats_ratelimited(struct mem_cgroup *memcg)
 {
 	/* Only flush if the periodic flusher is one full cycle late */
 	if (time_after64(jiffies_64, READ_ONCE(flush_last_time) + 2*FLUSH_TIME))
-		mem_cgroup_flush_stats(memcg);
+		mem_cgroup_flush_stats_relaxed(memcg);
 }
 
 static void flush_memcg_stats_dwork(struct work_struct *w)
@@ -913,7 +925,7 @@ static void flush_memcg_stats_dwork(struct work_struct *w)
 	 * Deliberately ignore memcg_vmstats_needs_flush() here so that flushing
 	 * in latency-sensitive paths is as cheap as possible.
 	 */
-	do_flush_stats(root_mem_cgroup);
+	do_flush_stats(root_mem_cgroup, false);
 	queue_delayed_work(system_unbound_wq, &stats_flush_dwork, FLUSH_TIME);
 }
 
@@ -8369,7 +8381,7 @@ bool obj_cgroup_may_zswap(struct obj_cgroup *objcg)
 		 * mem_cgroup_flush_stats() ignores small changes. Use
 		 * do_flush_stats() directly to get accurate stats for charging.
 		 */
-		do_flush_stats(memcg);
+		do_flush_stats(memcg, true);
 		pages = memcg_page_state(memcg, MEMCG_ZSWAP_B) / PAGE_SIZE;
 		if (pages < max)
 			continue;
diff --git a/mm/vmscan.c b/mm/vmscan.c
index 2e34de9cd0d4..ab1411545572 100644
--- a/mm/vmscan.c
+++ b/mm/vmscan.c
@@ -2247,7 +2247,7 @@ static void prepare_scan_control(pg_data_t *pgdat, struct scan_control *sc)
 	 * Flush the memory cgroup stats, so that we read accurate per-memcg
 	 * lruvec stats for heuristics.
 	 */
-	mem_cgroup_flush_stats(sc->target_mem_cgroup);
+	mem_cgroup_flush_stats_relaxed(sc->target_mem_cgroup);
 
 	/*
 	 * Determine the scan balance between anon and file LRUs.
diff --git a/mm/zswap.c b/mm/zswap.c
index a50e2986cd2f..db72afe37ba2 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -1237,7 +1237,7 @@ static unsigned long zswap_shrinker_count(struct shrinker *shrinker,
 	 * Without memcg, use the zswap pool-wide metrics.
 	 */
 	if (!mem_cgroup_disabled()) {
-		mem_cgroup_flush_stats(memcg);
+		mem_cgroup_flush_stats_relaxed(memcg);
 		nr_backing = memcg_page_state(memcg, MEMCG_ZSWAP_B) >> PAGE_SHIFT;
 		nr_stored = memcg_page_state(memcg, MEMCG_ZSWAPPED);
 	} else {
