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

get_page_from_freelist()の処理

linux kernel

前回__alloc_pages_nodemask()を見たので、今回はそこから呼ばれ、実際にallocationを行う部分の get_page_from_freelist()を見ていきます。

基本的な流れは、

  • boolの変数consider_zone_dirtyにalloc_flagsとgfpフラグの値から真偽値を決める  ここでgfpフラグに__GFP_WRITEがセットされているかというチェックがあり、これは「mm: try to distribute dirty pages fairly across zones」というpatchで追加されたもので、pageがallocation後、すぐにdirtyになることが分かっている場合にせっとするようです。

  • ローカル変数の定義が終わると本編で、zonelist_scanというgotoラベルが設定され、ループの先頭がここになる

  • for_each_zone_zonelist_nodemask()でzoneのリストからzoneをひとつずつ見て、pageの確保を行うzoneを探す

  • このzoneからpageの確保ができるかをzlc_zone_worth_trying()で軽くチェックし、無理なら次のzoneを調べるためにループ先頭に戻る

  • cpuset_zone_allowed()でzoneがあるNUMA nodeからpage確保がcpusetによって禁止されていたらcontinue
     __alloc_pages_nodemask()で最初のリクエストの時に__GFP_HARDWALLを付けてましたが、それはここで使われます。

  • alloc_flagsにALLOC_FAIRが立っている場合の処理  preferred_zoneと調べているzoneが同じならここからpage確保するのでループ終了。
     もし、zoneに設定されているフラグにZONE_FAIR_DEPLETEDが立っていたらこのzoneのチェックは終了して、次のzoneを調べる。  ALLOC_FAIRは「mm: page_alloc: spill to remote nodes before waking kswapd」のpatchで追加。 ZONE_FAIR_DEPLETEDは「mm: page_alloc: reduce cost of the fair zone allocation policy」で追加。  ALLOC_FAIR・ZONE_FAIR_DEPLETEDどちらもfair zone allocation policyに関連している。  これのベーシックなところは「[patch 3/3] mm: page_alloc: fair zone allocator policy」に。

  • 見ているzoneがdirtyになることを許可しているかチェックして、ダメならこのzoneは諦める  チエックとしてはconsider_zone_dirtyが1の場合にzone_dirty_ok()を呼ぶ。

  • 要求されたpage数(order)がzoneのwatermark(最低限残しておく空きページ数)が下回らないかzone_watermark_ok()でチェックして、falseが返ってきた場合は以下の処理に入る

  • alloc_flagsにALLOC_NO_WATERMARKS がセットされていたら(watermarkを無視してpage確保するフラグ)このzoneからpageを確保するということで決定する

  • zone listのキャッシュが作成されていなければ作成する
     このキャッシュはstruct zonelist_cacheで管理されていて、get_page_from_freelist()で空きpageを素早く見つけるのに使われる。最初の方に会ったzlc_zone_worth_trying()ではこのキャッシュを参照している。

  • zoneがpageの回収が可能かチェックして、できないようならthis_zone_fullラベルにジャンプ

  • 再度、最初と同じようにzlc_zone_worth_trying()でこのzoneからpage確保できるかチェックして、ダメなら次のzoneを調べる  ここで再度チェックするのはちょっと前の処理でzonelist cacheを作っている場合があるため。

  • zone_reclaim()でpageの回収処理をする  結果がZONE_RECLAIM_NOSCAN、ZONE_RECLAIM_FULLの場合はこのzoneでのpage確保が無理なので次のzoneを調べる  このzone_reclaim()はまた別の機会に詳しく見ましょう

  • pageのreclaimによってpageの確保ができるようになったならこのzoneからpage確保すると決定する

  • alloc_flagsにALLOC_WMARK_MINが立っていて、reclaim処理の戻り値がZONE_RECLAIM_SOMEの場合は、十分に空きpage数が確保できていないのでthis_zone_fullラベルにジャンプ

  • 上記のチェックに引っかからない場合は次のzoneを見に行く

  • ここまでが 最初のzone_watermark_ok()でfalseが返った場合の処理で、ここからはzoneが確定した場合でtry_this_zoneラベルの場所

  • buffered_rmqueue()でzoneからpageを確保する  なので、この buffered_rmqueue()がpage確保処理の本体っぽい。

  • pageが確保できた場合はprep_new_page()でpageを実際に使う前の前処理を行い、pageをreturnして関数を終了する  prep_new_page()の処理は__GFP_ZEROが設定されていたら0クリアするとか、pageのリファレンスカウンタを初期化するとかの処理。

  • もしbuffered_rmqueue()でpageの取得ができなかった場合は、this_zone_fullラベルの処理に進む

  • this_zone_fullラベルからの処理はzonelist cacheに対して、空きpageが無いと設定する  for_each_zone_zonelist_nodemask()によるforループの範囲はここまで({}の範囲)。

  • ここからはfor_each_zone_zonelist_nodemask()のループ中にpageを確保できなかった場合

  • alloc_flagsにALLOC_FAIRが立っていた場合、このbitを落とす

  • ALLOC_FAIRが立っていたためチェックをスキップしたzoneを再度チェックできるようにするreset_alloc_batches()で行う

  • onlineなNUMAノードが1以上あれば再度チェックするようにする

  • zonelist cacheを作成していた場合も再度チェックできるようにする

  • 上記3個の処理で再チェックするとした場合は、関数冒頭にあるzonelist_scanラベルに飛んで、for_each_zone_zonelist_nodemask()のループ処理を再度行う

  • 再チェックしない場合はNULLを返す

次回見ていくのはbuffered_rmqueue()ですね。