diff --git a/include/linux/mm.h b/include/linux/mm.h
index fdd7cb8609a5..b27a79a36a57 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -2694,30 +2694,58 @@ static inline bool get_user_page_fast_only(unsigned long addr,
 /*
  * per-process(per-mm_struct) statistics.
  */
+static inline long get_mm_counter_slow(struct mm_struct *mm,
+						int member)
+{
+	long ret = atomic64_read(&mm->rss_stat[member].global);
+
+	for (int i = 0; i < _MM_NUMA_COUNTERS; i++)
+		ret += atomic64_read(&mm->rss_stat[member].numa_counters[i]);
+	return ret;
+}
+
 static inline unsigned long get_mm_counter(struct mm_struct *mm, int member)
 {
-	return percpu_counter_read_positive(&mm->rss_stat[member]);
+	s64 val = atomic64_read(&mm->rss_stat[member].global);
+
+	if (val < 0)
+		return 0;
+	return val;
 }
 
 void mm_trace_rss_stat(struct mm_struct *mm, int member);
 
+static inline void __mm_update_numa_counter(struct mm_struct *mm, int member,
+					    long value)
+{
+	size_t index = numa_node_id() % _MM_NUMA_COUNTERS;
+	struct mm_pernuma_counter *rss_stat = &mm->rss_stat[member];
+	atomic64_t *numa_counter = &rss_stat->numa_counters[index];
+	s64 val = atomic64_add_return(value, numa_counter);
+
+	if (abs(val) >= _MM_NUMA_COUNTERS_BATCH) {
+		atomic64_add(val, &rss_stat->global);
+		atomic64_add(-val, numa_counter);
+	}
+}
+
 static inline void add_mm_counter(struct mm_struct *mm, int member, long value)
 {
-	percpu_counter_add(&mm->rss_stat[member], value);
+	__mm_update_numa_counter(mm, member, value);
 
 	mm_trace_rss_stat(mm, member);
 }
 
 static inline void inc_mm_counter(struct mm_struct *mm, int member)
 {
-	percpu_counter_inc(&mm->rss_stat[member]);
+	__mm_update_numa_counter(mm, member, 1);
 
 	mm_trace_rss_stat(mm, member);
 }
 
 static inline void dec_mm_counter(struct mm_struct *mm, int member)
 {
-	percpu_counter_dec(&mm->rss_stat[member]);
+	__mm_update_numa_counter(mm, member, -1);
 
 	mm_trace_rss_stat(mm, member);
 }
diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
index aac7c87b04e1..a305fbebc8f6 100644
--- a/include/linux/mm_types.h
+++ b/include/linux/mm_types.h
@@ -889,6 +889,13 @@ struct mm_cid {
 };
 #endif
 
+#define _MM_NUMA_COUNTERS MIN(MAX_NUMNODES, 32)
+#define _MM_NUMA_COUNTERS_BATCH max(32, (num_possible_cpus() >> NODES_SHIFT) * 2)
+struct mm_pernuma_counter {
+	atomic64_t global;
+	atomic64_t numa_counters[_MM_NUMA_COUNTERS];
+};
+
 struct kioctx_table;
 struct iommu_mm_data;
 struct mm_struct {
@@ -1055,7 +1062,7 @@ struct mm_struct {
 
 		unsigned long saved_auxv[AT_VECTOR_SIZE]; /* for /proc/PID/auxv */
 
-		struct percpu_counter rss_stat[NR_MM_COUNTERS];
+		struct mm_pernuma_counter rss_stat[NR_MM_COUNTERS];
 
 		struct linux_binfmt *binfmt;
 
diff --git a/include/trace/events/kmem.h b/include/trace/events/kmem.h
index f74925a6cf69..72b8278fdb40 100644
--- a/include/trace/events/kmem.h
+++ b/include/trace/events/kmem.h
@@ -477,7 +477,7 @@ TRACE_EVENT(rss_stat,
 		__entry->mm_id = mm_ptr_to_hash(mm);
 		__entry->curr = !!(current->mm == mm);
 		__entry->member = member;
-		__entry->size = (percpu_counter_sum_positive(&mm->rss_stat[member])
+		__entry->size = (atomic64_read(&mm->rss_stat[member].global)
 							    << PAGE_SHIFT);
 	),
 
diff --git a/kernel/fork.c b/kernel/fork.c
index 83cb82643817..3de71d335022 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -838,7 +838,7 @@ static void check_mm(struct mm_struct *mm)
 			 "Please make sure 'struct resident_page_types[]' is updated as well");
 
 	for (i = 0; i < NR_MM_COUNTERS; i++) {
-		long x = percpu_counter_sum(&mm->rss_stat[i]);
+		long x = get_mm_counter_slow(mm, i);
 
 		if (unlikely(x))
 			pr_alert("BUG: Bad rss-counter state mm:%p type:%s val:%ld\n",
@@ -940,7 +940,6 @@ void __mmdrop(struct mm_struct *mm)
 	put_user_ns(mm->user_ns);
 	mm_pasid_drop(mm);
 	mm_destroy_cid(mm);
-	percpu_counter_destroy_many(mm->rss_stat, NR_MM_COUNTERS);
 
 	free_mm(mm);
 }
@@ -1327,16 +1326,10 @@ static struct mm_struct *mm_init(struct mm_struct *mm, struct task_struct *p,
 	if (mm_alloc_cid(mm, p))
 		goto fail_cid;
 
-	if (percpu_counter_init_many(mm->rss_stat, 0, GFP_KERNEL_ACCOUNT,
-				     NR_MM_COUNTERS))
-		goto fail_pcpu;
-
 	mm->user_ns = get_user_ns(user_ns);
 	lru_gen_init_mm(mm);
 	return mm;
 
-fail_pcpu:
-	mm_destroy_cid(mm);
 fail_cid:
 	destroy_context(mm);
 fail_nocontext:
