summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/linux/slqb_def.h21
-rw-r--r--mm/slqb.c117
2 files changed, 99 insertions, 39 deletions
diff --git a/include/linux/slqb_def.h b/include/linux/slqb_def.h
index 8ae1a3711333..395b6f0ac6fd 100644
--- a/include/linux/slqb_def.h
+++ b/include/linux/slqb_def.h
@@ -111,7 +111,7 @@ struct kmem_cache_cpu {
struct kmlist rlist;
struct kmem_cache_list *remote_cache_list;
#endif
-} ____cacheline_aligned;
+} ____cacheline_aligned_in_smp;
/*
* Per-node, per-kmem_cache structure. Used for node-specific allocations.
@@ -128,10 +128,19 @@ struct kmem_cache {
unsigned long flags;
int hiwater; /* LIFO list high watermark */
int freebatch; /* LIFO freelist batch flush size */
+#ifdef CONFIG_SMP
+ struct kmem_cache_cpu **cpu_slab; /* dynamic per-cpu structures */
+#else
+ struct kmem_cache_cpu cpu_slab;
+#endif
int objsize; /* Size of object without meta data */
int offset; /* Free pointer offset. */
int objects; /* Number of objects in slab */
+#ifdef CONFIG_NUMA
+ struct kmem_cache_node **node_slab; /* dynamic per-node structures */
+#endif
+
int size; /* Size of object including meta data */
int order; /* Allocation order */
gfp_t allocflags; /* gfp flags to use on allocation */
@@ -148,15 +157,7 @@ struct kmem_cache {
#ifdef CONFIG_SLQB_SYSFS
struct kobject kobj; /* For sysfs */
#endif
-#ifdef CONFIG_NUMA
- struct kmem_cache_node *node[MAX_NUMNODES];
-#endif
-#ifdef CONFIG_SMP
- struct kmem_cache_cpu *cpu_slab[NR_CPUS];
-#else
- struct kmem_cache_cpu cpu_slab;
-#endif
-};
+} ____cacheline_aligned;
/*
* Kmalloc subsystem.
diff --git a/mm/slqb.c b/mm/slqb.c
index 7ebbf01e3593..4352dad169dc 100644
--- a/mm/slqb.c
+++ b/mm/slqb.c
@@ -56,7 +56,6 @@ static inline void struct_slqb_page_wrong_size(void)
#define PG_SLQB_BIT (1 << PG_slab)
-static int kmem_size __read_mostly;
#ifdef CONFIG_NUMA
static inline int slab_numa(struct kmem_cache *s)
{
@@ -1329,7 +1328,7 @@ static noinline void *__slab_alloc_page(struct kmem_cache *s,
#ifdef CONFIG_NUMA
struct kmem_cache_node *n;
- n = s->node[slqb_page_to_nid(page)];
+ n = s->node_slab[slqb_page_to_nid(page)];
l = &n->list;
page->list = l;
@@ -1373,7 +1372,7 @@ static void *__remote_slab_alloc_node(struct kmem_cache *s,
struct kmem_cache_list *l;
void *object;
- n = s->node[node];
+ n = s->node_slab[node];
if (unlikely(!n)) /* node has no memory */
return NULL;
l = &n->list;
@@ -1818,7 +1817,7 @@ static void init_kmem_cache_node(struct kmem_cache *s,
}
#endif
-/* Initial slabs. XXX: allocate dynamically (with bootmem maybe) */
+/* Initial slabs. */
#ifdef CONFIG_SMP
static DEFINE_PER_CPU(struct kmem_cache_cpu, kmem_cache_cpus);
#endif
@@ -1912,10 +1911,10 @@ static void free_kmem_cache_nodes(struct kmem_cache *s)
for_each_node_state(node, N_NORMAL_MEMORY) {
struct kmem_cache_node *n;
- n = s->node[node];
+ n = s->node_slab[node];
if (n) {
kmem_cache_free(&kmem_node_cache, n);
- s->node[node] = NULL;
+ s->node_slab[node] = NULL;
}
}
}
@@ -1933,7 +1932,7 @@ static int alloc_kmem_cache_nodes(struct kmem_cache *s)
return 0;
}
init_kmem_cache_node(s, n);
- s->node[node] = n;
+ s->node_slab[node] = n;
}
return 1;
}
@@ -2069,13 +2068,56 @@ static int calculate_sizes(struct kmem_cache *s)
}
+#ifdef CONFIG_SMP
+/*
+ * Per-cpu allocator can't be used because it always uses slab allocator,
+ * and it can't do per-node allocations.
+ */
+static void *kmem_cache_dyn_array_alloc(int ids)
+{
+ size_t size = sizeof(void *) * ids;
+
+ if (unlikely(!slab_is_available())) {
+ static void *nextmem;
+ void *ret;
+
+ /*
+ * Special case for setting up initial caches. These will
+ * never get freed by definition so we can do it rather
+ * simply.
+ */
+ if (!nextmem) {
+ nextmem = alloc_pages_exact(size, GFP_KERNEL);
+ if (!nextmem)
+ return NULL;
+ }
+ ret = nextmem;
+ nextmem = (void *)((unsigned long)ret + size);
+ if ((unsigned long)ret >> PAGE_SHIFT !=
+ (unsigned long)nextmem >> PAGE_SHIFT)
+ nextmem = NULL;
+ memset(ret, 0, size);
+ return ret;
+ } else {
+ return kzalloc(size, GFP_KERNEL);
+ }
+}
+
+static void kmem_cache_dyn_array_free(void *array)
+{
+ if (unlikely(!slab_is_available()))
+ return; /* error case without crashing here (will panic soon) */
+ kfree(array);
+}
+#endif
+
static int kmem_cache_open(struct kmem_cache *s,
const char *name, size_t size, size_t align,
unsigned long flags, void (*ctor)(void *), int alloc)
{
unsigned int left_over;
- memset(s, 0, kmem_size);
+ memset(s, 0, sizeof(struct kmem_cache));
s->name = name;
s->ctor = ctor;
s->objsize = size;
@@ -2094,10 +2136,26 @@ static int kmem_cache_open(struct kmem_cache *s,
s->colour_range = 0;
}
+ /*
+ * Protect all alloc_kmem_cache_cpus/nodes allocations with slqb_lock
+ * to lock out hotplug, just in case (probably not strictly needed
+ * here).
+ */
down_write(&slqb_lock);
+#ifdef CONFIG_SMP
+ s->cpu_slab = kmem_cache_dyn_array_alloc(nr_cpu_ids);
+ if (!s->cpu_slab)
+ goto error_lock;
+# ifdef CONFIG_NUMA
+ s->node_slab = kmem_cache_dyn_array_alloc(nr_node_ids);
+ if (!s->node_slab)
+ goto error_cpu_array;
+# endif
+#endif
+
if (likely(alloc)) {
if (!alloc_kmem_cache_nodes(s))
- goto error_lock;
+ goto error_node_array;
if (!alloc_kmem_cache_cpus(s))
goto error_nodes;
@@ -2111,6 +2169,14 @@ static int kmem_cache_open(struct kmem_cache *s,
error_nodes:
free_kmem_cache_nodes(s);
+error_node_array:
+#ifdef CONFIG_NUMA
+ kmem_cache_dyn_array_free(s->node_slab);
+#endif
+error_cpu_array:
+#ifdef CONFIG_SMP
+ kmem_cache_dyn_array_free(s->cpu_slab);
+#endif
error_lock:
up_write(&slqb_lock);
error:
@@ -2152,7 +2218,7 @@ int kmem_ptr_validate(struct kmem_cache *s, const void *ptr)
page = virt_to_head_slqb_page(ptr);
if (unlikely(!(page->flags & PG_SLQB_BIT)))
goto out;
- if (unlikely(page->list->cache != s))
+ if (unlikely(page->list->cache != s)) /* XXX: ouch, racy */
goto out;
return 1;
out:
@@ -2220,7 +2286,7 @@ void kmem_cache_destroy(struct kmem_cache *s)
struct kmem_cache_node *n;
struct kmem_cache_list *l;
- n = s->node[node];
+ n = s->node_slab[node];
if (!n)
continue;
l = &n->list;
@@ -2449,7 +2515,7 @@ int kmem_cache_shrink(struct kmem_cache *s)
struct kmem_cache_node *n;
struct kmem_cache_list *l;
- n = s->node[node];
+ n = s->node_slab[node];
if (!n)
continue;
l = &n->list;
@@ -2502,7 +2568,7 @@ static void kmem_cache_reap(void)
struct kmem_cache_node *n;
struct kmem_cache_list *l;
- n = s->node[node];
+ n = s->node_slab[node];
if (!n)
continue;
l = &n->list;
@@ -2529,7 +2595,7 @@ static void cache_trim_worker(struct work_struct *w)
list_for_each_entry(s, &slab_caches, list) {
#ifdef CONFIG_NUMA
int node = numa_node_id();
- struct kmem_cache_node *n = s->node[node];
+ struct kmem_cache_node *n = s->node_slab[node];
if (n) {
struct kmem_cache_list *l = &n->list;
@@ -2618,7 +2684,7 @@ static int slab_mem_going_online_callback(void *arg)
* since memory is not yet available from the node that
* is brought up.
*/
- if (s->node[nid]) /* could be lefover from last online */
+ if (s->node_slab[nid]) /* could be lefover from last online */
continue;
n = kmem_cache_alloc(&kmem_node_cache, GFP_KERNEL);
if (!n) {
@@ -2626,7 +2692,7 @@ static int slab_mem_going_online_callback(void *arg)
goto out;
}
init_kmem_cache_node(s, n);
- s->node[nid] = n;
+ s->node_slab[nid] = n;
}
out:
up_write(&slqb_lock);
@@ -2673,15 +2739,8 @@ void __init kmem_cache_init(void)
* All the ifdefs are rather ugly here, but it's just the setup code,
* so it doesn't have to be too readable :)
*/
-#ifdef CONFIG_SMP
- kmem_size = offsetof(struct kmem_cache, cpu_slab) +
- nr_cpu_ids * sizeof(struct kmem_cache_cpu *);
-#else
- kmem_size = sizeof(struct kmem_cache);
-#endif
-
kmem_cache_open(&kmem_cache_cache, "kmem_cache",
- kmem_size, 0, flags, NULL, 0);
+ sizeof(struct kmem_cache), 0, flags, NULL, 0);
#ifdef CONFIG_SMP
kmem_cache_open(&kmem_cpu_cache, "kmem_cache_cpu",
sizeof(struct kmem_cache_cpu), 0, flags, NULL, 0);
@@ -2719,15 +2778,15 @@ void __init kmem_cache_init(void)
n = &per_cpu(kmem_cache_nodes, i);
init_kmem_cache_node(&kmem_cache_cache, n);
- kmem_cache_cache.node[i] = n;
+ kmem_cache_cache.node_slab[i] = n;
n = &per_cpu(kmem_cpu_nodes, i);
init_kmem_cache_node(&kmem_cpu_cache, n);
- kmem_cpu_cache.node[i] = n;
+ kmem_cpu_cache.node_slab[i] = n;
n = &per_cpu(kmem_node_nodes, i);
init_kmem_cache_node(&kmem_node_cache, n);
- kmem_node_cache.node[i] = n;
+ kmem_node_cache.node_slab[i] = n;
}
#endif
@@ -2793,7 +2852,7 @@ void __init kmem_cache_init(void)
#endif
/*
* smp_init() has not yet been called, so no worries about memory
- * ordering here (eg. slab_is_available vs numa_platform)
+ * ordering with __slab_is_available.
*/
__slab_is_available = 1;
}
@@ -3036,7 +3095,7 @@ static void gather_stats(struct kmem_cache *s, struct stats_gather *stats)
#ifdef CONFIG_NUMA
for_each_online_node(node) {
- struct kmem_cache_node *n = s->node[node];
+ struct kmem_cache_node *n = s->node_slab[node];
struct kmem_cache_list *l = &n->list;
struct slqb_page *page;
unsigned long flags;