前回はここの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)
- 作者: Wolfgang Mauerer
- 出版社/メーカー: Wrox
- 発売日: 2008/10/13
- メディア: ペーパーバック
- 購入: 2人 クリック: 40回
- この商品を含むブログ (4件) を見る