diff options
| author | Yasunori Goto <y-goto@jp.fujitsu.com> | 2007-10-21 16:41:37 -0700 | 
|---|---|---|
| committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2007-10-22 08:13:17 -0700 | 
| commit | b9049e234401e1fad8459d69a952b174d76c399d (patch) | |
| tree | 619adc6533f33379baf25253ff56603b272453c8 | |
| parent | 7b78d335ac15b10bbcb0397c635d7f0d569b0270 (diff) | |
| download | olio-linux-3.10-b9049e234401e1fad8459d69a952b174d76c399d.tar.xz olio-linux-3.10-b9049e234401e1fad8459d69a952b174d76c399d.zip  | |
memory hotplug: make kmem_cache_node for SLUB on memory online avoid panic
Fix a panic due to access NULL pointer of kmem_cache_node at discard_slab()
after memory online.
When memory online is called, kmem_cache_nodes are created for all SLUBs
for new node whose memory are available.
slab_mem_going_online_callback() is called to make kmem_cache_node() in
callback of memory online event.  If it (or other callbacks) fails, then
slab_mem_offline_callback() is called for rollback.
In memory offline, slab_mem_going_offline_callback() is called to shrink
all slub cache, then slab_mem_offline_callback() is called later.
[akpm@linux-foundation.org: coding-style fixes]
[akpm@linux-foundation.org: locking fix]
[akpm@linux-foundation.org: build fix]
Signed-off-by: Yasunori Goto <y-goto@jp.fujitsu.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
| -rw-r--r-- | include/linux/memory.h | 4 | ||||
| -rw-r--r-- | mm/slub.c | 118 | 
2 files changed, 122 insertions, 0 deletions
diff --git a/include/linux/memory.h b/include/linux/memory.h index ec376e482ab..33f0ff0cf63 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -83,10 +83,14 @@ extern int memory_notify(unsigned long val, void *v);  #endif /* CONFIG_MEMORY_HOTPLUG_SPARSE */ +#ifdef CONFIG_MEMORY_HOTPLUG  #define hotplug_memory_notifier(fn, pri) {			\  	static struct notifier_block fn##_mem_nb =		\  		{ .notifier_call = fn, .priority = pri };	\  	register_memory_notifier(&fn##_mem_nb);			\  } +#else +#define hotplug_memory_notifier(fn, pri) do { } while (0) +#endif  #endif /* _LINUX_MEMORY_H_ */ diff --git a/mm/slub.c b/mm/slub.c index e29a42988c7..aac1dd3c657 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -20,6 +20,7 @@  #include <linux/mempolicy.h>  #include <linux/ctype.h>  #include <linux/kallsyms.h> +#include <linux/memory.h>  /*   * Lock order: @@ -2694,6 +2695,121 @@ int kmem_cache_shrink(struct kmem_cache *s)  }  EXPORT_SYMBOL(kmem_cache_shrink); +#if defined(CONFIG_NUMA) && defined(CONFIG_MEMORY_HOTPLUG) +static int slab_mem_going_offline_callback(void *arg) +{ +	struct kmem_cache *s; + +	down_read(&slub_lock); +	list_for_each_entry(s, &slab_caches, list) +		kmem_cache_shrink(s); +	up_read(&slub_lock); + +	return 0; +} + +static void slab_mem_offline_callback(void *arg) +{ +	struct kmem_cache_node *n; +	struct kmem_cache *s; +	struct memory_notify *marg = arg; +	int offline_node; + +	offline_node = marg->status_change_nid; + +	/* +	 * If the node still has available memory. we need kmem_cache_node +	 * for it yet. +	 */ +	if (offline_node < 0) +		return; + +	down_read(&slub_lock); +	list_for_each_entry(s, &slab_caches, list) { +		n = get_node(s, offline_node); +		if (n) { +			/* +			 * if n->nr_slabs > 0, slabs still exist on the node +			 * that is going down. We were unable to free them, +			 * and offline_pages() function shoudn't call this +			 * callback. So, we must fail. +			 */ +			BUG_ON(atomic_read(&n->nr_slabs)); + +			s->node[offline_node] = NULL; +			kmem_cache_free(kmalloc_caches, n); +		} +	} +	up_read(&slub_lock); +} + +static int slab_mem_going_online_callback(void *arg) +{ +	struct kmem_cache_node *n; +	struct kmem_cache *s; +	struct memory_notify *marg = arg; +	int nid = marg->status_change_nid; +	int ret = 0; + +	/* +	 * If the node's memory is already available, then kmem_cache_node is +	 * already created. Nothing to do. +	 */ +	if (nid < 0) +		return 0; + +	/* +	 * We are bringing a node online. No memory is availabe yet. We must +	 * allocate a kmem_cache_node structure in order to bring the node +	 * online. +	 */ +	down_read(&slub_lock); +	list_for_each_entry(s, &slab_caches, list) { +		/* +		 * XXX: kmem_cache_alloc_node will fallback to other nodes +		 *      since memory is not yet available from the node that +		 *      is brought up. +		 */ +		n = kmem_cache_alloc(kmalloc_caches, GFP_KERNEL); +		if (!n) { +			ret = -ENOMEM; +			goto out; +		} +		init_kmem_cache_node(n); +		s->node[nid] = n; +	} +out: +	up_read(&slub_lock); +	return ret; +} + +static int slab_memory_callback(struct notifier_block *self, +				unsigned long action, void *arg) +{ +	int ret = 0; + +	switch (action) { +	case MEM_GOING_ONLINE: +		ret = slab_mem_going_online_callback(arg); +		break; +	case MEM_GOING_OFFLINE: +		ret = slab_mem_going_offline_callback(arg); +		break; +	case MEM_OFFLINE: +	case MEM_CANCEL_ONLINE: +		slab_mem_offline_callback(arg); +		break; +	case MEM_ONLINE: +	case MEM_CANCEL_OFFLINE: +		break; +	} + +	ret = notifier_from_errno(ret); +	return ret; +} + +#endif /* CONFIG_MEMORY_HOTPLUG */ +  /********************************************************************   *			Basic setup of slabs   *******************************************************************/ @@ -2715,6 +2831,8 @@ void __init kmem_cache_init(void)  		sizeof(struct kmem_cache_node), GFP_KERNEL);  	kmalloc_caches[0].refcount = -1;  	caches++; + +	hotplug_memory_notifier(slab_memory_callback, 1);  #endif  	/* Able to allocate the per node structures */  |