try_charge()めも2

前回の続き

前回はここの2238行目のpage_counter_try_charge()を読んだのでその下からです。

2236         if (!do_swap_account ||
2237             !page_counter_try_charge(&memcg->memsw, batch, &counter)) {
2238                 if (!page_counter_try_charge(&memcg->memory, batch, &counter))
2239                         goto done_restock;
2240                 if (do_swap_account)
2241                         page_counter_uncharge(&memcg->memsw, batch);
2242                 mem_over_limit = mem_cgroup_from_counter(counter, memory);
2243         } else {
2244                 mem_over_limit = mem_cgroup_from_counter(counter, memsw);
2245                 may_swap = false;
2246         }
2247 

2回目のpage_counter_try_charge()で0が返ればcharge可能ということなのでdone_restockラベルにジャンプしてchargeの処理に入ってました。 では、2回目の呼び出しで0以外が帰った場合は2240行目のところになります。ここのif文はdo_swap_accountが真かどうかなので2236行目のチェックとは逆になってます。通常はdo_swap_accountは0でないでしょうから、この場合はmemswの課金を取り消してます。 そして、mem_cgroup_from_counterマクロでmem_cgroup構造体を取得します。 mem_cgroup_from_counterマクロはみんな大好きcontainer_ofマクロのラッパーです。どの変数を使うかmemory/memswはif文の結果次第です。if文が偽だったときはmay_swapをfalseにしてますが、これはcharge対象がswapじゃないよーということでしょう。この変数はtrueで初期化されています。

やっとif文を抜けて次は下のif文になります。chargeしようとしていたページ数(batch)がnr_pagesより大きい場合はbatchの数をnr_pagesに設定して、retryラベルにジャンプしてcharge処理を再チャレンジします。retryラベルはconsume_stock()を実行する手前にあります。

2248         if (batch > nr_pages) {
2249                 batch = nr_pages;
2250                 goto retry;
2251         }

retryラベルにジャンプしてもchargeが出来なかった場合や、if文が偽だった場合はここより下の処理が実行されます。これより先の処理はchargeできかった場合の処理になります。charge可能なら2239行目のgoto文でdone_restockラベルにジャンプしています。この後も色々な試みを行ってchargeしようとします。

コメントにあるようにここまで着たからといってメモリが無いわけでは無いということです。たしかに、ページの取得に失敗しているわけではないですからね。で、チェックしていることとしては、プロセスが終了に向かってるならbypassラベルにジャンプします。

2253         /*
2254          * Unlike in global OOM situations, memcg is not in a physical
2255          * memory shortage.  Allow dying and OOM-killed tasks to
2256          * bypass the last charges so that they can exit quickly and
2257          * free their memory.
2258          */
2259         if (unlikely(test_thread_flag(TIF_MEMDIE) ||
2260                      fatal_signal_pending(current) ||
2261                      current->flags & PF_EXITING))
2262                 goto bypass;

bypassラベルは-EINTRを返して終了です。

2319 bypass:
2320         return -EINTR;

プロセスがOOM中の場合はnomemラベルにジャンプします。

2264         if (unlikely(task_in_memcg_oom(current)))
2265                 goto nomem;

gfp_maskに__GFP_NOFAILが設定されている場合は -ENOMEMは返らずに、その直後にあるbypassラベルのところで-EINTRを返します。

2316 nomem:
2317         if (!(gfp_mask & __GFP_NOFAIL))
2318                 return -ENOMEM;

OOM中でなければこちらの処理が行われます。これはmemcg->stat->events[MEMCG_MAX]++的な処理です。

2270         mem_cgroup_events(mem_over_limit, MEMCG_MAX, 1);

次にもう一回ページ回収を試みます。

2272         nr_reclaimed = try_to_free_mem_cgroup_pages(mem_over_limit, nr_pages,
2273                                                     gfp_mask, may_swap);

そして、nr_pages分のchargeができそうならretryラベルに戻ってchargeの再チャレンジです。

2275         if (mem_cgroup_margin(mem_over_limit) >= nr_pages)
2276                 goto retry;

drainedはローカル変数で、falseで初期化されているため1回目は必ずこの条件は真になります。ここまではプロセスが動いているcpuからchargeできるか試みていましたが、drain_all_stock()でオンラインなcpu全てチェックしてchargeできるか調べます。そしてdrainedをtrueに返るので2度目はありません。

2278         if (!drained) {
2279                 drain_all_stock(mem_over_limit);
2280                 drained = true;
2281                 goto retry;
2282         }
2283 

次に以下のチェックが有り、リトライしなくて良いと言われていたらnomemラベルに飛びます。

2284         if (gfp_mask & __GFP_NORETRY)
2285                 goto nomem;

先程のページ回収処理で回収できたページ数がありかつ、charge対象のページ数が8ページ以下なら再チャレンジします。PAGE_ALLOC_COSTLY_ORDERの値は3です。

2286         /*
2287          * Even though the limit is exceeded at this point, reclaim
2288          * may have been able to free some pages.  Retry the charge
2289          * before killing the task.
2290          *
2291          * Only for regular pages, though: huge pages are rather
2292          * unlikely to succeed so close to the limit, and we fall back
2293          * to regular pages anyway in case of failure.
2294          */
2295         if (nr_reclaimed && nr_pages <= (1 << PAGE_ALLOC_COSTLY_ORDER))
2296                 goto retry;

もし、移動中のcgroupがあればそれの移動完了を待ってchargeのリトライがあります。これはかなり最後の手段っぽいですね。

2297         /*
2298          * At task move, charge accounts can be doubly counted. So, it's
2299          * better to wait until the end of task_move if something is going on.
2300          */
2301         if (mem_cgroup_wait_acct_move(mem_over_limit))
2302                 goto retry;

まだ頑張ってみます。nr_retriesはMEM_CGROUP_RECLAIM_RETRIESで初期化されています。

2304         if (nr_retries--)
2305                 goto retry;
2306

これはmm/memcontrol.cで5と定義されているので、gfp_maskに__GFP_NORETRYが設定されていなければ5回リトライを試みるようになっています。

  78 #define MEM_CGROUP_RECLAIM_RETRIES      5

リトライ回数を使い切った場合、gfp_maskに__GFP_NOFAILが設定されていなければ、bypassラベルにジャンプします。

2307         if (gfp_mask & __GFP_NOFAIL)
2308                 goto bypass;
2309 

シグナル待ちならbypassラベルにジャンプ。

2310         if (fatal_signal_pending(current))
2311                 goto bypass;
2312

最後にindexがMEMCG_OOMのstatusを1増やして、mem_cgroup_oom()を実行。

2313         mem_cgroup_events(mem_over_limit, MEMCG_OOM, 1);
2314 
2315         mem_cgroup_oom(mem_over_limit, gfp_mask, get_order(nr_pages));

mem_cgroup_oom()はOOMを発生させるものではなくて、OOM用に設定をしているだけです。

1862 static void mem_cgroup_oom(struct mem_cgroup *memcg, gfp_t mask, int order)
1863 {
1864         if (!current->memcg_oom.may_oom)
1865                 return;
1866         /*
1867          * We are in the middle of the charge context here, so we
1868          * don't want to block when potentially sitting on a callstack
1869          * that holds all kinds of filesystem and mm locks.
1870          *
1871          * Also, the caller may handle a failed allocation gracefully
1872          * (like optional page cache readahead) and so an OOM killer
1873          * invocation might not even be necessary.
1874          *
1875          * That's why we don't do anything here except remember the
1876          * OOM context and then deal with it at the end of the page
1877          * fault when the stack is unwound, the locks are released,
1878          * and when we know whether the fault was overall successful.
1879          */
1880         css_get(&memcg->css);
1881         current->memcg_oom.memcg = memcg;
1882         current->memcg_oom.gfp_mask = mask;
1883         current->memcg_oom.order = order;
1884 }

これでtry_charge()の処理を一通り読み終わり( ´ー`)フゥー...

Professional Linux Kernel Architecture (Wrox Programmer to Programmer)

Professional Linux Kernel Architecture (Wrox Programmer to Programmer)