chargeの処理としてはこの辺が重要だろうと思われる(要出典)try_charge()の処理を読みましょう。
早速処理を見ていきますが、まず対象のmemcgがルートのmemcgだった場合は何もしません。このチェックはmem_cgroup_is_root()で行えます。次にconsume_stock()を呼びます。引数としてはtry_charge()に渡されたmemcgを渡します。この関数は何をするかというと、このcpuに課金済みの使用可能なpageがあるか調べて、そういうpageがあればtrueを返します。try_charge()ではconsume_stock()がtrueを返したら処理を終了します。
次にmem_over_limit変数に値をセットするところがあります。swapを対象にしないとか、page_counter_try_charge()が0を返した場合は2238~2242行目の処理になります。do_swap_accountは明示的に無効にしない限りは大抵1になってると思うので、見るべきは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 }
batchという変数は宣言時に以下のように初期化されています。
unsigned int batch = max(CHARGE_BATCH, nr_pages);
CHARGE_BATCHはmm/memcontrol.cで定義されていて値は32です。
page_counter_try_charge()は自分がいるmemcgの階層からルート階層までの各階層でcharge可能かチェックします。全階層でcharge可能なら0を返し、 一箇所でもchargeできなければ-ENOMEMを返します。ということで、先のif文ではcharge可能な場合にifのブロックが実行されます。 次にもう一回page_counter_try_charge()を呼びますが、このときの1番目の引数は先ほどと違います。最初はmemcg->memswで今度はmemcg->memoryです。どちらもstruct page_counter型の変数です。この型はlinux/page_counter.hで定義されています。これらの使い分けはこれを書いてる現段階ではよく分かってないけど、名前からしてmemswはswap、memoryはrssに使うんじゃないか?という気がします。chargeするものの種別に毎に分かれてそうな気がします。TODO:これら3変数の使われ方から変数の意味を調べよう。
266 /* Accounted resources */ 267 struct page_counter memory; 268 struct page_counter memsw; 269 struct page_counter kmem; 270
ここでcharge可能だった場合はdone_restockラベルの場所にジャンプします。このラベルにジャンプする処理はここだけです。なのでどんな処理なのか見てしまいます。
2323 css_get_many(&memcg->css, batch); 2324 if (batch > nr_pages) 2325 refill_stock(memcg, batch - nr_pages); 2326 if (!(gfp_mask & __GFP_WAIT)) 2327 goto done; 2328 /* 2329 * If the hierarchy is above the normal consumption range, 2330 * make the charging task trim their excess contribution. 2331 */ 2332 do { 2333 if (page_counter_read(&memcg->memory) <= memcg->high) 2334 continue; 2335 mem_cgroup_events(memcg, MEMCG_HIGH, 1); 2336 try_to_free_mem_cgroup_pages(memcg, nr_pages, gfp_mask, true); 2337 } while ((memcg = parent_mem_cgroup(memcg)));
最初にcss_get_many()を呼んでますが、これは簡単に説明するとcssの参照カウンタを増やすだけです。つぎにbatchの値がnr_pagesより大きい場合はrefill_stock()を呼んでます。refill_stock()はさっき使用したconsume_stock()と関連します。refill_stock()でキャッシュして、consume_stock()で消費するような感じです。gfp_maskに__GFP_WAITが設定されていなければここで処理終了となります。そうでない場合は、do-whileの処理になります。
page_counter_read()はpage_counter構造体のcount変数をatomicに読み出してその値を返します。この値がmemcg構造体のhigh変数に設定されている値以下の場合はcontinueします。TODO: この辺の処理の意図は別途調べます。
次のmem_cgroup_events()は以下のような関数で、端的に書くとやってることとしては memcg->stat->events[MEMCG_HIGH] += nr;
ですね。this_cpu_add()なのでこのcpuにバインドされてる変数に対してというのはありますが。
5400 void mem_cgroup_events(struct mem_cgroup *memcg, 5401 enum mem_cgroup_events_index idx, 5402 unsigned int nr) 5403 { 5404 this_cpu_add(memcg->stat->events[idx], nr); 5405 }
次は try_to_free_mem_cgroup_pages(memcg, nr_pages, gfp_mask, true);
です。この関数はmm/vmscan.cにあります。この関数の子毎回処理内容は別途調べるとして、処理内容はページ回収です。nr_pages分のページ回収を行います。先のif文でmemcg->highとのチェックがあったので、上限超えてるからページ回収しないといけなということでしょうね。
最後はwhileの条件のところで、これはmemcg = parent_mem_cgroup(memcg)
となっています。
この関数は以下のような処理で、mem_cgroup構造体のpage_counter型の変数memoryから上位階層のmem_cgroup構造体を取得して返します。
4418 struct mem_cgroup *parent_mem_cgroup(struct mem_cgroup *memcg) 4419 { 4420 if (!memcg->memory.parent) 4421 return NULL; 4422 return mem_cgroup_from_counter(memcg->memory.parent, memory); 4423 }
これで自身の階層から上位に向かって処理していってるんですね。
try_charge()は一回で終わらすの大変そうなので、まずは切りの良いところでここまで ( ´ー`)フゥー...
まんがでわかるLinux シス管系女子 2(日経BP Next ICT選書)
- 作者: Piro(結城洋志)
- 出版社/メーカー: 日経BP社
- 発売日: 2015/12/22
- メディア: Kindle版
- この商品を含むブログを見る