虚拟化开源xen下时钟机制的研究

来源:互联网 发布:烟袋斜街10号 网络剧 编辑:程序博客网 时间:2024/05/22 09:46

 近来研究了HPET的规范以及在xen中HPET模拟和时钟流程的源码。由于今天把BIOS给刷坏了,暂时不能工作,只能写点东西。对近来的提交的时钟patch有了些疑问,希望大家耐心看完后提出宝贵意见。欢迎大家一起讨论:
1,patch: fix.tsc.sync.patch
  patch内容:
 diff -r bac5837c1689 xen/arch/x86/hvm/vpt.c
--- a/xen/arch/x86/hvm/vpt.c Wed May 16 04:50:40 2007 +0800
+++ b/xen/arch/x86/hvm/vpt.c Wed May 16 04:51:01 2007 +0800
@@ -69,7 +69,7 @@ void pt_thaw_time(struct vcpu *v)
 
     if ( v->arch.hvm_vcpu.guest_time )
     {
-        hvm_set_guest_time(v, v->arch.hvm_vcpu.guest_time);
+      //  hvm_set_guest_time(v, v->arch.hvm_vcpu.guest_time);
       
         list_for_each( list, head )
         {
@@ -169,8 +169,8 @@ void pt_intr_post(struct vcpu *v, int ve
     pt->pending_intr_nr--;
     pt->last_plt_gtime += pt->period_cycles;
 
-    if ( hvm_get_guest_time(pt->vcpu) < pt->last_plt_gtime )
-        hvm_set_guest_time(pt->vcpu, pt->last_plt_gtime);
+    //if ( hvm_get_guest_time(pt->vcpu) < pt->last_plt_gtime )
+    //    hvm_set_guest_time(pt->vcpu, pt->last_plt_gtime);
 
     if ( pt->cb != NULL )
         pt->cb(pt->vcpu, pt->priv);

要说明白这个patch的问题,首先要说一下HPET的工作机制:
   HPET(高精度时钟) 不是通过pci的CF8, CFC来访问其配置空间的。而是通过mmio映射的方式来访问其配置寄存器的。具体HPET映射到内存中的地址是通过ACPI table告诉给OS的(在xen中其实就是HPET TABLE 代码在/tools/firmware/hvmloader/acpi/build.c)。其具体资源会在ACPI dsdt(/tools/firmware/hvmloader/acpi/dsdt.asl)表中会有描述,这是由BIOS提供的。(在我们目前的G33平台,老版本的BIOS没有报告主板上有HPET,所以在native 的vista中可以看到资源管理器中,时钟设备用的是RTC)。
   HPET的配置寄存器占用内存1K的大小。一个block可以最大支持32个Timer。目前在在xen中最大支持3个timer,每个Timer可以单独配置成one-shot 和 period模式。one-shot 就是只对comparator写入一个初值在maincounter里面的值和comparator里一样发生一次中断后,不再发生中断(除非roll over and maincouter wraps around。period模式是当maincounter里面的值和comparator里一样发生一次中断后,会把comparator里的值加上上一次写入comparator
值最为下一次比较点。依次循环下去。
  对于one-short模式的Timer,period模式的timer。上面那个patch都会引起中断混乱的问题,还有可能会丢弃OS对HPET的配置。为什么呢:
1,在hpet初始化的hpet_init(xen/arch/x86/hvm/hpet.c)函数中,
         h->hpet.capability = 0x8086A201ULL; 这个和acpi 里面的HPET table里面的值是一至的。这个值是什么含义呢,我们看规范:

其实就是3个timer,64bit的maincounter,LegacyReplacement 寄存器等于一。
对于LegacyReplacement 我想补充说一点,LegacyReplacement =1 就是timer 0,取代PIT(8254)。timer 1,取代(RTC)。在这种模式下8254,RTC是不发中断的。
继续看代码:
   init_timer(&h->timers[i], hpet_timer_fn, &h->timer_fn_info[i],
                   v->processor);
给每个hpet的timer注册了一个hpet_timer_fn(定时器的回调函数)
在hpet_timer_fn函数中我们看到这行代码:
uint64_t mc = hpet_read_maincounter(h);
然而:
 static inline uint64_t hpet_read_maincounter(HPETState *h)
{
    if ( hpet_enabled(h) )
        return guest_time_hpet(h->vcpu) + h->mc_offset;
    else
        return h->hpet.mc64;
}

/* Frequency_of_TSC / frequency_of_HPET = 32 */
#define TSC_PER_HPET_TICK 32
#define guest_time_hpet(v) (hvm_get_guest_time(v) / TSC_PER_HPET_TICK)

uint64 hvm_get_guest_time(struct vcpu *v)
{
    u64 host_tsc;

    rdtscll(host_tsc);
    return host_tsc + v->arch.hvm_vcpu.cache_tsc_offset;
}
void hvm_set_guest_time(struct vcpu *v, u64 gtime)
{
    u64 host_tsc;

    rdtscll(host_tsc);

    v->arch.hvm_vcpu.cache_tsc_offset = gtime - host_tsc;
    hvm_funcs.set_tsc_offset(v, v->arch.hvm_vcpu.cache_tsc_offset);
}

根据以上的函数我们发现xen读虚拟hpet的maincounter的值并不像硬件那样直接从寄存器中读,而是对真实的cpu上TSC加上guest的tsc对正式tsc的偏移量。
下面我再说一下上面的patch:
pt_thaw_time()函数是在VCPU被调度出,重新调度得到时间片是执行的,这个函数的原意是在重新得到时间片时更新这个VCPU的cache_tsc_offset,但是patch中把这段给注释了,且不说这么做的好处。如果注释了这个,VCPU的cache_tsc_offset得不到更新,每次HPET 的timer 发完时钟中后更新main counter都会不准。造成没次时钟中断的间隔不一样。从而造成Guest Os系统时间不准,更有可能变慢。 

原创粉丝点击