分类: LINUX
2014-07-11 15:58:35
原文地址:Vi Linux内存 之 Slub分配器(三) 作者:palals
本节介绍如何从slub分配器中分配一个空闲对象。
kmem_cache_alloc
通过slub分配器获得一个空闲对象。直接调用slab_alloc函数。
slab_alloc
首先尝试快速路径分配,如果失败再走慢速路径分配。
参数:
1) s:cache指针。
2) gfpflags:分配标志。
3) node:NUMA,从指定内存节点分配
4) addr:初始调用函数地址
static __always_inline void *slab_alloc(struct kmem_cache *s,
gfp_t gfpflags, int node, unsigned long addr)
{
void **object;
struct kmem_cache_cpu *c;
unsigned long flags;
/* CONFIG_SLUB_DEBUG相关 */
if (slab_pre_alloc_hook(s, gfpflags))
return NULL;
local_irq_save(flags);
/* 获得本cpu的local slab,__this_cpu_ptr只是新版本的cpu_slab[NR_CPUS] */
c = __this_cpu_ptr(s->cpu_slab);
/* 获得local slab中的第一个空闲对象 */
object = c->freelist;
if (unlikely(!object || !node_match(c, node)))
/* local slab中无空闲对象或local slab与指定的内存节点不匹配,走慢速路径分配 */
object = __slab_alloc(s, gfpflags, node, addr, c);
else {
/* freelist指向下一个空闲对象。get_freepointer返回下一个空闲对象的地址 */
c->freelist = get_freepointer(s, object);
/* 快速路径local slab中分配成功 */
stat(s, ALLOC_FASTPATH);
}
local_irq_restore(flags);
/* 是否需要将对象清0 */
if (unlikely(gfpflags & __GFP_ZERO) && object)
memset(object, 0, s->objsize);
/* CONFIG_SLUB_DEBUG相关 */
slab_post_alloc_hook(s, gfpflags, object);
return object;
}
__slab_alloc
无法从local slab中分配空闲对象,走慢速路径分配。
static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,
unsigned long addr, struct kmem_cache_cpu *c)
{
void **object;
struct page *new;
/* We handle __GFP_ZERO in the caller */
gfpflags &= ~__GFP_ZERO;
/* 还没有local slab,从部分满slab链中提取一个slab作为local slab。Page指针保存local slab的首页 */
if (!c->page)
goto new_slab;
slab_lock(c->page);
/* local slab与指定内存节点不匹配,去激活local slab */
if (unlikely(!node_match(c, node)))
goto another_slab;
/* local slab重填统计 */
stat(s, ALLOC_REFILL);
/* 将local slab freelist中的空闲对象填充到cpu freelist中 */
load_freelist:
/* 检查local slab的freelist指针 */
object = c->page->freelist;
/* local slab没有空闲对象,去激活local slab */
if (unlikely(!object))
goto another_slab;
if (kmem_cache_debug(s))
goto debug;
/* 将local slab freelist中的对象全部填充到cpu freelist中。这个“填充”动作实际上只是一个指针接管 */
c->freelist = get_freepointer(s, object);
/* local slab的所有空闲对象都分配给了cpu freelist(而不是一般意义上的分配给进程)*/
c->page->inuse = c->page->objects;
/* slab freelist无空闲对象了,全部分给了cpu freelist */
c->page->freelist = NULL;
/* local slab所在的内存节点 */
c->node = page_to_nid(c->page);
unlock_out:
slab_unlock(c->page);
/* 更新慢速路径分配统计 */
stat(s, ALLOC_SLOWPATH);
/* 返回空闲对象 */
return object;
another_slab:
/* 去激活旧的local slab */
deactivate_slab(s, c);
new_slab:
/* 提取一个部分满的slab。如果没有指定node,那么可能来自于本内存节点,也可能来自于其他节点。如果指定了node,那么只可能来自于本内存节点。*/
new = get_partial(s, gfpflags, node);
if (new) {
/* local slab指向新的slab */
c->page = new;
stat(s, ALLOC_FROM_PARTIAL);
/* 重试freelist填充流程 */
goto load_freelist;
}
/* 走到这说明没有部分满的slab了,需要创建一个新slab */
gfpflags &= gfp_allowed_mask;
if (gfpflags & __GFP_WAIT)
local_irq_enable();
/* 创建一个新slab */
new = new_slab(s, gfpflags, node);
if (gfpflags & __GFP_WAIT)
local_irq_disable();
if (new) {
/* 获得local slab所在的struct kmem_cache_cpu指针 */
c = __this_cpu_ptr(s->cpu_slab);
/* 更新统计 */
stat(s, ALLOC_SLAB);
/* 清除旧的local slab */
if (c->page)
flush_slab(s, c);
slab_lock(new);
/* 冻结新的slab,表示它已经成为某个cpu的local slab */
__SetPageSlubFrozen(new);
/* 指向新的local slab */
c->page = new;
/* 重试freelist填充流程 */
goto load_freelist;
}
/* 走到这,说明已经没有空闲内存,分配失败 */
if (!(gfpflags & __GFP_NOWARN) && printk_ratelimit())
slab_out_of_memory(s, gfpflags, node);
return NULL;
debug:
if (!alloc_debug_processing(s, c->page, object, addr))
goto another_slab;
c->page->inuse++;
c->page->freelist = get_freepointer(s, object);
c->node = NUMA_NO_NODE;
goto unlock_out;
}
get_freepointer
static inline void *get_freepointer(struct kmem_cache *s, void *object)
{
/* 每个对象后面会放一个对象指针,指向下一个空闲对象。此处返回下一个空闲对象的指针。虽然cache中可能被不同的对象引用,对象大小可能有些许不同,但是offset是固定的 */
return *(void **)(object + s->offset);
}
deactivate_slab
去激活local slab。如果slab双链中的一个slab成为某cpu的local slab,我们称之为激活,反之,如果将某个local slab重新放回到slab双链中,我们称之为去激活。
static void deactivate_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
__releases(bitlock)
{
/* 获得local slab的首页 */
struct page *page = c->page;
int tail = 1;
/* slab的freelist不为空。通常一个local slab的freelist是空的,其对象已经全部分配给了cpu的feelist,参见__slab_alloc。不为空,说明有其他cpu释放了属于此cpu local slab的对象,参见__slab_free */
if (page->freelist)
stat(s, DEACTIVATE_REMOTE_FREES);
/*
* Merge cpu freelist into slab freelist. Typically we get here
* because both freelists are empty. So this is unlikely
* to occur.
*/
/* 首先将cpu freelist中的空闲对象归还给slab freelist。一个slab成为local slab时,其freelist中的对象均已分配cpu freelist。通常分配对象走到这时,cpu freelist为空,仅当内存节点不匹配时,cpu freelist可能非空。*/
while (unlikely(c->freelist)) {
void **object;
/* 指定了插入到部分满slab链表时的位置,最近使用的放到链表头 */
tail = 0; /* Hot objects. Put the slab first */
/* Retrieve object from cpu_freelist */
object = c->freelist;
c->freelist = get_freepointer(s, c->freelist);
/* And put onto the regular freelist */
set_freepointer(s, object, page->freelist);
page->freelist = object;
page->inuse--;
}
/* local slab指针置为空 */
c->page = NULL;
/* 解冻local slab */
unfreeze_slab(s, page, tail);
}
unfreeze_slab
激活一个slab成为local slab后,会被冻结。去激活slab时,需要解冻。
static void unfreeze_slab(struct kmem_cache *s, struct page *page, int tail)
__releases(bitlock)
{
struct kmem_cache_node *n = get_node(s, page_to_nid(page));
/* 清除冻结标记,表示不是local slab了 */
__ClearPageSlubFrozen(page);
if (page->inuse) {
/* slab中某些对象仍在使用 */
if (page->freelist) {
/* slab还有空闲对象,加入部分满slab链,tail为0时,添加到链表头 */
add_partial(n, page, tail);
stat(s, tail ? DEACTIVATE_TO_TAIL : DEACTIVATE_TO_HEAD);
} else {
/* slab中没有空闲对象,加入满slab链 */
stat(s, DEACTIVATE_FULL);
if (kmem_cache_debug(s) && (s->flags & SLAB_STORE_USER))
/* 加入满slab链 */
add_full(n, page);
}
slab_unlock(page);
} else {
/* slab中所有对象都是空闲的 */
stat(s, DEACTIVATE_EMPTY);
if (n->nr_partial < s->min_partial) {
/* 如果部分满slab个数小于最小值 */
/*
* Adding an empty slab to the partial slabs in order
* to avoid page allocator overhead. This slab needs
* to come after the other slabs with objects in
* so that the others get filled first. That way the
* size of the partial list stays small.
*
* kmem_cache_shrink can reclaim any empty slabs from
* the partial list.
*/
/* 将空slab加入到部分满slab链的后面。为了避免部分满slab链过多,内核会通过kmem_cache_shrink对部分满slab链按照inuse的大小进行排序(最大的在前),并回收空slab。*/
add_partial(n, page, 1);
slab_unlock(page);
} else {
/* 部分满slab个数满足最小值的要求,无需再维护这个slab了 */
slab_unlock(page);
stat(s, FREE_SLAB);
/* 销毁这个slab */
discard_slab(s, page);
}
}
}
discard_slab
销毁一个slab。
static void discard_slab(struct kmem_cache *s, struct page *page)
{
/* 更新统计 */
dec_slabs_node(s, page_to_nid(page), page->objects);
/* 销毁slab */
free_slab(s, page);
}
get_partial
提取一个部分满的slab。
static struct page *get_partial(struct kmem_cache *s, gfp_t flags, int node)
{
struct page *page;
/* 计算扫描节点,未指定NUMA节点,扫描本节点,否则扫描指定节点 */
int searchnode = (node == NUMA_NO_NODE) ? numa_node_id() : node;
/* 从扫描节点提取一个部分满slab */
page = get_partial_node(get_node(s, searchnode));
if (page || node != -1)
return page;
/* 从指定节点提取失败,扫描任意节点 */
return get_any_partial(s, flags);
}
get_partial_node
从某内存节点提取一个部分满slab。
static struct page *get_partial_node(struct kmem_cache_node *n)
{
struct page *page;
/*
* Racy check. If we mistakenly see no partial slabs then we
* just allocate an empty slab. If we mistakenly try to get a
* partial slab and there is none available then get_partials()
* will return NULL.
*/
if (!n || !n->nr_partial)
return NULL;
spin_lock(&n->list_lock);
/* 遍历部分满slab链 */
list_for_each_entry(page, &n->partial, lru)
/* 一个slab成为local slab后,需要被冻结 */
if (lock_and_freeze_slab(n, page))
goto out;
page = NULL;
out:
spin_unlock(&n->list_lock);
return page;
}
lock_and_freeze_slab
static inline int lock_and_freeze_slab(struct kmem_cache_node *n,
struct page *page)
{
if (slab_trylock(page)) {
/* 从部分满slab链上摘除 */
__remove_partial(n, page);
/* 冻结某个slab,表示此slab已经变成了某个cpu的local slab */
__SetPageSlubFrozen(page);
return 1;
}
return 0;
}
flush_slab
清除local slab。
static inline void flush_slab(struct kmem_cache *s, struct kmem_cache_cpu *c)
{
/* 更新统计 */
stat(s, CPUSLAB_FLUSH);
slab_lock(c->page);
/* 去激活local slab */
deactivate_slab(s, c);
}