追記:2020年5月頃からTSCまわりのコードが大幅に改善され,ここで示している cpu_counter_serializing()
などのコードは取り除かれています。
timecounter “TSC” の続き
CPUの周波数つまり struct cpu_info *ci
における ci->ci_data.cpu_cc_freq
がどこで設定されるかというと (src/sys/arch/)x86/x86/cpu.c の cpu_get_tsc_freq()
。
これは、x86_delay(100000)
の前後で cpu_counter_serializing()
を呼んで値の差をとって10倍している。
cpu_counter_serializing()
は、x86/x86/tsc.c の一番最後にあって、
uint64_t
cpu_counter_serializing(void)
{
if (tsc_good)
return rdmsr(MSR_TSC);
else
return cpu_counter();
}
この時点では tsc_good は 0 (偽)なので cpu_counter()
が呼ばれている。
cpu_counter()
は amd64/amd64/cpufunc.S にあって、
ENTRY(cpu_counter)
xorq %rax, %rax
rdtsc
shlq $32, %rdx
orq %rdx, %rax
addq CPUVAR(CC_SKEW), %rax
ret
END(cpu_counter)
x86のアセンブラは良く知らないが、rdtsc
命令はx86の伝統的な Time Stamp Counter (TSC) を取り出す命令そのものらしく、(互換性の関係上)32ビットずつに分かれてレジスタに収まっているのをシフトとORで64ビットの値にしているように見える。何故 CPUVAR(CC_SKEW)
(→ %gs:CPU_INFO_CC_SKEW
→ %gs:offsetof(struct cpu_info, ci_data.cpu_cc_skew)
?)を足してるのかはよくわからないが、cpuを跨ぐ場合以外は関係なさそう?
しかし、x86_delay(100000)
の前後で取り出された具体的な値を表示させたところ、16,120,975,782 と 16,664,790,833 で、差は 543,815,051。10倍すると 5,438,150,510 となり、推定クロックおよそ 5.4GHz ということになる。もちろんおかしい。
rdtsc 命令は(ハードの問題でないなら)これ以上疑いようがないので、あとは x86_delay(100000)
が想定より長い(0.1秒のつもりが0.5秒以上かかっている)可能性くらいか。
x86_delay()
の実体は x86/isa/clock.c の i8254_delay()
のようだ。
(hyperv の場合は置き換わったりするようだが、今回は関係ない。)
つづく(かどうか不明)