Date Tue 31 December 2013 Tags Linux / Tool

以前情報公開した新 208.5 日問題向けに、現在の TSC 値出力や numeric overflow 警告を行うツールを作成しました。

「今動いているサーバは大丈夫なのか?」という疑問を解決するためのツールです。GCC Inline Assembler を含め Linux Kernel 内の新 208.5 日問題ロジックとほぼ同等の処理を行います。記事にも書きましたが、本問題は旧 208.5 日問題とは異なり、 uptime 結果を確認することによって問題の有無を判断することが出来ません。再起動を行っても TSC 値がリセットされないことが問題の発生要因であるためです。ご注意ください。*1

https://github.com/hisayosh/tsc_checker

目的


  • 従来のオペレーション(再起動)による TSC 値の蓄積状況を確認する
  • 仮の CPU クロック周波数を基に現在の numeric overflow の発生可能性を定量的に確認する

出力内容


  • 現在の CPU 内の TSC 値
  • 引数で取得した CPU クロック周波数を元に算出した SC 値
  • 不具合を伴うロジックにより発生する pico sec order の 64 bit 変数値
  • 64 bit 変数の上限値

利用方法


tsc_checker の実行に必要な要素は、tsc_checker 実行環境の CPU クロック周波数のみです。以下の 2 ステップで実行します。

1. tsc_checker 実行サーバの CPU クロック周波数の取得

Intel Xeon E5 プロセッサーファミリーであれば、tsc_constant により TSC は CPU クロック周波数の動的変動の影響を受けません。コマンドラインから以下のコマンドを実行して該当 CPU の最大クロック周波数を取得します。

$ sudo cat /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_max_freq

2. tsc_checker の実行

tsc_checkerを実行します。「-f」オプションの引数として「1.」で取得したクロック周波数を指定します。

$ ./tsc_checker -f 2794000
  • 計算結果 : 通常時*2
TSC value        : 1476354219900
SC value         : 366
current value    : 540345644483400
threashold value : 18446744073709551615
  • 計算結果 : numeric overflow 発生時
TSC value        : 295504282125
SC value         : 366
***warning***
there is the possibility of numeric overflow at the next time of startup if you have not taken measure.

tsc_checker の仕組み


仕組みは単純です。まず、 TSC 値を Inline Assembler により取得します。具体的には、CPU に対し rdtsc ( read time stamp counter ) 命令を実行し、結果の上位 32 bit を EDX register、下位 32 bit を EAX register 経由で、それぞれ uint32_t 型の変数に格納します。そして「32 bit 左に shift した上位 bit 変数」と「下位 bit 変数」を OR 演算することで、uint64_t 型の変数にTSC 値を格納します。

uint64_t get_tsc(){

    uint64_t val;
    uint32_t low, high;

    asm volatile ("rdtsc" : "=a" (low), "=d" (high));
    val = (uint64_t)low | ((uint64_t)high << 32);

    return val;
}

次に、SC ( SCale ) の算出を行います。こちらも Linux Kernel と同一のロジックになっています。1000000 *3 に対して 10 bit *4 分の左 shift を行った数値をキロヘルツ単位のクロック周波数で除算することで SC を算出します。最後行の tsc * scale が numeric overflow 発生原因となる演算です。

    tsc = get_tsc();
    scale = (1000000 << CYC2NS_SCALE_FACTOR) / cpu_khz;
    current = tsc * scale;

参考までに、Linux Kernel では以下のマクロ演算で tsc * scale の計算を行うことにより numeric overflow の発生を抑止しています。tsc_checker の header file にマクロを追加していますので興味のある方はどうぞ。

/* from linux kernel (/include/linux/kernel.h b/include/linux/kernel.h)
 * Multiplies an integer by a fraction, while avoiding unnecessary
 * overflow or loss of precision.
 */
#define mult_frac(x, numer, denom)(                        \
{                                                          \
    typeof(x) quot = (x) / (denom);                        \
    typeof(x) rem  = (x) % (denom);                        \
    (quot * (numer)) + ((rem * (numer)) / (denom));        \
}                                                          \
)

tsc_checker 内でも以下のコードにより動作します。(91行目のコメントアウトを参照)

    current = mult_frac(tsc, scale, (1UL << CYC2NS_SCALE_FACTOR));

*1 : はてブや Twitter で誤解されている方がいらっしゃったので念の為にフォローします
*2 : TSC は常に増加し続けるため、本ツール実行時点で numeric overflow が発生しない場合も、その後の再起動時に問題が発生する可能性があります。あくまで TSC 蓄積値の確認や現在の演算結果の目安を確認するツールとしてご利用ください
*3 : nano sec per micro sec 値
*4 : scale factor 値


Comments

comments powered by Disqus