読者です 読者をやめる 読者になる 読者になる

slub: freelist用にpage確保してからstruct kmem_cache_cpuを設定する辺りのメモ

linux kernel

new_slab_objects()がfreelistを確保するためにnew_slab()を呼んで、それが戻ってきたらという辺りのメモです。
大まかにはslub:slab objectの作成処理を見ていく - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモで書いたりしているんだけど流れが見えてきたのでもう一度。

new_slab()を呼んだということはpageをallocateした場合です。 ちょっと思い出すために、new_slab()の最後の処理を見てみます。

1417         page->freelist = start;
1418         page->inuse = page->objects;
1419         page->frozen = 1;

page->freelistにはslab objetの領域の先頭アドレスが入ります。そうすると適当な絵にするとこんな風になるかと。

f:id:masami256:20141225194712p:plain

そしてnew_slab()から戻ってnew_slab_objects()cはper_cpu領域にあるデータ。古いデータがあった場合は値の入れ替えが行われる。flushっていうとキャッシュのflushを想像しちゃうけど処理内容としてはメンバ変数の入れ替え。flush_slab()は別の時に読もう。

2175         page = new_slab(s, flags, node);
2176         if (page) {
2177                 c = raw_cpu_ptr(s->cpu_slab);
2178                 if (c->page)
2179                         flush_slab(s, c);
2180 

次に行うのはfreelistをpageから外す作業。これでpageからはfreelistが参照できない。コメントにもあるけど、この時点だと誰もpage->freelistを参照していないでcmpxchg(実際は__cmpxchg_double_slabだと思うけど)を使わずに変数の更新が可能と。

2181                 /*
2182                  * No other reference to the page yet so we can
2183                  * muck around with it freely without cmpxchg
2184                  */
2185                 freelist = page->freelist;
2186                 page->freelist = NULL;
2187 

stat()はCONFIG_SLUB_STATSがyの時の処理。内容はs->cpu_slab(struct kmem_cache_cpu)のstat配列をALLOC_SLABをインデックスとして参照して、そこの値をインクリメントする。

2188                 stat(s, ALLOC_SLAB);

struct kmem_cache_cpuのメンバ変数のpageにpageを設定。

2189                 c->page = page;

pcはstruct kmem_cache_cpuでnew_slab_object()の4番目の引数として渡ってきたもの。

2190                 *pc = c;
2191         } else

このpcは呼び出し元の__slab_alloc()でこのように取ったもの。CONFIG_PREEMPTはyな前提で。

2268 #ifdef CONFIG_PREEMPT
2269         /*
2270          * We may have been preempted and rescheduled on a different
2271          * cpu before disabling interrupts. Need to reload cpu area
2272          * pointer.
2273          */
2274         c = this_cpu_ptr(s->cpu_slab);
2275 #endif

new_slab_objects()は戻り値としてfreelistを返す。

2198 
2199         return freelist;
2200 }

new_slab_objects()から戻る時にはnew_slab()で確保したpageはほぼそのままper_cpuなほうのcpu_slabのpageに設定される。

ついでなのでnew_slab_object()から__slab_alloc()に戻ってからの処理も見てみます。 返ってきたfreelistがNULLだった場合の処理を飛ばすとここに。特に問題がなければload_freelistラベルにジャンプ。

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

load_freelistはこのような処理。new_slab_object()で設定しなかったkmem_cache_cpuの変数を設定。
先ほどpage->freelistをNULLにしていたけど、ここではkmem_cache_cpuのfreelistを設定する。freelistをそのままc->freelistに入れないのはslab objectの先頭を飛ばす必要がある場合があるため。これはslub: kmem_cache構造体とslab object作成時のめも - φ(・・*)ゞ ウーン カーネルとか弄ったりのメモで見たようにs->offsetを足してポインタをsetしたりgetしたりする。

2317 load_freelist:
2318         /*
2319          * freelist is pointing to the list of objects to be used.
2320          * page is pointing to the page from which the objects are obtained.
2321          * That page must be frozen for per cpu allocations to work.
2322          */
2323         VM_BUG_ON(!c->page->frozen);
2324         c->freelist = get_freepointer(s, freelist);

tidはスレッドIDではなくてGlobally unique transaction id とのこと。最後にローカルIRQの割り込みを有効にしてfreelistを返す。

2325         c->tid = next_tid(c->tid);
2326         local_irq_restore(flags);
2327         return freelist;

tidは__slab_alloc()の呼び出し元のslab_alloc_node()で以下のように書かれています。

2400         /*
2401          * The transaction ids are globally unique per cpu and per operation on
2402          * a per cpu queue. Thus they can be guarantee that the cmpxchg_double
2403          * occurs on the right processor and that there was no operation on the
2404          * linked list in between.
2405          */

Rubyのしくみ -Ruby Under a Microscope-

Rubyのしくみ -Ruby Under a Microscope-