diff --git a/mm/slab.h b/mm/slab.h
index 54deeb0428c6..a0e7672ba648 100644
--- a/mm/slab.h
+++ b/mm/slab.h
@@ -11,6 +11,7 @@
 #include <linux/memcontrol.h>
 #include <linux/kfence.h>
 #include <linux/kasan.h>
+#include <linux/percpu_counter.h>
 
 /*
  * Internal slab definitions
@@ -277,6 +278,9 @@ struct kmem_cache {
 	unsigned int red_left_pad;	/* Left redzone padding size */
 	const char *name;		/* Name (only for display!) */
 	struct list_head list;		/* List of slab caches */
+#ifdef CONFIG_SLUB_DEBUG
+	struct percpu_counter free_objects;
+#endif
 #ifdef CONFIG_SYSFS
 	struct kobject kobj;		/* For sysfs */
 #endif
diff --git a/mm/slub.c b/mm/slub.c
index 2ef88bbf56a3..44f8ded96574 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -736,6 +736,12 @@ static inline bool slab_update_freelist(struct kmem_cache *s, struct slab *slab,
 static unsigned long object_map[BITS_TO_LONGS(MAX_OBJS_PER_PAGE)];
 static DEFINE_SPINLOCK(object_map_lock);
 
+static inline void
+__update_kmem_cache_free_objs(struct kmem_cache *s, s64 delta)
+{
+	percpu_counter_add_batch(&s->free_objects, delta, INT_MAX);
+}
+
 static void __fill_map(unsigned long *obj_map, struct kmem_cache *s,
 		       struct slab *slab)
 {
@@ -1829,6 +1835,9 @@ slab_flags_t kmem_cache_flags(unsigned int object_size,
 	return flags | slub_debug_local;
 }
 #else /* !CONFIG_SLUB_DEBUG */
+static inline void
+__update_kmem_cache_free_objs(struct kmem_cache *s, s64 delta) {}
+
 static inline void setup_object_debug(struct kmem_cache *s, void *object) {}
 static inline
 void setup_slab_debug(struct kmem_cache *s, struct slab *slab, void *addr) {}
@@ -2369,6 +2378,7 @@ static struct slab *allocate_slab(struct kmem_cache *s, gfp_t flags, int node)
 	slab->inuse = 0;
 	slab->frozen = 0;
 
+	__update_kmem_cache_free_objs(s, slab->objects);
 	account_slab(slab, oo_order(oo), s, flags);
 
 	slab->slab_cache = s;
@@ -2445,6 +2455,7 @@ static void free_slab(struct kmem_cache *s, struct slab *slab)
 		call_rcu(&slab->rcu_head, rcu_free_slab);
 	else
 		__free_slab(s, slab);
+	__update_kmem_cache_free_objs(s, -slab->objects);
 }
 
 static void discard_slab(struct kmem_cache *s, struct slab *slab)
@@ -3859,6 +3870,8 @@ static __fastpath_inline void *slab_alloc_node(struct kmem_cache *s, struct list
 	 */
 	slab_post_alloc_hook(s, objcg, gfpflags, 1, &object, init, orig_size);
 
+	if (object)
+		__update_kmem_cache_free_objs(s, -1);
 	return object;
 }
 
@@ -4235,6 +4248,7 @@ static __always_inline void do_slab_free(struct kmem_cache *s,
 	unsigned long tid;
 	void **freelist;
 
+	__update_kmem_cache_free_objs(s, cnt);
 redo:
 	/*
 	 * Determine the currently cpus per cpu slab.
@@ -4286,6 +4300,7 @@ static void do_slab_free(struct kmem_cache *s,
 				struct slab *slab, void *head, void *tail,
 				int cnt, unsigned long addr)
 {
+	__update_kmem_cache_free_objs(s, cnt);
 	__slab_free(s, slab, head, tail, cnt, addr);
 }
 #endif /* CONFIG_SLUB_TINY */
@@ -4658,6 +4673,7 @@ int kmem_cache_alloc_bulk(struct kmem_cache *s, gfp_t flags, size_t size,
 		memcg_slab_alloc_error_hook(s, size, objcg);
 	}
 
+	__update_kmem_cache_free_objs(s, -i);
 	return i;
 }
 EXPORT_SYMBOL(kmem_cache_alloc_bulk);
@@ -4899,6 +4915,9 @@ void __kmem_cache_release(struct kmem_cache *s)
 	cache_random_seq_destroy(s);
 #ifndef CONFIG_SLUB_TINY
 	free_percpu(s->cpu_slab);
+#endif
+#ifdef CONFIG_SLUB_DEBUG
+	percpu_counter_destroy(&s->free_objects);
 #endif
 	free_kmem_cache_nodes(s);
 }
@@ -5109,6 +5128,14 @@ static int kmem_cache_open(struct kmem_cache *s, slab_flags_t flags)
 	s->random = get_random_long();
 #endif
 
+#ifdef CONFIG_SLUB_DEBUG
+	int ret;
+
+	ret = percpu_counter_init(&s->free_objects, 0, GFP_KERNEL);
+	if (ret)
+		return ret;
+#endif
+
 	if (!calculate_sizes(s))
 		goto error;
 	if (disable_higher_order_debug) {
@@ -7100,15 +7127,15 @@ void get_slabinfo(struct kmem_cache *s, struct slabinfo *sinfo)
 {
 	unsigned long nr_slabs = 0;
 	unsigned long nr_objs = 0;
-	unsigned long nr_free = 0;
+	unsigned long nr_free;
 	int node;
 	struct kmem_cache_node *n;
 
 	for_each_kmem_cache_node(s, node, n) {
 		nr_slabs += node_nr_slabs(n);
 		nr_objs += node_nr_objs(n);
-		nr_free += count_partial(n, count_free);
 	}
+	nr_free = percpu_counter_sum_positive(&s->free_objects);
 
 	sinfo->active_objs = nr_objs - nr_free;
 	sinfo->num_objs = nr_objs;
