PHP
首页 | 下载 | 博客 | 链接

区域

工具

查看代码的工具推荐用cscope,配合vim查找原型/函数/结构相当方便,如果怕麻烦,lxr也是个不错的选择,比如lxr.linux.no


xen 3.4 代码分析

xen的启动(schedule)
xen的运行(schedule)
schedule && credit
Event Channel
per cpu
上下文切换过程
数据结构
hypercall


xen 3.0 代码分析

xen启动/运行过程简介


资源链接

to be continue...


说明

文章中引用的版本是xen-3.0.4,分析的是x86_64平台. 为了保证行号的一致性,中文的注释没有插入回车/换行符.可能会显得有些紧巴巴的.
由于个人能力有限,网站内容存在许多错误和不足,希望读者批评指正. 本人联系方式:yubo@yubo.org

Per-cpu data

在linux和xen中都大量使用到了per_cpu这个宏,用来得到某颗cpu的数据地址,进行读/写操作
这里对xen中的per_cpu进行分析,跟linux中的per_cpu是一样的

为了方便起见,我们分析在csched_vcpu_wake函数的一个片段

per_cpu()使用

xen/common/sched_credit.c

  1.  static void
  2.  csched_vcpu_wake(struct vcpu *vc)
  3.  {
  4.   struct csched_vcpu * const svc = CSCHED_VCPU(vc);
  5.   const unsigned int cpu = vc->processor;
  6.  
  7.   BUG_ON( is_idle_vcpu(vc) );
  8.  
  9.   if ( unlikely(per_cpu(schedule_data, cpu).curr == vc) ) //如果cpu的schedule_data.curr 和 vc 指向相同的地址
  10.   {
  11.   CSCHED_STAT_CRANK(vcpu_wake_running); //将csched_priv.stats.vcpu_wake_running++
  12.   return; //返回
  13.   }

可以看到per_cpu的作用就是得到编号为cpu的处理器的相应数据地址,使用起来相当方便的

为了搞清楚per_cpu的工作原理,先来看看per_cpu()的定义

xen/include/asm-ia64/linux-xen/asm/percpu.h

  1.  #define per_cpu(var, cpu) (*RELOC_HIDE(&per_cpu__##var, __per_cpu_offset[cpu]))

再看看RELOC_HIDE的定义

xen/include/xen/compiler.h

  1.  #define RELOC_HIDE(ptr, off) \
  2.   ({ unsigned long __ptr; \
  3.   __asm__ ("" : "=r"(__ptr) : "0"(ptr)); \
  4.   (typeof(ptr)) (__ptr + (off)); })

整个合起来,对于per_cpu(schedule_data, cpu)来说,就是

*({ unsigned long __ptr;                       \
     __asm__ ("" : "=r"(__ptr) : "0"(&per_cpu__schedule_data));      \
     (typeof(&per_cpu__schedule_data)) (__ptr + ( __per_cpu_offset[cpu])); })

这里的&per_cpu__schedule_data就是per_cpu的schedule_data数据类型的基地址,__per_cpu_offset[n]是目标地址-基地址的偏移量,所以基地址+偏移量,就能得到想要的数据了.在讲到数据初始化之前,我们先来看看xen在编译的时候,如何往per_cpu里注册变量

xen/include/asm-x86/percpu.h

  1.  /* Separate out the type, so (int[3], foo) works. */
  2.  #define DEFINE_PER_CPU(type, name) \
  3.   __attribute__((__section__(".data.percpu"))) \
  4.   __typeof__(type) per_cpu__##name

上面的过程很简单,对我们给出的例子来说,就是声明一个名为per_cpu__schedule_data的变量,特别的是编译器会在连接的时候将这个变量放到.data.percpu区块.编译完成之后,来看下xen的ld脚本,它决定了per_cpu在内存的分布情况

xen/arch/ia64/xen/xen.lds.S

  1.   /* Per-cpu data: */
  2.   percpu : { } :percpu
  3.   . = ALIGN(PERCPU_PAGE_SIZE); //设置对齐大小,这个设置只影响下一个区块的起始位置,不会影响当前位置
  4.   __phys_per_cpu_start = .; //将当前位置保存到__phys_per_cpu_start
  5.   .data.percpu PERCPU_ADDR : AT(__phys_per_cpu_start - LOAD_OFFSET) //貌似这里所有的section都减去了LOAD_OFFSET,作用不明***
  6.   {
  7.   __per_cpu_start = .; //保存当前位置
  8.   *(.data.percpu) //将.data.percpu的内容连接到这里
  9.   __per_cpu_end = .; //保存结束的位置
  10.   }
  11.   . = __phys_per_cpu_start + PERCPU_PAGE_SIZE; /* ensure percpu data fits
  12.   * into percpu page size
  13.   */

下面是per_cpu的初始化

xen/arch/ia64/linux-xen/mm_contig.c

  1.  void *
  2.  per_cpu_init (void)
  3.  {
  4.   void *cpu_data;
  5.   int cpu;
  6.  
  7.   /*
  8.   * get_free_pages() cannot be used before cpu_init() done. BSP
  9.   * allocates "NR_CPUS" pages for all CPUs to avoid that AP calls
  10.   * get_zeroed_page().
  11.   */
  12.   if (smp_processor_id() == 0) {
  13.  #ifdef XEN
  14.   cpu_data = get_per_cpu_area();
  15.   if (cpu_data == NULL)
  16.   panic("can't allocate per cpu area.\n");
  17.  #else //申请内存空间,每个cpu一个PERCPU_PAGE_SIZE大小的块
  18.   cpu_data = __alloc_bootmem(PERCPU_PAGE_SIZE * NR_CPUS,
  19.   PERCPU_PAGE_SIZE, __pa(MAX_DMA_ADDRESS));
  20.  #endif
  21.   for (cpu = 0; cpu < NR_CPUS; cpu++) {
  22.   memcpy(cpu_data, __phys_per_cpu_start, __per_cpu_end - __per_cpu_start); //将原始值cp到刚申请到的内存地址的开始出
  23.   __per_cpu_offset[cpu] = (char *) cpu_data - __per_cpu_start; //保存偏移量,要不然就找不到了
  24.   cpu_data += PERCPU_PAGE_SIZE; //将指针指向下一个percpu块
  25.   per_cpu(local_per_cpu_offset, cpu) = __per_cpu_offset[cpu]; //将偏移量写入per_cpu,貌似没必要
  26.   }
  27.   }
  28.   return __per_cpu_start + __per_cpu_offset[smp_processor_id()]; //返回当前cpu的per_cpu起始地址
  29.  }


|_________|_ . . . . . _|__CPU0__|__CPU1__|__CPU2__|__...|__CPUNR_|
p0        p1            p2       p3       p4       p5    pn         

p0:__phys_per_cpu_start
p1:__phys_per_cpu_start
p2:__per_cpu_start+__per_cpu_offset[0]
p3:__per_cpu_start+__per_cpu_offset[1]
p4:__per_cpu_start+__per_cpu_offset[2]
p5:__per_cpu_start+__per_cpu_offset[3]
pn:__per_cpu_start+__per_cpu_offset[NR-1]


__per_cpu_offset的定义

xen/arch/ia64/linux-xen/setup.c

  1.  #ifdef CONFIG_SMP
  2.  unsigned long __per_cpu_offset[NR_CPUS];
  3.  EXPORT_SYMBOL(__per_cpu_offset);
  4.  #endif

 
Done in 0.101609945297 seconds