Skip to contentSkip to conversation

mm: Implement Slab Movable Objects (SMO)

RFC Superseded Tobin C. Harding 5 participants 41 messages Mar 8, 2019 – Mar 12, 2019

Subsystems (5)

v1 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

cover mm: Implement Slab Movable Objects (SMO)

Hi,

Here is a patch set implementing movable objects within the SLUB allocator. This is work based on Christopher's patch set:

https://lore.kernel.org/patchwork/project/lkml/list/?series=377335

The original code logic is from that set and implemented by Christopher. Clean up, refactoring, documentation, and additional features by myself. Blame for any bugs remaining falls solely with myself. Patches using Christopher's code use the Co-developed-by tag.

After movable objects are implemented a number of useful features become possible. Some of these are implemented in this series, including:

- Cache defragmentation.

Currently the SLUB allocator is susceptible to internal fragmentation. This occurs when a large number of cached objects are allocated and then freed in an arbitrary order. As the cache fragments the number of pages used by the partial slabs list increases. This wastes memory.

Patch set implements the machinery to facilitate conditional cache defragmentation (via kmem_cache_defrag()) and unconditional defragmentation (via kmem_cache_shrink()). Various sysfs knobs are provided to interact with and configure this.

Patch set implements movable objects and cache defragmentation for the XArray.

- Moving objects to and from a specific NUMA node.

- Balancing objects across all NUMA nodes.

We add a test module to facilitate playing around with movable objects and a python test suite that uses the module.

Everything except the NUMA stuff was tested on bare metal, the NUMA stuff was tested with Qemu NUMA emulation.

Possible further work:

1. Implementing movable objects for the inode and dentry caches.

2. Tying into the page migration and page defragmentation logic so that so far unmovable pages that are in the way of creating a contiguous block of memory will become movable. This would mean checking for slab pages in the migration logic and calling slab to see if it can move the page by migrating all objects.

Patch 1-4 - Implement Slab Movable Objects.
Patch 5-9 - Implement slab cache defragmentation.
Patch 10 - Adds the test module.
Patch 11 - Adds the test suite.
Patch 12-13 - Adds object migration to the XArray (and test code). Patch 14 - Adds moving objects to and from a specified NUMA node. Patch 15 - Adds object balancing across all NUMA nodes.

Patch 12 introduces an build warning, I tried a bunch of things and I couldn't work out what it should be.

linux/lib/xarray.c:1961:16: warning: comparison between pointer and zero character constant [-Wpointer-compare]

    if (!xa || xa == XA_FREE_MARK)
                ^~
  linux/lib/xarray.c:1961:13: note: did you mean to dereference the pointer?
    if (!xa || xa == XA_FREE_MARK)

Perhaps you will put me out of my misery Willy and just tell me what its supposed to be.

Patch 14 and 15 are particularly early stage (I hacked those :)

thanks,
Tobin.

Tobin C. Harding (15):
slub: Create sysfs field /sys/slab/<cache>/ops
slub: Add isolate() and migrate() methods tools/vm/slabinfo: Add support for -C and -F options
slub: Enable Slab Movable Objects (SMO)
slub: Sort slab cache list
tools/vm/slabinfo: Add remote node defrag ratio output
slub: Add defrag_used_ratio field and sysfs support tools/vm/slabinfo: Add defrag_used_ratio output
slub: Enable slab defragmentation using SMO tools/testing/slab: Add object migration test module tools/testing/slab: Add object migration test suite
xarray: Implement migration function for objects tools/testing/slab: Add XArray movable objects tests
slub: Enable move _all_ objects to node
slub: Enable balancing slab objects across nodes

 Documentation/ABI/testing/sysfs-kernel-slab |  14 +
 include/linux/slab.h                        |  70 ++
 include/linux/slub_def.h                    |  10 +
 lib/radix-tree.c                            |  13 +
 lib/xarray.c                                |  44 ++
 mm/Kconfig                                  |   7 +
 mm/slab_common.c                            |   6 +-
 mm/slub.c                                   | 800 ++++++++++++++++++--
 tools/testing/slab/Makefile                 |  10 +
 tools/testing/slab/slub_defrag.c            | 567 ++++++++++++++
 tools/testing/slab/slub_defrag.py           | 451 +++++++++++
 tools/testing/slab/slub_defrag_xarray.c     | 211 ++++++
 tools/vm/slabinfo.c                         |  51 +-
 13 files changed, 2172 insertions(+), 82 deletions(-)
 create mode 100644 tools/testing/slab/Makefile
 create mode 100644 tools/testing/slab/slub_defrag.c
 create mode 100755 tools/testing/slab/slub_defrag.py
 create mode 100644 tools/testing/slab/slub_defrag_xarray.c
Signature · 1 line
2.21.0
#1 lore raw

RFC 1/15 slub: Create sysfs field /sys/slab/<cache>/ops

Create an ops field in /sys/slab/*/ops to contain all the callback operations defined for a slab cache. This will be used to display the additional callbacks that will be defined soon to enable movable objects.

Display the existing ctor callback in the ops fields contents.

---
 mm/slub.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)
mm/slub.c +9 -4
index dc777761b6b7..69164aa7cbbf 100644
@@ -5009,13 +5009,18 @@ static ssize_t cpu_partial_store(struct kmem_cache *s, const char *buf,
 }
 SLAB_ATTR(cpu_partial);
 
-static ssize_t ctor_show(struct kmem_cache *s, char *buf)
+static ssize_t ops_show(struct kmem_cache *s, char *buf)
 {
+	int x = 0;
+
 	if (!s->ctor)
 		return 0;
-	return sprintf(buf, "%pS\n", s->ctor);
+
+	if (s->ctor)
+		x += sprintf(buf + x, "ctor : %pS\n", s->ctor);
+	return x;
 }
-SLAB_ATTR_RO(ctor);
+SLAB_ATTR_RO(ops);
 
 static ssize_t aliases_show(struct kmem_cache *s, char *buf)
 {
@@ -5428,7 +5433,7 @@ static struct attribute *slab_attrs[] = {
 	&objects_partial_attr.attr,
 	&partial_attr.attr,
 	&cpu_slabs_attr.attr,
-	&ctor_attr.attr,
+	&ops_attr.attr,
 	&aliases_attr.attr,
 	&align_attr.attr,
 	&hwcache_align_attr.attr,
Signature · 1 line
2.21.0
#2 lore raw

Re: [RFC 01/15] slub: Create sysfs field /sys/slab/<cache>/ops

Quoted 9 lines · Tobin C. Harding

Create an ops field in /sys/slab/*/ops to contain all the callback operations defined for a slab cache. This will be used to display the additional callbacks that will be defined soon to enable movable objects.

Display the existing ctor callback in the ops fields contents.

Hi Tobin!

Quoted 9 lines
---
 mm/slub.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)
mm/slub.c +0 -0
index dc777761b6b7..69164aa7cbbf 100644

mm/slub.c static ssize_t cpu_partial_store(struct kmem_cache *s, const char *buf, · line 5017

Quoted 10 lines
mm/slub.c +3 -1
@@ -5009,13 +5009,18 @@ static ssize_t cpu_partial_store(struct kmem_cache *s, const char *buf,
 }
 SLAB_ATTR(cpu_partial);
 
-static ssize_t ctor_show(struct kmem_cache *s, char *buf)
+static ssize_t ops_show(struct kmem_cache *s, char *buf)
 {
+	int x = 0;
+
 	if (!s->ctor)
 		return 0;
        ^^^^^^^^^^^^^^^^^

You can drop this part, can't you?

Also, it's not clear (without looking into following patches) why do you make this refactoring. So, please, add a note, or move this change into the patch 3.

Thanks!

#3 lore raw

Re: [RFC 01/15] slub: Create sysfs field /sys/slab/<cache>/ops

Quoted 33 lines · Roman Gushchin
Quoted 9 lines · Tobin C. Harding

Create an ops field in /sys/slab/*/ops to contain all the callback operations defined for a slab cache. This will be used to display the additional callbacks that will be defined soon to enable movable objects.

Display the existing ctor callback in the ops fields contents.

Hi Tobin!

Quoted 9 lines
---
 mm/slub.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)
mm/slub.c +0 -0
index dc777761b6b7..69164aa7cbbf 100644

mm/slub.c static ssize_t cpu_partial_store(struct kmem_cache *s, const char *buf, · line 5017

Quoted 10 lines
mm/slub.c +3 -1
@@ -5009,13 +5009,18 @@ static ssize_t cpu_partial_store(struct kmem_cache *s, const char *buf,
 }
 SLAB_ATTR(cpu_partial);
 
-static ssize_t ctor_show(struct kmem_cache *s, char *buf)
+static ssize_t ops_show(struct kmem_cache *s, char *buf)
 {
+	int x = 0;
+
 	if (!s->ctor)
 		return 0;
        ^^^^^^^^^^^^^^^^^
Quoted 5 lines

You can drop this part, can't you?

Also, it's not clear (without looking into following patches) why do you make this refactoring. So, please, add a note, or move this change into the patch 3.

No worries, thanks for looking at this. Will amend the commit message and drop the constructor check.

thanks,
Tobin

#4 lore raw

RFC 2/15 slub: Add isolate() and migrate() methods

In reply to Tobin C. Harding

Add the two methods needed for moving objects and enable the display of the callbacks via the /sys/kernel/slab interface.

Add documentation explaining the use of these methods and the prototypes for slab.h. Add functions to setup the callbacks method for a slab cache.

Add empty functions for SLAB/SLOB. The API is generic so it could be theoretically implemented for these allocators as well.

---
 include/linux/slab.h     | 69 ++++++++++++++++++++++++++++++++++++++++
 include/linux/slub_def.h |  3 ++
 mm/slab_common.c         |  4 +++
 mm/slub.c                | 42 ++++++++++++++++++++++++
 4 files changed, 118 insertions(+)
include/linux/slab.h +69 -0
index 11b45f7ae405..22e87c41b8a4 100644
@@ -152,6 +152,75 @@ void memcg_create_kmem_cache(struct mem_cgroup *, struct kmem_cache *);
 void memcg_deactivate_kmem_caches(struct mem_cgroup *);
 void memcg_destroy_kmem_caches(struct mem_cgroup *);
 
+/*
+ * Function prototypes passed to kmem_cache_setup_mobility() to enable
+ * mobile objects and targeted reclaim in slab caches.
+ */
+
+/**
+ * typedef kmem_cache_isolate_func - Object migration callback function.
+ * @s: The cache we are working on.
+ * @ptr: Pointer to an array of pointers to the objects to migrate.
+ * @nr: Number of objects in array.
+ *
+ * The purpose of kmem_cache_isolate_func() is to pin each object so that
+ * they cannot be freed until kmem_cache_migrate_func() has processed
+ * them. This may be accomplished by increasing the refcount or setting
+ * a flag.
+ *
+ * The object pointer array passed is also passed to
+ * kmem_cache_migrate_func().  The function may remove objects from the
+ * array by setting pointers to NULL. This is useful if we can determine
+ * that an object is being freed because kmem_cache_isolate_func() was
+ * called when the subsystem was calling kmem_cache_free().  In that
+ * case it is not necessary to increase the refcount or specially mark
+ * the object because the release of the slab lock will lead to the
+ * immediate freeing of the object.
+ *
+ * Context: Called with locks held so that the slab objects cannot be
+ *          freed.  We are in an atomic context and no slab operations
+ *          may be performed.
+ * Return: A pointer that is passed to the migrate function. If any
+ *         objects cannot be touched at this point then the pointer may
+ *         indicate a failure and then the migration function can simply
+ *         remove the references that were already obtained. The private
+ *         data could be used to track the objects that were already pinned.
+ */
+typedef void *kmem_cache_isolate_func(struct kmem_cache *s, void **ptr, int nr);
+
+/**
+ * typedef kmem_cache_migrate_func - Object migration callback function.
+ * @s: The cache we are working on.
+ * @ptr: Pointer to an array of pointers to the objects to migrate.
+ * @nr: Number of objects in array.
+ * @node: The NUMA node where the object should be allocated.
+ * @private: The pointer returned by kmem_cache_isolate_func().
+ *
+ * This function is responsible for migrating objects.  Typically, for
+ * each object in the input array you will want to allocate an new
+ * object, copy the original object, update any pointers, and free the
+ * old object.
+ *
+ * After this function returns all pointers to the old object should now
+ * point to the new object.
+ *
+ * Context: Called with no locks held and interrupts enabled.  Sleeping
+ *          is possible.  Any operation may be performed.
+ */
+typedef void kmem_cache_migrate_func(struct kmem_cache *s, void **ptr,
+				     int nr, int node, void *private);
+
+/*
+ * kmem_cache_setup_mobility() is used to setup callbacks for a slab cache.
+ */
+#ifdef CONFIG_SLUB
+void kmem_cache_setup_mobility(struct kmem_cache *, kmem_cache_isolate_func,
+			       kmem_cache_migrate_func);
+#else
+static inline void kmem_cache_setup_mobility(struct kmem_cache *s,
+	kmem_cache_isolate_func isolate, kmem_cache_migrate_func migrate) {}
+#endif
+
 /*
  * Please use this macro to create slab caches. Simply specify the
  * name of the structure and maybe some flags that are listed above.
include/linux/slub_def.h +3 -0
index 3a1a1dbc6f49..a7340a1ed5dc 100644
@@ -99,6 +99,9 @@ struct kmem_cache {
 	gfp_t allocflags;	/* gfp flags to use on each alloc */
 	int refcount;		/* Refcount for slab cache destroy */
 	void (*ctor)(void *);
+	kmem_cache_isolate_func *isolate;
+	kmem_cache_migrate_func *migrate;
+
 	unsigned int inuse;		/* Offset to metadata */
 	unsigned int align;		/* Alignment */
 	unsigned int red_left_pad;	/* Left redzone padding size */
mm/slab_common.c +4 -0
index f9d89c1b5977..754acdb292e4 100644
@@ -298,6 +298,10 @@ int slab_unmergeable(struct kmem_cache *s)
 	if (!is_root_cache(s))
 		return 1;
 
+	/*
+	 * s->isolate and s->migrate imply s->ctor so no need to
+	 * check them explicitly.
+	 */
 	if (s->ctor)
 		return 1;
 
mm/slub.c +42 -0

54 lines not on this page · raw message

Signature · 1 line
2.21.0
#5 lore raw

Re: [RFC 02/15] slub: Add isolate() and migrate() methods

mm/slab_common.c int slab_unmergeable(struct kmem_cache *s) · line 304

Quoted 12 lines · Tobin C. Harding
mm/slab_common.c +4 -0
index f9d89c1b5977..754acdb292e4 100644
@@ -298,6 +298,10 @@ int slab_unmergeable(struct kmem_cache *s)
 	if (!is_root_cache(s))
 		return 1;
 
+	/*
+	 * s->isolate and s->migrate imply s->ctor so no need to
+	 * check them explicitly.
+	 */

Shouldn't this implication go the other way, i.e.

    s->ctor => s->isolate & s->migrate

?

mm/slab_common.c int slab_unmergeable(struct kmem_cache *s) · line 306

Quoted 2 lines
mm/slab_common.c +0 -0
 	if (s->ctor)
 		return 1;

Tycho

#6 lore raw

Re: [RFC 02/15] slub: Add isolate() and migrate() methods

Quoted 9 lines · Tycho Andersen
Quoted 8 lines · Tobin C. Harding
mm/slab_common.c +0 -0
index f9d89c1b5977..754acdb292e4 100644
@@ -298,6 +298,10 @@ int slab_unmergeable(struct kmem_cache *s)
 	if (!is_root_cache(s))
 		return 1;
 
Quoted 7 lines

mm/slab_common.c int slab_unmergeable(struct kmem_cache *s) · line 304

Quoted 4 lines
mm/slab_common.c +4 -0
+	/*
+	 * s->isolate and s->migrate imply s->ctor so no need to
+	 * check them explicitly.
+	 */

Shouldn't this implication go the other way, i.e.

    s->ctor => s->isolate & s->migrate

A cache can have a constructor but the object may not be movable (I.e. currently dentries and inodes).

#7 lore raw

Re: [RFC 02/15] slub: Add isolate() and migrate() methods

Quoted 19 lines · Christopher Lameter
Quoted 9 lines · Tycho Andersen
Quoted 8 lines · Tobin C. Harding
mm/slab_common.c +0 -0
index f9d89c1b5977..754acdb292e4 100644
@@ -298,6 +298,10 @@ int slab_unmergeable(struct kmem_cache *s)
 	if (!is_root_cache(s))
 		return 1;
 
Quoted 7 lines

mm/slab_common.c int slab_unmergeable(struct kmem_cache *s) · line 304

Quoted 4 lines
mm/slab_common.c +4 -0
+	/*
+	 * s->isolate and s->migrate imply s->ctor so no need to
+	 * check them explicitly.
+	 */

Shouldn't this implication go the other way, i.e.

    s->ctor => s->isolate & s->migrate
Quoted 2 lines

A cache can have a constructor but the object may not be movable (I.e. currently dentries and inodes).

Yep, thanks. Somehow I got confused by the comment.

Tycho

#8 lore raw

Re: [RFC 02/15] slub: Add isolate() and migrate() methods

Quoted 20 lines · Tycho Andersen
Quoted 19 lines · Christopher Lameter
Quoted 16 lines · Tycho Andersen

mm/slab_common.c int slab_unmergeable(struct kmem_cache *s) · line 304

Quoted 12 lines · Tobin C. Harding
mm/slab_common.c +4 -0
index f9d89c1b5977..754acdb292e4 100644
@@ -298,6 +298,10 @@ int slab_unmergeable(struct kmem_cache *s)
 	if (!is_root_cache(s))
 		return 1;
 
+	/*
+	 * s->isolate and s->migrate imply s->ctor so no need to
+	 * check them explicitly.
+	 */

Shouldn't this implication go the other way, i.e.

    s->ctor => s->isolate & s->migrate
Quoted 4 lines
Quoted 2 lines

A cache can have a constructor but the object may not be movable (I.e. currently dentries and inodes).

Yep, thanks. Somehow I got confused by the comment.

I removed code here from the original RFC-v2, if this comment is confusing perhaps we are better off without it.

thanks,
Tobin.

#9 lore raw

Re: [RFC 02/15] slub: Add isolate() and migrate() methods

Quoted 21 lines · Tobin C. Harding
Quoted 20 lines · Tycho Andersen
Quoted 19 lines · Christopher Lameter
Quoted 16 lines · Tycho Andersen

mm/slab_common.c int slab_unmergeable(struct kmem_cache *s) · line 304

Quoted 12 lines · Tobin C. Harding
mm/slab_common.c +4 -0
index f9d89c1b5977..754acdb292e4 100644
@@ -298,6 +298,10 @@ int slab_unmergeable(struct kmem_cache *s)
 	if (!is_root_cache(s))
 		return 1;
 
+	/*
+	 * s->isolate and s->migrate imply s->ctor so no need to
+	 * check them explicitly.
+	 */

Shouldn't this implication go the other way, i.e.

    s->ctor => s->isolate & s->migrate
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

A cache can have a constructor but the object may not be movable (I.e. currently dentries and inodes).

Yep, thanks. Somehow I got confused by the comment.

I removed code here from the original RFC-v2, if this comment is confusing perhaps we are better off without it.

I'd say leave it, unless others have objections. I got lost in the "no need" and return true for unmergable too-many-nots goop, but it's definitely worth noting that one implies the other. An alternative might be to move it to a comment on the struct member instead.

Tycho

#10 lore raw

Re: [RFC 02/15] slub: Add isolate() and migrate() methods

In reply to Tobin C. Harding
Quoted 133 lines · Tobin C. Harding

Add the two methods needed for moving objects and enable the display of the callbacks via the /sys/kernel/slab interface.

Add documentation explaining the use of these methods and the prototypes for slab.h. Add functions to setup the callbacks method for a slab cache.

Add empty functions for SLAB/SLOB. The API is generic so it could be theoretically implemented for these allocators as well.

---
 include/linux/slab.h     | 69 ++++++++++++++++++++++++++++++++++++++++
 include/linux/slub_def.h |  3 ++
 mm/slab_common.c         |  4 +++
 mm/slub.c                | 42 ++++++++++++++++++++++++
 4 files changed, 118 insertions(+)
include/linux/slab.h +69 -0
index 11b45f7ae405..22e87c41b8a4 100644
@@ -152,6 +152,75 @@ void memcg_create_kmem_cache(struct mem_cgroup *, struct kmem_cache *);
 void memcg_deactivate_kmem_caches(struct mem_cgroup *);
 void memcg_destroy_kmem_caches(struct mem_cgroup *);
 
+/*
+ * Function prototypes passed to kmem_cache_setup_mobility() to enable
+ * mobile objects and targeted reclaim in slab caches.
+ */
+
+/**
+ * typedef kmem_cache_isolate_func - Object migration callback function.
+ * @s: The cache we are working on.
+ * @ptr: Pointer to an array of pointers to the objects to migrate.
+ * @nr: Number of objects in array.
+ *
+ * The purpose of kmem_cache_isolate_func() is to pin each object so that
+ * they cannot be freed until kmem_cache_migrate_func() has processed
+ * them. This may be accomplished by increasing the refcount or setting
+ * a flag.
+ *
+ * The object pointer array passed is also passed to
+ * kmem_cache_migrate_func().  The function may remove objects from the
+ * array by setting pointers to NULL. This is useful if we can determine
+ * that an object is being freed because kmem_cache_isolate_func() was
+ * called when the subsystem was calling kmem_cache_free().  In that
+ * case it is not necessary to increase the refcount or specially mark
+ * the object because the release of the slab lock will lead to the
+ * immediate freeing of the object.
+ *
+ * Context: Called with locks held so that the slab objects cannot be
+ *          freed.  We are in an atomic context and no slab operations
+ *          may be performed.
+ * Return: A pointer that is passed to the migrate function. If any
+ *         objects cannot be touched at this point then the pointer may
+ *         indicate a failure and then the migration function can simply
+ *         remove the references that were already obtained. The private
+ *         data could be used to track the objects that were already pinned.
+ */
+typedef void *kmem_cache_isolate_func(struct kmem_cache *s, void **ptr, int nr);
+
+/**
+ * typedef kmem_cache_migrate_func - Object migration callback function.
+ * @s: The cache we are working on.
+ * @ptr: Pointer to an array of pointers to the objects to migrate.
+ * @nr: Number of objects in array.
+ * @node: The NUMA node where the object should be allocated.
+ * @private: The pointer returned by kmem_cache_isolate_func().
+ *
+ * This function is responsible for migrating objects.  Typically, for
+ * each object in the input array you will want to allocate an new
+ * object, copy the original object, update any pointers, and free the
+ * old object.
+ *
+ * After this function returns all pointers to the old object should now
+ * point to the new object.
+ *
+ * Context: Called with no locks held and interrupts enabled.  Sleeping
+ *          is possible.  Any operation may be performed.
+ */
+typedef void kmem_cache_migrate_func(struct kmem_cache *s, void **ptr,
+				     int nr, int node, void *private);
+
+/*
+ * kmem_cache_setup_mobility() is used to setup callbacks for a slab cache.
+ */
+#ifdef CONFIG_SLUB
+void kmem_cache_setup_mobility(struct kmem_cache *, kmem_cache_isolate_func,
+			       kmem_cache_migrate_func);
+#else
+static inline void kmem_cache_setup_mobility(struct kmem_cache *s,
+	kmem_cache_isolate_func isolate, kmem_cache_migrate_func migrate) {}
+#endif
+
 /*
  * Please use this macro to create slab caches. Simply specify the
  * name of the structure and maybe some flags that are listed above.
include/linux/slub_def.h +3 -0
index 3a1a1dbc6f49..a7340a1ed5dc 100644
@@ -99,6 +99,9 @@ struct kmem_cache {
 	gfp_t allocflags;	/* gfp flags to use on each alloc */
 	int refcount;		/* Refcount for slab cache destroy */
 	void (*ctor)(void *);
+	kmem_cache_isolate_func *isolate;
+	kmem_cache_migrate_func *migrate;
+
 	unsigned int inuse;		/* Offset to metadata */
 	unsigned int align;		/* Alignment */
 	unsigned int red_left_pad;	/* Left redzone padding size */
mm/slab_common.c +4 -0
index f9d89c1b5977..754acdb292e4 100644
@@ -298,6 +298,10 @@ int slab_unmergeable(struct kmem_cache *s)
 	if (!is_root_cache(s))
 		return 1;
 
+	/*
+	 * s->isolate and s->migrate imply s->ctor so no need to
+	 * check them explicitly.
+	 */
 	if (s->ctor)
 		return 1;
 
mm/slub.c +0 -0
index 69164aa7cbbf..0133168d1089 100644

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4331

Quoted 7 lines
mm/slub.c +4 -0
@@ -4325,6 +4325,34 @@ int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags)
 	return err;
 }
 
+void kmem_cache_setup_mobility(struct kmem_cache *s,
+			       kmem_cache_isolate_func isolate,
+			       kmem_cache_migrate_func migrate)
+{

I wonder if it's better to adapt kmem_cache_create() to take two additional argument? I suspect mobility is not a dynamic option, so it can be set on kmem_cache creation.

Quoted 10 lines
mm/slub.c +10 -0
+	/*
+	 * Mobile objects must have a ctor otherwise the object may be
+	 * in an undefined state on allocation.  Since the object may
+	 * need to be inspected by the migration function at any time
+	 * after allocation we must ensure that the object always has a
+	 * defined state.
+	 */
+	if (!s->ctor) {
+		pr_err("%s: cannot setup mobility without a constructor\n",
+		       s->name);

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4351

Quoted 10 lines
mm/slub.c +10 -0
+		return;
+	}
+
+	s->isolate = isolate;
+	s->migrate = migrate;
+
+	/*
+	 * Sadly serialization requirements currently mean that we have
+	 * to disable fast cmpxchg based processing.
+	 */

Can you, please, elaborate a bit more here?

Quoted 8 lines
mm/slub.c +4 -0
+	s->flags &= ~__CMPXCHG_DOUBLE;
+}
+EXPORT_SYMBOL(kmem_cache_setup_mobility);
+
 void *__kmalloc_track_caller(size_t size, gfp_t gfpflags, unsigned long caller)
 {
 	struct kmem_cache *s;

mm/slub.c static ssize_t ops_show(struct kmem_cache *s, char *buf) · line 5055

Quoted 10 lines
mm/slub.c +7 -0
@@ -5018,6 +5046,20 @@ static ssize_t ops_show(struct kmem_cache *s, char *buf)
 
 	if (s->ctor)
 		x += sprintf(buf + x, "ctor : %pS\n", s->ctor);
+
+	if (s->isolate) {
+		x += sprintf(buf + x, "isolate : ");
+		x += sprint_symbol(buf + x,
+				(unsigned long)s->isolate);
+		x += sprintf(buf + x, "\n");
+	}

Is there a reason why s->ctor and s->isolate/migrate are printed using different methods?

Quoted 13 lines
mm/slub.c +7 -0
+
+	if (s->migrate) {
+		x += sprintf(buf + x, "migrate : ");
+		x += sprint_symbol(buf + x,
+				(unsigned long)s->migrate);
+		x += sprintf(buf + x, "\n");
+	}
 	return x;
 }
 SLAB_ATTR_RO(ops);
Signature · 1 line
2.21.0

Thanks!

#11 lore raw

Re: [RFC 02/15] slub: Add isolate() and migrate() methods

Quoted 137 lines · Roman Gushchin
Quoted 136 lines · Tobin C. Harding

Add the two methods needed for moving objects and enable the display of the callbacks via the /sys/kernel/slab interface.

Add documentation explaining the use of these methods and the prototypes for slab.h. Add functions to setup the callbacks method for a slab cache.

Add empty functions for SLAB/SLOB. The API is generic so it could be theoretically implemented for these allocators as well.

---
 include/linux/slab.h     | 69 ++++++++++++++++++++++++++++++++++++++++
 include/linux/slub_def.h |  3 ++
 mm/slab_common.c         |  4 +++
 mm/slub.c                | 42 ++++++++++++++++++++++++
 4 files changed, 118 insertions(+)
include/linux/slab.h +69 -0
index 11b45f7ae405..22e87c41b8a4 100644
@@ -152,6 +152,75 @@ void memcg_create_kmem_cache(struct mem_cgroup *, struct kmem_cache *);
 void memcg_deactivate_kmem_caches(struct mem_cgroup *);
 void memcg_destroy_kmem_caches(struct mem_cgroup *);
 
+/*
+ * Function prototypes passed to kmem_cache_setup_mobility() to enable
+ * mobile objects and targeted reclaim in slab caches.
+ */
+
+/**
+ * typedef kmem_cache_isolate_func - Object migration callback function.
+ * @s: The cache we are working on.
+ * @ptr: Pointer to an array of pointers to the objects to migrate.
+ * @nr: Number of objects in array.
+ *
+ * The purpose of kmem_cache_isolate_func() is to pin each object so that
+ * they cannot be freed until kmem_cache_migrate_func() has processed
+ * them. This may be accomplished by increasing the refcount or setting
+ * a flag.
+ *
+ * The object pointer array passed is also passed to
+ * kmem_cache_migrate_func().  The function may remove objects from the
+ * array by setting pointers to NULL. This is useful if we can determine
+ * that an object is being freed because kmem_cache_isolate_func() was
+ * called when the subsystem was calling kmem_cache_free().  In that
+ * case it is not necessary to increase the refcount or specially mark
+ * the object because the release of the slab lock will lead to the
+ * immediate freeing of the object.
+ *
+ * Context: Called with locks held so that the slab objects cannot be
+ *          freed.  We are in an atomic context and no slab operations
+ *          may be performed.
+ * Return: A pointer that is passed to the migrate function. If any
+ *         objects cannot be touched at this point then the pointer may
+ *         indicate a failure and then the migration function can simply
+ *         remove the references that were already obtained. The private
+ *         data could be used to track the objects that were already pinned.
+ */
+typedef void *kmem_cache_isolate_func(struct kmem_cache *s, void **ptr, int nr);
+
+/**
+ * typedef kmem_cache_migrate_func - Object migration callback function.
+ * @s: The cache we are working on.
+ * @ptr: Pointer to an array of pointers to the objects to migrate.
+ * @nr: Number of objects in array.
+ * @node: The NUMA node where the object should be allocated.
+ * @private: The pointer returned by kmem_cache_isolate_func().
+ *
+ * This function is responsible for migrating objects.  Typically, for
+ * each object in the input array you will want to allocate an new
+ * object, copy the original object, update any pointers, and free the
+ * old object.
+ *
+ * After this function returns all pointers to the old object should now
+ * point to the new object.
+ *
+ * Context: Called with no locks held and interrupts enabled.  Sleeping
+ *          is possible.  Any operation may be performed.
+ */
+typedef void kmem_cache_migrate_func(struct kmem_cache *s, void **ptr,
+				     int nr, int node, void *private);
+
+/*
+ * kmem_cache_setup_mobility() is used to setup callbacks for a slab cache.
+ */
+#ifdef CONFIG_SLUB
+void kmem_cache_setup_mobility(struct kmem_cache *, kmem_cache_isolate_func,
+			       kmem_cache_migrate_func);
+#else
+static inline void kmem_cache_setup_mobility(struct kmem_cache *s,
+	kmem_cache_isolate_func isolate, kmem_cache_migrate_func migrate) {}
+#endif
+
 /*
  * Please use this macro to create slab caches. Simply specify the
  * name of the structure and maybe some flags that are listed above.
include/linux/slub_def.h +3 -0
index 3a1a1dbc6f49..a7340a1ed5dc 100644
@@ -99,6 +99,9 @@ struct kmem_cache {
 	gfp_t allocflags;	/* gfp flags to use on each alloc */
 	int refcount;		/* Refcount for slab cache destroy */
 	void (*ctor)(void *);
+	kmem_cache_isolate_func *isolate;
+	kmem_cache_migrate_func *migrate;
+
 	unsigned int inuse;		/* Offset to metadata */
 	unsigned int align;		/* Alignment */
 	unsigned int red_left_pad;	/* Left redzone padding size */
mm/slab_common.c +4 -0
index f9d89c1b5977..754acdb292e4 100644
@@ -298,6 +298,10 @@ int slab_unmergeable(struct kmem_cache *s)
 	if (!is_root_cache(s))
 		return 1;
 
+	/*
+	 * s->isolate and s->migrate imply s->ctor so no need to
+	 * check them explicitly.
+	 */
 	if (s->ctor)
 		return 1;
 
mm/slub.c +0 -0
index 69164aa7cbbf..0133168d1089 100644
@@ -4325,6 +4325,34 @@ int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags)
 	return err;
 }
 
Quoted 8 lines

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4331

Quoted 4 lines
mm/slub.c +4 -0
+void kmem_cache_setup_mobility(struct kmem_cache *s,
+			       kmem_cache_isolate_func isolate,
+			       kmem_cache_migrate_func migrate)
+{

I wonder if it's better to adapt kmem_cache_create() to take two additional argument? I suspect mobility is not a dynamic option, so it can be set on kmem_cache creation.

Thanks for the review. You are correct mobility is not dynamic (at the moment once enabled it cannot be disabled). I don't think we want to change every caller of kmem_cache_create() though, adding two new parameters that are almost always going to be NULL. Also, I cannot ATM see how object migration would be useful to SLOB so changing the API for all slab allocators does not seem like a good thing.

thanks,
Tobin.

#12 lore raw

Re: [RFC 02/15] slub: Add isolate() and migrate() methods

In reply to Roman Gushchin
Quoted 6 lines · Roman Gushchin
Quoted 6 lines
mm/slub.c +0 -0
@@ -4325,6 +4325,34 @@ int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags)
 	return err;
 }
 
Quoted 8 lines

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4331

Quoted 4 lines
mm/slub.c +4 -0
+void kmem_cache_setup_mobility(struct kmem_cache *s,
+			       kmem_cache_isolate_func isolate,
+			       kmem_cache_migrate_func migrate)
+{

I wonder if it's better to adapt kmem_cache_create() to take two additional argument? I suspect mobility is not a dynamic option, so it can be set on kmem_cache creation.

One other idea that prior versions of this patchset used was to change kmem_cache_create() so that the ctor parameter becomes an ops vector.

However, in order to reduce the size of the patchset I dropped that. It could be easily moved back to the way it was before.

Quoted 6 lines

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4351

Quoted 4 lines
mm/slub.c +4 -0
+	/*
+	 * Sadly serialization requirements currently mean that we have
+	 * to disable fast cmpxchg based processing.
+	 */

Can you, please, elaborate a bit more here?

cmpxchg based processing does not lock the struct page. SMO requires to ensure that all changes on a slab page can be stopped. The page->lock will accomplish that. I think we could avoid dealing with actually locking the page with some more work.

#13 lore raw

Re: [RFC 02/15] slub: Add isolate() and migrate() methods

Quoted 17 lines · Christopher Lameter
Quoted 6 lines · Roman Gushchin
Quoted 6 lines
mm/slub.c +0 -0
@@ -4325,6 +4325,34 @@ int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags)
 	return err;
 }
 
Quoted 8 lines

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4331

Quoted 4 lines
mm/slub.c +4 -0
+void kmem_cache_setup_mobility(struct kmem_cache *s,
+			       kmem_cache_isolate_func isolate,
+			       kmem_cache_migrate_func migrate)
+{

I wonder if it's better to adapt kmem_cache_create() to take two additional argument? I suspect mobility is not a dynamic option, so it can be set on kmem_cache creation.

Quoted 5 lines

One other idea that prior versions of this patchset used was to change kmem_cache_create() so that the ctor parameter becomes an ops vector.

However, in order to reduce the size of the patchset I dropped that. It could be easily moved back to the way it was before.

Understood. I like the idea of an ops vector, but it can be done later, agree.

Quoted 8 lines
Quoted 7 lines

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4351

Quoted 5 lines
mm/slub.c +4 -0
+	/*
+	 * Sadly serialization requirements currently mean that we have
+	 * to disable fast cmpxchg based processing.
+	 */

Can you, please, elaborate a bit more here?

Quoted 4 lines

cmpxchg based processing does not lock the struct page. SMO requires to ensure that all changes on a slab page can be stopped. The page->lock will accomplish that. I think we could avoid dealing with actually locking the page with some more work.

Thank you for the explanation!

#14 lore raw

RFC 3/15 tools/vm/slabinfo: Add support for -C and -F options

In reply to Tobin C. Harding

-F lists caches that support object migration.

-C lists caches that use a ctor.

Add command line options to show caches with a constructor and caches with that are migratable (i.e. have isolate and migrate functions).

---
 tools/vm/slabinfo.c | 40 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 36 insertions(+), 4 deletions(-)
tools/vm/slabinfo.c +36 -4
index 73818f1b2ef8..6ba8ffb4ea50 100644
@@ -33,6 +33,7 @@ struct slabinfo {
 	unsigned int hwcache_align, object_size, objs_per_slab;
 	unsigned int sanity_checks, slab_size, store_user, trace;
 	int order, poison, reclaim_account, red_zone;
+	int movable, ctor;
 	unsigned long partial, objects, slabs, objects_partial, objects_total;
 	unsigned long alloc_fastpath, alloc_slowpath;
 	unsigned long free_fastpath, free_slowpath;
@@ -67,6 +68,8 @@ int show_report;
 int show_alias;
 int show_slab;
 int skip_zero = 1;
+int show_movable;
+int show_ctor;
 int show_numa;
 int show_track;
 int show_first_alias;
@@ -109,14 +112,17 @@ static void fatal(const char *x, ...)
 
 static void usage(void)
 {
-	printf("slabinfo 4/15/2011. (c) 2007 sgi/(c) 2011 Linux Foundation.\n\n"
-		"slabinfo [-aADefhilnosrStTvz1LXBU] [N=K] [-dafzput] [slab-regexp]\n"
+	printf("slabinfo 4/15/2017. (c) 2007 sgi/(c) 2011 Linux Foundation/(c) 2017 Jump Trading LLC.\n\n"
+	       "slabinfo [-aACDefFhilnosrStTvz1LXBU] [N=K] [-dafzput] [slab-regexp]\n"
+
 		"-a|--aliases           Show aliases\n"
 		"-A|--activity          Most active slabs first\n"
 		"-B|--Bytes             Show size in bytes\n"
+		"-C|--ctor              Show slabs with ctors\n"
 		"-D|--display-active    Switch line format to activity\n"
 		"-e|--empty             Show empty slabs\n"
 		"-f|--first-alias       Show first alias\n"
+		"-F|--movable           Show caches that support movable objects\n"
 		"-h|--help              Show usage information\n"
 		"-i|--inverted          Inverted list\n"
 		"-l|--slabs             Show slabs\n"
@@ -588,6 +594,12 @@ static void slabcache(struct slabinfo *s)
 	if (show_empty && s->slabs)
 		return;
 
+	if (show_movable && !s->movable)
+		return;
+
+	if (show_ctor && !s->ctor)
+		return;
+
 	if (sort_loss == 0)
 		store_size(size_str, slab_size(s));
 	else
@@ -602,6 +614,10 @@ static void slabcache(struct slabinfo *s)
 		*p++ = '*';
 	if (s->cache_dma)
 		*p++ = 'd';
+	if (s->movable)
+		*p++ = 'F';
+	if (s->ctor)
+		*p++ = 'C';
 	if (s->hwcache_align)
 		*p++ = 'A';
 	if (s->poison)
@@ -636,7 +652,8 @@ static void slabcache(struct slabinfo *s)
 		printf("%-21s %8ld %7d %15s %14s %4d %1d %3ld %3ld %s\n",
 			s->name, s->objects, s->object_size, size_str, dist_str,
 			s->objs_per_slab, s->order,
-			s->slabs ? (s->partial * 100) / s->slabs : 100,
+			s->slabs ? (s->partial * 100) /
+					(s->slabs * s->objs_per_slab) : 100,
 			s->slabs ? (s->objects * s->object_size * 100) /
 				(s->slabs * (page_size << s->order)) : 100,
 			flags);
@@ -1256,6 +1273,13 @@ static void read_slab_dir(void)
 			slab->alloc_node_mismatch = get_obj("alloc_node_mismatch");
 			slab->deactivate_bypass = get_obj("deactivate_bypass");
 			chdir("..");
+			if (read_slab_obj(slab, "ops")) {
+				if (strstr(buffer, "ctor :"))
+					slab->ctor = 1;
+				if (strstr(buffer, "migrate :"))
+					slab->movable = 1;
+			}
+
 			if (slab->name[0] == ':')
 				alias_targets++;
 			slab++;
@@ -1332,6 +1356,8 @@ static void xtotals(void)
 }
 
 struct option opts[] = {
+	{ "ctor", no_argument, NULL, 'C' },
+	{ "movable", no_argument, NULL, 'F' },
 	{ "aliases", no_argument, NULL, 'a' },
 	{ "activity", no_argument, NULL, 'A' },
 	{ "debug", optional_argument, NULL, 'd' },
@@ -1367,7 +1393,7 @@ int main(int argc, char *argv[])
 
 	page_size = getpagesize();
 
-	while ((c = getopt_long(argc, argv, "aAd::Defhil1noprstvzTSN:LXBU",
+	while ((c = getopt_long(argc, argv, "aACd::DefFhil1noprstvzTSN:LXBU",
 						opts, NULL)) != -1)
 		switch (c) {
 		case '1':
@@ -1423,6 +1449,12 @@ int main(int argc, char *argv[])
 		case 'z':
 			skip_zero = 0;
 			break;
+		case 'C':
+			show_ctor = 1;
+			break;
+		case 'F':
+			show_movable = 1;
+			break;
 		case 'T':
 			show_totals = 1;
 			break;
Signature · 1 line
2.21.0
#15 lore raw

Re: [RFC 03/15] tools/vm/slabinfo: Add support for -C and -F options

Quoted 1 line · Tobin C. Harding

-F lists caches that support object migration.

Maybe -M?

Quoted 11 lines

-C lists caches that use a ctor.

Add command line options to show caches with a constructor and caches with that are migratable (i.e. have isolate and migrate functions).

---
 tools/vm/slabinfo.c | 40 ++++++++++++++++++++++++++++++++++++----
 1 file changed, 36 insertions(+), 4 deletions(-)

Thanks!

#16 lore raw

Re: [RFC 03/15] tools/vm/slabinfo: Add support for -C and -F options

Quoted 4 lines · Roman Gushchin
Quoted 1 line · Tobin C. Harding

-F lists caches that support object migration.

Maybe -M?

Seems reasonable, will use in v2, thanks.

Tobin.

#17 lore raw

RFC 4/15 slub: Enable Slab Movable Objects (SMO)

In reply to Tobin C. Harding

We have now in place a mechanism for adding callbacks to a cache in order to be able to implement object migration.

Add a function __move() that implements SMO by moving all objects in a slab page using the isolate/migrate callback methods.

---
 mm/slub.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)
mm/slub.c +85 -0
index 0133168d1089..6ce866b420f1 100644
@@ -4325,6 +4325,91 @@ int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags)
 	return err;
 }
 
+/*
+ * Allocate a slab scratch space that is sufficient to keep pointers to
+ * individual objects for all objects in cache and also a bitmap for the
+ * objects (used to mark which objects are active).
+ */
+static inline void *alloc_scratch(struct kmem_cache *s)
+{
+	unsigned int size = oo_objects(s->max);
+
+	return kmalloc(size * sizeof(void *) +
+		       BITS_TO_LONGS(size) * sizeof(unsigned long),
+		       GFP_KERNEL);
+}
+
+/*
+ * __move() - Move all objects in the given slab.
+ * @page: The slab we are working on.
+ * @scratch: Pointer to scratch space.
+ * @node: The target node to move objects to.
+ *
+ * If the target node is not the current node then the object is moved
+ * to the target node.  If the target node is the current node then this
+ * is an effective way of defragmentation since the current slab page
+ * with its object is exempt from allocation.
+ */
+static void __move(struct page *page, void *scratch, int node)
+{
+	unsigned long objects;
+	struct kmem_cache *s;
+	unsigned long flags;
+	unsigned long *map;
+	void *private;
+	int count;
+	void *p;
+	void **vector = scratch;
+	void *addr = page_address(page);
+
+	local_irq_save(flags);
+	slab_lock(page);
+
+	BUG_ON(!PageSlab(page)); /* Must be s slab page */
+	BUG_ON(!page->frozen);	 /* Slab must have been frozen earlier */
+
+	s = page->slab_cache;
+	objects = page->objects;
+	map = scratch + objects * sizeof(void **);
+
+	/* Determine used objects */
+	bitmap_fill(map, objects);
+	for (p = page->freelist; p; p = get_freepointer(s, p))
+		__clear_bit(slab_index(p, s, addr), map);
+
+	/* Build vector of pointers to objects */
+	count = 0;
+	memset(vector, 0, objects * sizeof(void **));
+	for_each_object(p, s, addr, objects)
+		if (test_bit(slab_index(p, s, addr), map))
+			vector[count++] = p;
+
+	if (s->isolate)
+		private = s->isolate(s, vector, count);
+	else
+		/* Objects do not need to be isolated */
+		private = NULL;
+
+	/*
+	 * Pinned the objects. Now we can drop the slab lock. The slab
+	 * is frozen so it cannot vanish from under us nor will
+	 * allocations be performed on the slab. However, unlocking the
+	 * slab will allow concurrent slab_frees to proceed. So the
+	 * subsystem must have a way to tell from the content of the
+	 * object that it was freed.
+	 *
+	 * If neither RCU nor ctor is being used then the object may be
+	 * modified by the allocator after being freed which may disrupt
+	 * the ability of the migrate function to tell if the object is
+	 * free or not.
+	 */
+	slab_unlock(page);
+	local_irq_restore(flags);
+
+	/* Perform callback to move the objects */
+	s->migrate(s, vector, count, node, private);
+}
+
 void kmem_cache_setup_mobility(struct kmem_cache *s,
 			       kmem_cache_isolate_func isolate,
 			       kmem_cache_migrate_func migrate)
Signature · 1 line
2.21.0
#18 lore raw

Re: [RFC 04/15] slub: Enable Slab Movable Objects (SMO)

Quoted 22 lines · Tobin C. Harding

We have now in place a mechanism for adding callbacks to a cache in order to be able to implement object migration.

Add a function __move() that implements SMO by moving all objects in a slab page using the isolate/migrate callback methods.

---
 mm/slub.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)
mm/slub.c +2 -0
index 0133168d1089..6ce866b420f1 100644
@@ -4325,6 +4325,91 @@ int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags)
 	return err;
 }
 
+/*
+ * Allocate a slab scratch space that is sufficient to keep pointers to

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4339

Quoted 10 lines
mm/slub.c +10 -0
+ * individual objects for all objects in cache and also a bitmap for the
+ * objects (used to mark which objects are active).
+ */
+static inline void *alloc_scratch(struct kmem_cache *s)
+{
+	unsigned int size = oo_objects(s->max);
+
+	return kmalloc(size * sizeof(void *) +
+		       BITS_TO_LONGS(size) * sizeof(unsigned long),
+		       GFP_KERNEL);

I wonder how big this allocation can be?
Given that the reason for migration is probably highly fragmented memory, we probably don't want to have a high-order allocation here. So maybe kvmalloc()?

Quoted 5 lines
mm/slub.c +5 -0
+}
+
+/*
+ * __move() - Move all objects in the given slab.
+ * @page: The slab we are working on.

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4354

Quoted 10 lines
mm/slub.c +10 -0
+ * @scratch: Pointer to scratch space.
+ * @node: The target node to move objects to.
+ *
+ * If the target node is not the current node then the object is moved
+ * to the target node.  If the target node is the current node then this
+ * is an effective way of defragmentation since the current slab page
+ * with its object is exempt from allocation.
+ */
+static void __move(struct page *page, void *scratch, int node)
+{

__move() isn't a very explanatory name. kmem_cache_move() (as in Christopher's version) is much better, IMO. Or maybe move_slab_objects()?

Also, it's usually better to avoid adding new functions without calling them. Maybe it's possible to merge this patch with (9)?

Thanks!

Quoted 64 lines
mm/slub.c +58 -0
+	unsigned long objects;
+	struct kmem_cache *s;
+	unsigned long flags;
+	unsigned long *map;
+	void *private;
+	int count;
+	void *p;
+	void **vector = scratch;
+	void *addr = page_address(page);
+
+	local_irq_save(flags);
+	slab_lock(page);
+
+	BUG_ON(!PageSlab(page)); /* Must be s slab page */
+	BUG_ON(!page->frozen);	 /* Slab must have been frozen earlier */
+
+	s = page->slab_cache;
+	objects = page->objects;
+	map = scratch + objects * sizeof(void **);
+
+	/* Determine used objects */
+	bitmap_fill(map, objects);
+	for (p = page->freelist; p; p = get_freepointer(s, p))
+		__clear_bit(slab_index(p, s, addr), map);
+
+	/* Build vector of pointers to objects */
+	count = 0;
+	memset(vector, 0, objects * sizeof(void **));
+	for_each_object(p, s, addr, objects)
+		if (test_bit(slab_index(p, s, addr), map))
+			vector[count++] = p;
+
+	if (s->isolate)
+		private = s->isolate(s, vector, count);
+	else
+		/* Objects do not need to be isolated */
+		private = NULL;
+
+	/*
+	 * Pinned the objects. Now we can drop the slab lock. The slab
+	 * is frozen so it cannot vanish from under us nor will
+	 * allocations be performed on the slab. However, unlocking the
+	 * slab will allow concurrent slab_frees to proceed. So the
+	 * subsystem must have a way to tell from the content of the
+	 * object that it was freed.
+	 *
+	 * If neither RCU nor ctor is being used then the object may be
+	 * modified by the allocator after being freed which may disrupt
+	 * the ability of the migrate function to tell if the object is
+	 * free or not.
+	 */
+	slab_unlock(page);
+	local_irq_restore(flags);
+
+	/* Perform callback to move the objects */
+	s->migrate(s, vector, count, node, private);
+}
+
 void kmem_cache_setup_mobility(struct kmem_cache *s,
 			       kmem_cache_isolate_func isolate,
 			       kmem_cache_migrate_func migrate)
Signature · 1 line
2.21.0
#19 lore raw

Re: [RFC 04/15] slub: Enable Slab Movable Objects (SMO)

Quoted 49 lines · Roman Gushchin
Quoted 22 lines · Tobin C. Harding

We have now in place a mechanism for adding callbacks to a cache in order to be able to implement object migration.

Add a function __move() that implements SMO by moving all objects in a slab page using the isolate/migrate callback methods.

---
 mm/slub.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)
mm/slub.c +2 -0
index 0133168d1089..6ce866b420f1 100644
@@ -4325,6 +4325,91 @@ int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags)
 	return err;
 }
 
+/*
+ * Allocate a slab scratch space that is sufficient to keep pointers to

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4339

Quoted 10 lines
mm/slub.c +10 -0
+ * individual objects for all objects in cache and also a bitmap for the
+ * objects (used to mark which objects are active).
+ */
+static inline void *alloc_scratch(struct kmem_cache *s)
+{
+	unsigned int size = oo_objects(s->max);
+
+	return kmalloc(size * sizeof(void *) +
+		       BITS_TO_LONGS(size) * sizeof(unsigned long),
+		       GFP_KERNEL);

I wonder how big this allocation can be?
Given that the reason for migration is probably highly fragmented memory, we probably don't want to have a high-order allocation here. So maybe kvmalloc()?

Quoted 10 lines
mm/slub.c +10 -0
+}
+
+/*
+ * __move() - Move all objects in the given slab.
+ * @page: The slab we are working on.
+ * @scratch: Pointer to scratch space.
+ * @node: The target node to move objects to.
+ *
+ * If the target node is not the current node then the object is moved
+ * to the target node.  If the target node is the current node then this
Quoted 8 lines

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4354

Quoted 5 lines
mm/slub.c +5 -0
+ * is an effective way of defragmentation since the current slab page
+ * with its object is exempt from allocation.
+ */
+static void __move(struct page *page, void *scratch, int node)
+{

__move() isn't a very explanatory name. kmem_cache_move() (as in Christopher's version) is much better, IMO. Or maybe move_slab_objects()?

How about move_slab_page()? We use kmem_cache_move() later in the series. __move() moves all objects in the given page but not all objects in this cache (which kmem_cache_move() later does). Open to further suggestions though, naming things is hard :)

Christopher's original patch uses kmem_cache_move() for a function that only moves objects from within partial slabs, I changed it because I did not think this name suitably describes the behaviour. So from the original I rename:

	__move() -> __defrag()
	kmem_cache_move() -> __move()

And reuse kmem_cache_move() for move _all_ objects (includes full list).

With this set applied we have the call chains

kmem_cache_shrink() # Defined in slab_common.c, exported to kernel.
-> __kmem_cache_shrink() # Defined in slub.c
-> __defrag() # Unconditionally (i.e 100%)

     -> __move()

kmem_cache_defrag() # Exported to kernel
-> __defrag()
-> __move()

move_store() # sysfs
-> kmem_cache_move()
-> __move()
or
-> __move_all_objects_to()
-> kmem_cache_move()

     -> __move()

Suggested improvements?

Quoted 2 lines

Also, it's usually better to avoid adding new functions without calling them. Maybe it's possible to merge this patch with (9)?

Understood. The reason behind this is that I attempted to break this set up separating the implementation of SMO with the addition of each feature. This function is only called when features are implemented to use SMO, I did not want to elevate any feature above any other by including it in this patch. I'm open to suggestions on how to order though, also I'm happy to order it differently if/when we do PATCH version. Is that acceptable for the RFC versions?

thanks,
Tobin.

#20 lore raw

Re: [RFC 04/15] slub: Enable Slab Movable Objects (SMO)

Quoted 91 lines · Tobin C. Harding
Quoted 49 lines · Roman Gushchin
Quoted 22 lines · Tobin C. Harding

We have now in place a mechanism for adding callbacks to a cache in order to be able to implement object migration.

Add a function __move() that implements SMO by moving all objects in a slab page using the isolate/migrate callback methods.

---
 mm/slub.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 85 insertions(+)
mm/slub.c +2 -0
index 0133168d1089..6ce866b420f1 100644
@@ -4325,6 +4325,91 @@ int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags)
 	return err;
 }
 
+/*
+ * Allocate a slab scratch space that is sufficient to keep pointers to

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4339

Quoted 10 lines
mm/slub.c +10 -0
+ * individual objects for all objects in cache and also a bitmap for the
+ * objects (used to mark which objects are active).
+ */
+static inline void *alloc_scratch(struct kmem_cache *s)
+{
+	unsigned int size = oo_objects(s->max);
+
+	return kmalloc(size * sizeof(void *) +
+		       BITS_TO_LONGS(size) * sizeof(unsigned long),
+		       GFP_KERNEL);

I wonder how big this allocation can be?
Given that the reason for migration is probably highly fragmented memory, we probably don't want to have a high-order allocation here. So maybe kvmalloc()?

Quoted 10 lines
mm/slub.c +10 -0
+}
+
+/*
+ * __move() - Move all objects in the given slab.
+ * @page: The slab we are working on.
+ * @scratch: Pointer to scratch space.
+ * @node: The target node to move objects to.
+ *
+ * If the target node is not the current node then the object is moved
+ * to the target node.  If the target node is the current node then this
Quoted 8 lines

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4354

Quoted 5 lines
mm/slub.c +5 -0
+ * is an effective way of defragmentation since the current slab page
+ * with its object is exempt from allocation.
+ */
+static void __move(struct page *page, void *scratch, int node)
+{

__move() isn't a very explanatory name. kmem_cache_move() (as in Christopher's version) is much better, IMO. Or maybe move_slab_objects()?

How about move_slab_page()? We use kmem_cache_move() later in the series. __move() moves all objects in the given page but not all objects in this cache (which kmem_cache_move() later does). Open to further suggestions though, naming things is hard :)

Christopher's original patch uses kmem_cache_move() for a function that only moves objects from within partial slabs, I changed it because I did not think this name suitably describes the behaviour. So from the original I rename:

	__move() -> __defrag()
	kmem_cache_move() -> __move()

And reuse kmem_cache_move() for move _all_ objects (includes full list).

With this set applied we have the call chains

kmem_cache_shrink() # Defined in slab_common.c, exported to kernel.
-> __kmem_cache_shrink() # Defined in slub.c
-> __defrag() # Unconditionally (i.e 100%)

     -> __move()

kmem_cache_defrag() # Exported to kernel
-> __defrag()
-> __move()

move_store() # sysfs
-> kmem_cache_move()
-> __move()
or
-> __move_all_objects_to()
-> kmem_cache_move()

Quoted 4 lines
     -> __move()

Suggested improvements?

move_slab_page() looks good to me. I don't have a strong opinion on naming here, I just think that unique names are always preferred even for static functions (that's why I don't like __move()). Also, it's not clear what '__' means here. So maybe move_slab_page(), defrag_kmem_node(), etc? Or something like this.

Quoted 11 lines
Quoted 3 lines

Also, it's usually better to avoid adding new functions without calling them. Maybe it's possible to merge this patch with (9)?

Understood. The reason behind this is that I attempted to break this set up separating the implementation of SMO with the addition of each feature. This function is only called when features are implemented to use SMO, I did not want to elevate any feature above any other by including it in this patch. I'm open to suggestions on how to order though, also I'm happy to order it differently if/when we do PATCH version. Is that acceptable for the RFC versions?

The general rule is that any patch should be self-sufficient. If it's required to look into further patches to understand why something has been done, it makes the review process harder.

I think it's possible to make the patchset to conform this rule with some almost cosmetic changes, e.g. move some changes from one patch to another, add some comments, etc.

Anyway, it's definitely fine for an RFC, just something to think of in the next version.

Thanks!

#21 lore raw

Re: [RFC 04/15] slub: Enable Slab Movable Objects (SMO)

In reply to Roman Gushchin
Quoted 4 lines · Roman Gushchin
Quoted 4 lines
mm/slub.c +4 -0
+static inline void *alloc_scratch(struct kmem_cache *s)
+{
+	unsigned int size = oo_objects(s->max);
+
Quoted 8 lines

mm/slub.c int __kmem_cache_create(struct kmem_cache *s, slab_flags_t flags) · line 4339

Quoted 3 lines
mm/slub.c +3 -0
+	return kmalloc(size * sizeof(void *) +
+		       BITS_TO_LONGS(size) * sizeof(unsigned long),
+		       GFP_KERNEL);

I wonder how big this allocation can be?
Given that the reason for migration is probably highly fragmented memory, we probably don't want to have a high-order allocation here. So maybe kvmalloc()?

The smallest object size is 8 bytes which is one word which would be places in an order 0 page. So it comes out to about a page again.

Larger allocation orders are possible if the slab pages itself can have larger orders of course. If you set the min_order to the huge page order then we can have similar sized orders for the allocation of the scratch space. However, that is not a problem since the allocations for the slab pages itself are also already of that same order.

#22 lore raw

RFC 5/15 slub: Sort slab cache list

In reply to Tobin C. Harding

It is advantageous to have all defragmentable slabs together at the beginning of the list of slabs so that there is no need to scan the complete list. Put defragmentable caches first when adding a slab cache and others last.

---
 mm/slab_common.c | 2 +-
 mm/slub.c        | 6 ++++++
 2 files changed, 7 insertions(+), 1 deletion(-)
mm/slab_common.c +1 -1
index 754acdb292e4..1d492b59eee1 100644
@@ -397,7 +397,7 @@ static struct kmem_cache *create_cache(const char *name,
 		goto out_free_cache;
 
 	s->refcount = 1;
-	list_add(&s->list, &slab_caches);
+	list_add_tail(&s->list, &slab_caches);
 	memcg_link_cache(s);
 out:
 	if (err)
mm/slub.c +6 -0
index 6ce866b420f1..f37103e22d3f 100644
@@ -4427,6 +4427,8 @@ void kmem_cache_setup_mobility(struct kmem_cache *s,
 		return;
 	}
 
+	mutex_lock(&slab_mutex);
+
 	s->isolate = isolate;
 	s->migrate = migrate;
 
@@ -4435,6 +4437,10 @@ void kmem_cache_setup_mobility(struct kmem_cache *s,
 	 * to disable fast cmpxchg based processing.
 	 */
 	s->flags &= ~__CMPXCHG_DOUBLE;
+
+	list_move(&s->list, &slab_caches);	/* Move to top */
+
+	mutex_unlock(&slab_mutex);
 }
 EXPORT_SYMBOL(kmem_cache_setup_mobility);
 
Signature · 1 line
2.21.0
#23 lore raw

RFC 6/15 tools/vm/slabinfo: Add remote node defrag ratio output

In reply to Tobin C. Harding

Add output line for NUMA remote node defrag ratio.

---
 tools/vm/slabinfo.c | 7 +++++++
 1 file changed, 7 insertions(+)
tools/vm/slabinfo.c +7 -0
index 6ba8ffb4ea50..9cdccdaca349 100644
@@ -34,6 +34,7 @@ struct slabinfo {
 	unsigned int sanity_checks, slab_size, store_user, trace;
 	int order, poison, reclaim_account, red_zone;
 	int movable, ctor;
+	int remote_node_defrag_ratio;
 	unsigned long partial, objects, slabs, objects_partial, objects_total;
 	unsigned long alloc_fastpath, alloc_slowpath;
 	unsigned long free_fastpath, free_slowpath;
@@ -377,6 +378,10 @@ static void slab_numa(struct slabinfo *s, int mode)
 	if (skip_zero && !s->slabs)
 		return;
 
+	if (mode) {
+		printf("\nNUMA remote node defrag ratio: %3d\n",
+		       s->remote_node_defrag_ratio);
+	}
 	if (!line) {
 		printf("\n%-21s:", mode ? "NUMA nodes" : "Slab");
 		for(node = 0; node <= highest_node; node++)
@@ -1272,6 +1277,8 @@ static void read_slab_dir(void)
 			slab->cpu_partial_free = get_obj("cpu_partial_free");
 			slab->alloc_node_mismatch = get_obj("alloc_node_mismatch");
 			slab->deactivate_bypass = get_obj("deactivate_bypass");
+			slab->remote_node_defrag_ratio =
+					get_obj("remote_node_defrag_ratio");
 			chdir("..");
 			if (read_slab_obj(slab, "ops")) {
 				if (strstr(buffer, "ctor :"))
Signature · 1 line
2.21.0
#24 lore raw

RFC 7/15 slub: Add defrag_used_ratio field and sysfs support

In reply to Tobin C. Harding

In preparation for enabling defragmentation of slab pages.

"defrag_used_ratio" is used to set the threshold at which defragmentation should be attempted on a slab page.

"defrag_used_ratio" is a percentage in the range of 0 - 100 (inclusive). If less than that percentage of slots in a slab page are in use then the slab page will become subject to defragmentation.

Add a defrag ratio field and set it to 30% by default. A limit of 30% specifies that more than 3 out of 10 available slots for objects need to be in use otherwise slab defragmentation will be attempted on the remaining objects.

---
 Documentation/ABI/testing/sysfs-kernel-slab | 14 ++++++++++++
 include/linux/slub_def.h                    |  7 ++++++
 mm/slub.c                                   | 24 +++++++++++++++++++++
 3 files changed, 45 insertions(+)
Documentation/ABI/testing/sysfs-kernel-slab +14 -0
index 29601d93a1c2..7770c03be6b4 100644
@@ -180,6 +180,20 @@ Description:
 		list.  It can be written to clear the current count.
 		Available when CONFIG_SLUB_STATS is enabled.
 
+What:		/sys/kernel/slab/cache/defrag_used_ratio
+Date:		February 2019
+KernelVersion:	5.0
+Contact:	Christoph Lameter <[email protected]>
+		Pekka Enberg <[email protected]>,
+Description:
+		The defrag_used_ratio file allows the control of how aggressive
+		slab fragmentation reduction works at reclaiming objects from
+		sparsely populated slabs. This is a percentage. If a slab has
+		less than this percentage of objects allocated then reclaim will
+		attempt to reclaim objects so that the whole slab page can be
+		freed. 0% specifies no reclaim attempt (defrag disabled), 100%
+		specifies attempt to reclaim all pages.  The default is 30%.
+
 What:		/sys/kernel/slab/cache/deactivate_to_tail
 Date:		February 2008
 KernelVersion:	2.6.25
include/linux/slub_def.h +7 -0
index a7340a1ed5dc..6da6197ca973 100644
@@ -107,6 +107,13 @@ 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 */
+	int defrag_used_ratio;	/*
+				 * Ratio used to check against the
+				 * percentage of objects allocated in a
+				 * slab page.  If less than this ratio
+				 * is allocated then reclaim attempts
+				 * are made.
+				 */
 #ifdef CONFIG_SYSFS
 	struct kobject kobj;	/* For sysfs */
 	struct work_struct kobj_remove_work;
mm/slub.c +24 -0
index f37103e22d3f..515db0f36c55 100644
@@ -3642,6 +3642,7 @@ static int kmem_cache_open(struct kmem_cache *s, slab_flags_t flags)
 
 	set_cpu_partial(s);
 
+	s->defrag_used_ratio = 30;
 #ifdef CONFIG_NUMA
 	s->remote_node_defrag_ratio = 1000;
 #endif
@@ -5261,6 +5262,28 @@ static ssize_t destroy_by_rcu_show(struct kmem_cache *s, char *buf)
 }
 SLAB_ATTR_RO(destroy_by_rcu);
 
+static ssize_t defrag_used_ratio_show(struct kmem_cache *s, char *buf)
+{
+	return sprintf(buf, "%d\n", s->defrag_used_ratio);
+}
+
+static ssize_t defrag_used_ratio_store(struct kmem_cache *s,
+				       const char *buf, size_t length)
+{
+	unsigned long ratio;
+	int err;
+
+	err = kstrtoul(buf, 10, &ratio);
+	if (err)
+		return err;
+
+	if (ratio <= 100)
+		s->defrag_used_ratio = ratio;
+
+	return length;
+}
+SLAB_ATTR(defrag_used_ratio);
+
 #ifdef CONFIG_SLUB_DEBUG
 static ssize_t slabs_show(struct kmem_cache *s, char *buf)
 {
@@ -5585,6 +5608,7 @@ static struct attribute *slab_attrs[] = {
 	&validate_attr.attr,
 	&alloc_calls_attr.attr,
 	&free_calls_attr.attr,
+	&defrag_used_ratio_attr.attr,
 #endif
 #ifdef CONFIG_ZONE_DMA
 	&cache_dma_attr.attr,
Signature · 1 line
2.21.0
#25 lore raw

Re: [RFC 07/15] slub: Add defrag_used_ratio field and sysfs support

Quoted 21 lines · Tobin C. Harding
mm/slub.c +8 -0
@@ -3642,6 +3642,7 @@ static int kmem_cache_open(struct kmem_cache *s, slab_flags_t flags)
 
 	set_cpu_partial(s);
 
+	s->defrag_used_ratio = 30;
 #ifdef CONFIG_NUMA
 	s->remote_node_defrag_ratio = 1000;
 #endif
@@ -5261,6 +5262,28 @@ static ssize_t destroy_by_rcu_show(struct kmem_cache *s, char *buf)
 }
 SLAB_ATTR_RO(destroy_by_rcu);
 
+static ssize_t defrag_used_ratio_show(struct kmem_cache *s, char *buf)
+{
+	return sprintf(buf, "%d\n", s->defrag_used_ratio);
+}
+
+static ssize_t defrag_used_ratio_store(struct kmem_cache *s,
+				       const char *buf, size_t length)

mm/slub.c static ssize_t destroy_by_rcu_show(struct kmem_cache *s, char *buf) · line 5281

Quoted 10 lines
mm/slub.c +10 -0
+{
+	unsigned long ratio;
+	int err;
+
+	err = kstrtoul(buf, 10, &ratio);
+	if (err)
+		return err;
+
+	if (ratio <= 100)
+		s->defrag_used_ratio = ratio;
    else
        return -EINVAL;

maybe?

Tycho

#26 lore raw

Re: [RFC 07/15] slub: Add defrag_used_ratio field and sysfs support

Quoted 26 lines · Tycho Andersen
Quoted 25 lines · Tobin C. Harding
mm/slub.c +12 -0
@@ -3642,6 +3642,7 @@ static int kmem_cache_open(struct kmem_cache *s, slab_flags_t flags)
 
 	set_cpu_partial(s);
 
+	s->defrag_used_ratio = 30;
 #ifdef CONFIG_NUMA
 	s->remote_node_defrag_ratio = 1000;
 #endif
@@ -5261,6 +5262,28 @@ static ssize_t destroy_by_rcu_show(struct kmem_cache *s, char *buf)
 }
 SLAB_ATTR_RO(destroy_by_rcu);
 
+static ssize_t defrag_used_ratio_show(struct kmem_cache *s, char *buf)
+{
+	return sprintf(buf, "%d\n", s->defrag_used_ratio);
+}
+
+static ssize_t defrag_used_ratio_store(struct kmem_cache *s,
+				       const char *buf, size_t length)
+{
+	unsigned long ratio;
+	int err;
+
Quoted 8 lines

mm/slub.c static ssize_t destroy_by_rcu_show(struct kmem_cache *s, char *buf) · line 5281

Quoted 6 lines
mm/slub.c +6 -0
+	err = kstrtoul(buf, 10, &ratio);
+	if (err)
+		return err;
+
+	if (ratio <= 100)
+		s->defrag_used_ratio = ratio;
    else
        return -EINVAL;

Nice, thanks. I moulded your suggestion into

	if (ratio > 100)
		return -EINVAL;

	s->defrag_used_ratio = ratio;
	return length;

thanks,
Tobin.

#27 lore raw

RFC 8/15 tools/vm/slabinfo: Add defrag_used_ratio output

In reply to Tobin C. Harding

Add output for the newly added defrag_used_ratio sysfs knob.

---
 tools/vm/slabinfo.c | 4 ++++
 1 file changed, 4 insertions(+)
tools/vm/slabinfo.c +4 -0
index 9cdccdaca349..8cf3bbd061e2 100644
@@ -34,6 +34,7 @@ struct slabinfo {
 	unsigned int sanity_checks, slab_size, store_user, trace;
 	int order, poison, reclaim_account, red_zone;
 	int movable, ctor;
+	int defrag_used_ratio;
 	int remote_node_defrag_ratio;
 	unsigned long partial, objects, slabs, objects_partial, objects_total;
 	unsigned long alloc_fastpath, alloc_slowpath;
@@ -549,6 +550,8 @@ static void report(struct slabinfo *s)
 		printf("** Slabs are destroyed via RCU\n");
 	if (s->reclaim_account)
 		printf("** Reclaim accounting active\n");
+	if (s->movable)
+		printf("** Defragmentation at %d%%\n", s->defrag_used_ratio);
 
 	printf("\nSizes (bytes)     Slabs              Debug                Memory\n");
 	printf("------------------------------------------------------------------------\n");
@@ -1279,6 +1282,7 @@ static void read_slab_dir(void)
 			slab->deactivate_bypass = get_obj("deactivate_bypass");
 			slab->remote_node_defrag_ratio =
 					get_obj("remote_node_defrag_ratio");
+			slab->defrag_used_ratio = get_obj("defrag_used_ratio");
 			chdir("..");
 			if (read_slab_obj(slab, "ops")) {
 				if (strstr(buffer, "ctor :"))
Signature · 1 line
2.21.0
#28 lore raw

RFC 9/15 slub: Enable slab defragmentation using SMO

In reply to Tobin C. Harding

If many objects are allocated with the slab allocator and freed in an arbitrary order then the slab caches can become internally fragmented. Now that the slab allocator supports movable objects we can defragment any cache that has this feature enabled.

Slab defragmentation may occur:

1. Unconditionally when __kmem_cache_shrink() is called on a slab cache by the kernel calling kmem_cache_shrink().

2. Unconditionally through the use of the slabinfo command.

slabinfo <cache> -s

3. Conditionally via the use of kmem_cache_defrag()

Use SMO when shrinking cache. Currently when the kernel calls kmem_cache_shrink() we curate the partial slabs list. If object migration is not enabled for the cache we still do this, if however SMO is enabled, we attempt to move objects in partially full slabs in order to defragment the cache. Shrink attempts to move all objects in order to reduce the cache to a single partial slab for each node.

kmem_cache_defrag() differs from shrink in that it operates dependent on the defrag_used_ratio and only attempts to move objects if the number of partial slabs exceeds MAX_PARTIAL (for each node).

Add function kmem_cache_defrag(int node).

kmem_cache_defrag() only performs defragmentation if the usage ratio of the slab is lower than the configured percentage (sysfs file added in previous patch). Fragmentation ratios are measured by calculating the percentage of objects in use compared to the total number of objects that the slab page can accommodate.

The scanning of slab caches is optimized because the defragmentable slabs come first on the list. Thus we can terminate scans on the first slab encountered that does not support defragmentation.

kmem_cache_defrag() takes a node parameter. This can either be -1 if defragmentation should be performed on all nodes, or a node number.

Defragmentation may be disabled by setting defrag ratio to 0

echo 0 > /sys/kernel/slab/<cache>/defrag_used_ratio

In order for a cache to be defragmentable the cache must support object migration (SMO). Enabling SMO for a cache is done via a call to the recently added function:

	void kmem_cache_setup_mobility(struct kmem_cache *,
				       kmem_cache_isolate_func,
			               kmem_cache_migrate_func);
---
 include/linux/slab.h |   1 +
 mm/slub.c            | 266 +++++++++++++++++++++++++++++++------------
 2 files changed, 194 insertions(+), 73 deletions(-)
include/linux/slab.h +1 -0
index 22e87c41b8a4..b9b46bc9937e 100644
@@ -147,6 +147,7 @@ struct kmem_cache *kmem_cache_create_usercopy(const char *name,
 			void (*ctor)(void *));
 void kmem_cache_destroy(struct kmem_cache *);
 int kmem_cache_shrink(struct kmem_cache *);
+int kmem_cache_defrag(int node);
 
 void memcg_create_kmem_cache(struct mem_cgroup *, struct kmem_cache *);
 void memcg_deactivate_kmem_caches(struct mem_cgroup *);
mm/slub.c +193 -73

284 lines not on this page · raw message

Signature · 1 line
2.21.0
#29 lore raw

Re: [RFC 09/15] slub: Enable slab defragmentation using SMO

Quoted 180 lines · Tobin C. Harding

If many objects are allocated with the slab allocator and freed in an arbitrary order then the slab caches can become internally fragmented. Now that the slab allocator supports movable objects we can defragment any cache that has this feature enabled.

Slab defragmentation may occur:

1. Unconditionally when __kmem_cache_shrink() is called on a slab cache by the kernel calling kmem_cache_shrink().

2. Unconditionally through the use of the slabinfo command.

slabinfo <cache> -s

3. Conditionally via the use of kmem_cache_defrag()

Use SMO when shrinking cache. Currently when the kernel calls kmem_cache_shrink() we curate the partial slabs list. If object migration is not enabled for the cache we still do this, if however SMO is enabled, we attempt to move objects in partially full slabs in order to defragment the cache. Shrink attempts to move all objects in order to reduce the cache to a single partial slab for each node.

kmem_cache_defrag() differs from shrink in that it operates dependent on the defrag_used_ratio and only attempts to move objects if the number of partial slabs exceeds MAX_PARTIAL (for each node).

Add function kmem_cache_defrag(int node).

kmem_cache_defrag() only performs defragmentation if the usage ratio of the slab is lower than the configured percentage (sysfs file added in previous patch). Fragmentation ratios are measured by calculating the percentage of objects in use compared to the total number of objects that the slab page can accommodate.

The scanning of slab caches is optimized because the defragmentable slabs come first on the list. Thus we can terminate scans on the first slab encountered that does not support defragmentation.

kmem_cache_defrag() takes a node parameter. This can either be -1 if defragmentation should be performed on all nodes, or a node number.

Defragmentation may be disabled by setting defrag ratio to 0

echo 0 > /sys/kernel/slab/<cache>/defrag_used_ratio

In order for a cache to be defragmentable the cache must support object migration (SMO). Enabling SMO for a cache is done via a call to the recently added function:

	void kmem_cache_setup_mobility(struct kmem_cache *,
				       kmem_cache_isolate_func,
			               kmem_cache_migrate_func);
---
 include/linux/slab.h |   1 +
 mm/slub.c            | 266 +++++++++++++++++++++++++++++++------------
 2 files changed, 194 insertions(+), 73 deletions(-)
include/linux/slab.h +1 -0
index 22e87c41b8a4..b9b46bc9937e 100644
@@ -147,6 +147,7 @@ struct kmem_cache *kmem_cache_create_usercopy(const char *name,
 			void (*ctor)(void *));
 void kmem_cache_destroy(struct kmem_cache *);
 int kmem_cache_shrink(struct kmem_cache *);
+int kmem_cache_defrag(int node);
 
 void memcg_create_kmem_cache(struct mem_cgroup *, struct kmem_cache *);
 void memcg_deactivate_kmem_caches(struct mem_cgroup *);
mm/slub.c +12 -73
index 515db0f36c55..53dd4cb5b5a4 100644
@@ -354,6 +354,12 @@ static __always_inline void slab_lock(struct page *page)
 	bit_spin_lock(PG_locked, &page->flags);
 }
 
+static __always_inline int slab_trylock(struct page *page)
+{
+	VM_BUG_ON_PAGE(PageTail(page), page);
+	return bit_spin_trylock(PG_locked, &page->flags);
+}
+
 static __always_inline void slab_unlock(struct page *page)
 {
 	VM_BUG_ON_PAGE(PageTail(page), page);
@@ -3959,79 +3965,6 @@ void kfree(const void *x)
 }
 EXPORT_SYMBOL(kfree);
 
-#define SHRINK_PROMOTE_MAX 32
-
-/*
- * kmem_cache_shrink discards empty slabs and promotes the slabs filled
- * up most to the head of the partial lists. New allocations will then
- * fill those up and thus they can be removed from the partial lists.
- *
- * The slabs with the least items are placed last. This results in them
- * being allocated from last increasing the chance that the last objects
- * are freed in them.
- */
-int __kmem_cache_shrink(struct kmem_cache *s)
-{
-	int node;
-	int i;
-	struct kmem_cache_node *n;
-	struct page *page;
-	struct page *t;
-	struct list_head discard;
-	struct list_head promote[SHRINK_PROMOTE_MAX];
-	unsigned long flags;
-	int ret = 0;
-
-	flush_all(s);
-	for_each_kmem_cache_node(s, node, n) {
-		INIT_LIST_HEAD(&discard);
-		for (i = 0; i < SHRINK_PROMOTE_MAX; i++)
-			INIT_LIST_HEAD(promote + i);
-
-		spin_lock_irqsave(&n->list_lock, flags);
-
-		/*
-		 * Build lists of slabs to discard or promote.
-		 *
-		 * Note that concurrent frees may occur while we hold the
-		 * list_lock. page->inuse here is the upper limit.
-		 */
-		list_for_each_entry_safe(page, t, &n->partial, lru) {
-			int free = page->objects - page->inuse;
-
-			/* Do not reread page->inuse */
-			barrier();
-
-			/* We do not keep full slabs on the list */
-			BUG_ON(free <= 0);
-
-			if (free == page->objects) {
-				list_move(&page->lru, &discard);
-				n->nr_partial--;
-			} else if (free <= SHRINK_PROMOTE_MAX)
-				list_move(&page->lru, promote + free - 1);
-		}
-
-		/*
-		 * Promote the slabs filled up most to the head of the
-		 * partial list.
-		 */
-		for (i = SHRINK_PROMOTE_MAX - 1; i >= 0; i--)
-			list_splice(promote + i, &n->partial);
-
-		spin_unlock_irqrestore(&n->list_lock, flags);
-
-		/* Release empty slabs */
-		list_for_each_entry_safe(page, t, &discard, lru)
-			discard_slab(s, page);
-
-		if (slabs_node(s, node))
-			ret = 1;
-	}
-
-	return ret;
-}
-
 #ifdef CONFIG_MEMCG
 static void kmemcg_cache_deact_after_rcu(struct kmem_cache *s)
 {
@@ -4411,6 +4344,193 @@ static void __move(struct page *page, void *scratch, int node)
 	s->migrate(s, vector, count, node, private);
 }
 
+/*
+ * __defrag() - Defragment node.
+ * @s: cache we are working on.
+ * @node: The node to move objects from.
+ * @target_node: The node to move objects to.
+ * @ratio: The defrag ratio (percentage, between 0 and 100).

mm/slub.c static void __move(struct page *page, void *scratch, int node) · line 4362

Quoted 10 lines
mm/slub.c +10 -0
+ *
+ * Release slabs with zero objects and try to call the migration function
+ * for slabs with less than the 'ratio' percentage of objects allocated.
+ *
+ * Moved objects are allocated on @target_node.
+ *
+ * Return: The number of partial slabs left on the node after the operation.
+ */
+static unsigned long __defrag(struct kmem_cache *s, int node, int target_node,
+			      int ratio)

Maybe kmem_cache_defrag_node()?

Quoted 152 lines
mm/slub.c +152 -0
+{
+	struct kmem_cache_node *n = get_node(s, node);
+	struct page *page, *page2;
+	LIST_HEAD(move_list);
+	unsigned long flags;
+
+	if (node == target_node && n->nr_partial <= 1) {
+		/*
+		 * Trying to reduce fragmentation on a node but there is
+		 * only a single or no partial slab page. This is already
+		 * the optimal object density that we can reach.
+		 */
+		return n->nr_partial;
+	}
+
+	spin_lock_irqsave(&n->list_lock, flags);
+	list_for_each_entry_safe(page, page2, &n->partial, lru) {
+		if (!slab_trylock(page))
+			/* Busy slab. Get out of the way */
+			continue;
+
+		if (page->inuse) {
+			if (page->inuse > ratio * page->objects / 100) {
+				slab_unlock(page);
+				/*
+				 * Skip slab because the object density
+				 * in the slab page is high enough.
+				 */
+				continue;
+			}
+
+			list_move(&page->lru, &move_list);
+			if (s->migrate) {
+				/* Stop page being considered for allocations */
+				n->nr_partial--;
+				page->frozen = 1;
+			}
+			slab_unlock(page);
+		} else {	/* Empty slab page */
+			list_del(&page->lru);
+			n->nr_partial--;
+			slab_unlock(page);
+			discard_slab(s, page);
+		}
+	}
+
+	if (!s->migrate) {
+		/*
+		 * No defrag method. By simply putting the zaplist at the
+		 * end of the partial list we can let them simmer longer
+		 * and thus increase the chance of all objects being
+		 * reclaimed.
+		 *
+		 */
+		list_splice(&move_list, n->partial.prev);
+	}
+
+	spin_unlock_irqrestore(&n->list_lock, flags);
+
+	if (s->migrate && !list_empty(&move_list)) {
+		void **scratch = alloc_scratch(s);
+		struct page *page, *page2;
+
+		if (scratch) {
+			/* Try to remove / move the objects left */
+			list_for_each_entry(page, &move_list, lru) {
+				if (page->inuse)
+					__move(page, scratch, target_node);
+			}
+			kfree(scratch);
+		}
+
+		/* Inspect results and dispose of pages */
+		spin_lock_irqsave(&n->list_lock, flags);
+		list_for_each_entry_safe(page, page2, &move_list, lru) {
+			list_del(&page->lru);
+			slab_lock(page);
+			page->frozen = 0;
+
+			if (page->inuse) {
+				/*
+				 * Objects left in slab page, move it to the
+				 * tail of the partial list to increase the
+				 * chance that the freeing of the remaining
+				 * objects will free the slab page.
+				 */
+				n->nr_partial++;
+				list_add_tail(&page->lru, &n->partial);
+				slab_unlock(page);
+			} else {
+				slab_unlock(page);
+				discard_slab(s, page);
+			}
+		}
+		spin_unlock_irqrestore(&n->list_lock, flags);
+	}
+
+	return n->nr_partial;
+}
+
+/**
+ * kmem_cache_defrag() - Defrag slab caches.
+ * @node: The node to defrag or -1 for all nodes.
+ *
+ * Defrag slabs conditional on the amount of fragmentation in a page.
+ */
+int kmem_cache_defrag(int node)
+{
+	struct kmem_cache *s;
+	unsigned long left = 0;
+
+	/*
+	 * kmem_cache_defrag may be called from the reclaim path which may be
+	 * called for any page allocator alloc. So there is the danger that we
+	 * get called in a situation where slub already acquired the slub_lock
+	 * for other purposes.
+	 */
+	if (!mutex_trylock(&slab_mutex))
+		return 0;
+
+	list_for_each_entry(s, &slab_caches, list) {
+		/*
+		 * Defragmentable caches come first. If the slab cache is not
+		 * defragmentable then we can stop traversing the list.
+		 */
+		if (!s->migrate)
+			break;
+
+		if (node == -1) {
+			int nid;
+
+			for_each_node_state(nid, N_NORMAL_MEMORY)
+				if (s->node[nid]->nr_partial > MAX_PARTIAL)
+					left += __defrag(s, nid, nid, s->defrag_used_ratio);
+		} else {
+			if (s->node[node]->nr_partial > MAX_PARTIAL)
+				left += __defrag(s, node, node, s->defrag_used_ratio);
+		}
+	}
+	mutex_unlock(&slab_mutex);
+	return left;
+}
+EXPORT_SYMBOL(kmem_cache_defrag);
+
+/**
+ * __kmem_cache_shrink() - Shrink a cache.
+ * @s: The cache to shrink.
+ *
+ * Reduces the memory footprint of a slab cache by as much as possible.
+ *
+ * This works by:
+ *  1. Removing empty slabs from the partial list.

mm/slub.c static void __move(struct page *page, void *scratch, int node) · line 4524

Quoted 10 lines
mm/slub.c +10 -0
+ *  2. Migrating slab objects to denser slab pages if the slab cache
+ *  supports migration.  If not, reorganizing the partial list so that
+ *  more densely allocated slab pages come first.
+ *
+ * Not called directly, called by kmem_cache_shrink().
+ */
+int __kmem_cache_shrink(struct kmem_cache *s)
+{
+	int node;
+	int left = 0;

s/int/unsigned long? Or s/unsigned long/int in __defrag()?

Thanks!

#30 lore raw

Re: [RFC 09/15] slub: Enable slab defragmentation using SMO

Quoted 350 lines · Roman Gushchin
Quoted 180 lines · Tobin C. Harding

If many objects are allocated with the slab allocator and freed in an arbitrary order then the slab caches can become internally fragmented. Now that the slab allocator supports movable objects we can defragment any cache that has this feature enabled.

Slab defragmentation may occur:

1. Unconditionally when __kmem_cache_shrink() is called on a slab cache by the kernel calling kmem_cache_shrink().

2. Unconditionally through the use of the slabinfo command.

slabinfo <cache> -s

3. Conditionally via the use of kmem_cache_defrag()

Use SMO when shrinking cache. Currently when the kernel calls kmem_cache_shrink() we curate the partial slabs list. If object migration is not enabled for the cache we still do this, if however SMO is enabled, we attempt to move objects in partially full slabs in order to defragment the cache. Shrink attempts to move all objects in order to reduce the cache to a single partial slab for each node.

kmem_cache_defrag() differs from shrink in that it operates dependent on the defrag_used_ratio and only attempts to move objects if the number of partial slabs exceeds MAX_PARTIAL (for each node).

Add function kmem_cache_defrag(int node).

kmem_cache_defrag() only performs defragmentation if the usage ratio of the slab is lower than the configured percentage (sysfs file added in previous patch). Fragmentation ratios are measured by calculating the percentage of objects in use compared to the total number of objects that the slab page can accommodate.

The scanning of slab caches is optimized because the defragmentable slabs come first on the list. Thus we can terminate scans on the first slab encountered that does not support defragmentation.

kmem_cache_defrag() takes a node parameter. This can either be -1 if defragmentation should be performed on all nodes, or a node number.

Defragmentation may be disabled by setting defrag ratio to 0

echo 0 > /sys/kernel/slab/<cache>/defrag_used_ratio

In order for a cache to be defragmentable the cache must support object migration (SMO). Enabling SMO for a cache is done via a call to the recently added function:

	void kmem_cache_setup_mobility(struct kmem_cache *,
				       kmem_cache_isolate_func,
			               kmem_cache_migrate_func);
---
 include/linux/slab.h |   1 +
 mm/slub.c            | 266 +++++++++++++++++++++++++++++++------------
 2 files changed, 194 insertions(+), 73 deletions(-)
include/linux/slab.h +1 -0
index 22e87c41b8a4..b9b46bc9937e 100644
@@ -147,6 +147,7 @@ struct kmem_cache *kmem_cache_create_usercopy(const char *name,
 			void (*ctor)(void *));
 void kmem_cache_destroy(struct kmem_cache *);
 int kmem_cache_shrink(struct kmem_cache *);
+int kmem_cache_defrag(int node);
 
 void memcg_create_kmem_cache(struct mem_cgroup *, struct kmem_cache *);
 void memcg_deactivate_kmem_caches(struct mem_cgroup *);
mm/slub.c +12 -73
index 515db0f36c55..53dd4cb5b5a4 100644
@@ -354,6 +354,12 @@ static __always_inline void slab_lock(struct page *page)
 	bit_spin_lock(PG_locked, &page->flags);
 }
 
+static __always_inline int slab_trylock(struct page *page)
+{
+	VM_BUG_ON_PAGE(PageTail(page), page);
+	return bit_spin_trylock(PG_locked, &page->flags);
+}
+
 static __always_inline void slab_unlock(struct page *page)
 {
 	VM_BUG_ON_PAGE(PageTail(page), page);
@@ -3959,79 +3965,6 @@ void kfree(const void *x)
 }
 EXPORT_SYMBOL(kfree);
 
-#define SHRINK_PROMOTE_MAX 32
-
-/*
- * kmem_cache_shrink discards empty slabs and promotes the slabs filled
- * up most to the head of the partial lists. New allocations will then
- * fill those up and thus they can be removed from the partial lists.
- *
- * The slabs with the least items are placed last. This results in them
- * being allocated from last increasing the chance that the last objects
- * are freed in them.
- */
-int __kmem_cache_shrink(struct kmem_cache *s)
-{
-	int node;
-	int i;
-	struct kmem_cache_node *n;
-	struct page *page;
-	struct page *t;
-	struct list_head discard;
-	struct list_head promote[SHRINK_PROMOTE_MAX];
-	unsigned long flags;
-	int ret = 0;
-
-	flush_all(s);
-	for_each_kmem_cache_node(s, node, n) {
-		INIT_LIST_HEAD(&discard);
-		for (i = 0; i < SHRINK_PROMOTE_MAX; i++)
-			INIT_LIST_HEAD(promote + i);
-
-		spin_lock_irqsave(&n->list_lock, flags);
-
-		/*
-		 * Build lists of slabs to discard or promote.
-		 *
-		 * Note that concurrent frees may occur while we hold the
-		 * list_lock. page->inuse here is the upper limit.
-		 */
-		list_for_each_entry_safe(page, t, &n->partial, lru) {
-			int free = page->objects - page->inuse;
-
-			/* Do not reread page->inuse */
-			barrier();
-
-			/* We do not keep full slabs on the list */
-			BUG_ON(free <= 0);
-
-			if (free == page->objects) {
-				list_move(&page->lru, &discard);
-				n->nr_partial--;
-			} else if (free <= SHRINK_PROMOTE_MAX)
-				list_move(&page->lru, promote + free - 1);
-		}
-
-		/*
-		 * Promote the slabs filled up most to the head of the
-		 * partial list.
-		 */
-		for (i = SHRINK_PROMOTE_MAX - 1; i >= 0; i--)
-			list_splice(promote + i, &n->partial);
-
-		spin_unlock_irqrestore(&n->list_lock, flags);
-
-		/* Release empty slabs */
-		list_for_each_entry_safe(page, t, &discard, lru)
-			discard_slab(s, page);
-
-		if (slabs_node(s, node))
-			ret = 1;
-	}
-
-	return ret;
-}
-
 #ifdef CONFIG_MEMCG
 static void kmemcg_cache_deact_after_rcu(struct kmem_cache *s)
 {
@@ -4411,6 +4344,193 @@ static void __move(struct page *page, void *scratch, int node)
 	s->migrate(s, vector, count, node, private);
 }
 
+/*
+ * __defrag() - Defragment node.
+ * @s: cache we are working on.
+ * @node: The node to move objects from.
+ * @target_node: The node to move objects to.
+ * @ratio: The defrag ratio (percentage, between 0 and 100).

mm/slub.c static void __move(struct page *page, void *scratch, int node) · line 4362

Quoted 10 lines
mm/slub.c +10 -0
+ *
+ * Release slabs with zero objects and try to call the migration function
+ * for slabs with less than the 'ratio' percentage of objects allocated.
+ *
+ * Moved objects are allocated on @target_node.
+ *
+ * Return: The number of partial slabs left on the node after the operation.
+ */
+static unsigned long __defrag(struct kmem_cache *s, int node, int target_node,
+			      int ratio)

Maybe kmem_cache_defrag_node()?

Quoted 156 lines
mm/slub.c +156 -0
+{
+	struct kmem_cache_node *n = get_node(s, node);
+	struct page *page, *page2;
+	LIST_HEAD(move_list);
+	unsigned long flags;
+
+	if (node == target_node && n->nr_partial <= 1) {
+		/*
+		 * Trying to reduce fragmentation on a node but there is
+		 * only a single or no partial slab page. This is already
+		 * the optimal object density that we can reach.
+		 */
+		return n->nr_partial;
+	}
+
+	spin_lock_irqsave(&n->list_lock, flags);
+	list_for_each_entry_safe(page, page2, &n->partial, lru) {
+		if (!slab_trylock(page))
+			/* Busy slab. Get out of the way */
+			continue;
+
+		if (page->inuse) {
+			if (page->inuse > ratio * page->objects / 100) {
+				slab_unlock(page);
+				/*
+				 * Skip slab because the object density
+				 * in the slab page is high enough.
+				 */
+				continue;
+			}
+
+			list_move(&page->lru, &move_list);
+			if (s->migrate) {
+				/* Stop page being considered for allocations */
+				n->nr_partial--;
+				page->frozen = 1;
+			}
+			slab_unlock(page);
+		} else {	/* Empty slab page */
+			list_del(&page->lru);
+			n->nr_partial--;
+			slab_unlock(page);
+			discard_slab(s, page);
+		}
+	}
+
+	if (!s->migrate) {
+		/*
+		 * No defrag method. By simply putting the zaplist at the
+		 * end of the partial list we can let them simmer longer
+		 * and thus increase the chance of all objects being
+		 * reclaimed.
+		 *
+		 */
+		list_splice(&move_list, n->partial.prev);
+	}
+
+	spin_unlock_irqrestore(&n->list_lock, flags);
+
+	if (s->migrate && !list_empty(&move_list)) {
+		void **scratch = alloc_scratch(s);
+		struct page *page, *page2;
+
+		if (scratch) {
+			/* Try to remove / move the objects left */
+			list_for_each_entry(page, &move_list, lru) {
+				if (page->inuse)
+					__move(page, scratch, target_node);
+			}
+			kfree(scratch);
+		}
+
+		/* Inspect results and dispose of pages */
+		spin_lock_irqsave(&n->list_lock, flags);
+		list_for_each_entry_safe(page, page2, &move_list, lru) {
+			list_del(&page->lru);
+			slab_lock(page);
+			page->frozen = 0;
+
+			if (page->inuse) {
+				/*
+				 * Objects left in slab page, move it to the
+				 * tail of the partial list to increase the
+				 * chance that the freeing of the remaining
+				 * objects will free the slab page.
+				 */
+				n->nr_partial++;
+				list_add_tail(&page->lru, &n->partial);
+				slab_unlock(page);
+			} else {
+				slab_unlock(page);
+				discard_slab(s, page);
+			}
+		}
+		spin_unlock_irqrestore(&n->list_lock, flags);
+	}
+
+	return n->nr_partial;
+}
+
+/**
+ * kmem_cache_defrag() - Defrag slab caches.
+ * @node: The node to defrag or -1 for all nodes.
+ *
+ * Defrag slabs conditional on the amount of fragmentation in a page.
+ */
+int kmem_cache_defrag(int node)
+{
+	struct kmem_cache *s;
+	unsigned long left = 0;
+
+	/*
+	 * kmem_cache_defrag may be called from the reclaim path which may be
+	 * called for any page allocator alloc. So there is the danger that we
+	 * get called in a situation where slub already acquired the slub_lock
+	 * for other purposes.
+	 */
+	if (!mutex_trylock(&slab_mutex))
+		return 0;
+
+	list_for_each_entry(s, &slab_caches, list) {
+		/*
+		 * Defragmentable caches come first. If the slab cache is not
+		 * defragmentable then we can stop traversing the list.
+		 */
+		if (!s->migrate)
+			break;
+
+		if (node == -1) {
+			int nid;
+
+			for_each_node_state(nid, N_NORMAL_MEMORY)
+				if (s->node[nid]->nr_partial > MAX_PARTIAL)
+					left += __defrag(s, nid, nid, s->defrag_used_ratio);
+		} else {
+			if (s->node[node]->nr_partial > MAX_PARTIAL)
+				left += __defrag(s, node, node, s->defrag_used_ratio);
+		}
+	}
+	mutex_unlock(&slab_mutex);
+	return left;
+}
+EXPORT_SYMBOL(kmem_cache_defrag);
+
+/**
+ * __kmem_cache_shrink() - Shrink a cache.
+ * @s: The cache to shrink.
+ *
+ * Reduces the memory footprint of a slab cache by as much as possible.
+ *
+ * This works by:
+ *  1. Removing empty slabs from the partial list.
+ *  2. Migrating slab objects to denser slab pages if the slab cache
+ *  supports migration.  If not, reorganizing the partial list so that
+ *  more densely allocated slab pages come first.
+ *
Quoted 8 lines

mm/slub.c static void __move(struct page *page, void *scratch, int node) · line 4524

Quoted 6 lines
mm/slub.c +6 -0
+ * Not called directly, called by kmem_cache_shrink().
+ */
+int __kmem_cache_shrink(struct kmem_cache *s)
+{
+	int node;
+	int left = 0;

s/int/unsigned long? Or s/unsigned long/int in __defrag()?

Nice catch, thank you.

Tobin

#31 lore raw

RFC 10/15 tools/testing/slab: Add object migration test module

In reply to Tobin C. Harding

We just implemented slab movable objects for the SLUB allocator. We should test that code. In order to do so we need to be able to a number of things

- Create a cache
- Allocate objects to the cache
- Free objects from within specific slabs of the cache
- Enable Slab Movable Objects for the cache

We can do all this via a loadable module.

Add a module that defines functions that can be triggered from userspace via a debugfs entry. From the source:

/*
* SLUB defragmentation a.k.a. Slab Movable Objects (SMO). *
* This module is used for testing the SLUB allocator. Enables
* userspace to run kernel functions via a debugfs file.
*
* debugfs: /sys/kernel/debugfs/smo/callfn (write only)
*
* String written to callfn is parsed by the module and associated
* function is called. See fn_tab for mapping of strings to functions. */

References to allocated objects are kept by the module in a linked list so that userspace can control which object to free.

We introduce the following four functions via the function table

"alloc X": Allocates X objects
"free X [Y]": Frees X objects starting at list position Y (default Y==0) "enable": Enables object migration for the test cache. "test": Runs [stress] tests from within the module (see below).

       {"alloc", smo_alloc_objects},
       {"free", smo_free_object},
       {"enable", smo_enable_cache_mobility},
       {"test", smo_run_module_tests},

Freeing from the start of the list creates a hole in the slab being freed from (i.e. creates a partial slab). The results of running these commands can be see using slabinfo (available in tools/vm/):

make -o slabinfo tools/vm/slabinfo.c

Stress tests can be run from within the module. These tests are internal to the module because we verify that object references are still good after object migration. These are called 'stress' tests because it is intended that they create/free a lot of objects. Userspace can control the number of objects to create, default is 1000.

Example test session
--------------------

Relevant /proc/slabinfo column headers:

name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab>

  # mount -t debugfs none /sys/kernel/debug/
  $ cd path/to/linux/tools/testing/slab; make
  ...
  # insmod slub_defrag.ko
  # cat /proc/slabinfo | grep smo_test | sed 's/:.*//'
  smo_test               0      0    392   20    2

From this we can see that the module created cache 'smo_test' with 20 objects per slab and 2 pages per slab (and cache is currently empty).

We can run the stress tests (with the default number of objects):

  # cd /sys/kernel/debug/smo
  # echo 'test' > callfn
  [    3.576617] smo: test using nr_objs: 1000 keep: 10
  [    3.580169] smo: Module tests completed successfully

And we can play with the slab allocator manually:

  # insmod slub_defrag.ko
  # echo 'alloc 21' > callfn
  # cat /proc/slabinfo | grep smo_test | sed 's/:.*//'
  smo_test              21     40    392   20    2

We see here that 21 active objects have been allocated creating 2 slabs (40 total objects).

  # slabinfo smo_test --report

Slabcache: smo_test Aliases: 0 Order : 1 Objects: 21

Sizes (bytes) Slabs Debug Memory
------------------------------------------------------------------------

  Object :      56  Total  :       2   Sanity Checks : On   Total:   16384
  SlabObj:     392  Full   :       1   Redzoning     : On   Used :    1176
  SlabSiz:    8192  Partial:       1   Poisoning     : On   Loss :   15208
  Loss   :     336  CpuSlab:       0   Tracking      : On   Lalig:    7056
  Align  :       8  Objects:      20   Tracing       : Off  Lpadd:     704

Now free an object from the first slot of the first slab

  # echo 'free 1' > callfn
  # cat /proc/slabinfo | grep smo_test | sed 's/:.*//'
  smo_test              20     40    392   20    2
  # slabinfo smo_test --report

Slabcache: smo_test Aliases: 0 Order : 1 Objects: 20

Sizes (bytes) Slabs Debug Memory
------------------------------------------------------------------------

  Object :      56  Total  :       2   Sanity Checks : On   Total:   16384
  SlabObj:     392  Full   :       0   Redzoning     : On   Used :    1120
  SlabSiz:    8192  Partial:       2   Poisoning     : On   Loss :   15264
  Loss   :     336  CpuSlab:       0   Tracking      : On   Lalig:    6720
  Align  :       8  Objects:      20   Tracing       : Off  Lpadd:     704

Calling shrink now on the cache does nothing because object migration is not enabled (output omitted). If we enable object migration then shrink the cache we expect the object from the second slab to me moved to the first slot in the first slab and the second slab to be removed from the partial list.

  # echo 'enable' > callfn
  # slabinfo smo_test --shrink
  # slabinfo smo_test --report

Slabcache: smo_test Aliases: 0 Order : 1 Objects: 20
** Defragmentation at 30%

Sizes (bytes) Slabs Debug Memory
------------------------------------------------------------------------

  Object :      56  Total  :       1   Sanity Checks : On   Total:    8192
  SlabObj:     392  Full   :       1   Redzoning     : On   Used :    1120
  SlabSiz:    8192  Partial:       0   Poisoning     : On   Loss :    7072
  Loss   :     336  CpuSlab:       0   Tracking      : On   Lalig:    6720
  Align  :       8  Objects:      20   Tracing       : Off  Lpadd:     352
---
 tools/testing/slab/Makefile      |  10 +
 tools/testing/slab/slub_defrag.c | 566 +++++++++++++++++++++++++++++++
 2 files changed, 576 insertions(+)
 create mode 100644 tools/testing/slab/Makefile
 create mode 100644 tools/testing/slab/slub_defrag.c
tools/testing/slab/Makefile new file +10 -0
new file mode 100644
index 000000000000..440c2e3e356f
@@ -0,0 +1,10 @@
+obj-m += slub_defrag.o
+
+KTREE=../../..
+
+all:
+	make -C ${KTREE} M=$(PWD) modules
+
+clean:
+	make -C ${KTREE} M=$(PWD) clean
+
tools/testing/slab/slub_defrag.c new file +566 -0

566 lines not on this page · raw message

Signature · 1 line
2.21.0
#32 lore raw

RFC 11/15 tools/testing/slab: Add object migration test suite

In reply to Tobin C. Harding

We just added a module that enables testing the SLUB allocators ability to defrag/shrink caches via movable objects. Tests are better when they are automated.

Add automated testing via a python script for SLUB movable objects.

Example output:

  $ cd path/to/linux/tools/testing/slab
  $ /slub_defrag.py
  Please run script as root
  $ sudo ./slub_defrag.py
  <test are quiet, no output on success>
  $ sudo ./slub_defrag.py --debug
  Loading module ...
  Slab cache smo_test created
  Objects per slab: 20
  Running sanity checks ...

Running module stress test (see dmesg for additional test output) ... Removing module slub_defrag ...
Loading module ...
Slab cache smo_test created

Running test non-movable ...
testing slab 'smo_test' prior to enabling movable objects ... verified non-movable slabs are NOT shrinkable

Running test movable ...
testing slab 'smo_test' after enabling movable objects ... verified movable slabs are shrinkable

Removing module slub_defrag ...

---
 tools/testing/slab/slub_defrag.c  |   1 +
 tools/testing/slab/slub_defrag.py | 451 ++++++++++++++++++++++++++++++
 2 files changed, 452 insertions(+)
 create mode 100755 tools/testing/slab/slub_defrag.py
tools/testing/slab/slub_defrag.c +1 -0
index 502ddd8a67e8..206545d62021 100644
@@ -337,6 +337,7 @@ static int smo_run_module_tests(int nr_objs, int keep)
 
 /*
  * struct functions() - Map command to a function pointer.
+ * If you update this please update the documentation in slub_defrag.py
  */
 struct functions {
 	char *fn_name;
tools/testing/slab/slub_defrag.py new file +451 -0

451 lines not on this page · raw message

Signature · 1 line
2.21.0
#33 lore raw

RFC 12/15 xarray: Implement migration function for objects

In reply to Tobin C. Harding

Implement functions to migrate objects. This is based on initial code by Matthew Wilcox and was modified to work with slab object migration.

---
 lib/radix-tree.c | 13 +++++++++++++
 lib/xarray.c     | 44 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 57 insertions(+)
lib/radix-tree.c +13 -0
index 14d51548bea6..9412c2853726 100644
@@ -1613,6 +1613,17 @@ static int radix_tree_cpu_dead(unsigned int cpu)
 	return 0;
 }
 
+extern void xa_object_migrate(void *tree_node, int numa_node);
+
+static void radix_tree_migrate(struct kmem_cache *s, void **objects, int nr,
+			       int node, void *private)
+{
+	int i;
+
+	for (i = 0; i < nr; i++)
+		xa_object_migrate(objects[i], node);
+}
+
 void __init radix_tree_init(void)
 {
 	int ret;
@@ -1627,4 +1638,6 @@ void __init radix_tree_init(void)
 	ret = cpuhp_setup_state_nocalls(CPUHP_RADIX_DEAD, "lib/radix:dead",
 					NULL, radix_tree_cpu_dead);
 	WARN_ON(ret < 0);
+	kmem_cache_setup_mobility(radix_tree_node_cachep, NULL,
+				  radix_tree_migrate);
 }
lib/xarray.c +44 -0

50 lines not on this page · raw message

Signature · 1 line
2.21.0
#34 lore raw

Re: [RFC 12/15] xarray: Implement migration function for objects

Quoted 48 lines · Tobin C. Harding

Implement functions to migrate objects. This is based on initial code by Matthew Wilcox and was modified to work with slab object migration.

---
 lib/radix-tree.c | 13 +++++++++++++
 lib/xarray.c     | 44 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 57 insertions(+)
lib/radix-tree.c +13 -0
index 14d51548bea6..9412c2853726 100644
@@ -1613,6 +1613,17 @@ static int radix_tree_cpu_dead(unsigned int cpu)
 	return 0;
 }
 
+extern void xa_object_migrate(void *tree_node, int numa_node);
+
+static void radix_tree_migrate(struct kmem_cache *s, void **objects, int nr,
+			       int node, void *private)
+{
+	int i;
+
+	for (i = 0; i < nr; i++)
+		xa_object_migrate(objects[i], node);
+}
+
 void __init radix_tree_init(void)
 {
 	int ret;
@@ -1627,4 +1638,6 @@ void __init radix_tree_init(void)
 	ret = cpuhp_setup_state_nocalls(CPUHP_RADIX_DEAD, "lib/radix:dead",
 					NULL, radix_tree_cpu_dead);
 	WARN_ON(ret < 0);
+	kmem_cache_setup_mobility(radix_tree_node_cachep, NULL,
+				  radix_tree_migrate);
 }
lib/xarray.c +0 -0
index 81c3171ddde9..4f6f17c87769 100644
@@ -1950,6 +1950,50 @@ void xa_destroy(struct xarray *xa)
 }
 EXPORT_SYMBOL(xa_destroy);
 

lib/xarray.c void xa_destroy(struct xarray *xa) · line 1962

Quoted 10 lines
lib/xarray.c +10 -0
+void xa_object_migrate(struct xa_node *node, int numa_node)
+{
+	struct xarray *xa = READ_ONCE(node->array);
+	void __rcu **slot;
+	struct xa_node *new_node;
+	int i;
+
+	/* Freed or not yet in tree then skip */
+	if (!xa || xa == XA_FREE_MARK)
+		return;

XA_FREE_MARK is equal to 0, so the second check is redundant.

#define XA_MARK_0 ((__force xa_mark_t)0U)
#define XA_MARK_1 ((__force xa_mark_t)1U)
#define XA_MARK_2 ((__force xa_mark_t)2U)
#define XA_PRESENT ((__force xa_mark_t)8U)
#define XA_MARK_MAX XA_MARK_2
#define XA_FREE_MARK XA_MARK_0

xa_node_free() sets node->array to XA_RCU_FREE, so maybe it's what you need. I'm not sure however, Matthew should know better.

lib/xarray.c void xa_destroy(struct xarray *xa) · line 1965

Quoted 3 lines
lib/xarray.c +3 -0
+
+	new_node = kmem_cache_alloc_node(radix_tree_node_cachep,
+					 GFP_KERNEL, numa_node);

We need to check here if the allocation was successful.

Thanks!

#35 lore raw

Re: [RFC 12/15] xarray: Implement migration function for objects

Quoted 69 lines · Roman Gushchin
Quoted 48 lines · Tobin C. Harding

Implement functions to migrate objects. This is based on initial code by Matthew Wilcox and was modified to work with slab object migration.

---
 lib/radix-tree.c | 13 +++++++++++++
 lib/xarray.c     | 44 ++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 57 insertions(+)
lib/radix-tree.c +13 -0
index 14d51548bea6..9412c2853726 100644
@@ -1613,6 +1613,17 @@ static int radix_tree_cpu_dead(unsigned int cpu)
 	return 0;
 }
 
+extern void xa_object_migrate(void *tree_node, int numa_node);
+
+static void radix_tree_migrate(struct kmem_cache *s, void **objects, int nr,
+			       int node, void *private)
+{
+	int i;
+
+	for (i = 0; i < nr; i++)
+		xa_object_migrate(objects[i], node);
+}
+
 void __init radix_tree_init(void)
 {
 	int ret;
@@ -1627,4 +1638,6 @@ void __init radix_tree_init(void)
 	ret = cpuhp_setup_state_nocalls(CPUHP_RADIX_DEAD, "lib/radix:dead",
 					NULL, radix_tree_cpu_dead);
 	WARN_ON(ret < 0);
+	kmem_cache_setup_mobility(radix_tree_node_cachep, NULL,
+				  radix_tree_migrate);
 }
lib/xarray.c +0 -0
index 81c3171ddde9..4f6f17c87769 100644
@@ -1950,6 +1950,50 @@ void xa_destroy(struct xarray *xa)
 }
 EXPORT_SYMBOL(xa_destroy);
 

lib/xarray.c void xa_destroy(struct xarray *xa) · line 1962

Quoted 10 lines
lib/xarray.c +10 -0
+void xa_object_migrate(struct xa_node *node, int numa_node)
+{
+	struct xarray *xa = READ_ONCE(node->array);
+	void __rcu **slot;
+	struct xa_node *new_node;
+	int i;
+
+	/* Freed or not yet in tree then skip */
+	if (!xa || xa == XA_FREE_MARK)
+		return;

XA_FREE_MARK is equal to 0, so the second check is redundant.

#define XA_MARK_0 ((__force xa_mark_t)0U)
#define XA_MARK_1 ((__force xa_mark_t)1U)
#define XA_MARK_2 ((__force xa_mark_t)2U)
#define XA_PRESENT ((__force xa_mark_t)8U)
#define XA_MARK_MAX XA_MARK_2
#define XA_FREE_MARK XA_MARK_0

Quoted 2 lines

xa_node_free() sets node->array to XA_RCU_FREE, so maybe it's what you need. I'm not sure however, Matthew should know better

Cheers, will wait for his input.

Quoted 5 lines

lib/xarray.c void xa_destroy(struct xarray *xa) · line 1965

Quoted 3 lines
lib/xarray.c +3 -0
+
+	new_node = kmem_cache_alloc_node(radix_tree_node_cachep,
+					 GFP_KERNEL, numa_node);

We need to check here if the allocation was successful.

Woops, bad Tobin. Thanks.

Tobin.

#36 lore raw

RFC 13/15 tools/testing/slab: Add XArray movable objects tests

In reply to Tobin C. Harding

We just implemented movable objects for the XArray. Let's test it intree.

Add test module for the XArray's movable objects implementation.

Functionality of the XArray Slab Movable Object implementation can usually be seen by simply by using slabinfo on a running machine since the radix tree is typically in use on a running machine and will have partial slabs. For repeated testing we can use the test module to run to simulate a workload on the XArray then use slabinfo to test object migration is functioning.

If testing on freshly spun up VM (low radix tree workload) it may be necessary to load/unload the module a number of times to create partial slabs.

Example test session
--------------------

Relevant /proc/slabinfo column headers:

name <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab>

Prior to testing slabinfo report for radix_tree_node:

  # slabinfo radix_tree_node --report

Slabcache: radix_tree_node Aliases: 0 Order : 2 Objects: 8352 ** Reclaim accounting active
** Defragmentation at 30%

Sizes (bytes) Slabs Debug Memory
------------------------------------------------------------------------

  Object :     576  Total  :     497   Sanity Checks : On   Total: 8142848
  SlabObj:     912  Full   :     473   Redzoning     : On   Used : 4810752
  SlabSiz:   16384  Partial:      24   Poisoning     : On   Loss : 3332096
  Loss   :     336  CpuSlab:       0   Tracking      : On   Lalig: 2806272
  Align  :       8  Objects:      17   Tracing       : Off  Lpadd:  437360

Here you can see the kernel was built with Slab Movable Objects enabled for the XArray (XArray uses the radix tree below the surface).

After inserting the test module (note we have triggered allocation of a number of radix tree nodes increasing the object count but decreasing the number of partial slabs):

  # slabinfo radix_tree_node --report

Slabcache: radix_tree_node Aliases: 0 Order : 2 Objects: 8442 ** Reclaim accounting active
** Defragmentation at 30%

Sizes (bytes) Slabs Debug Memory
------------------------------------------------------------------------

  Object :     576  Total  :     499   Sanity Checks : On   Total: 8175616
  SlabObj:     912  Full   :     484   Redzoning     : On   Used : 4862592
  SlabSiz:   16384  Partial:      15   Poisoning     : On   Loss : 3313024
  Loss   :     336  CpuSlab:       0   Tracking      : On   Lalig: 2836512
  Align  :       8  Objects:      17   Tracing       : Off  Lpadd:  439120

Now we can shrink the radix_tree_node cache:

  # slabinfo radix_tree_node --shrink
  # slabinfo radix_tree_node --report

Slabcache: radix_tree_node Aliases: 0 Order : 2 Objects: 8515 ** Reclaim accounting active
** Defragmentation at 30%

Sizes (bytes) Slabs Debug Memory
------------------------------------------------------------------------

  Object :     576  Total  :     501   Sanity Checks : On   Total: 8208384
  SlabObj:     912  Full   :     500   Redzoning     : On   Used : 4904640
  SlabSiz:   16384  Partial:       1   Poisoning     : On   Loss : 3303744
  Loss   :     336  CpuSlab:       0   Tracking      : On   Lalig: 2861040
  Align  :       8  Objects:      17   Tracing       : Off  Lpadd:  440880

Note the single remaining partial slab.

---
 tools/testing/slab/Makefile             |   2 +-
 tools/testing/slab/slub_defrag_xarray.c | 211 ++++++++++++++++++++++++
 2 files changed, 212 insertions(+), 1 deletion(-)
 create mode 100644 tools/testing/slab/slub_defrag_xarray.c
tools/testing/slab/Makefile +1 -1
index 440c2e3e356f..44c18d9a4d52 100644
@@ -1,4 +1,4 @@
-obj-m += slub_defrag.o
+obj-m += slub_defrag.o slub_defrag_xarray.o
 
 KTREE=../../..
 
tools/testing/slab/slub_defrag_xarray.c new file +211 -0

211 lines not on this page · raw message

Signature · 1 line
2.21.0
#37 lore raw

RFC 14/15 slub: Enable move _all_ objects to node

In reply to Tobin C. Harding

We have just implemented Slab Movable Objects (object migration). Currently object migration is used to defrag a cache. On NUMA systems it would be nice to be able to move objects and control the source and destination nodes.

Add CONFIG_SMO_NODE to guard this feature. CONFIG_SMO_NODE depends on CONFIG_SLUB_DEBUG because we use the full list. Leave it like this for the RFC because the patch will be less cluttered to review, separate full list out of CONFIG_DEBUG before doing a PATCH version.

Implement moving all objects (including those in full slabs) to a specific node. Expose this functionality to userspace via a sysfs entry.

Add sysfs entry:

/sysfs/kernel/slab/<cache>/move

With this users get access to the following functionality:

- Move all objects to specified node.

echo "N1" > move

- Move all objects from specified node to other specified node (from N1 -> to N2):

echo "N1 N2" > move

This also enables shrinking slabs on a specific node:

echo "N1 N1" > move

---
 mm/Kconfig |   7 ++
 mm/slub.c  | 249 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 256 insertions(+)
mm/Kconfig +7 -0
index 25c71eb8a7db..47040d939f3b 100644
@@ -258,6 +258,13 @@ config ARCH_ENABLE_HUGEPAGE_MIGRATION
 config ARCH_ENABLE_THP_MIGRATION
 	bool
 
+config SMO_NODE
+       bool "Enable per node control of Slab Movable Objects"
+       depends on SLUB && SYSFS
+       select SLUB_DEBUG
+       help
+         On NUMA systems enable moving objects to and from a specified node.
+
 config PHYS_ADDR_T_64BIT
 	def_bool 64BIT
 
mm/slub.c +249 -0

273 lines not on this page · raw message

Signature · 1 line
2.21.0
#38 lore raw

RFC 15/15 slub: Enable balancing slab objects across nodes

In reply to Tobin C. Harding

We have just implemented Slab Movable Objects (SMO). On NUMA systems slabs can become unbalanced i.e. many objects on one node while other nodes have few objects. Using SMO we can balance the objects across all the nodes.

The algorithm used is as follows:

1. Move all objects to node 0 (this has the effect of defragmenting the

    cache).

2. Calculate the desired number of slabs for each node (this is done using the approximation nr_slabs / nr_nodes).

3. Loop over the nodes moving the desired number of slabs from node 0 to the node.

Feature is conditionally built in with CONFIG_SMO_NODE, this is because we need the full list (we enable SLUB_DEBUG to get this). Future version may separate final list out of SLUB_DEBUG.

Expose this functionality to userspace via a sysfs entry. Add sysfs entry:

/sysfs/kernel/slab/<cache>/balance

Write of '1' to this file triggers balance, no other value accepted.

This feature relies on SMO being enable for the cache, this is done with a call to, after the isolate/migrate functions have been defined.

kmem_cache_setup_mobility(s, isolate, migrate)

---
 mm/slub.c | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 115 insertions(+)
mm/slub.c +115 -0
index ac9b8f592e10..65cf305a70c3 100644
@@ -4584,6 +4584,104 @@ static unsigned long __move_all_objects_to(struct kmem_cache *s, int node)
 
 	return left;
 }
+
+/*
+ * __move_n_slabs() - Attempt to move 'num' slabs to target_node,
+ * Return: The number of slabs moved or error code.
+ */
+static long __move_n_slabs(struct kmem_cache *s, int node, int target_node,
+			   long num)
+{
+	struct kmem_cache_node *n = get_node(s, node);
+	LIST_HEAD(move_list);
+	struct page *page, *page2;
+	unsigned long flags;
+	void **scratch;
+	long done = 0;
+
+	if (node == target_node)
+		return -EINVAL;
+
+	scratch = alloc_scratch(s);
+	if (!scratch)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&n->list_lock, flags);
+	list_for_each_entry_safe(page, page2, &n->full, lru) {
+		if (!slab_trylock(page))
+			/* Busy slab. Get out of the way */
+			continue;
+
+		list_move(&page->lru, &move_list);
+		page->frozen = 1;
+		slab_unlock(page);
+
+		if (++done >= num)
+			break;
+	}
+	spin_unlock_irqrestore(&n->list_lock, flags);
+
+	list_for_each_entry(page, &move_list, lru) {
+		if (page->inuse)
+			__move(page, scratch, target_node);
+	}
+	kfree(scratch);
+
+	/* Inspect results and dispose of pages */
+	spin_lock_irqsave(&n->list_lock, flags);
+	list_for_each_entry_safe(page, page2, &move_list, lru) {
+		list_del(&page->lru);
+		slab_lock(page);
+		page->frozen = 0;
+
+		if (page->inuse) {
+			/*
+			 * This is best effort only, if slab still has
+			 * objects just put it back on the partial list.
+			 */
+			n->nr_partial++;
+			list_add_tail(&page->lru, &n->partial);
+			slab_unlock(page);
+		} else {
+			slab_unlock(page);
+			discard_slab(s, page);
+		}
+	}
+	spin_unlock_irqrestore(&n->list_lock, flags);
+
+	return done;
+}
+
+/*
+ * __balance_nodes_partial() - Balance partial objects.
+ * @s: The cache we are working on.
+ *
+ * Attempt to balance the objects that are in partial slabs evenly
+ * across all nodes.
+ */
+static void __balance_nodes_partial(struct kmem_cache *s)
+{
+	struct kmem_cache_node *n = get_node(s, 0);
+	unsigned long desired_nr_slabs_per_node;
+	unsigned long nr_slabs;
+	int nr_nodes = 0;
+	int nid;
+
+	(void)__move_all_objects_to(s, 0);
+
+	for_each_node_state(nid, N_NORMAL_MEMORY)
+		nr_nodes++;
+
+	nr_slabs = atomic_long_read(&n->nr_slabs);
+	desired_nr_slabs_per_node = nr_slabs / nr_nodes;
+
+	for_each_node_state(nid, N_NORMAL_MEMORY) {
+		if (nid == 0)
+			continue;
+
+		__move_n_slabs(s, 0, nid, desired_nr_slabs_per_node);
+	}
+}
 #endif
 
 /**
@@ -5836,6 +5934,22 @@ static ssize_t move_store(struct kmem_cache *s, const char *buf, size_t length)
 	return length;
 }
 SLAB_ATTR(move);
+
+static ssize_t balance_show(struct kmem_cache *s, char *buf)
+{
+	return 0;
+}
+
+static ssize_t balance_store(struct kmem_cache *s,
+			     const char *buf, size_t length)
+{
+	if (buf[0] == '1')
+		__balance_nodes_partial(s);
+	else
+		return -EINVAL;
+	return length;
+}
+SLAB_ATTR(balance);
 #endif	/* CONFIG_SMO_NODE */
 
 #ifdef CONFIG_NUMA
@@ -5964,6 +6078,7 @@ static struct attribute *slab_attrs[] = {
 	&shrink_attr.attr,
 #ifdef CONFIG_SMO_NODE
 	&move_attr.attr,
+	&balance_attr.attr,
 #endif
 	&slabs_cpu_partial_attr.attr,
 #ifdef CONFIG_SLUB_DEBUG
Signature · 1 line
2.21.0
#39 lore raw
In reply to Tobin C. Harding
Quoted 43 lines · Tobin C. Harding

Hi,

Here is a patch set implementing movable objects within the SLUB allocator. This is work based on Christopher's patch set:

https://lore.kernel.org/patchwork/project/lkml/list/?series=377335

The original code logic is from that set and implemented by Christopher. Clean up, refactoring, documentation, and additional features by myself. Blame for any bugs remaining falls solely with myself. Patches using Christopher's code use the Co-developed-by tag.

After movable objects are implemented a number of useful features become possible. Some of these are implemented in this series, including:

- Cache defragmentation.

Currently the SLUB allocator is susceptible to internal fragmentation. This occurs when a large number of cached objects are allocated and then freed in an arbitrary order. As the cache fragments the number of pages used by the partial slabs list increases. This wastes memory.

Patch set implements the machinery to facilitate conditional cache defragmentation (via kmem_cache_defrag()) and unconditional defragmentation (via kmem_cache_shrink()). Various sysfs knobs are provided to interact with and configure this.

Patch set implements movable objects and cache defragmentation for the XArray.

- Moving objects to and from a specific NUMA node.

- Balancing objects across all NUMA nodes.

We add a test module to facilitate playing around with movable objects and a python test suite that uses the module.

Everything except the NUMA stuff was tested on bare metal, the NUMA stuff was tested with Qemu NUMA emulation.

Possible further work:

Quoted 7 lines

1. Implementing movable objects for the inode and dentry caches.

2. Tying into the page migration and page defragmentation logic so that so far unmovable pages that are in the way of creating a contiguous block of memory will become movable. This would mean checking for slab pages in the migration logic and calling slab to see if it can move the page by migrating all objects.

Hi Tobin!

Very interesting and promising patchset! Looking forward for inode/dentry moving support, might be a big deal for allocating huge pages dynamically.

Thanks!

#40 lore raw
Quoted 53 lines · Roman Gushchin
Quoted 43 lines · Tobin C. Harding

Hi,

Here is a patch set implementing movable objects within the SLUB allocator. This is work based on Christopher's patch set:

https://lore.kernel.org/patchwork/project/lkml/list/?series=377335

The original code logic is from that set and implemented by Christopher. Clean up, refactoring, documentation, and additional features by myself. Blame for any bugs remaining falls solely with myself. Patches using Christopher's code use the Co-developed-by tag.

After movable objects are implemented a number of useful features become possible. Some of these are implemented in this series, including:

- Cache defragmentation.

Currently the SLUB allocator is susceptible to internal fragmentation. This occurs when a large number of cached objects are allocated and then freed in an arbitrary order. As the cache fragments the number of pages used by the partial slabs list increases. This wastes memory.

Patch set implements the machinery to facilitate conditional cache defragmentation (via kmem_cache_defrag()) and unconditional defragmentation (via kmem_cache_shrink()). Various sysfs knobs are provided to interact with and configure this.

Patch set implements movable objects and cache defragmentation for the XArray.

- Moving objects to and from a specific NUMA node.

- Balancing objects across all NUMA nodes.

We add a test module to facilitate playing around with movable objects and a python test suite that uses the module.

Everything except the NUMA stuff was tested on bare metal, the NUMA stuff was tested with Qemu NUMA emulation.

Possible further work:

Quoted 7 lines

1. Implementing movable objects for the inode and dentry caches.

2. Tying into the page migration and page defragmentation logic so that so far unmovable pages that are in the way of creating a contiguous block of memory will become movable. This would mean checking for slab pages in the migration logic and calling slab to see if it can move the page by migrating all objects.

Quoted 4 lines

Hi Tobin!

Very interesting and promising patchset! Looking forward for inode/dentry moving support, might be a big deal for allocating huge pages dynamically.

Thanks Roman, appreciate the support. I'm working on inode and dentry now.

Tobin