{
int cpu;
+ /*
+ * We may be unwinding cache creation that failed before or during the
+ * allocation of this.
+ */
+ if (!s->cpu_sheaves)
+ return;
+
+ /* pcs->main can only point to the bootstrap sheaf, nothing to free */
+ if (!cache_has_sheaves(s))
+ goto free_pcs;
+
for_each_possible_cpu(cpu) {
struct slub_percpu_sheaves *pcs;
pcs = per_cpu_ptr(s->cpu_sheaves, cpu);
- /* can happen when unwinding failed create */
+ /* This can happen when unwinding failed cache creation. */
if (!pcs->main)
continue;
}
}
+free_pcs:
free_percpu(s->cpu_sheaves);
s->cpu_sheaves = NULL;
}
{
struct slub_percpu_sheaves *pcs;
- if (!s->cpu_sheaves)
+ if (!cache_has_sheaves(s))
return false;
pcs = per_cpu_ptr(s->cpu_sheaves, cpu);
s = sfw->s;
- if (s->cpu_sheaves)
+ if (cache_has_sheaves(s))
pcs_flush_all(s);
flush_this_cpu_slab(s);
mutex_lock(&slab_mutex);
list_for_each_entry(s, &slab_caches, list) {
- if (!s->cpu_sheaves)
+ if (!cache_has_sheaves(s))
continue;
flush_rcu_sheaves_on_cache(s);
}
mutex_lock(&slab_mutex);
list_for_each_entry(s, &slab_caches, list) {
__flush_cpu_slab(s, cpu);
- if (s->cpu_sheaves)
+ if (cache_has_sheaves(s))
__pcs_flush_all_cpu(s, cpu);
}
mutex_unlock(&slab_mutex);
lockdep_assert_held(this_cpu_ptr(&s->cpu_sheaves->lock));
+ /* Bootstrap or debug cache, back off */
+ if (unlikely(!cache_has_sheaves(s))) {
+ local_unlock(&s->cpu_sheaves->lock);
+ return NULL;
+ }
+
if (pcs->spare && pcs->spare->size > 0) {
swap(pcs->main, pcs->spare);
return pcs;
struct slab_sheaf *full;
struct node_barn *barn;
+ if (unlikely(!cache_has_sheaves(s))) {
+ local_unlock(&s->cpu_sheaves->lock);
+ return allocated;
+ }
+
if (pcs->spare && pcs->spare->size > 0) {
swap(pcs->main, pcs->spare);
goto do_alloc;
if (unlikely(object))
goto out;
- if (s->cpu_sheaves)
- object = alloc_from_pcs(s, gfpflags, node);
+ object = alloc_from_pcs(s, gfpflags, node);
if (!object)
object = __slab_alloc_node(s, gfpflags, node, addr, orig_size);
struct slab_sheaf *sheaf = NULL;
struct node_barn *barn;
- if (unlikely(size > s->sheaf_capacity)) {
+ if (unlikely(!size))
+ return NULL;
- /*
- * slab_debug disables cpu sheaves intentionally so all
- * prefilled sheaves become "oversize" and we give up on
- * performance for the debugging. Same with SLUB_TINY.
- * Creating a cache without sheaves and then requesting a
- * prefilled sheaf is however not expected, so warn.
- */
- WARN_ON_ONCE(s->sheaf_capacity == 0 &&
- !IS_ENABLED(CONFIG_SLUB_TINY) &&
- !(s->flags & SLAB_DEBUG_FLAGS));
+ if (unlikely(size > s->sheaf_capacity)) {
sheaf = kzalloc(struct_size(sheaf, objects, size), gfp);
if (!sheaf)
restart:
lockdep_assert_held(this_cpu_ptr(&s->cpu_sheaves->lock));
+ /* Bootstrap or debug cache, back off */
+ if (unlikely(!cache_has_sheaves(s))) {
+ local_unlock(&s->cpu_sheaves->lock);
+ return NULL;
+ }
+
barn = get_barn(s);
if (!barn) {
local_unlock(&s->cpu_sheaves->lock);
struct slab_sheaf *empty;
struct node_barn *barn;
+ /* Bootstrap or debug cache, fall back */
+ if (unlikely(!cache_has_sheaves(s))) {
+ local_unlock(&s->cpu_sheaves->lock);
+ goto fail;
+ }
+
if (pcs->spare && pcs->spare->size == 0) {
pcs->rcu_free = pcs->spare;
pcs->spare = NULL;
if (unlikely(!slab_free_hook(s, object, slab_want_init_on_free(s), false)))
return;
- if (s->cpu_sheaves && likely(!IS_ENABLED(CONFIG_NUMA) ||
- slab_nid(slab) == numa_mem_id())
- && likely(!slab_test_pfmemalloc(slab))) {
+ if (likely(!IS_ENABLED(CONFIG_NUMA) || slab_nid(slab) == numa_mem_id())
+ && likely(!slab_test_pfmemalloc(slab))) {
if (likely(free_to_pcs(s, object)))
return;
}
* freeing to sheaves is so incompatible with the detached freelist so
* once we go that way, we have to do everything differently
*/
- if (s && s->cpu_sheaves) {
+ if (s && cache_has_sheaves(s)) {
free_to_pcs_bulk(s, size, p);
return;
}
size--;
}
- if (s->cpu_sheaves)
- i = alloc_from_pcs_bulk(s, size, p);
+ i = alloc_from_pcs_bulk(s, size, p);
if (i < size) {
/*
static int init_percpu_sheaves(struct kmem_cache *s)
{
+ static struct slab_sheaf bootstrap_sheaf = {};
int cpu;
for_each_possible_cpu(cpu) {
local_trylock_init(&pcs->lock);
- pcs->main = alloc_empty_sheaf(s, GFP_KERNEL);
+ /*
+ * Bootstrap sheaf has zero size so fast-path allocation fails.
+ * It has also size == s->sheaf_capacity, so fast-path free
+ * fails. In the slow paths we recognize the situation by
+ * checking s->sheaf_capacity. This allows fast paths to assume
+ * s->cpu_sheaves and pcs->main always exists and are valid.
+ * It's also safe to share the single static bootstrap_sheaf
+ * with zero-sized objects array as it's never modified.
+ *
+ * Bootstrap_sheaf also has NULL pointer to kmem_cache so we
+ * recognize it and not attempt to free it when destroying the
+ * cache.
+ *
+ * We keep bootstrap_sheaf for kmem_cache and kmem_cache_node,
+ * caches with debug enabled, and all caches with SLUB_TINY.
+ * For kmalloc caches it's used temporarily during the initial
+ * bootstrap.
+ */
+ if (!s->sheaf_capacity)
+ pcs->main = &bootstrap_sheaf;
+ else
+ pcs->main = alloc_empty_sheaf(s, GFP_KERNEL);
if (!pcs->main)
return -ENOMEM;
void __kmem_cache_release(struct kmem_cache *s)
{
cache_random_seq_destroy(s);
- if (s->cpu_sheaves)
- pcs_destroy(s);
+ pcs_destroy(s);
#ifdef CONFIG_PREEMPT_RT
if (s->cpu_slab)
lockdep_unregister_key(&s->lock_key);
continue;
}
- if (s->cpu_sheaves) {
+ if (cache_has_sheaves(s)) {
barn = kmalloc_node(sizeof(*barn), GFP_KERNEL, node);
if (!barn)
flush_all_cpus_locked(s);
/* we might have rcu sheaves in flight */
- if (s->cpu_sheaves)
+ if (cache_has_sheaves(s))
rcu_barrier();
/* Attempt to free all objects */
if (get_node(s, nid))
continue;
- if (s->cpu_sheaves) {
+ if (cache_has_sheaves(s)) {
barn = kmalloc_node(sizeof(*barn), GFP_KERNEL, nid);
if (!barn) {
set_cpu_partial(s);
- if (s->sheaf_capacity) {
- s->cpu_sheaves = alloc_percpu(struct slub_percpu_sheaves);
- if (!s->cpu_sheaves) {
- err = -ENOMEM;
- goto out;
- }
+ s->cpu_sheaves = alloc_percpu(struct slub_percpu_sheaves);
+ if (!s->cpu_sheaves) {
+ err = -ENOMEM;
+ goto out;
}
#ifdef CONFIG_NUMA
if (!alloc_kmem_cache_cpus(s))
goto out;
- if (s->cpu_sheaves) {
- err = init_percpu_sheaves(s);
- if (err)
- goto out;
- }
+ err = init_percpu_sheaves(s);
+ if (err)
+ goto out;
err = 0;