今日はLinuxカーネルもくもく会 #12 を利用してbuffered_rmqueue()から読んでく。
buffered_rmqueue()
最初の大きな分岐はページ確保のorderが0(1page)かどうかで処理が変わる
まずは1page分の場合から見ていく if文には1pageにlikelyが使われいるのでこっちがやはり多いんすね。
最初にper_cpu_pages構造体のデータを取得する これは名前の通り、cpu毎に持つ構造体。データ構造は下記のようになっていて、リストにつながっているページ数等のデータを保持する。listは配列になっていてMIGRATE_PCPTYPES個。
252 struct per_cpu_pages { 253 int count; /* number of pages in the list */ 254 int high; /* high watermark, emptying needed */ 255 int batch; /* chunk size for buddy add/remove */ 256 257 /* Lists of pages, one per migrate type stored on the pcp-lists */ 258 struct list_head lists[MIGRATE_PCPTYPES]; 259 };
MIGRATE_PCPTYPESはinclude/linux/mmzone.hに定義されている。
38 enum { 39 MIGRATE_UNMOVABLE, 40 MIGRATE_RECLAIMABLE, 41 MIGRATE_MOVABLE, 42 MIGRATE_PCPTYPES, /* the number of types on the pcp lists */ 43 MIGRATE_RESERVE = MIGRATE_PCPTYPES,
if文内で最初にするのはlocal_irq_save()でロック取得
this_cpu_ptr()でper_cpu_pages構造体の取得 取得元はzone構造体のメンバ変数にあるper_cpu_pageset構造体のpcp変数。
per_cpu_pageset構造体はこのような構造。
261 struct per_cpu_pageset { 262 struct per_cpu_pages pcp; 263 #ifdef CONFIG_NUMA 264 s8 expire; 265 #endif 266 #ifdef CONFIG_SMP 267 s8 stat_threshold; 268 s8 vm_stat_diff[NR_VM_ZONE_STAT_ITEMS]; 269 #endif 270 };
pcpを取得したらページのリスト(lists)をインデックスをmigratetypeとしてリストを取得
上記で取得したリストが空かチェックし、空ならrmqueue_bulk()で空きページを見つけてリストに補填する これでも空pageが見つからなければpageをの確保失敗になる。rmqueue_bulk()は後ほど確認しましょう
pageが見つかったら、gfp_flagsに__GFP_COLDが立っているかどうかでリストのtail/headどちら側に繋ぐかを変える __GFP_COLDが立っていない場合はhead側に、立っている場合はtail側に繋ぐ。
最後にpageをlruリストから外す(page->lruのリスト)
ここまでが1pageの場合、ここから複数pageの場合を見ていく
spin_lock_irqsave()でロックを取って__rmqueue()でpageを確保 __rmqueue()も後ほど
__mod_zone_freepage_stateを呼んで空きページのstateを変更する
ここまでが1page以上の場合
ここからは共通な処理になるんだけど、そんなに面白そうなことはなさそうなので、後回しにしたものを見ていきますか。
rmqueue_bulk()
- インターフェースはこう zoneはpageを確保する対象のzone、orderはそのままorderで、buffered_rmqueue()ではorder 0の時に呼ぶので0を渡している。 次の変数はcountとなっているけど、buffered_rmqueue()ではpcp->batchを渡しているので、チャンクのサイズを渡していることになる。リストはbuffered_rmqueue()で見ていたリストで、見つかった空きpageをここに繋げる。migratetypeは__alloc_pages_nodemask()で設定した値、coldは__GFP_COLDが立っていたら1。
1274 static int rmqueue_bulk(struct zone *zone, unsigned int order, 1275 unsigned long count, struct list_head *list, 1276 int migratetype, bool cold)
処理はcount分のループで行われる
最初に__rmqueue()で確保する
pageが確保できなければbreakして終了
次は先ほどと同じくcoldの値によって、リストのhead/tailどちらにpageを繋ぐかを変えてpage->lruに繋ぐ
引数で渡されたlistをlist = page->lruする ここでlistを設定することでbuffered_rmqueue()に戻ってからのリスト操作にはこのpage->lruが使われることになる
取得したpageのmigratetypeがContiguous Memory Allocatorなら__mod_zone_page_state()を呼ぶ
ここまでの処理をcount分繰り返す
ループを抜けたら __mod_zone_page_state()を呼ぶ
ロックを解放してループした回数をreturnして終了
__rmqueue()
最初に__rmqueue_smallest()を呼んでpageを確保
pageの確保に失敗&&migratetypeがMIGRATE_RESERVE(これはMIGRATE_PCPTYPESと一緒 )なら__rmqueue_fallback()を呼ぶ
ここまでやってもpageが確保できなければmigratetypeをMIGRATE_RESERVEに変えてリトライ それでもダメなら完全にpage確保失敗
見つかったpageもしくはNULLを返して終了
今日のまとめ
page確保の処理は__rmqueue_smallest()と__rmqueue_fallback()が重要っぽい。
というわけで次回はその辺をm( )m
- 作者: 當仲寛哲,菅雄一,白羽玲子,上田隆一,濱口誠一,大内智明,法林浩之,波田野裕一,松浦智之,水間丈博,後藤大地,大岩元,すずきひろのぶ,熊野憲辰,ちょまど,桑原滝弥,USP研究所,ジーズバンク
- 出版社/メーカー: USP研究所
- 発売日: 2015/05/25
- メディア: 雑誌
- この商品を含むブログを見る