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

buddy system: __alloc_pages_slowpath()のざっくりとした流れ。

linux kernel

LinuxのPage Allocatorで __alloc_pages_slowpath()の流れをメモ程度に。 読んでいるカーネルのversionは4.0。

__alloc_pages_nodemask()get_page_from_freelist()を呼んだ時にpageの確保ができなかった場合に呼ばれるのが __alloc_pages_slowpath()get_page_from_freelist()もpageを確保するために色々とやっているけど、それでも失敗した場合の処理なので、いろいろやることが多い。

ここからは実際にコードを見ていきます。 まず、最初のほうで。まず、gfp_maskをチェックして、このnode以外からのpage確保はしないなっていたらpge確保を諦めます。

2633         if (IS_ENABLED(CONFIG_NUMA) &&
2634             (gfp_mask & GFP_THISNODE) == GFP_THISNODE)
2635                 goto nopage;
2636 

次にretryラベルがあり、このラベルは色々と手を尽くしたけどダメだった場合に、リトライするならここにきます。次のgfp_maskのチェックでswapの処理を呼んでも良いとなっていたらkswapdにswapの処理をしてもらいます。

2637 retry:
2638         if (!(gfp_mask & __GFP_NO_KSWAPD))
2639                 wake_all_kswapds(order, ac);

そして、メモリ確保のフラグを再設定します。

2641         /*
2642          * OK, we're below the kswapd watermark and have kicked background
2643          * reclaim. Now things get more complex, so set up alloc_flags according
2644          * to how we want to proceed.
2645          */
2646         alloc_flags = gfp_to_alloc_flags(gfp_mask);

どのzoneからpageを確保するか決定します。

2648         /*
2649          * Find the true preferred zone if the allocation is unconstrained by
2650          * cpusets.
2651          */
2652         if (!(alloc_flags & ALLOC_CPUSET) && !ac->nodemask) {
2653                 struct zoneref *preferred_zoneref;
2654                 preferred_zoneref = first_zones_zonelist(ac->zonelist,
2655                                 ac->high_zoneidx, NULL, &ac->preferred_zone);
2656                 ac->classzone_idx = zonelist_zone_idx(preferred_zoneref);
2657         }

ここでもう一度get_page_from_freelist()を呼んでpageの確保にtryします。この時にalloc_flagsからALLOC_NO_WATERMARKSを外しているので、watermarkを無視しないようにします。。 pageの確保ができればgot_pgラベルに飛びます。

2659         /* This is the last chance, in general, before the goto nopage. */
2660         page = get_page_from_freelist(gfp_mask, order,
2661                                 alloc_flags & ~ALLOC_NO_WATERMARKS, ac);
2662         if (page)
2663                 goto got_pg;

上記のget_page_from_freelist()でpageの確保に失敗した場合で、alloc_flagsにALLOC_NO_WATERMARKSが立っていた場合は、緊急ということで__alloc_pages_high_priority()を呼んで優先度高としてpageの確保にいき、pageが確保できればgot_pgラベルに飛びます。

2665         /* Allocate without watermarks if the context allows */
2666         if (alloc_flags & ALLOC_NO_WATERMARKS) {
2667                 /*
2668                  * Ignore mempolicies if ALLOC_NO_WATERMARKS on the grounds
2669                  * the allocation is high priority and these type of
2670                  * allocations are system rather than user orientated
2671                  */
2672                 ac->zonelist = node_zonelist(numa_node_id(), gfp_mask);
2673 
2674                 page = __alloc_pages_high_priority(gfp_mask, order, ac);
2675 
2676                 if (page) {
2677                         goto got_pg;
2678                 }
2679         }
2680 

次のwaitはgfp_mask & GFP_WAITをした結果で、GFP_WAITが立っていない場合は待teないのでpageの確保を諦めます。

2681         /* Atomic allocations - we can't balance anything */
2682         if (!wait) {
2683                 /*
2684                  * All existing users of the deprecated __GFP_NOFAIL are
2685                  * blockable, so warn of any new users that actually allow this
2686                  * type of allocation to fail.
2687                  */
2688                 WARN_ON_ONCE(gfp_mask & __GFP_NOFAIL);
2689                 goto nopage;
2690         }

次の2つのチェックもフラグを見て諦めるか続けるかを決定します。

692         /* Avoid recursion of direct reclaim */
2693         if (current->flags & PF_MEMALLOC)
2694                 goto nopage;
2695 
2696         /* Avoid allocations with no watermarks from looping endlessly */
2697         if (test_thread_flag(TIF_MEMDIE) && !(gfp_mask & __GFP_NOFAIL))
2698                 goto nopage;
2699 

ここまでやってもpageの確保ができていないので、次の手段としてpageのコンパクションを行います。

2700         /*
2701          * Try direct compaction. The first pass is asynchronous. Subsequent
2702          * attempts after direct reclaim are synchronous
2703          */
2704         page = __alloc_pages_direct_compact(gfp_mask, order, alloc_flags, ac,
2705                                         migration_mode,
2706                                         &contended_compaction,
2707                                         &deferred_compaction);
2708         if (page)
2709                 goto got_pg;

コンパクションしてもpageが確保できなかったら、次のif文の条件に該当をチェックして、該当する場合は各種フラグのチェックで諦めるかどうか決めます。

2712         if ((gfp_mask & GFP_TRANSHUGE) == GFP_TRANSHUGE) {

〜略〜

2740         }

コンパクションでもダメだったので、次はページの回収を行います。

2751         /* Try direct reclaim and then allocating */
2752         page = __alloc_pages_direct_reclaim(gfp_mask, order, alloc_flags, ac,
2753                                                         &did_some_progress);
2754         if (page)
2755                 goto got_pg;

pageの回収をしてもまだpageの確保にした場合、リトライするかどうか判断します。 リトライする場合、誰かを殺してでもpage確保するということになり、 __alloc_pages_may_oom()が呼ばれます。 これでpageが確保できればgot_pgに飛びます。oomを走らせられない状況だった場合はpage確保を諦めます。pageは確保できなかったけども、oomの処理が動いたならretryラベルに飛んで、また最初から実行し直します。

2757         /* Check if we should retry the allocation */
2758         pages_reclaimed += did_some_progress;
2759         if (should_alloc_retry(gfp_mask, order, did_some_progress,
2760                                                 pages_reclaimed)) {
2761                 /*
2762                  * If we fail to make progress by freeing individual
2763                  * pages, but the allocation wants us to keep going,
2764                  * start OOM killing tasks.
2765                  */
2766                 if (!did_some_progress) {
2767                         page = __alloc_pages_may_oom(gfp_mask, order, ac,
2768                                                         &did_some_progress);
2769                         if (page)
2770                                 goto got_pg;
2771                         if (!did_some_progress)
2772                                 goto nopage;
2773                 }
2774                 /* Wait for some write requests to complete then retry */
2775                 wait_iff_congested(ac->preferred_zone, BLK_RW_ASYNC, HZ/50);
2776                 goto retry;
2777         } else {

リトライしない場合は、もう一度コンパクションを実行します。これでpageが確保できなかったらwarningを出した後にNULLをreturnして終了です。

2777         } else {
2778                 /*
2779                  * High-order allocations do not necessarily loop after
2780                  * direct reclaim and reclaim/compaction depends on compaction
2781                  * being called after reclaim so call directly if necessary
2782                  */
2783                 page = __alloc_pages_direct_compact(gfp_mask, order,
2784                                         alloc_flags, ac, migration_mode,
2785                                         &contended_compaction,
2786                                         &deferred_compaction);
2787                 if (page)
2788                         goto got_pg;
2789         }

と、ざっくりと __alloc_pages_slowpath()のコード・リーディングめもでした。

CANSAY nu board (ヌーボード) A4判 NGA403FN08

CANSAY nu board (ヌーボード) A4判 NGA403FN08