CONFIG_VIRT_CPU_ACCOUNTING
会社で近くに座っているすごい優秀な技術者がCONFIG_VIRT_CPU_ACCOUNTINGをいじっていたので、うらまやしくなってi386版でも作ろうかとほげり始める。
が、いろいろと 2.6.24-rc6 だと構造が変わっていて元々のパッチは素直に移植できないことが分かったので、いまのCPU時間統計の仕組みをちょいと調べてみた
以下はほぼ自分用の備忘録
1.VIRT_CPU_ACCOUNTINGとはなにか?
名前がよくないのだが、最近流行の仮想化とはまったく関係ない。
よーするにcpu時間情報がtick単位でしか集計できないのは今となっては荒すぎるので
tscレジスタ相当の機能を使ってもっと細かくとれるようにしましょう。と
そういう機能
2.どうやってつかうの?
netlink機能のなかのtaskstatの情報を吸い出すことで見ることができる。
サンプルが linux/Documentation/getdelays.c あたりにある
3.サポートされてるプラットフォームは?
ppcとs390(だけ)
よく、MIな部分にコードつっこめれたよね
4.ポーティング方法は?
POWERがMD層でやっている事を以下に列挙するのでそれを真似してちょ
# なお、POWERには時間を取得するレジスタが3種類あって、それぞれ意味が違う。
# それは記事末尾で補足する。
A. 移植したいアーキのKconfig にCONFIG_VIRT_CPU_ACCOUNTINGを追加
B. include/asm/cputime.h を書き換えてcputime_tの粒度をtickからPURR
の粒度に定義しなおす。
C.システムコールの入り口で以下の処理をおこなう
- PURRをとる
- get_paca()->startpurr とPURR現在値の差分をget_paca()->user_timeに加算する
- get_paca()->startpurr にPURR現在値を保存
D.システムコール出口で以下の処理を行う
- PURRをとる
- get_paca()->startpurr とPURR現在値の差分をget_paca()->system_timeに加算する
- get_paca()->startpurr にPURR現在値を保存
E.__switch_toに以下の処理を追加
account_system_vtime(current);
account_process_tick(current, 0);
calculate_steal_time();
要するに、プロセスコンテキストの切り替えの直前に今までper_cpu変数にためこんでおいた
CPU時刻を吐き出しているのである
F. account_system_vtime という関数を作る。これはirq, softirqの出入り口と
__switch_toから呼ばれる関数。
以下のように定義する
(ここのPOWERの実装は気が狂ってるとしか思えないのでS390も併記する)
- PURRとget_paca()->startpurrの差分をとる(delta)
- get_puaca()->startpurrを現在時刻で初期化
- SPURRとget_paca()->startspurrの差分をとる(deltascaled)
- get_puaca()->startspurrを現在時刻で初期化
- 割り込み中じゃなければ以下の補正
o deltascaled *= system_time / (system_time + user_time)
これは全体の時間中のsystem_time時間の%を意味する。
って、システムコールの出入り口でstartspurrを更新してないのはおかしいんとちゃうか・・
o delta += system_time
o get_paca()->system_timeを0初期化
- account_system_timeをdeltaを引数にして呼び出す
ここで、hardirq_offset引数を常に0にしているのはたぶんバグ
preempt_count()で簡単にとれるはずやろ・・・
- account_system_time_scaledをdeltascaledを引数にして呼び出す
- get_paca()->purrdelta = delta
- get_paca()->spurrdelta = deltascaled
以下、S390の場合
- CPUタイマの前回時刻との差分をsystem_timeにセット
- CPUタイマの前回時刻を現在時刻にセット
- account_system_timeを呼び出す
G. update_process_times()から呼ばれるCPU時間更新処理account_process_tick()が
MIな定義部分が #ifndef CONFIG_VIRT_CPU_ACCOUNTING
で囲まれているので、自分のアーキのMD部分に自分用の定義を作る。
- 引数user_tickは無視(CPUのレジスタでもっと細かい時間がとれるから)
- get_paca()->user_time を引数に account_user_time()を呼び出す
- get_paca()->user_time * spurrdelta / purrdelta を引数に account_user_time_scaled()を呼び出す
- get_paca()->user_time, get_paca()->spurrdelta, get_paca()->purrdeltaを0で初期化
なお、purrdelataが0でないことは以下で保障している(0割の考慮がないコードに見えたので調べた)
- hard irq の出入り口でaccount_system_vtimeが呼ばれているので、ここを通るときは
絶対purrdelta, spurrdeltaに値が入っている
- 後述する __switch_toから呼ぶときもaccount_process_tickの直前にaccount_system_vtimeを呼んでいる
<補足 POWERの時間関係のレジスタの違いについて>
TB: 現実世界の時間と同じように進む。チップセットのタイマーでいいじゃんという気がしなくもない
PURR: SMT時に自分がactive stateだったcycleのみカウンタがすすむ周波数カウンタ
haltしてもう一方のスレッドに処理をゆずっているときはカウンタは増えない
SPURR: PURR * (feffective/fnominal) * (1 - cyclesthrottled/cyclestotal)
などというよく分からない数式で定義されているカウンタ。ようするに省電力モードで
周波数が落ちたときはそれに応じて進み方が遅くなる。と
ちなみにaccount_user_time_scaled()のscaledはSPURRがScaled PURRの略だからだ。ばかすぎる
なので、PURRだと省エネモードのときに実際の動いたクロックよりもすんげー速く進んでしまって
悲しい思いをすることになる。
困ったことにx86はtscの定義があいまいなので、省エネモードのときの動作はimplementation defined
っぽい気がする
<補足2 結局scaledtimeとstealedtimeってなんなん?>
scaledtime:省電力モードで周波数が半分になっているときは半分だけカウントアップしてくれる
steal: SMTの相方のせいで動けなかった時間
(これが計測できるってことはPOWER5の公称SMTは実装としてはVMTってことだよね:)
急いでいたので、乱文、失礼。
急いでいたので! ランキング!
会社で近くに座っているすごい優秀な技術者がCONFIG_VIRT_CPU_ACCOUNTINGをいじっていたので、うらまやしくなってi386版でも作ろうかとほげり始める。
が、いろいろと 2.6.24-rc6 だと構造が変わっていて元々のパッチは素直に移植できないことが分かったので、いまのCPU時間統計の仕組みをちょいと調べてみた
以下はほぼ自分用の備忘録
1.VIRT_CPU_ACCOUNTINGとはなにか?
名前がよくないのだが、最近流行の仮想化とはまったく関係ない。
よーするにcpu時間情報がtick単位でしか集計できないのは今となっては荒すぎるので
tscレジスタ相当の機能を使ってもっと細かくとれるようにしましょう。と
そういう機能
2.どうやってつかうの?
netlink機能のなかのtaskstatの情報を吸い出すことで見ることができる。
サンプルが linux/Documentation/getdelays.c あたりにある
3.サポートされてるプラットフォームは?
ppcとs390(だけ)
よく、MIな部分にコードつっこめれたよね
4.ポーティング方法は?
POWERがMD層でやっている事を以下に列挙するのでそれを真似してちょ
# なお、POWERには時間を取得するレジスタが3種類あって、それぞれ意味が違う。
# それは記事末尾で補足する。
A. 移植したいアーキのKconfig にCONFIG_VIRT_CPU_ACCOUNTINGを追加
B. include/asm/cputime.h を書き換えてcputime_tの粒度をtickからPURR
の粒度に定義しなおす。
C.システムコールの入り口で以下の処理をおこなう
- PURRをとる
- get_paca()->startpurr とPURR現在値の差分をget_paca()->user_timeに加算する
- get_paca()->startpurr にPURR現在値を保存
D.システムコール出口で以下の処理を行う
- PURRをとる
- get_paca()->startpurr とPURR現在値の差分をget_paca()->system_timeに加算する
- get_paca()->startpurr にPURR現在値を保存
E.__switch_toに以下の処理を追加
account_system_vtime(current);
account_process_tick(current, 0);
calculate_steal_time();
要するに、プロセスコンテキストの切り替えの直前に今までper_cpu変数にためこんでおいた
CPU時刻を吐き出しているのである
F. account_system_vtime という関数を作る。これはirq, softirqの出入り口と
__switch_toから呼ばれる関数。
以下のように定義する
(ここのPOWERの実装は気が狂ってるとしか思えないのでS390も併記する)
- PURRとget_paca()->startpurrの差分をとる(delta)
- get_puaca()->startpurrを現在時刻で初期化
- SPURRとget_paca()->startspurrの差分をとる(deltascaled)
- get_puaca()->startspurrを現在時刻で初期化
- 割り込み中じゃなければ以下の補正
o deltascaled *= system_time / (system_time + user_time)
これは全体の時間中のsystem_time時間の%を意味する。
って、システムコールの出入り口でstartspurrを更新してないのはおかしいんとちゃうか・・
o delta += system_time
o get_paca()->system_timeを0初期化
- account_system_timeをdeltaを引数にして呼び出す
ここで、hardirq_offset引数を常に0にしているのはたぶんバグ
preempt_count()で簡単にとれるはずやろ・・・
- account_system_time_scaledをdeltascaledを引数にして呼び出す
- get_paca()->purrdelta = delta
- get_paca()->spurrdelta = deltascaled
以下、S390の場合
- CPUタイマの前回時刻との差分をsystem_timeにセット
- CPUタイマの前回時刻を現在時刻にセット
- account_system_timeを呼び出す
G. update_process_times()から呼ばれるCPU時間更新処理account_process_tick()が
MIな定義部分が #ifndef CONFIG_VIRT_CPU_ACCOUNTING
で囲まれているので、自分のアーキのMD部分に自分用の定義を作る。
- 引数user_tickは無視(CPUのレジスタでもっと細かい時間がとれるから)
- get_paca()->user_time を引数に account_user_time()を呼び出す
- get_paca()->user_time * spurrdelta / purrdelta を引数に account_user_time_scaled()を呼び出す
- get_paca()->user_time, get_paca()->spurrdelta, get_paca()->purrdeltaを0で初期化
なお、purrdelataが0でないことは以下で保障している(0割の考慮がないコードに見えたので調べた)
- hard irq の出入り口でaccount_system_vtimeが呼ばれているので、ここを通るときは
絶対purrdelta, spurrdeltaに値が入っている
- 後述する __switch_toから呼ぶときもaccount_process_tickの直前にaccount_system_vtimeを呼んでいる
<補足 POWERの時間関係のレジスタの違いについて>
TB: 現実世界の時間と同じように進む。チップセットのタイマーでいいじゃんという気がしなくもない
PURR: SMT時に自分がactive stateだったcycleのみカウンタがすすむ周波数カウンタ
haltしてもう一方のスレッドに処理をゆずっているときはカウンタは増えない
SPURR: PURR * (feffective/fnominal) * (1 - cyclesthrottled/cyclestotal)
などというよく分からない数式で定義されているカウンタ。ようするに省電力モードで
周波数が落ちたときはそれに応じて進み方が遅くなる。と
ちなみにaccount_user_time_scaled()のscaledはSPURRがScaled PURRの略だからだ。ばかすぎる
なので、PURRだと省エネモードのときに実際の動いたクロックよりもすんげー速く進んでしまって
悲しい思いをすることになる。
困ったことにx86はtscの定義があいまいなので、省エネモードのときの動作はimplementation defined
っぽい気がする
<補足2 結局scaledtimeとstealedtimeってなんなん?>
scaledtime:省電力モードで周波数が半分になっているときは半分だけカウントアップしてくれる
steal: SMTの相方のせいで動けなかった時間
(これが計測できるってことはPOWER5の公称SMTは実装としてはVMTってことだよね:)
急いでいたので、乱文、失礼。
急いでいたので! ランキング!
- 関連記事
-
- seto_benchが再現できない件について (2008/01/01)
- CONFIG_VIRT_CPU_ACCOUNTINGまとめ (2008/01/01)
- mem notification v4 候補 (2007/12/31)