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

kmalloc()とBUG: sleeping function called from invalid context〜とslab_alloc_node()

kernel linux

カーネルのバグで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を作るという意味合いが強いのでまた別途調べよう。

詳解 Linuxカーネル 第3版

詳解 Linuxカーネル 第3版