カーネルのバグでspin lock取っているコンテキストでsleepする可能性のある関数を使うと「BUG: sleeping function called from invalid context at mm/slub.c:965」なんて感じでエラーがでますけど、kmalloc()はGFP_XXXなflagsをどのように使っているのかなと思い調べてみる。なんで気になったかというと3.15 + αなカーネルでspin lock中にkamlloc()にGFP_NOIOを渡していて「BUG: sleeping function 〜」が出るケースに遭遇したから。 ずっと前にkmalloc()の概要は見てたんだけど今回は前回とは違う視点で。。
読むカーネルは3.14でslab allocatorはslubです。 kmallocはlinux/slab.hに合って、コンパイル時点でサイズが分かっているかどうかで処理が変わって多少変わる。
コンパイル時点でallocするsizeが分かっている場合
- kmallocのスラブキャッシュのキャッシュ容量よりもsizeが大きければkmalloc_large()を呼ぶ
- メモリのZONEがDMAじゃない場合はkmalloc_indexでスラブキャッシュのインデックスを取得して、以下のような流れになる
kmem_cache_alloc_trace() -> slab_alloc() -> slab_alloc_node()
実行時にallocするsizeが分かる場合は__kmalloc()を呼ぶ
- __kmalloc()も処理としては先ほどコンパイル時点でsizeが分かっている場合とだいたい一緒
- sizeがスラブキャッシュの最大キャッシュサイズより大きければkmalloc_large()を使う
- kmalloc_slab()を読んでスラブキャッシュからメモリが確保できればそれでOKで、メモリ確保できなければslab_alloc()を呼ぶ
というわけで、__kmalloc()、kmalloc()ではZONE_DMAからメモリ確保するかどうかにしかflagsは使わなくて、sleepする/しないという意味で使うのはslab_alloc_node()のほうというのが分かった。 kmallocのスラブキャッシュは/proc/slabinfoで確認できます。こんな感じになっていると思います。
dma-kmalloc-8192 0 0 8192 4 8 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-4096 0 0 4096 8 8 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-2048 0 0 2048 16 8 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-1024 0 0 1024 32 8 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-512 0 0 512 32 4 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-256 0 0 256 32 2 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-128 0 0 128 32 1 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-64 0 0 64 64 1 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-32 0 0 32 128 1 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-16 0 0 16 256 1 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-8 0 0 8 512 1 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-192 0 0 192 21 1 : tunables 0 0 0 : slabdata 0 0 0 dma-kmalloc-96 0 0 96 42 1 : tunables 0 0 0 : slabdata 0 0 0 kmalloc-8192 100 100 8192 4 8 : tunables 0 0 0 : slabdata 25 25 0 kmalloc-4096 301 344 4096 8 8 : tunables 0 0 0 : slabdata 43 43 0 kmalloc-2048 1672 1808 2048 16 8 : tunables 0 0 0 : slabdata 113 113 0 kmalloc-1024 1312 1312 1024 32 8 : tunables 0 0 0 : slabdata 41 41 0 kmalloc-512 2016 2016 512 32 4 : tunables 0 0 0 : slabdata 63 63 0 kmalloc-256 2383 2656 256 32 2 : tunables 0 0 0 : slabdata 83 83 0 kmalloc-192 3336 3549 192 21 1 : tunables 0 0 0 : slabdata 169 169 0 kmalloc-128 3847 4000 128 32 1 : tunables 0 0 0 : slabdata 125 125 0 kmalloc-96 1764 1764 96 42 1 : tunables 0 0 0 : slabdata 42 42 0 kmalloc-64 23836 25984 64 64 1 : tunables 0 0 0 : slabdata 406 406 0 kmalloc-32 2944 2944 32 128 1 : tunables 0 0 0 : slabdata 23 23 0 kmalloc-16 7680 7680 16 256 1 : tunables 0 0 0 : slabdata 30 30 0 kmalloc-8 8192 8192 8 512 1 : tunables 0 0 0 : slabdata 16 16 0
では、ここからはslab_alloc_node()を見ていきます。
slab_alloc_node()最初の処理はslab_pre_alloc_hook()でここでsleepしても良いかどうかの判定があります。
965 might_sleep_if(flags & __GFP_WAIT);
なのでBUG: sleeping function called from invalid context at mm/slub.c:965というのはまさしくここですね。 この後はshould_failslab()を呼んでその戻り値がこの関数の戻り値になるんだけど、これはfault injectionフレームワークの部分なので読み飛ばしましょう。
次はmemcg_kmem_get_cache()でキャッシュ(struct kmem_cache)を取得するんだけど、memory cgroupを使ってない無い場合は下のように引数で渡したcachepがそのまま返却されるのでここはmemcgはdisabledだったとして読み進める。
604 if (!memcg_kmem_enabled()) 605 return cachep;
そして、Preemptionをdisabledにしたうえでtransaction idと言うものを取得する。これはstruct kmem_cacheの最初のメンバ変数でkmem_cache_cpu構造体のtidというメンバ変数。 うーん、これはこれで調べないとよく分からないのでとりあえずtidを取得するというというレベルで抑えないと闇にはまる。。。
cはstruct kmem_cache_cpuでfreelistに空いているオブジェクトが連結されている。
2432 object = c->freelist; 2433 page = c->page;
freelstが空、もしくはpageがこの(NUMA)のnodeにない場合は__slab_alloc()を使い、そうでなければelseブロックに入っていく。
2434 if (unlikely(!object || !node_match(page, node))) 2435 object = __slab_alloc(s, gfpflags, node, addr, c); 2436 2437 else { 2438 void *next_object = get_freepointer_safe(s, object);
__slab_alloc()は面倒なので先にelseの場合から。get_freepointer_safe()でfreelistからobjectを取得して、次にfreelistが必要になった場合に備えておく(大雑把に)。
先のif/elseブロックを抜けたら返すメモリ領域を0クリアする必要があれば(__GFP_ZEROが立っている。kzmalloc()とか)0クリアする。 最後の処理はにslab_post_alloc_hook()が呼ばれるけどこれもdebug用にkmemcheckに関連する部分が主なので飛ばす。 そして最後に確保したobjectを返却する。
そして先ほど飛ばした__slab_alloc()を、と行きたいとこだけどこれも結構色々あるので今回は止めておこう。。。 こちらはslab allocatorからメモリを確保するというよりはkmallocのためにslab allocatorを作るという意味合いが強いのでまた別途調べよう。
- 作者: Daniel P. Bovet,Marco Cesati,高橋浩和,杉田由美子,清水正明,高杉昌督,平松雅巳,安井隆宏
- 出版社/メーカー: オライリー・ジャパン
- 発売日: 2007/02/26
- メディア: 大型本
- 購入: 9人 クリック: 269回
- この商品を含むブログ (71件) を見る