Commit 3c517a61 authored by Christoph Lameter's avatar Christoph Lameter Committed by Linus Torvalds

[PATCH] slab: better fallback allocation behavior

Currently we simply attempt to allocate from all allowed nodes using
GFP_THISNODE.  However, GFP_THISNODE does not do reclaim (it wont do any at
all if the recent GFP_THISNODE patch is accepted).  If we truly run out of
memory in the whole system then fallback_alloc may return NULL although
memory may still be available if we would perform more thorough reclaim.

This patch changes fallback_alloc() so that we first only inspect all the
per node queues for available slabs.  If we find any then we allocate from
those.  This avoids slab fragmentation by first getting rid of all partial
allocated slabs on every node before allocating new memory.

If we cannot satisfy the allocation from any per node queue then we extend
a slab.  We now call into the page allocator without specifying
GFP_THISNODE.  The page allocator will then implement its own fallback (in
the given cpuset context), perform necessary reclaim (again considering not
a single node but the whole set of allowed nodes) and then return pages for
a new slab.

We identify from which node the pages were allocated and then insert the
pages into the corresponding per node structure.  In order to do so we need
to modify cache_grow() to take a parameter that specifies the new slab.
kmem_getpages() can no longer set the GFP_THISNODE flag since we need to be
able to use kmem_getpage to allocate from an arbitrary node.  GFP_THISNODE
needs to be specified when calling cache_grow().

One key advantage is that the decision from which node to allocate new
memory is removed from slab fallback processing.  The patch allows to go
back to use of the page allocators fallback/reclaim logic.
Signed-off-by: default avatarChristoph Lameter <clameter@sgi.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 952f3b51
...@@ -1605,12 +1605,7 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid) ...@@ -1605,12 +1605,7 @@ static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
flags |= __GFP_COMP; flags |= __GFP_COMP;
#endif #endif
/* flags |= cachep->gfpflags;
* Under NUMA we want memory on the indicated node. We will handle
* the needed fallback ourselves since we want to serve from our
* per node object lists first for other nodes.
*/
flags |= cachep->gfpflags | GFP_THISNODE;
page = alloc_pages_node(nodeid, flags, cachep->gfporder); page = alloc_pages_node(nodeid, flags, cachep->gfporder);
if (!page) if (!page)
...@@ -2567,7 +2562,7 @@ static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp, ...@@ -2567,7 +2562,7 @@ static struct slab *alloc_slabmgmt(struct kmem_cache *cachep, void *objp,
if (OFF_SLAB(cachep)) { if (OFF_SLAB(cachep)) {
/* Slab management obj is off-slab. */ /* Slab management obj is off-slab. */
slabp = kmem_cache_alloc_node(cachep->slabp_cache, slabp = kmem_cache_alloc_node(cachep->slabp_cache,
local_flags, nodeid); local_flags & ~GFP_THISNODE, nodeid);
if (!slabp) if (!slabp)
return NULL; return NULL;
} else { } else {
...@@ -2708,10 +2703,10 @@ static void slab_map_pages(struct kmem_cache *cache, struct slab *slab, ...@@ -2708,10 +2703,10 @@ static void slab_map_pages(struct kmem_cache *cache, struct slab *slab,
* Grow (by 1) the number of slabs within a cache. This is called by * Grow (by 1) the number of slabs within a cache. This is called by
* kmem_cache_alloc() when there are no active objs left in a cache. * kmem_cache_alloc() when there are no active objs left in a cache.
*/ */
static int cache_grow(struct kmem_cache *cachep, gfp_t flags, int nodeid) static int cache_grow(struct kmem_cache *cachep,
gfp_t flags, int nodeid, void *objp)
{ {
struct slab *slabp; struct slab *slabp;
void *objp;
size_t offset; size_t offset;
gfp_t local_flags; gfp_t local_flags;
unsigned long ctor_flags; unsigned long ctor_flags;
...@@ -2763,12 +2758,14 @@ static int cache_grow(struct kmem_cache *cachep, gfp_t flags, int nodeid) ...@@ -2763,12 +2758,14 @@ static int cache_grow(struct kmem_cache *cachep, gfp_t flags, int nodeid)
* Get mem for the objs. Attempt to allocate a physical page from * Get mem for the objs. Attempt to allocate a physical page from
* 'nodeid'. * 'nodeid'.
*/ */
objp = kmem_getpages(cachep, flags, nodeid); if (!objp)
objp = kmem_getpages(cachep, flags, nodeid);
if (!objp) if (!objp)
goto failed; goto failed;
/* Get slab management. */ /* Get slab management. */
slabp = alloc_slabmgmt(cachep, objp, offset, local_flags, nodeid); slabp = alloc_slabmgmt(cachep, objp, offset,
local_flags & ~GFP_THISNODE, nodeid);
if (!slabp) if (!slabp)
goto opps1; goto opps1;
...@@ -3006,7 +3003,7 @@ static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags) ...@@ -3006,7 +3003,7 @@ static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)
if (unlikely(!ac->avail)) { if (unlikely(!ac->avail)) {
int x; int x;
x = cache_grow(cachep, flags, node); x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL);
/* cache_grow can reenable interrupts, then ac could change. */ /* cache_grow can reenable interrupts, then ac could change. */
ac = cpu_cache_get(cachep); ac = cpu_cache_get(cachep);
...@@ -3166,9 +3163,11 @@ static void *alternate_node_alloc(struct kmem_cache *cachep, gfp_t flags) ...@@ -3166,9 +3163,11 @@ static void *alternate_node_alloc(struct kmem_cache *cachep, gfp_t flags)
/* /*
* Fallback function if there was no memory available and no objects on a * Fallback function if there was no memory available and no objects on a
* certain node and we are allowed to fall back. We mimick the behavior of * certain node and fall back is permitted. First we scan all the
* the page allocator. We fall back according to a zonelist determined by * available nodelists for available objects. If that fails then we
* the policy layer while obeying cpuset constraints. * perform an allocation without specifying a node. This allows the page
* allocator to do its reclaim / fallback magic. We then insert the
* slab into the proper nodelist and then allocate from it.
*/ */
void *fallback_alloc(struct kmem_cache *cache, gfp_t flags) void *fallback_alloc(struct kmem_cache *cache, gfp_t flags)
{ {
...@@ -3176,15 +3175,51 @@ void *fallback_alloc(struct kmem_cache *cache, gfp_t flags) ...@@ -3176,15 +3175,51 @@ void *fallback_alloc(struct kmem_cache *cache, gfp_t flags)
->node_zonelists[gfp_zone(flags)]; ->node_zonelists[gfp_zone(flags)];
struct zone **z; struct zone **z;
void *obj = NULL; void *obj = NULL;
int nid;
retry:
/*
* Look through allowed nodes for objects available
* from existing per node queues.
*/
for (z = zonelist->zones; *z && !obj; z++) { for (z = zonelist->zones; *z && !obj; z++) {
int nid = zone_to_nid(*z); nid = zone_to_nid(*z);
if (cpuset_zone_allowed(*z, flags) &&
cache->nodelists[nid] &&
cache->nodelists[nid]->free_objects)
obj = ____cache_alloc_node(cache,
flags | GFP_THISNODE, nid);
}
if (zone_idx(*z) <= ZONE_NORMAL && if (!obj) {
cpuset_zone_allowed(*z, flags) && /*
cache->nodelists[nid]) * This allocation will be performed within the constraints
obj = ____cache_alloc_node(cache, * of the current cpuset / memory policy requirements.
flags | __GFP_THISNODE, nid); * We may trigger various forms of reclaim on the allowed
* set and go into memory reserves if necessary.
*/
obj = kmem_getpages(cache, flags, -1);
if (obj) {
/*
* Insert into the appropriate per node queues
*/
nid = page_to_nid(virt_to_page(obj));
if (cache_grow(cache, flags, nid, obj)) {
obj = ____cache_alloc_node(cache,
flags | GFP_THISNODE, nid);
if (!obj)
/*
* Another processor may allocate the
* objects in the slab since we are
* not holding any locks.
*/
goto retry;
} else {
kmem_freepages(cache, obj);
obj = NULL;
}
}
} }
return obj; return obj;
} }
...@@ -3241,7 +3276,7 @@ static void *____cache_alloc_node(struct kmem_cache *cachep, gfp_t flags, ...@@ -3241,7 +3276,7 @@ static void *____cache_alloc_node(struct kmem_cache *cachep, gfp_t flags,
must_grow: must_grow:
spin_unlock(&l3->list_lock); spin_unlock(&l3->list_lock);
x = cache_grow(cachep, flags, nodeid); x = cache_grow(cachep, flags | GFP_THISNODE, nodeid, NULL);
if (x) if (x)
goto retry; goto retry;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment