该函数用于创建Slab缓存。
参数介绍:
name: 缓存名字
size: 对象的大小
align:对齐
flag:Slab标志位
ctor:对象构造函数
dtor:对象析构函数
struct kmem_cache *
kmem_cache_create (const char *name, size_t size, size_t align,
unsigned long flags, void (*ctor)(void*, struct kmem_cache *, unsigned long),
void (*dtor)(void*, struct kmem_cache *, unsigned long))
{
size_t left_over, slab_size, ralign;
struct kmem_cache *cachep = NULL;
struct list_head *p;
对函数使用的检查,下面一种情况发生都将导致死机
1.name为null
2.处于中断上下文中
3.对象大小小于4个字节或者大于32个页面
4.构造函数存在,析构函数不存在
if ((!name) ||
in_interrupt() ||
(size < BYTES_PER_WORD) ||
(size > (1 << MAX_OBJ_ORDER) * PAGE_SIZE) || (dtor && !ctor)) {
printk(KERN_ERR "%s: Early error in slab %s/n",
__FUNCTION__, name);
BUG();
}
锁住CPU热插拔功能
lock_cpu_hotplug();
为cache_chain加互斥锁
mutex_lock(&cache_chain_mutex);
遍历cache_chain链表
list_for_each(p, &cache_chain) {
struct kmem_cache *pc = list_entry(p, struct kmem_cache, next);
由于__get_user函数只在用户环境中执行,因此我们要
改变fs寄存器,在内核空间也能调用该函数
mm_segment_t old_fs = get_fs();
char tmp;
int res;
设置fs段的内容为内核数据段
set_fs(KERNEL_DS);
res = __get_user(tmp, pc->name);
还原fs段
set_fs(old_fs);
if (res) {
printk("SLAB: cache with size %d has lost its name/n",
pc->buffer_size);
continue;
}
判断是不是为name的cache是不是已经存在
if (!strcmp(pc->name, name)) {
printk("kmem_cache_create: duplicate cache %s/n", name);
dump_stack();
goto oops;
}
}
如果采用2.6提供的新的锁机制,read-copy-update来对其释放,
检查析构函数是不是为空。
if (flags & SLAB_DESTROY_BY_RCU)
BUG_ON(dtor);
检查创建时相应的标志位是不是设置好了。
CREATE_MASK: (# define CREATE_MASK (SLAB_HWCACHE_ALIGN | SLAB_NO_REAP | /
SLAB_CACHE_DMA | SLAB_MUST_HWCACHE_ALIGN | /
SLAB_RECLAIM_ACCOUNT | SLAB_PANIC | /
SLAB_DESTROY_BY_RCU))
if (flags & ~CREATE_MASK)
BUG();
检查对象的大小是不是32位对齐,如果不是
则进行调整
if (size & (BYTES_PER_WORD - 1)) {
size += (BYTES_PER_WORD - 1);
size &= ~(BYTES_PER_WORD - 1);
}
检查Slab是不是按照硬件缓冲行对齐
if (flags & SLAB_HWCACHE_ALIGN) {
取硬件缓冲行大小
ralign = cache_line_size();
进行对齐大小的调整,直到对象大小大于对齐的大小
while (size <= ralign / 2)
ralign /= 2;
} else {
默认情况下,对齐大小为4个字节
ralign = BYTES_PER_WORD;
}
对align进行重新调整,确定最终的align值
if (ralign < ARCH_SLAB_MINALIGN) {
ralign = ARCH_SLAB_MINALIGN;
if (ralign > BYTES_PER_WORD)
flags &= ~(SLAB_RED_ZONE | SLAB_STORE_USER);
}
if (ralign < align) {
ralign = align;
if (ralign > BYTES_PER_WORD)
flags &= ~(SLAB_RED_ZONE | SLAB_STORE_USER);
}
align = ralign;
从cache_cache中划分一个新的kmem_cache节点。
cachep = kmem_cache_alloc(&cache_cache, SLAB_KERNEL);
if (!cachep)
goto oops;
初始化得到的内存
memset(cachep, 0, sizeof(struct kmem_cache));
如果发现对象大小是大于512字节,那么Slab缓存的管理区部分应该和
对象分开存放
if (size >= (PAGE_SIZE >> 3))
flags |= CFLGS_OFF_SLAB;
对齐
size = ALIGN(size, align);
计算存放大小为size的对象的order值,以及可以存放的对象数量。返回Slab缓存的
碎片大小。
left_over = calculate_slab_order(cachep, size, align, flags);
如果发现对象数量为0,表示不能创建
if (!cachep->num) {
printk("kmem_cache_create: couldn't create cache %s./n", name);
kmem_cache_free(&cache_cache, cachep);
cachep = NULL;
goto oops;
}
计算Slab缓存的管理区的大小:slab结构的大小+每个对象有一个控制域的大小(kmem_bufctl_t)
slab_size = ALIGN(cachep->num * sizeof(kmem_bufctl_t)
+ sizeof(struct slab), align);
如果Slab管理区是和对象分开存放的,并且Slab中的碎片大小足够存放
管理区。则需要重新调整Slab管理区的存放位置,可以和Slab对象一起存放。
if (flags & CFLGS_OFF_SLAB && left_over >= slab_size) {
flags &= ~CFLGS_OFF_SLAB;
left_over -= slab_size;
}
如果Slab管理区和对象分开存放,Slab管理区的大小为:slab结构的大小+每个对象有一个控制域的大小(kmem_bufctl_t)
if (flags & CFLGS_OFF_SLAB) {
slab_size =
cachep->num * sizeof(kmem_bufctl_t) + sizeof(struct slab);
}
计算着色偏移区:初始值为硬件缓存行大小
cachep->colour_off = cache_line_size();
调整着色偏移区
if (cachep->colour_off < align)
cachep->colour_off = align;
计算颜色大小:Slab碎片大小除以着色偏移区
cachep->colour = left_over / cachep->colour_off;
cachep->slab_size = slab_size;
cachep->flags = flags;
cachep->gfpflags = 0;
if (flags & SLAB_CACHE_DMA)
cachep->gfpflags |= GFP_DMA;
初始化自旋锁
spin_lock_init(&cachep->spinlock);
buffer_size为一个对象的大小
cachep->buffer_size = size;
如果对象和管理区分开存放,使用slabp_cache指针来指向管理区的内存,从通用缓存中申请。
if (flags & CFLGS_OFF_SLAB)
cachep->slabp_cache = kmem_find_general_cachep(slab_size, 0u);
cachep->ctor = ctor;
cachep->dtor = dtor;
cachep->name = name;
接下来是对kmem_list3/array cache的一个初始化。每个CPU对应一个array cache
系统先是分配array cache中的对象,目的是减少对Slab cache的访问次数来
提高系统性能。
if (g_cpucache_up == FULL) {
enable_cpucache(cachep);
} else {
if (g_cpucache_up == NONE) {
为array cache和list3分配内存
cachep->array[smp_processor_id()] =
&initarray_generic.cache;
set_up_list3s(cachep, SIZE_AC);
if (INDEX_AC == INDEX_L3)
g_cpucache_up = PARTIAL_L3;
else
g_cpucache_up = PARTIAL_AC;
} else {
为array cache和list3分配内存
cachep->array[smp_processor_id()] =
kmalloc(sizeof(struct arraycache_init), GFP_KERNEL);
if (g_cpucache_up == PARTIAL_AC) {
set_up_list3s(cachep, SIZE_L3);
g_cpucache_up = PARTIAL_L3;
} else {
int node;
初始化kmem_list3
for_each_online_node(node) {
cachep->nodelists[node] =
kmalloc_node(sizeof
(struct kmem_list3),
GFP_KERNEL, node);
BUG_ON(!cachep->nodelists[node]);
kmem_list3_init(cachep->
nodelists[node]);
}
}
}
cachep->nodelists[numa_node_id()]->next_reap =
jiffies + REAPTIMEOUT_LIST3 +
((unsigned long)cachep) % REAPTIMEOUT_LIST3;
初始化array cache
cpu_cache_get(cachep)->avail = 0;
cpu_cache_get(cachep)->limit = BOOT_CPUCACHE_ENTRIES;
cpu_cache_get(cachep)->batchcount = 1;
cpu_cache_get(cachep)->touched = 0;
cachep->batchcount = 1;
cachep->limit = BOOT_CPUCACHE_ENTRIES;
}
将创建的新的kmem_cache连入链表cache_cache中。
list_add(&cachep->next, &cache_chain);
oops:
if (!cachep && (flags & SLAB_PANIC))
panic("kmem_cache_create(): failed to create slab `%s'/n",
name);
mutex_unlock(&cache_chain_mutex);
unlock_cpu_hotplug();
return cachep;
}