slub alloc and free

50
slub: alloc and free Masami Ichikawa @masami256

Upload: masami-ichikawa

Post on 20-Jul-2015

916 views

Category:

Technology


3 download

TRANSCRIPT

Page 1: Slub alloc and free

slub: alloc and freeMasami Ichikawa

@masami256

Page 2: Slub alloc and free

Intro• 今回はslabのalloc/free/discardの処理を見ていきます

• 前回はslabのデータ構造と作成部分を見ました

• Slub data structure

• http://www.slideshare.net/masamiichikawa/slub-data-structure

• 本スライドは上記資料の続編となってます

• slab cacheの新規作成部分は多少重複してます

Page 3: Slub alloc and free

Common variable name• slub.cでよく使われれる変数名

• このスライドでも単にcと書いている場合はstruct kmem_cache_cpuの変数を指します

• s

• struct kmem_cacheの変数

• c

• struct kmem_cache_cpuの変数

• n

• struct kmem_cache_nodeの変数

Page 4: Slub alloc and free

Important variables• s->cpu_slab

• 現在のcpuに紐づくstruct kmem_cache_cpuへのポインタ

• c->page

• slab objectが使用しているstruct pageへのポインタ

• c->freelist

• リストと名前が付いてるが次の空きslabオブジェクトを指す

Page 5: Slub alloc and free
Page 6: Slub alloc and free

Interfaces

Page 7: Slub alloc and free

Interfaces• 外部に公開している主なI/F

• kmem_cache_alloc()

• kmem_cache_alloc_node()

• CONFIG_NUMA=y

• kmem_cache_free()

• Slub内部のI/F

• slab_alloc_node()

• slab_free()

Page 8: Slub alloc and free

kmem_cache_alloc

• allocate対象のslabを管理するオブジェクト

• GFPフラグ

• kmalloc()で使うのと同じもの

void *kmem_cache_alloc(struct kmem_cache *, gfp_t flags);

Page 9: Slub alloc and free

slab_alloc_node

• allocate対象のslabを管理するオブジェクト

• GFPフラグ

• NUMAノード

• kmem_cache_alloc()から呼ばれた場合はNUMA_NO_NODEが使われる

• 呼び出し元のアドレス

• デバッグ用

• _RET_IP_マクロ(__builtin_return_address(0))を渡す

static __always_inline void *slab_alloc_node(struct kmem_cache *s, gfp_t gfpflags, int node, unsigned long addr)

Page 10: Slub alloc and free

kmem_cache_free

• free対象のslabを管理するオブジェクト

• free対象のslabキャッシュ

void kmem_cache_free(struct kmem_cache *, void *);

Page 11: Slub alloc and free

slab_free

• free対象のslabを管理するオブジェクト

• free対象のobjectを含むpage

• free対象のslabキャッシュ

• 呼び出し元のアドレス

• slab_alloc_node()と同様にデバッグ用

static __always_inline void slab_free(struct kmem_cache *s, struct page *page, void *x, unsigned long addr)

Page 12: Slub alloc and free

Implementation

Page 13: Slub alloc and free

Allocation

Page 14: Slub alloc and free

Allocation flow-> kmem_cache_alloc() -> slab_alloc_node() current cpuにslabキャッシュがある?(c->freelist != NULL)

* あれば、次回alloc時の返却用オブジェクトをキャッシュに載せ、freelistを返す * なければ__slab_alloc()でpartial listを探す -> __slab_alloc() NUMAノードを見ていって使えそうなslabキャッシュがある? * 使えるものがあればそれを返す * 見つからなければnew_slab_objects()で更に検索 * __slab_alloc()以降で返ってきたオブジェクトをfreelistとして設定 -> new_slab() partial listの検索と、必要ならslab cacheの新規作成 * partial listを辿って使えるものがあればそれを返却 * なければnew_slab()で新規作成 -> new_slab() allocate_slab()でpageを確保 * 確保したpageに対してslab objectを設定

Page 15: Slub alloc and free

slab_alloc_node()• 下記の処理でslabを新規に作るかチェック

• unlikelyの条件に入らない場合(fast path)

• 次回のallocに備える処理を実行し、objectを返す

• unlikelyな条件に該当した場合(slow path)

• 空きslabの探索と必要なら新規作成

• __slab_alloc()を呼び出し

object = c->freelist; page = c->page; if (unlikely(!object || !node_match(page, node))) {

nodeがNUMA_NO_NODEな場合は1が返る。 kmem_cache_alloc()の場合、nodeはNUMA_NO_NODEが使われる。

Page 16: Slub alloc and free

slab_alloc_node()• fast pathでは次回のallocに向けて先読みを実行

void *next_object = get_freepointer_safe(s, object);

if (unlikely(!this_cpu_cmpxchg_double( s->cpu_slab->freelist, s->cpu_slab->tid, object, tid, next_object, next_tid(tid)))) {

note_cmpxchg_failure("slab_alloc", s, tid); goto redo; } prefetch_freepointer(s, next_object);

s->cpu_slab->freelist == objecdt && s->cpu_slab->tid == tid ならnext_object,next_tid(tid)が新しいfreelist,tidになる

次回のkmem_cache_alloc()で返すobjectをcpuのキャッシュに載せる

次の空きobject取得

Page 17: Slub alloc and free

__slab_alloc()• 使えるslabキャッシュ or 新規作成し、freelistを設定

• c->pageがNULLならslabキャッシュの新規作成

• node指定がある場合

• pageとpageがあるnodeが一致しない場合は新規作成

• pageとkmem_cache_alloc()で使用したGFPフラグが違う場合も新規作成

• page->freelistがNULL(空きオブジェクト無し)の場合も新規作成

• freelistにslab objectがあればload_freelistラベルの処理へ

Page 18: Slub alloc and free

__slab_alloc()• freelistの設定

• 今回返却分はすでにあるのでload_freelistで次回分を設定

load_freelist: c->freelist = get_freepointer(s, freelist); c->tid = next_tid(c->tid); local_irq_restore(flags); return freelist;

次回のallocで返すobject、tidを設定

Page 19: Slub alloc and free

__slab_alloc()• slab cacheの新規作成

new_slab:

if (c->partial) { page = c->page = c->partial; c->partial = page->next; stat(s, CPU_PARTIAL_ALLOC); c->freelist = NULL; goto redo; }

freelist = new_slab_objects(s, gfpflags, node, &c);

if (unlikely(!freelist)) { slab_out_of_memory(s, gfpflags, node); local_irq_restore(flags); return NULL; }

page = c->page; if (likely(!kmem_cache_debug(s) && pfmemalloc_match(page, gfpflags))) goto load_freelist;

新規作成前にcpuに関連するpartial listから使えるキャッシュを探す (NUMAノードとの比較部分から再実行)

使えそうなものがなかったので新規作成

問題なければload_freelistラベルに飛んでfreelistを設定

Page 20: Slub alloc and free

new_slab_objects()• 最初にget_partial()でpartial listより使えるslabを探す

static void *get_partial(struct kmem_cache *s, gfp_t flags, int node, struct kmem_cache_cpu *c) { void *object; int searchnode = node;

if (node == NUMA_NO_NODE) searchnode = numa_mem_id(); else if (!node_present_pages(node)) searchnode = node_to_mem_node(node);

object = get_partial_node(s, get_node(s, searchnode), c, flags); if (object || node != NUMA_NO_NODE) return object;

return get_any_partial(s, flags, c); }

検索対象のnodeを決めて検索を実行

全NUMAゾーンから使えるslab cacheがあるか探す

Page 21: Slub alloc and free

new_slab_objects()• 使えるslab cacheがなければnew_slab()で新規作成

if (page) { c = raw_cpu_ptr(s->cpu_slab); if (c->page) flush_slab(s, c);

freelist = page->freelist; page->freelist = NULL;

stat(s, ALLOC_SLAB); c->page = page; *pc = c; }

既存のキャッシュがあったらslabをcから外す

Page 22: Slub alloc and free

get_partial_node() list_for_each_entry_safe(page, page2, &n->partial, lru) { void *t;

if (!pfmemalloc_match(page, flags)) continue;

t = acquire_slab(s, n, page, object == NULL, &objects); if (!t) break;

available += objects; if (!object) { c->page = page; stat(s, ALLOC_FROM_PARTIAL); object = t; } else { put_cpu_partial(s, page, 0); stat(s, CPU_PARTIAL_NODE); } if (!kmem_cache_has_cpu_partial(s) || available > s->cpu_partial / 2) break; }

nで指定されたnodeのpartial listを辿る

slabのあるpageを既存のpageと入れ替え

Page 23: Slub alloc and free

acquire_slab() freelist = page->freelist; counters = page->counters; new.counters = counters; *objects = new.objects - new.inuse; if (mode) { new.inuse = page->objects; new.freelist = NULL; } else { new.freelist = freelist; }

VM_BUG_ON(new.frozen); new.frozen = 1;

if (!__cmpxchg_double_slab(s, page, freelist, counters, new.freelist, new.counters, "acquire_slab")) return NULL;

page->freelist = new.freelist page->counters = new.counters

Page 24: Slub alloc and free

get_any_partial()

• 全てのNUMA nodeに対してget_partial_node()を実行して使えるslabを検索

Page 25: Slub alloc and free

new_slab()• allocate_slab()でfreelist用のpageを確保

• slab objectを初期化

for_each_object_idx(p, idx, s, start, page->objects) { setup_object(s, page, p); if (likely(idx < page->objects)) set_freepointer(s, p, p + s->size); else set_freepointer(s, p, NULL); }

for_each_object_idxマクロの終了条件はidx <= page->objects

Page 26: Slub alloc and free

allocate_slab()

• pageの確保

• 実際の確保はalloc_slab_page()が行う

Page 27: Slub alloc and free

alloc_slab_page()• 実際にpage確保の関数を呼ぶのが個々

• NUMAノードの指定有無で呼ぶ関数が変わる

• node == NUMA_NO_NODE(ノード不問)

• alloc_pages()

• node != NUMA_NO_NODE

• alloc_pages_exact_node()

Page 28: Slub alloc and free

setup_object()

• setup_object_debug()でデバッグ用のオブジェクト設定

• POISON、RED ZONE、Tracking

• s->ctorが設定されていればコンストラクタ関数の呼び出し

Page 29: Slub alloc and free

set_freepointer()• object + s->offsetのところに次のobjectのアドレスを設定

static inline void set_freepointer(struct kmem_cache *s, void *object, void *fp) { *(void **)(object + s->offset) = fp; }

for_each_object_idx(p, idx, s, start, page->objects) { setup_object(s, page, p); if (likely(idx < page->objects)) set_freepointer(s, p, p + s->size); else set_freepointer(s, p, NULL); }

Page 30: Slub alloc and free

get_freepointer()• object + s->offsetに書かれているアドレスにあるobjectを返す

static inline void *get_freepointer(struct kmem_cache *s, void *object) { return *(void **)(object + s->offset); }

Page 31: Slub alloc and free

Free

Page 32: Slub alloc and free

Freeing flow-> kmem_cache_free() free対象のslab objectを含むpageを取得 -> slab_free() freeしようとしているobjectがc->pageにある? * yesの場合はset_freepointer()でfreeの処理を行う * noの場合は__slab_free()で処理を行う -> __slab_free()

Page 33: Slub alloc and free

slab_free() if (likely(page == c->page)) { set_freepointer(s, object, c->freelist);

if (unlikely(!this_cpu_cmpxchg_double( s->cpu_slab->freelist, s->cpu_slab->tid, c->freelist, tid, object, next_tid(tid)))) {

note_cmpxchg_failure("slab_free", s, tid); goto redo; } stat(s, FREE_FASTPATH); } else __slab_free(s, page, x, addr);

次の空きobjectをc->freelistに

今freeしたobjectを次回返すように

slow path

解放するobjectがc->pageにある?

Page 34: Slub alloc and free

__slab_free()• freeするobjectが指す次のobjectを設定

• freeするobjectが所属するstruct pageを設定

• 設定したpageを適切なリストへ登録・解除

• すべてのobjectが未使用になったらslabそのものを破棄できる

Page 35: Slub alloc and free

__slab_free()prior = page->freelist; counters = page->counters; set_freepointer(s, object, prior); new.counters = counters; was_frozen = new.frozen; new.inuse--;

do { } whileで下記の条件中繰り返す !cmpxchg_double_slab(s, page, prior, counters, object, new.counters, “__slab_free") ↑ざっくりとした内容としてはpage->freelist, page->countersがprior, countersと同じなら以下をする page->freelist = object page->counters = new.couters

if ((!new.inuse || !prior) && !was_frozen) { if (kmem_cache_has_cpu_partial(s) && !prior) { new.frozen = 1;

} else { /* Needs to be taken off a list */  n = get_node(s, page_to_nid(page)); spin_lock_irqsave(&n->list_lock, flags);

}

上記ループ中のnew.inuse--の後にある処理。 別nodeにあるobjectをfreeするかで後の処理が変わる

Page 36: Slub alloc and free

__slab_free()if (likely(!n)) { if (new.frozen && !was_frozen) {

put_cpu_partial(s, page, 1); stat(s, CPU_PARTIAL_FREE);

} if (was_frozen) stat(s, FREE_FROZEN);

return; }

これまでの処理でnode(n)が設定されておらず、再設定したpageにはfrozen bitがセットされている場合はカレントcpuのpartial listへpageをつなぐ

frozenが1ということはs->cpu_slabにあったということ。was_frozenが0の場合は、freするobjectがあるpageはアクティブではなかった。

Page 37: Slub alloc and free

__slab_free()if (unlikely(!new.inuse && n->nr_partial >= s->min_partial)) goto slab_empty;

slab_empty: if (prior) { remove_partial(n, page); } else { remove_full(s, n, page); }

spin_unlock_irqrestore(&n->list_lock, flags); discard_slab(s, page);

freeによって使用中objectがslabからなくなった場合はリストからpageを外してdiscard_slab()でslabを破棄する

Page 38: Slub alloc and free

__slab_free()if (!kmem_cache_has_cpu_partial(s) && unlikely(!prior)) { if (kmem_cache_debug(s)) remove_full(s, n, page); add_partial(n, page, DEACTIVATE_TO_TAIL); stat(s, FREE_ADD_PARTIAL);

}

空きobject無しの状態からfreeによって空きobjectができた場合はpageをfull listからpartial listへ移動

Page 39: Slub alloc and free

Misc

Page 40: Slub alloc and free

flush_slab()• deactivate_slab()でcpuからslabを外すのとcのメンバ変数初期化

• c->tid

• next_tid()で次のtidをセット

• c->page

• NULL

• c->freelist

• NULL

Page 41: Slub alloc and free

deactivate_slab()• s->cpu_slabからslabを外す

• slab_alloc()やnew_slab_objects()で使用

• 処理は2段階

• slabの状態により最終的に下記の処理が行われる

• partial listへ追加 or 削除

• full listへの追加 or 削除

• slabの破棄(discard_slab())

CONFIG_SLUB_DEBUG=y

Page 42: Slub alloc and free

deactivate_slab while (freelist && (nextfree = get_freepointer(s, freelist))) { void *prior; unsigned long counters;

do { prior = page->freelist; counters = page->counters; set_freepointer(s, freelist, prior); new.counters = counters; new.inuse--; VM_BUG_ON(!new.frozen);

} while (!__cmpxchg_double_slab(s, page, prior, counters, freelist, new.counters, "drain percpu freelist"));

freelist = nextfree; }

page->freelist != prior && page->counters != counters なら page->freelist = freelist page->counters = new.counters

page->freelistに空きobjectを設定していく

newはstruct page

Page 43: Slub alloc and free

deactivate_slab() old.freelist = page->freelist; old.counters = page->counters; VM_BUG_ON(!old.frozen);

/* Determine target state of the slab */ new.counters = old.counters; if (freelist) { new.inuse--; set_freepointer(s, freelist, old.freelist); new.freelist = freelist; } else new.freelist = old.freelist;

new.freelistが最終的にpage->freelistになる

Page 44: Slub alloc and free

deactivate_slab()

if (!new.inuse && n->nr_partial >= s->min_partial) m = M_FREE; else if (new.freelist) { m = M_PARTIAL; } else { m = M_FULL; }

slabの使用状況セットし、 M_FREE以外の場合は名前に該当するlistへの追加 or 削除が行われる

if (!__cmpxchg_double_slab(s, page, old.freelist, old.counters, new.freelist, new.counters, "unfreezing slab"))

freelistの入れ替え

Page 45: Slub alloc and free

deactivate_slab()

if (m == M_FREE) { stat(s, DEACTIVATE_EMPTY); discard_slab(s, page); stat(s, FREE_SLAB);

}

使用中のobjectがなければslabを破棄

Page 46: Slub alloc and free

discard_slab()• slabを解放するときに用

• 処理方法として、RCUを使うか設定できる

• 実際の処理は__free_slab()で行う

• 紛らわしいけど前者はslabが使用しているpageを解放するときに、後者はslab objectのfree処理で使用

• free_slab()、__free_slab()

• slab_free()、__slab_free()

Page 47: Slub alloc and free

__free_slab()• check_object()でobjectが壊れたりしていないかチェック

• pageにセットされているflagのクリア

• __free_page()でpageの解放

• memcg_unchar_slab()でmemcgからpageの使用量を減らす

Page 48: Slub alloc and free

Summary• kmem_cache_alloc()

• c->cpu_slabに空きobjectがない場合、下記の順番で使えるobjectを探す

• 自NUMAノードのpartial list

• 他ノードのpartial list

• alloc_pages()で新規にページ確保

Page 49: Slub alloc and free

Summary• kmem_cache_free()

• free対象のobjectがc->cpu_slabにあれば、次回のkmem_cache_alloc()でそのobjectを返すようにする

• c->cpu_slabになければobjectがあるリストを探して処理

• freeによって使用中objectがなくなればslabを解放

• 空きobject無し状態からのfreeならfull listからpartial listへ移動

Page 50: Slub alloc and free

References

• Slab allocators in the Linux Kernel: SLAB, SLOB, SLUB

• http://events.linuxfoundation.org/sites/events/files/slides/slaballocators.pdf