slob: poor man's NUMA support.
Subsystems (2)
PATCH 1/1 slob: poor man's NUMA support.
This adds preliminary NUMA support to SLOB, primarily aimed at systems with small nodes (tested all the way down to a 128kB SRAM block), whether asymmetric or otherwise.
We follow the same conventions as SLAB/SLUB, preferring current node placement for new pages, or with explicit placement, if a node has been specified. Presently on UP NUMA this has the side-effect of preferring node#0 allocations (since numa_node_id() == 0, though this could be reworked if we could hand off a pfn to determine node placement), so single-CPU NUMA systems will want to place smaller nodes further out in terms of node id. Once a page has been bound to a node (via explicit node id typing), we only do block allocations from partial free pages that have a matching node id in the page flags.
The current implementation does have some scalability problems, in that all partial free pages are tracked in the global freelist (with contention due to the single spinlock). However, these are things that are being reworked for SMP scalability first, while things like per-node freelists can easily be built on top of this sort of functionality once it's been added.
More background can be found in:
http://marc.info/?l=linux-mm&m=118117916022379&w=2 http://marc.info/?l=linux-mm&m=118170446306199&w=2 http://marc.info/?l=linux-mm&m=118187859420048&w=2
and subsequent threads.
--
include/linux/slab.h | 126 +++++++++++++++++++++++------------------------ include/linux/slab_def.h | 4 + include/linux/slob_def.h | 46 +++++++++++++++++ include/linux/slub_def.h | 6 +- mm/slob.c | 72 ++++++++++++++++++++------ 5 files changed, 172 insertions(+), 82 deletions(-)
include/linux/slab.h +62 -64
@@ -44,7 +44,6 @@ struct kmem_cache *kmem_cache_create(const char *, size_t, size_t, void (*)(void *, struct kmem_cache *, unsigned long));void kmem_cache_destroy (struct kmem_cache *);int kmem_cache_shrink (struct kmem_cache *); -void *kmem_cache_alloc (struct kmem_cache *,gfp_t );void *kmem_cache_zalloc (struct kmem_cache *,gfp_t );void kmem_cache_free (struct kmem_cache *,void *);unsigned int kmem_cache_size (struct kmem_cache *);
@@ -63,16 +62,6 @@ int kmem_ptr_validate(struct kmem_cache *cachep, const void *ptr);sizeof (struct __struct ), __alignof__(struct __struct ),\ (__flags), NULL, NULL) -#ifdef CONFIG_NUMA -extern void *kmem_cache_alloc_node (struct kmem_cache *,gfp_t flags,int node); -#else -static inline void *kmem_cache_alloc_node (struct kmem_cache *cachep, -gfp_t flags,int node) -{ -return kmem_cache_alloc (cachep, flags); -} -#endif -/* * The largest kmalloc size supported by the slab allocators is * 32 megabyte (2^25) or the maximum allocatable page order if that is
@@ -91,7 +80,6 @@ static inline void *kmem_cache_alloc_node(struct kmem_cache *cachep,/* * Common kmalloc functions provided by all allocators */ -void *__kmalloc (size_t ,gfp_t );void *__kzalloc (size_t ,gfp_t );void *__must_check krealloc (const void *,size_t ,gfp_t );void kfree (const void *);
@@ -102,40 +90,6 @@ size_t ksize(const void *);* @n: number of elements. * @size: element size. * @flags: the type of memory to allocate. -*/ -static inline void *kcalloc (size_t n,size_t size,gfp_t flags) -{ -if (n !=0 && size >ULONG_MAX / n) -return NULL ; -return __kzalloc (n * size, flags); -} - -/* -* Allocator specific definitions. These are mainly used to establish optimized -* ways to convert kmalloc() calls to kmem_cache_alloc() invocations by selecting -* the appropriate general cache at compile time. -*/ - -#if defined(CONFIG_SLAB ) || defined(CONFIG_SLUB ) -#ifdef CONFIG_SLUB -#include <linux/slub_def.h> -#else -#include <linux/slab_def.h> -#endif /* !CONFIG_SLUB */ -#else - -/* -* Fallback definitions for an allocator not wanting to provide -* its own optimized kmalloc definitions (like SLOB). -*/ - -/** -* kmalloc - allocate memory -* @size: how many bytes of memory are required. -* @flags: the type of memory to allocate. -* -* kmalloc is the normal method of allocating memory -* in the kernel. * * The @flags argument may be one of: *
@@ -143,7 +97,7 @@ static inline void *kcalloc(size_t n, size_t size, gfp_t flags)* * %GFP_KERNEL - Allocate normal kernel ram. May sleep. * -* %GFP_ATOMIC - Allocation will not sleep. +* %GFP_ATOMIC - Allocation will not sleep. May use emergency pools. * For example, use this inside interrupt handlers. * * %GFP_HIGHUSER - Allocate pages from high memory.
@@ -152,18 +106,22 @@ static inline void *kcalloc(size_t n, size_t size, gfp_t flags)* * %GFP_NOFS - Do not make any fs calls while trying to get memory. * +* %GFP_NOWAIT - Allocation will not sleep. +* +* %GFP_THISNODE - Allocate node-local memory only. +* +* %GFP_DMA - Allocation suitable for DMA. +* Should only be used for kmalloc() caches. Otherwise, use a +* slab created with SLAB_DMA. +* * Also it is possible to set different flags by OR'ing * in one or more of the following additional @flags: * * %__GFP_COLD - Request cache-cold pages instead of * trying to return cache-warm pages. * -* %__GFP_DMA - Request memory from the DMA-capable zone. -* * %__GFP_HIGH - This allocation has high priority and may use emergency pools. * -* %__GFP_HIGHMEM - Allocated memory may be from highmem. -* * %__GFP_NOFAIL - Indicate that this allocation is in no way allowed to fail * (think twice before using). *
@@ -173,24 +131,57 @@ static inline void *kcalloc(size_t n, size_t size, gfp_t flags)* %__GFP_NOWARN - If allocation fails, don't issue any warnings. * * %__GFP_REPEAT - If allocation fails initially, try once more before failing. +* +* There are other flags available as well, but these are not intended +* for general use, and so are not documented here. For a full list of +* potential flags, always refer to linux/gfp.h. */ -static inline void *kmalloc (size_t size,gfp_t flags) +static inline void *kcalloc (size_t n,size_t size,gfp_t flags) { -return __kmalloc (size, flags); +if (n !=0 && size >ULONG_MAX / n) +return NULL ; +return __kzalloc (n * size, flags); } -/** -* kzalloc - allocate memory. The memory is set to zero. -* @size: how many bytes of memory are required. -* @flags: the type of memory to allocate (see kmalloc). +/* +* Allocator specific definitions. These are mainly used to establish optimized +* ways to convert kmalloc() calls to kmem_cache_alloc() invocations by +* selecting the appropriate general cache at compile time. +* +* Allocators must define at least: +* +* kmem_cache_alloc() +* __kmalloc() +* kmalloc() +* kzalloc() +* +* Those wishing to support NUMA must also define: +* +* kmem_cache_alloc_node() +* kmalloc_node() +* +* See each allocator definition file for additional comments and +* implementation notes. */ -static inline void *kzalloc (size_t size,gfp_t flags) -{ -return __kzalloc (size, flags); -} +#ifdef CONFIG_SLUB +#include <linux/slub_def.h> +#elif defined(CONFIG_SLOB ) +#include <linux/slob_def.h> +#else +#include <linux/slab_def.h> #endif -#ifndef CONFIG_NUMA +#if !defined(CONFIG_NUMA ) && !defined(CONFIG_SLOB ) +/** +* kmalloc_node - allocate memory from a specific node +* @size: how many bytes of memory are required. +* @flags: the type of memory to allocate (see kcalloc). +* @node: node to allocate from. +* +* kmalloc() for non-local nodes, used to allocate from a specific node +* if available. Equivalent to kmalloc() in the non-NUMA single-node +* case. +*/ static inline void *kmalloc_node (size_t size,gfp_t flags,int node) {return kmalloc (size, flags);
@@ -200,7 +191,15 @@ static inline void *__kmalloc_node(size_t size, gfp_t flags, int node) {return __kmalloc (size, flags); } -#endif /* !CONFIG_NUMA */ + +void *kmem_cache_alloc (struct kmem_cache *,gfp_t ); + +static inline void *kmem_cache_alloc_node (struct kmem_cache *cachep, +gfp_t flags,int node) +{ +return kmem_cache_alloc (cachep, flags); +} +#endif /* !CONFIG_NUMA && !CONFIG_SLOB */ /* * kmalloc_track_caller is a special version of kmalloc that records the
@@ -247,4 +246,3 @@ extern void *__kmalloc_node_track_caller(size_t, gfp_t, int, void *);#endif /* __KERNEL__ */ #endif /* _LINUX_SLAB_H */ -
include/linux/slab_def.h +4 -0
@@ -25,6 +25,9 @@ struct cache_sizes { };extern struct cache_sizes malloc_sizes[]; +void *kmem_cache_alloc (struct kmem_cache *,gfp_t ); +void *__kmalloc (size_t size,gfp_t flags); +static inline void *kmalloc (size_t size,gfp_t flags) {if (__builtin_constant_p (size)) {
@@ -79,6 +82,7 @@ found:#ifdef CONFIG_NUMA extern void *__kmalloc_node (size_t size,gfp_t flags,int node); +extern void *kmem_cache_alloc_node (struct kmem_cache *,gfp_t flags,int node);static inline void *kmalloc_node (size_t size,gfp_t flags,int node) {
include/linux/slob_def.h new file +46 -0
46 lines not on this page · raw message
include/linux/slub_def.h +5 -1
@@ -153,6 +153,9 @@ static inline struct kmem_cache *kmalloc_slab(size_t size)#define SLUB_DMA 0#endif +void *kmem_cache_alloc (struct kmem_cache *,gfp_t ); +void *__kmalloc (size_t size,gfp_t flags); +static inline void *kmalloc (size_t size,gfp_t flags) {if (__builtin_constant_p (size) && !(flags &SLUB_DMA )) {
@@ -180,7 +183,8 @@ static inline void *kzalloc(size_t size, gfp_t flags) }#ifdef CONFIG_NUMA -extern void *__kmalloc_node (size_t size,gfp_t flags,int node); +void *__kmalloc_node (size_t size,gfp_t flags,int node); +void *kmem_cache_alloc_node (struct kmem_cache *,gfp_t flags,int node);static inline void *kmalloc_node (size_t size,gfp_t flags,int node) {
mm/slob.c +55 -17
175 lines not on this page · raw message
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 3 lines · Paul Mundt
This adds preliminary NUMA support to SLOB, primarily aimed at systems with small nodes (tested all the way down to a 128kB SRAM block), whether asymmetric or otherwise.
Fine by me as well, FWIW. My points about per-cpu/node queues were not to say that I'm really opposed to getting this in first. In a way, you sell yourself short with the patch name: the implementation may be just a basic one, but simplicity is a key point of SLOB... Adding numa awareness to the slob APIs is obviously a key step and makes it much easier to experiment with enhancements to the implementation.
Unless it has been picked up already, I'd call it "initial NUMA support" ;) Thanks! Would be great to hear about your experiences using SLOB as well -- how much memory you're saving, how it performs, etc.
Signature · 7 lines
SUSE Labs, Novell Inc. -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 12 lines · Nick Piggin
Quoted 3 lines · Paul Mundt
This adds preliminary NUMA support to SLOB, primarily aimed at systems with small nodes (tested all the way down to a 128kB SRAM block), whether asymmetric or otherwise.
Fine by me as well, FWIW. My points about per-cpu/node queues were not to say that I'm really opposed to getting this in first. In a way, you sell yourself short with the patch name: the implementation may be just a basic one, but simplicity is a key point of SLOB... Adding numa awareness to the slob APIs is obviously a key step and makes it much easier to experiment with enhancements to the implementation.
Quoted 3 lines
Unless it has been picked up already, I'd call it "initial NUMA support" ;) Thanks! Would be great to hear about your experiences using SLOB as well -- how much memory you're saving, how it performs, etc.
I haven't seen the usual echo from Andrew, so I think Paul should resend it with three Acked-bys.
Signature · 7 lines
Mathematics is the supreme nostalgia of our time. -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Andrew Morton
maintainerQuoted 31 lines · On Tue, 19 Jun 2007 18:06:16 +0900 Paul Mundt <[email protected]> wrote:
This adds preliminary NUMA support to SLOB, primarily aimed at systems with small nodes (tested all the way down to a 128kB SRAM block), whether asymmetric or otherwise.
We follow the same conventions as SLAB/SLUB, preferring current node placement for new pages, or with explicit placement, if a node has been specified. Presently on UP NUMA this has the side-effect of preferring node#0 allocations (since
numa_node_id()== 0, though this could be reworked if we could hand off a pfn to determine node placement), so single-CPU NUMA systems will want to place smaller nodes further out in terms of node id. Once a page has been bound to a node (via explicit node id typing), we only do block allocations from partial free pages that have a matching node id in the page flags.The current implementation does have some scalability problems, in that all partial free pages are tracked in the global freelist (with contention due to the single spinlock). However, these are things that are being reworked for SMP scalability first, while things like per-node freelists can easily be built on top of this sort of functionality once it's been added.
More background can be found in:
http://marc.info/?l=linux-mm&m=118117916022379&w=2 http://marc.info/?l=linux-mm&m=118170446306199&w=2 http://marc.info/?l=linux-mm&m=118187859420048&w=2and subsequent threads.
...
mm/slob.c static int slob_last(slob_t *s) · line 231
Quoted 10 lines
mm/slob.c +10
-0+static void *slob_new_page (gfp_t gfp,int order,int node) +{ +void *page; + +#ifdef CONFIG_NUMA +if (node !=-1 ) + page =alloc_pages_node (node, gfp, order); + else +#endif + page =alloc_pages (gfp, order);
Isn't the above equivalent to a bare
page = alloc_pages_node(node, gfp, order);
?
Quoted 19 lines
mm/slob.c +7
-1+if (!page) +return NULL ; + +return page_address (page); +} +/* * Allocate a slob block within a given slob_page sp. */ @@ -258,7 +290,7 @@ static void *slob_page_alloc(struct slob_page *sp, size_t size, int align)/* * slob_alloc: entry point into the slob allocator. */ -static void *slob_alloc (size_t size,gfp_t gfp,int align) +static void *slob_alloc (size_t size,gfp_t gfp,int align,int node) {struct slob_page *sp;slob_t *b =NULL ;
mm/slob.c static void *slob_alloc(size_t size, gfp_t gfp, int align) · line 307
Quoted 9 lines
mm/slob.c +6
-0@@ -267,6 +299,15 @@ static void *slob_alloc(size_t size, gfp_t gfp, int align)spin_lock_irqsave (&slob_lock, flags);/* Iterate through each partially free page, try to find room */ list_for_each_entry (sp, &free_slob_pages, list) { +#ifdef CONFIG_NUMA +/* +* If there's a node specification, search for a partial +* page with a matching node id in the freelist. +*/ +if (node !=-1 &&page_to_nid (&sp->page) != node)
Other code does
if (node < 0
rather than comparing with -1 exactly.
On many CPUs it'll save a few bytes of code.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 9 lines · Andrew Morton
mm/slob.c static int slob_last(slob_t *s) · line 231
Quoted 6 lines
mm/slob.c +6
-0+#ifdef CONFIG_NUMA +if (node !=-1 ) + page =alloc_pages_node (node, gfp, order); + else +#endif + page =alloc_pages (gfp, order);Isn't the above equivalent to a bare
Quoted 3 lines
page = alloc_pages_node(node, gfp, order);?
No. alloc_pages follows memory policy. alloc_pages_node does not. One of the reasons that I want a new memory policy layer are these kinds of strange uses.
Quoted 6 lines
if (node < 0
rather than comparing with -1 exactly.
On many CPUs it'll save a few bytes of code.
-1 means no node specified and much of the NUMA code compares with -1.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 11 lines · Christoph Lameter
Quoted 9 lines · Andrew Morton
mm/slob.c static int slob_last(slob_t *s) · line 231
Quoted 6 lines
mm/slob.c +6
-0+#ifdef CONFIG_NUMA +if (node !=-1 ) + page =alloc_pages_node (node, gfp, order); + else +#endif + page =alloc_pages (gfp, order);Isn't the above equivalent to a bare
Quoted 7 lines
Quoted 3 lines
page = alloc_pages_node(node, gfp, order);?
No.
alloc_pagesfollows memory policy.alloc_pages_nodedoes not. One of the reasons that I want a new memory policy layer are these kinds of strange uses.
What would break by changing, in alloc_pages_node()
if (nid < 0)
nid = numa_node_id();to
if (nid < 0)
return alloc_pages_current(gfp_mask, order);beyond needing to make alloc_pages_current() defined if !NUMA too.
Thanks,
Nish
Quoted 9 lines · Nish Aravamudan
Quoted 3 lines
No.
alloc_pagesfollows memory policy.alloc_pages_nodedoes not. One of the reasons that I want a new memory policy layer are these kinds of strange uses.What would break by changing, in
alloc_pages_node()if (nid < 0) nid = numa_node_id();
Quoted 6 lines
to
if (nid < 0) return alloc_pages_current(gfp_mask, order);beyond needing to make
alloc_pages_current()defined if !NUMA too.
It would make alloc_pages_node obey memory policies instead of only following cpuset constraints. An a memory policy may redirect the allocation from the local node ;-).
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 13 lines · Christoph Lameter
Quoted 11 lines · Nish Aravamudan
Quoted 3 lines
No.
alloc_pagesfollows memory policy.alloc_pages_nodedoes not. One of the reasons that I want a new memory policy layer are these kinds of strange uses.What would break by changing, in
alloc_pages_node()if (nid < 0) nid = numa_node_id();to
Quoted 8 lines
Quoted 4 lines
if (nid < 0) return alloc_pages_current(gfp_mask, order);beyond needing to make
alloc_pages_current()defined if !NUMA too.It would make
alloc_pages_nodeobey memory policies instead of only following cpuset constraints. An a memory policy may redirect the allocation from the local node ;-).
heh, true true.
Hrm, I guess the simplest looking solution is rarely the best. Could we add more smarts in alloc_pages_current() to make GFP_THISNODE be equivalent to bind_zonelist(thisnode_only_mask)? I'll keep thinking, maybe I'll come up with something.
Thanks,
Nish
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Quoted 4 lines · Nish Aravamudan
Hrm, I guess the simplest looking solution is rarely the best. Could we add more smarts in
alloc_pages_current()to makeGFP_THISNODEbe equivalent tobind_zonelist(thisnode_only_mask)? I'll keep thinking, maybe I'll come up with something.
Yes that is what the most recent GFP_THISNODE patch does. See V1 of the meoryless node patchset.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>