admin管理员组

文章数量:1130349

文章目录

  • 硬件基础
    • vendor
    • signature
    • feature
  • 数据结构
    • 物理抽象
      • virCPUx86CPUID
      • virCPUx86DataItem
      • virCPUx86Data
    • 配置信息
      • virCPUx86Vendor
      • virCPUx86Feature
      • virCPUx86Model
      • virCPUx86Map
    • 概念抽象
      • virCPUFeatureDef
      • virCPUDef
  • 工具函数
    • 数据加载
      • virCPUx86LoadMap
    • 基本操作
      • virCPUx86DataItemCmp
      • virCPUx86DataNext
      • virCPUx86DataGet
      • virCPUx86DataItemAndBits
      • virCPUx86DataItemClearBits
    • 集合操作
      • x86DataAdd
      • x86DataSubtract
      • x86DataIntersect
    • 信息提取
      • x86DataToCPUFeatures
      • x86DataToVendor
  • virsh 工具
    • capabilities
    • cpu-baseline
    • cpu-compare

硬件基础

  • 虚拟机的迁移需要保证两端的主机能够暴露给虚机的cpu feature相同,如果不相同无法保证迁移后虚机能够正常运行,因此迁移前会判断两端虚机特性,不同会报错,终止迁移。为了让资源池中新加入一台服务器并能够顺利将虚机迁移过去,需要计算源和目的两端服务器的cpu feature交集,然后暴露给虚机,保证迁移顺利,这是虚拟化组件需要解决的事情。本节主要介绍其硬件基础。
  • Intel通过cpuid指令查询cpu的signature和feature,signature标识着cpu的版本信息,feature包含了cpu的支持的硬件特性,比如虚拟化相关的vmx(Virtual Machine Extensions)特性,内存管理相关的pae(Physical Address Extension)特性,或者MSR(Model Specific Registers)等。通过cpuid指令可以查到两类信息,一类是cpu基本(Basic)信息,一类是cpu扩展(Extended)信息。
  • cpuid指令虽然没有操作数,但它的输出较其它有操作数的指令更为复杂,它将寄存器(EAX,ECX)作为输入,将寄存器(EAX,EBX,ECX,EDX)作为输出。输入的不同,执行cpuid指令得到的输出信息不同。比如,在EAX=0,ECX=0的情况下,如果执行cpuid指令,得到的是cpu的vendor信息和输入EAX的最大值;在EAX=1,ECX=0的情况下,如果执行cpuid指令,得到的是cpu的signature和feature。CPU feature相关的cpuid指令就是上面这两个,下面具体介绍它们的输出格式,以及如何获取vendor、signature和feature信息。关于cpu指令的详细介绍,参考intel手册的vol 2A-3.3-CPU Identification小节的介绍。demo可参考:cpuid demo

vendor

  • 当EAX被设置成0,作为输入时,执行cpuid指令,它的输出分别是EAX,保存EAX作为输入的最大值和vendor信息。Intel手册中关于vendor信息的获取说明如下:
  • 假设最大输入值为Maximum,那么EAX作为输入的取值范围就是[0, Maximum]。当EAX在这区间范围内作为输入时,执行cpuid指令输出的信息为Basic类信息。vendor为厂商信息,由EBX/ECX/ECX三个寄存器共同提供。对于x86架构有三个厂商信息,分别是:GenuineIntel(Intel)、AuthenticAMD(AMD)和HygonGenuine(Hygon)

signature

  • 当EAX被设置成1,作为输入时,执行cpuid指令,它的输出寄存器中,EAX保存cpu的signature的信息。ECX和EDX保存cpu的feature信息,Intel手册说明如下:
  • EAX的signature信息格式如下:

feature

  • feature信息和signature一样,通过设置EAX为1执行cpuid指令得到。它的信息保存在两个寄存器ECX、EDX中,寄存器的每个bit标识着一个cpu的特性,如果该bit被置位,表示cpu支持该特性,反之,如果该bit被清零,标志cpu不支持该特性。
  • ECX和ECX表示的feature格式如下:

数据结构

  • Libvirt提供了探测主机cpu feature的工具、计算指定cpu feature集合与主机cpu feature集合关系的工具,以及计算不同cpu feature交集的工具。通过这些工具,上层应用可以计算出两个cpu之间feature的交集,从而决定如何暴露给虚拟机,顺利实现虚机的迁移。本节主要介绍Libvirt中与cpu feature相关的数据结构。

物理抽象

virCPUx86CPUID

  • virCPUx86CPUID用于描述执行一条cpuid指令前后的输入和输出。
typedef struct _virCPUx86CPUID virCPUx86CPUID;
typedef virCPUx86CPUID *virCPUx86CPUIDPtr;
struct _virCPUx86CPUID {
    uint32_t eax_in;		/* 输入:寄存器EAX的值 */
    uint32_t ecx_in; 		/* 输入:寄存器ECX的值 */
    uint32_t eax;			/* 输出:寄存器EAX的值 */
    uint32_t ebx;			/* 同上 */
    uint32_t ecx;
    uint32_t edx;
};
  • 在Libvirt中,使用嵌入式汇编调用cpuid指令,在执行过程中用到了virCPUx86CPUID结构,如下:
cpuidCall(virCPUx86CPUID *cpuid)
{
    asm("xor %%ebx, %%ebx;" /* clear the other registers as some cpuid */
        "xor %%edx, %%edx;" /* functions may use them as additional arguments */
        "cpuid;"
        : "=a" (cpuid->eax),		/* 将eax的值作为输出保存到cpuid->eax中*/
          "=b" (cpuid->ebx),		/* 原理同上 */
          "=c" (cpuid->ecx),
          "=d" (cpuid->edx)
        : "a" (cpuid->eax_in),		/* 指定寄存器eax的值从cpuid->eax_in中读取 */
          "c" (cpuid->ecx_in));		/* 指定寄存器ecx的值从cpuid->ecx_in中读取 */
}

virCPUx86DataItem

  • virCPUx86DataItem在type为VIR_CPU_X86_DATA_CPUID时,保存的是一条cpuid指令输入输出。
typedef struct _virCPUx86DataItem virCPUx86DataItem;
typedef virCPUx86DataItem *virCPUx86DataItemPtr;
struct _virCPUx86DataItem {
    virCPUx86DataType type;		/* 当type=VIR_CPU_X86_DATA_CPUID时,data的cpuid值有效 */
    union {
        virCPUx86CPUID cpuid;	/* 保存一条cpuid的输入输出*/
        virCPUx86MSR msr;
    } data;
};

virCPUx86Data

  • 对于一个cpu来说,当使用cpuid指令查询它的相关信息时,它会有很多的输出,每改变一次输入的值,执行cpuid查到的输出值意义就不一样,因此cpuid的输入输出组成的条目非常多,Libvirt通过virCPUx86Data来描述执行cpuid的所有输入输出组成的数组。virCPUx86Data可以认为保存的是cpuid指令查询后,得到的原始数据。
typedef struct _virCPUx86Data virCPUx86Data;
struct _virCPUx86Data {
    size_t len;						/* 数组的大小,数组的每个元素表示cpuid指令的一个输入输出 */
    virCPUx86DataItem *items;		/* 数组的基地址 */
};

配置信息

  • Libvirt为了管理cpu feature,将支持的所有架构的所有feature组织成xml文件,持久化到磁盘上,同时将支持的所有model和vendor也组织成xml文件,持久化到磁盘上。在libvirt获取host capabilities或者计算feature交集时,会首先将支持的所有feature,vendor,model都加载到内存中,用于feature集合的计算。xml所在目录为/usr/share/libvirt/cpu_map/,下面主要介绍这些xml格式在内存中的数据结构。

virCPUx86Vendor

  • vendor信息可以通过cpuid指令查询得到,virCPUx86Vendor结构用于存放查询vendor的cpuid指令的输入输出,组成的一个item。
typedef struct _virCPUx86Vendor virCPUx86Vendor;
typedef virCPUx86Vendor *virCPUx86VendorPtr;
struct _virCPUx86Vendor {
    char *name;					/* 厂商名称,对于x86架构,可能的名称就是Intel、AMD和Hygon */
    virCPUx86DataItem data;	 	/* cpuid查询得到的条目,它的输出EBX、ECX和EDX就是厂商名称的accii码值 */
};
  • Libvirt保存了支持的vendor信息到xml中,当virsh工具需要处理vendor相关信息时,Libvirt从xml中读取支持的vendor信息,加载到内存,对应的数据结构就是virCPUx86Vendor。Libvirt的vendor信息保存在/usr/share/libvirt/cpu_map/x86_vendors.xml 中,如下:
<cpus>
  <vendor name='Intel' string='GenuineIntel'/>       /* 厂商名: Intel; 输出EBX/ECX/EDX组成的字符串为'GenuineIntel'  */
  <vendor name='AMD' string='AuthenticAMD'/>
  <vendor name='Hygon' string='HygonGenuine'/>
</cpus>

virCPUx86Feature

  • cpu的feature信息也通过cpuid指令查询得到,virCPUx86Feature的data域存放的数组通常只有一个元素,代表一个bit对应的feature。
typedef struct _virCPUx86Feature virCPUx86Feature;
typedef virCPUx86Feature *virCPUx86FeaturePtr;
struct _virCPUx86Feature {
	/* feature名 */
    char *name;				
    /* feature对应的寄存器值,对应寄存器中的一个bit
     * 这里看上去data包含的是一个item数组
     * 但实际上通常情况下只有一个元素 */
    virCPUx86Data data;		
    /* 如果该feature不影响迁移(可迁移)
     * 设置为true,反之,设置为false */
    bool migratable;
}; 
  • libvirt将所有feature以一定格式组织起来,存放到/usr/share/libvirt/cpu_map/x86_features.xml中,这个xml描述了x86架构下所有厂商的feature属性,包括该通过什么输入得到,输出的feature值对应寄存器的哪一位;该feature是否可以迁移等。每当Libvirt需要计算feature时,将这些feature加载到内存,进行操作。xml中一个典型的feature描述如下:
  <feature name='vmx'>							/* feature名字: vmx*/
    <cpuid eax_in='0x01' ecx='0x00000020'/>		/* 获取feature时输入EAX的值为0x01,该feature对应输出ECX的第5bit */
  </feature>

virCPUx86Model

  • cpu的model信息通过cpuid指令查到,得到的原始数据被保存到一个virCPUx86Data数据结构中。同时,virCPUx86Model结构中还将原数据解析出来,分别存放到vendor和signature中。
typedef struct _virCPUx86Model virCPUx86Model;
typedef virCPUx86Model *virCPUx86ModelPtr;
struct _virCPUx86Model {
    char *name;						/* Model名 */
    virCPUx86VendorPtr vendor;

文章目录

  • 硬件基础
    • vendor
    • signature
    • feature
  • 数据结构
    • 物理抽象
      • virCPUx86CPUID
      • virCPUx86DataItem
      • virCPUx86Data
    • 配置信息
      • virCPUx86Vendor
      • virCPUx86Feature
      • virCPUx86Model
      • virCPUx86Map
    • 概念抽象
      • virCPUFeatureDef
      • virCPUDef
  • 工具函数
    • 数据加载
      • virCPUx86LoadMap
    • 基本操作
      • virCPUx86DataItemCmp
      • virCPUx86DataNext
      • virCPUx86DataGet
      • virCPUx86DataItemAndBits
      • virCPUx86DataItemClearBits
    • 集合操作
      • x86DataAdd
      • x86DataSubtract
      • x86DataIntersect
    • 信息提取
      • x86DataToCPUFeatures
      • x86DataToVendor
  • virsh 工具
    • capabilities
    • cpu-baseline
    • cpu-compare

硬件基础

  • 虚拟机的迁移需要保证两端的主机能够暴露给虚机的cpu feature相同,如果不相同无法保证迁移后虚机能够正常运行,因此迁移前会判断两端虚机特性,不同会报错,终止迁移。为了让资源池中新加入一台服务器并能够顺利将虚机迁移过去,需要计算源和目的两端服务器的cpu feature交集,然后暴露给虚机,保证迁移顺利,这是虚拟化组件需要解决的事情。本节主要介绍其硬件基础。
  • Intel通过cpuid指令查询cpu的signature和feature,signature标识着cpu的版本信息,feature包含了cpu的支持的硬件特性,比如虚拟化相关的vmx(Virtual Machine Extensions)特性,内存管理相关的pae(Physical Address Extension)特性,或者MSR(Model Specific Registers)等。通过cpuid指令可以查到两类信息,一类是cpu基本(Basic)信息,一类是cpu扩展(Extended)信息。
  • cpuid指令虽然没有操作数,但它的输出较其它有操作数的指令更为复杂,它将寄存器(EAX,ECX)作为输入,将寄存器(EAX,EBX,ECX,EDX)作为输出。输入的不同,执行cpuid指令得到的输出信息不同。比如,在EAX=0,ECX=0的情况下,如果执行cpuid指令,得到的是cpu的vendor信息和输入EAX的最大值;在EAX=1,ECX=0的情况下,如果执行cpuid指令,得到的是cpu的signature和feature。CPU feature相关的cpuid指令就是上面这两个,下面具体介绍它们的输出格式,以及如何获取vendor、signature和feature信息。关于cpu指令的详细介绍,参考intel手册的vol 2A-3.3-CPU Identification小节的介绍。demo可参考:cpuid demo

vendor

  • 当EAX被设置成0,作为输入时,执行cpuid指令,它的输出分别是EAX,保存EAX作为输入的最大值和vendor信息。Intel手册中关于vendor信息的获取说明如下:
  • 假设最大输入值为Maximum,那么EAX作为输入的取值范围就是[0, Maximum]。当EAX在这区间范围内作为输入时,执行cpuid指令输出的信息为Basic类信息。vendor为厂商信息,由EBX/ECX/ECX三个寄存器共同提供。对于x86架构有三个厂商信息,分别是:GenuineIntel(Intel)、AuthenticAMD(AMD)和HygonGenuine(Hygon)

signature

  • 当EAX被设置成1,作为输入时,执行cpuid指令,它的输出寄存器中,EAX保存cpu的signature的信息。ECX和EDX保存cpu的feature信息,Intel手册说明如下:
  • EAX的signature信息格式如下:

feature

  • feature信息和signature一样,通过设置EAX为1执行cpuid指令得到。它的信息保存在两个寄存器ECX、EDX中,寄存器的每个bit标识着一个cpu的特性,如果该bit被置位,表示cpu支持该特性,反之,如果该bit被清零,标志cpu不支持该特性。
  • ECX和ECX表示的feature格式如下:

数据结构

  • Libvirt提供了探测主机cpu feature的工具、计算指定cpu feature集合与主机cpu feature集合关系的工具,以及计算不同cpu feature交集的工具。通过这些工具,上层应用可以计算出两个cpu之间feature的交集,从而决定如何暴露给虚拟机,顺利实现虚机的迁移。本节主要介绍Libvirt中与cpu feature相关的数据结构。

物理抽象

virCPUx86CPUID

  • virCPUx86CPUID用于描述执行一条cpuid指令前后的输入和输出。
typedef struct _virCPUx86CPUID virCPUx86CPUID;
typedef virCPUx86CPUID *virCPUx86CPUIDPtr;
struct _virCPUx86CPUID {
    uint32_t eax_in;		/* 输入:寄存器EAX的值 */
    uint32_t ecx_in; 		/* 输入:寄存器ECX的值 */
    uint32_t eax;			/* 输出:寄存器EAX的值 */
    uint32_t ebx;			/* 同上 */
    uint32_t ecx;
    uint32_t edx;
};
  • 在Libvirt中,使用嵌入式汇编调用cpuid指令,在执行过程中用到了virCPUx86CPUID结构,如下:
cpuidCall(virCPUx86CPUID *cpuid)
{
    asm("xor %%ebx, %%ebx;" /* clear the other registers as some cpuid */
        "xor %%edx, %%edx;" /* functions may use them as additional arguments */
        "cpuid;"
        : "=a" (cpuid->eax),		/* 将eax的值作为输出保存到cpuid->eax中*/
          "=b" (cpuid->ebx),		/* 原理同上 */
          "=c" (cpuid->ecx),
          "=d" (cpuid->edx)
        : "a" (cpuid->eax_in),		/* 指定寄存器eax的值从cpuid->eax_in中读取 */
          "c" (cpuid->ecx_in));		/* 指定寄存器ecx的值从cpuid->ecx_in中读取 */
}

virCPUx86DataItem

  • virCPUx86DataItem在type为VIR_CPU_X86_DATA_CPUID时,保存的是一条cpuid指令输入输出。
typedef struct _virCPUx86DataItem virCPUx86DataItem;
typedef virCPUx86DataItem *virCPUx86DataItemPtr;
struct _virCPUx86DataItem {
    virCPUx86DataType type;		/* 当type=VIR_CPU_X86_DATA_CPUID时,data的cpuid值有效 */
    union {
        virCPUx86CPUID cpuid;	/* 保存一条cpuid的输入输出*/
        virCPUx86MSR msr;
    } data;
};

virCPUx86Data

  • 对于一个cpu来说,当使用cpuid指令查询它的相关信息时,它会有很多的输出,每改变一次输入的值,执行cpuid查到的输出值意义就不一样,因此cpuid的输入输出组成的条目非常多,Libvirt通过virCPUx86Data来描述执行cpuid的所有输入输出组成的数组。virCPUx86Data可以认为保存的是cpuid指令查询后,得到的原始数据。
typedef struct _virCPUx86Data virCPUx86Data;
struct _virCPUx86Data {
    size_t len;						/* 数组的大小,数组的每个元素表示cpuid指令的一个输入输出 */
    virCPUx86DataItem *items;		/* 数组的基地址 */
};

配置信息

  • Libvirt为了管理cpu feature,将支持的所有架构的所有feature组织成xml文件,持久化到磁盘上,同时将支持的所有model和vendor也组织成xml文件,持久化到磁盘上。在libvirt获取host capabilities或者计算feature交集时,会首先将支持的所有feature,vendor,model都加载到内存中,用于feature集合的计算。xml所在目录为/usr/share/libvirt/cpu_map/,下面主要介绍这些xml格式在内存中的数据结构。

virCPUx86Vendor

  • vendor信息可以通过cpuid指令查询得到,virCPUx86Vendor结构用于存放查询vendor的cpuid指令的输入输出,组成的一个item。
typedef struct _virCPUx86Vendor virCPUx86Vendor;
typedef virCPUx86Vendor *virCPUx86VendorPtr;
struct _virCPUx86Vendor {
    char *name;					/* 厂商名称,对于x86架构,可能的名称就是Intel、AMD和Hygon */
    virCPUx86DataItem data;	 	/* cpuid查询得到的条目,它的输出EBX、ECX和EDX就是厂商名称的accii码值 */
};
  • Libvirt保存了支持的vendor信息到xml中,当virsh工具需要处理vendor相关信息时,Libvirt从xml中读取支持的vendor信息,加载到内存,对应的数据结构就是virCPUx86Vendor。Libvirt的vendor信息保存在/usr/share/libvirt/cpu_map/x86_vendors.xml 中,如下:
<cpus>
  <vendor name='Intel' string='GenuineIntel'/>       /* 厂商名: Intel; 输出EBX/ECX/EDX组成的字符串为'GenuineIntel'  */
  <vendor name='AMD' string='AuthenticAMD'/>
  <vendor name='Hygon' string='HygonGenuine'/>
</cpus>

virCPUx86Feature

  • cpu的feature信息也通过cpuid指令查询得到,virCPUx86Feature的data域存放的数组通常只有一个元素,代表一个bit对应的feature。
typedef struct _virCPUx86Feature virCPUx86Feature;
typedef virCPUx86Feature *virCPUx86FeaturePtr;
struct _virCPUx86Feature {
	/* feature名 */
    char *name;				
    /* feature对应的寄存器值,对应寄存器中的一个bit
     * 这里看上去data包含的是一个item数组
     * 但实际上通常情况下只有一个元素 */
    virCPUx86Data data;		
    /* 如果该feature不影响迁移(可迁移)
     * 设置为true,反之,设置为false */
    bool migratable;
}; 
  • libvirt将所有feature以一定格式组织起来,存放到/usr/share/libvirt/cpu_map/x86_features.xml中,这个xml描述了x86架构下所有厂商的feature属性,包括该通过什么输入得到,输出的feature值对应寄存器的哪一位;该feature是否可以迁移等。每当Libvirt需要计算feature时,将这些feature加载到内存,进行操作。xml中一个典型的feature描述如下:
  <feature name='vmx'>							/* feature名字: vmx*/
    <cpuid eax_in='0x01' ecx='0x00000020'/>		/* 获取feature时输入EAX的值为0x01,该feature对应输出ECX的第5bit */
  </feature>

virCPUx86Model

  • cpu的model信息通过cpuid指令查到,得到的原始数据被保存到一个virCPUx86Data数据结构中。同时,virCPUx86Model结构中还将原数据解析出来,分别存放到vendor和signature中。
typedef struct _virCPUx86Model virCPUx86Model;
typedef virCPUx86Model *virCPUx86ModelPtr;
struct _virCPUx86Model {
    char *name;						/* Model名 */
    virCPUx86VendorPtr vendor;

本文标签: LibvirtCPUfeature