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

buffered_rmqueue()めも

linux kernel

今日は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,

 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の場合を見ていく

  • 最初に、orderが1以上なのにgfp_flagsに__GFP_NOFAILが立っている場合はWarningを出す

  • 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

シェルスクリプトマガジン vol.26

シェルスクリプトマガジン vol.26

  • 作者: 當仲寛哲,菅雄一,白羽玲子,上田隆一,濱口誠一,大内智明,法林浩之,波田野裕一,松浦智之,水間丈博,後藤大地,大岩元,すずきひろのぶ,熊野憲辰,ちょまど,桑原滝弥,USP研究所,ジーズバンク
  • 出版社/メーカー: USP研究所
  • 発売日: 2015/05/25
  • メディア: 雑誌
  • この商品を含むブログを見る