Skip to content

Latest commit

 

History

History
200 lines (160 loc) · 7.71 KB

2014-05-11-init-slab.md

File metadata and controls

200 lines (160 loc) · 7.71 KB
layout title category description tags
post
slab初始化
内存管理
slab初始化...
slab

slab初始化并不是特别麻烦,因为伙伴系统已经完全启用,内核没有收到其他的特别限制。尽管如此,由于slab分配器的结构所示,这就产生了一个鸡与蛋的问题。

为初始化slab数据结构,内核需要若干远小于一整页的内存块,这些最适合由kmalloc分配,这里的关键所在是,只有slab系统启用之后才能使用kmalloc

更确切的说,该问题涉及kmalloc的per-CPU缓存的初始化,这些缓存能够初始化之前,kmalloc必须可以用来分配所需的内存空间,而kmalloc自身也处于初始化的过程中,也就是说,kmalloc只能在kmalloc已经初始化之后初始化,这是个不可能的场景,所以内核需要使用一些技巧。

kmalloc_cache_init函数用于初始化slab分配器,它在内核初始化阶段、伙伴系统启用之后调用。但在多处理器系统上,启动CPU此时正在运行,而其他的CPU尚未初始化,所以kmalloc_cache_init采用了一个多步骤过程,逐步激活slab分配器。

<mm/slab.c>

{% highlight c++%} void __init kmem_cache_init(void) { size_t left_over; struct cache_sizes *sizes; struct cache_names *names; int i; int order; int node;

if (num_possible_nodes() == 1)
    use_alien_caches = 0;

for (i = 0; i < NUM_INIT_LISTS; i++) {
    kmem_list3_init(&initkmem_list3[i]);
    if (i < MAX_NUMNODES)
        cache_cache.nodelists[i] = NULL;
}
set_up_list3s(&cache_cache, CACHE_CACHE);

if (totalram_pages > (32 << 20) >> PAGE_SHIFT)
    slab_break_gfp_order = BREAK_GFP_ORDER_HI;

node = numa_node_id();

/* 1) 创建 cache_cache 对象 */
INIT_LIST_HEAD(&cache_chain);
list_add(&cache_cache.next, &cache_chain);
cache_cache.colour_off = cache_line_size();
cache_cache.array[smp_processor_id()] = &initarray_cache.cache;
cache_cache.nodelists[node] = 
    &initkmem_list3[CACHE_CACHE + node];

cache_cache.buffer_size = 
    offsetof(struct kmem_cache, nodelists) +
    nr_node_ids * sizeof(struct kmem_list3 *);

#if DEBUG cache_cache.obj_size = cache_cache.buffer_size; #endif cache_cache.buffer_size = ALIGN(cache_cache.buffer_size, cache_line_size()); cache_cache.reciprocal_buffer_size = reciprocal_value(cache_cache.buffer_size);

for (order = 0; order < MAX_ORDER; order++) {
    cache_estimate(order, cache_cache.buffer_size,
        cache_line_size(), 0, &left_over, &cache_cache.num);
    if (cache_cache.num)
        break;
}
BUG_ON(!cache_cache.num);
cache_cache.gfporder = order;
cache_cache.colour = left_over / cache_cache.colour_off;
cache_cache.slab_size = 
    ALIGN(cache_cache.num * sizeof(kmem_bufctl_t) +
          sizeof(struct slab), cache_line_size());

/* 2+3) 创建 kmalloc 缓存 */
sizes = malloc_sizes;
names = cache_names;

sizes[INDEX_AC].cs_cachep = 
    kmem_cache_create(names[INDEX_AC].name,
                sizes[INDEX_AC].cs_size,
                ARCH_KMALLOC_MINALIGN,
                ARCH_KMALLOC_FLAGS|SLAB_PANIC,
                NULL);

if (INDEX_AC != INDEX_L3) {
    sizes[INDEX_L3].cs_cachep =
        kmem_cache_create(names[INDEX_L3].name,
            sizes[INDEX_L3].cs_size,
            ARCH_KMALLOC_MINALIGN,
            ARCH_KMALLOC_FLAGS|SLAB_PANIC,
            NULL);
}

slab_early_init = 0;

while (sizes->cs_size != ULONG_MAX) {
    if (!sizes->cs_cachep) {
        sizes->cs_cachep = kmem_cache_create(names->name,
                sizes->cs_size,
                ARCH_KMALLOC_MINALIGN,
                ARCH_KMALLOC_FLAGS|SLAB_PANIC,
                NULL);
    }

#ifdef CONFIG_ZONE_DMA sizes->cs_dmacachep = kmem_cache_create( names->name_dma, sizes->cs_size, ARCH_KMALLOC_MINALIGN, ARCH_KMALLOC_FLAGS|SLAB_CACHE_DMA| SLAB_PANIC, NULL); #endif sizes++; names++; } /* 4) 替换bootstrap head arrays */ { struct array_cache *ptr;

    ptr = kmalloc(sizeof(struct arraycache_init), GFP_NOWAIT);

    BUG_ON(
        cpu_cache_get(&cache_cache) != &initarray_cache.cache
    );
    memcpy(ptr, cpu_cache_get(&cache_cache),
           sizeof(struct arraycache_init));
    spin_lock_init(&ptr->lock);

    cache_cache.array[smp_processor_id()] = ptr;

    ptr = kmalloc(sizeof(struct arraycache_init), GFP_NOWAIT);

    BUG_ON(cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep)
           != &initarray_generic.cache);
    memcpy(ptr, cpu_cache_get(malloc_sizes[INDEX_AC].cs_cachep),
           sizeof(struct arraycache_init));
    spin_lock_init(&ptr->lock);

    malloc_sizes[INDEX_AC].cs_cachep->array[smp_processor_id()]
        = ptr;
}
/* 5) 替换 bootstrap kmem_list3's */
{
    int nid;

    for_each_online_node(nid) {
        init_list(&cache_cache,
            &initkmem_list3[CACHE_CACHE + nid], nid);

        init_list(malloc_sizes[INDEX_AC].cs_cachep,
              &initkmem_list3[SIZE_AC + nid], nid);

        if (INDEX_AC != INDEX_L3) {
            init_list(malloc_sizes[INDEX_L3].cs_cachep,
                  &initkmem_list3[SIZE_L3 + nid], nid);
        }
    }
}

g_cpucache_up = EARLY;

} {% endhighlight %}

首先,kmalloc_cache_init创建系统中的第一个slab缓存,以便为kmem_cache的实例提供内存,为此,内核使用的主要是在编译时创建的静态数据,实际上,一个静态数据结构用作per-CPU数组。创建的slab缓存名字为cache_cache

然后,kmalloc_cache_init接下来初始化一般性的缓存,用作kmalloc内存的来源。为此,针对所需的各个缓存长度,分别调用kmem_cache_create函数。这个函数起初只需要cache_cache缓存已经建立即可,但在初始化per-CPU缓存时,该函数必须借助于kmalloc,在目前这个情况,依旧不可能。

所以,内核使用了g_cpucache_up变量,可以接受NONEPARTIAL_ACPARTIAL_L3以及FULL四个值,以反映kmalloc初始化的状态。最初内核的状态时NONE,在最小的kmalloc缓存初始化时,在其将一个静态的变量用于per-CPU的缓存数据。

g_cpucache_up接下来设置为PARTIAL_AC,表示array_cache的实例可以立即分配,如果初始化的长度还足够分配kmem_list3实例,则状态立即转换为PARTIAL_L3,否则只能等下一个更大的缓存初始化之后才能变更。

剩余的kmalloc缓存的per-CPU数据现在可以用kmalloc创建,这是一个arraycache_init实例,只需要最小的kmalloc内存区。

kmalloc_cache_init的最后一步,把到现在为止一直使用的数据结构的所有静态实例化的成员,用kmalloc动态分配的版本替换。这个时候g_cpucache_up变量的状态时FULL,表示slab分配器已经就绪可以使用。

<mm/slab.c>

{% highlight c++%} void __init kmem_cache_init_late(void) { struct kmem_cache *cachep;

/* 重新改变head数组的长度到它们真实分配的长度 */
mutex_lock(&cache_chain_mutex);
list_for_each_entry(cachep, &cache_chain, next)
    if (enable_cpucache(cachep, GFP_NOWAIT))
        BUG();
mutex_unlock(&cache_chain_mutex);

/* 完成了! */
g_cpucache_up = FULL;

init_lock_keys();

register_cpu_notifier(&cpucache_notifier);

} {% endhighlight %}

处理完成代码如上。