昨年の始め頃、 208.5 日以上連続稼働すると動作が停止したり kernel panic が発生したりする可能性があるという TSC 絡みの不具合が話題になりました。
Linux カーネルの sched_clock() が 208.5 日の連続稼働でオーバーフローする現象について
https://access.redhat.com/site/solutions/121233
Does Red Hat Enterprise Linux 6 or 5 have a reboot problem which is caused by sched_clock() overflow around 208.5 days?
https://access.redhat.com/site/solutions/68466
しかし、上記の不具合を修正したカーネルには落とし穴があったようです。(誰も話題にしないので、注意喚起の意味で URL を貼っておきます。)
Systems with Intel® Xeon® Processor E5 hung after upgrade of Red Hat Enterprise Linux 6
https://access.redhat.com/site/solutions/433883
手元の VMware Player 上で 4 VCPU を割り当てた CentOS 6 32bit 環境において、 TSC の書き換えにより連続稼働をエミュレートするという方法で再現試験を行った限りでは、この不具合の再現率は100%のように見受けられます。
Intel Xeon E5 は業務用サーバとして広く使われていると思いますので、油断していると襲われますよ〜。
どうか正確に判定したいと思っております (カーネルをどうしても
変更したくない事情があり、クロックソースを TSC 以外にすることで
回避できないか実際に確かめたい)。
その際、TSC の書き換え方法がわからず困っております。読み出しで
あれば
asm volatile ("rdtsc" : "=a" (low), "=d" (high));
等でいいのでしょうが、更新方法は見つからず…。
よろしければ TSC の書き換え方法をご教示いただけないでしょうか。
よろしくお願いします。
というのに行き着きました。
http://tzpst.hatenablog.com/entry/2014/01/04/193008
wmsr にて大きめの値に更新して、reboot して、起動時止まれば NG
ってことでしょうか。試してみます。
> クロックソースを TSC 以外にすることで回避できないか
クロックソース( cat /sys/devices/system/clocksource/clocksource0/current_clocksource で
表示される値)として tsc 以外を使用していれば、今回の問題には該当しません。
> TSC の書き換え方法がわからず困っております。
TSC の書き換えを行うのは危険であり、 soft lockup や予期せぬ再起動の発生を
許容できる環境を用意する必要があります。
> TSC の書き換え方法
CPU 間の TSC の同期を失わずに TSC を書きかえるためにはカーネルモジュールの
助けを借りる必要があります。 Linux カーネルの開発経験がないと厳しいです。
msr-tools を使って /dev/cpu/*/msr への読み書きを行うことは可能です。しかし、
ユーザ空間からの要求では全ての CPU の TSC の値をアトミックに更新することは
できないため、 CPU 間の TSC の同期が失われてしまう可能性があります。
再起動してみて事象が発生しなかったと喜んでみたら、実はクロックソースとして
tsc が使われない状態になっていたというオチに引っかからないようにしてください。
> 表示される値)として tsc 以外を使用していれば、今回の問題には該当しません。
ありがとうございます!
ただ、linux-2.6.32.27 だとブート時は下記のような流れになっているので、
結局はどのクロックソースでも set_cyc2ns_scale が呼ばれてしまい
回避できないように見えるのですが、この辺はいかがでしょうか。
試験環境はあるのですぐにでも試してみたいところですが、事前調整が
必要なため、質問ばかりで誠に申し訳ございません。気が向いたら
アドバイスいただけると幸いです。
--------
./init/main.c
asmlinkage void __init start_kernel(void){
(snip)
time_init();
(snip)
if (late_time_init)
late_time_init();
./arch/x86/kernel/time.c
static __init void x86_late_time_init(void){
(snip)
tsc_init();
}
void __init time_init(void){
late_time_init = x86_late_time_init;
}
./arch/x86/kernel/tsc.c
void __init tsc_init(void){
(snip)
for_each_possible_cpu(cpu)
set_cyc2ns_scale(cpu_khz, cpu); // 問題の関数
--------
> msr-tools を使って /dev/cpu/*/msr への読み書きを行うことは可能です。しかし、
> ユーザ空間からの要求では全ての CPU の TSC の値をアトミックに更新することは
> できないため、 CPU 間の TSC の同期が失われてしまう可能性があります。
なるほど、コア単位でしか変更できないのですね。kernel land で
全コア更新→即座に再起動 というモジュールをさくっと作れれば
よいのですが、残念ながらその技量はなく…。
> 再起動してみて事象が発生しなかったと喜んでみたら、実はクロックソースとして
> tsc が使われない状態になっていたというオチに引っかからないようにしてください。
ありがとうございます。いずれにせよいろいろ実験してみます。
set_cyc2ns_scale() で設定された値を使用するのはクロックソースが tsc の
場合のみです。従って、クロックソースとして tsc を使わないようにすれば、
set_cyc2ns_scale() の中で整数オーバーフローにより想定外の値が設定されたとしても
影響を受けません。