日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Libvirt CPU Feature

發布時間:2023/12/20 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Libvirt CPU Feature 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

  • 硬件基礎
    • 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信息。

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;size_t nsignatures;uint32_t *signatures;virCPUx86Data data; };

virCPUx86Map

typedef struct _virCPUx86Map virCPUx86Map; typedef virCPUx86Map *virCPUx86MapPtr; struct _virCPUx86Map {size_t nvendors;virCPUx86VendorPtr *vendors;size_t nfeatures;virCPUx86FeaturePtr *features;size_t nmodels;virCPUx86ModelPtr *models;size_t nblockers;virCPUx86FeaturePtr *migrate_blockers; };

概念抽象

virCPUFeatureDef

  • 使用cpuid指令查詢得到的cpu feature被保存到兩個寄存器中,寄存器的每個bit代表一個feature,Libvirt對應地將每個feature抽象成一個virCPUFeatureDef數據結構。結構中的policy描述了vcpu采用該feature的策略。
typedef enum {VIR_CPU_FEATURE_FORCE, /* vcpu需要強行應用該feature,無論主機是否支持此feature */VIR_CPU_FEATURE_REQUIRE, /* vcpu請求使用該feature,如果主機不支持或者hypervisor無法模擬,虛機在start的時候會報錯 */VIR_CPU_FEATURE_OPTIONAL, /* vcpu只有在探測到主機支持該feature時,才使用該feature,因此這種策略不會虛機啟動不會報錯 */VIR_CPU_FEATURE_DISABLE, /* vcpu被主機禁止使用該feature */VIR_CPU_FEATURE_FORBID, /* 禁止主機支持該feature。如果主機支持,虛機啟動時報錯*/VIR_CPU_FEATURE_LAST } virCPUFeaturePolicy; typedef struct _virCPUFeatureDef virCPUFeatureDef; typedef virCPUFeatureDef *virCPUFeatureDefPtr; struct _virCPUFeatureDef {char *name; /* feature名字,比如'vmx'、'msr'等 */int policy; /* enum virCPUFeaturePolicy */ };

virCPUDef

  • cpu的capabilities信息展示給用戶,通常包含架構、廠商、Model、特性等等,Libvirt將這些cpu相關的信息包含在virCPUDef結構中。
typedef struct _virCPUDef virCPUDef; typedef virCPUDef *virCPUDefPtr; struct _virCPUDef { int type; /* enum virCPUType */int mode; /* enum virCPUMode */int match; /* enum virCPUMatch */virCPUCheck check;virArch arch;char *model;char *vendor_id; /* vendor id returned by CPUID in the guest */int fallback; /* enum virCPUFallback */char *vendor; /* 廠商名 */......size_t nfeatures;size_t nfeatures_max;virCPUFeatureDefPtr features; /* CPU包含的除Model外的所有feature集合 */...... };

工具函數

數據加載

virCPUx86LoadMap

  • 該函數主要負責將/usr/share/libvirt/cpu_map目錄下的所有信息加載到內存中,最終保存在靜態全局變量cpuMap中,virCPUx86LoadMap函數只在Libvirtd啟動時執行一次。一旦cpuMap被加載到內存中,后續的所有關于cpu feature集合的計算都使用這個結構

基本操作

virCPUx86DataItemCmp

int virCPUx86DataItemCmp(const virCPUx86DataItem *item1, const virCPUx86DataItem *item2)
  • 比較item1和item2是否相同,比較的標準是判斷查詢cpuid的輸入寄存器eax_in和ecx_in的值,如果值相等表示相同,反之則不同。

virCPUx86DataNext

virCPUx86DataItemPtr virCPUx86DataNext(virCPUx86DataIteratorPtr iterator)
  • 根據迭代器中的位置pos,取出其指向的非零item。

virCPUx86DataGet

virCPUx86DataItemPtr virCPUx86DataGet(const virCPUx86Data *data, const virCPUx86DataItem *item)
  • 根據item中的eax_in和ecx_in,查找data集合中對應的item,取出data集合中對應的item。

virCPUx86DataItemAndBits

void virCPUx86DataItemAndBits(virCPUx86DataItemPtr item, const virCPUx86DataItem *mask)
  • 將item中對應的eax,ebx,ecx,edx中與mask中eac,ebx,ecx,edx進行位與操作

virCPUx86DataItemClearBits

void virCPUx86DataItemClearBits(virCPUx86DataItemPtr item, const virCPUx86DataItem *mask)
  • 根據mask提供的掩碼位,將item中對應的eax,ebx,ecx,edx中對應的位請零

集合操作

x86DataAdd

int x86DataAdd(virCPUx86Data *data1, const virCPUx86Data *data2)
  • 函數將data2集合中的所有非空的item添加到data1集合中,所謂空item,item中的eax,ebx,ecx,edx都為0。添加有兩種情況,如果data1中已經存在對應的item,將data2中的item與data1中對應的item取并集操作,否則直接將data2中的item內容拷貝到data1集合中。該函數提供了feature集合的加操作。
static int x86DataAdd(virCPUx86Data *data1,const virCPUx86Data *data2) {virCPUx86DataIterator iter;virCPUx86DataItemPtr item;/* 初始化遍歷data2集合的迭代器* 為遍歷data2做準備 */virCPUx86DataIteratorInit(&iter, data2); /* 將data2中所有非空的item取出 */ while ((item = virCPUx86DataNext(&iter))) {/* 將item添加到data1中 */if (virCPUx86DataAddItem(data1, item) < 0)return -1;}return 0; }

x86DataSubtract

void x86DataSubtract(virCPUx86Data *data1, const virCPUx86Data *data2)
  • 從data1集合中,減去data2集合中包含的所有item。該函數提供了feature集合的減操作。
static void x86DataSubtract(virCPUx86Data *data1,const virCPUx86Data *data2) {virCPUx86DataIterator iter;virCPUx86DataItemPtr item1;virCPUx86DataItemPtr item2;/* 初始化遍歷data1集合的迭代器 */virCPUx86DataIteratorInit(&iter, data1);/* 遍歷data1,依次取出它包含的item */while ((item1 = virCPUx86DataNext(&iter))) {/* 遍歷data2集合中的item,* 如果有與data1中item相等的* 將data1中的對應item清零 */item2 = virCPUx86DataGet(data2, item1);virCPUx86DataItemClearBits(item1, item2);} }

x86DataIntersect

void x86DataIntersect(virCPUx86Data *data1, const virCPUx86Data *data2)
  • 該函數將data1集合中的item依次與data2集合中的item比較,如果data1集合中的item在data2集合中沒有,則將data1中的Item刪除掉,如果有,則將data2中的對應item也取出來,兩個item取交集,結果存放在data1中。該函數實現了取集合交集的操作。
static void x86DataIntersect(virCPUx86Data *data1,const virCPUx86Data *data2) {virCPUx86DataIterator iter;virCPUx86DataItemPtr item1;virCPUx86DataItemPtr item2;/* 初始化遍歷data1集合的迭代器 */virCPUx86DataIteratorInit(&iter, data1);/* 遍歷data1,依次取出它包含的item */while ((item1 = virCPUx86DataNext(&iter))) {/* 查找data2,如果集合中有包含data1中的item將其取出 */item2 = virCPUx86DataGet(data2, item1);if (item2)/* 如果data2中有相同的item,與data1中的item進行位與操作 */virCPUx86DataItemAndBits(item1, item2);else/* 如果data2中沒有對應item,將data1中的item清零 */virCPUx86DataItemClearBits(item1, item1);} }

信息提取

x86DataToCPUFeatures

int x86DataToCPUFeatures(virCPUDefPtr cpu, int policy, virCPUx86Data *data, virCPUx86MapPtr map)
  • 將data集合中包含的所有屬于map的feature添加到cpu的features域中。遍歷map中包含的所有feature,檢查它的每一個item是否屬于data集合的子集,如果是,將data中對應的item減去,同時將feature添加加入到cpu的features中。注意,加入到cpu的features中的feature在data集合中就被刪除了
/* also removes all detected features from data */ static int x86DataToCPUFeatures(virCPUDefPtr cpu,int policy,virCPUx86Data *data,virCPUx86MapPtr map) {size_t i;for (i = 0; i < map->nfeatures; i++) {/* 依次取出x86_features.xml中的所有feature */virCPUx86FeaturePtr feature = map->features[i];/* 如果取出的feature屬于data表示的集合 */if (x86DataIsSubset(data, &feature->data)) {/* 將feature從data集合中刪除 */x86DataSubtract(data, &feature->data);/* 同時將feature添加到cpu->features中 */if (virCPUDefAddFeature(cpu, feature->name, policy) < 0)return -1;}}return 0; }

x86DataToVendor

  • 返回map中與data集合中匹配的第一個vendor。從data集合取出vendor,與map中包含的所有vendor比較,如果有相同的item,首先清零data中對應的item,同時返回map中指向的vendor。注意,在map匹配到對應vendor后,同時會刪除data中對應的vendor。
/* also removes bits corresponding to vendor string from data */ static virCPUx86VendorPtr x86DataToVendor(const virCPUx86Data *data,virCPUx86MapPtr map) {virCPUx86DataItemPtr item;size_t i;for (i = 0; i < map->nvendors; i++) {/* 依次取出x86_vendors.xml中所有的vendor */virCPUx86VendorPtr vendor = map->vendors[i];/* 如果data集合中包含了該vendor對應的item(輸入值相同) */if ((item = virCPUx86DataGet(data, &vendor->data)) &&/* 同時取出的item與vendor中對應item的輸出值也相同 */virCPUx86DataItemMatchMasked(item, &vendor->data)) {/* 將data中包含的vendor對應的item清零 */virCPUx86DataItemClearBits(item, &vendor->data);/* 返回map中包含的vendor */return vendor;}}return NULL; }

virsh 工具

  • Libvirt提供了三個與cpu feature相關的接口,分別是:
  • virsh capabilites: 探測主機包含的cpu feature。該接口可以搜集主機支持的cpu feature集合。
  • virsh cpu-baseline: 給出多個cpu feature的集合,計算這些cpu feature集合的基線,實際上就是取給出的所有cpu feature集合的交集。該接口可以用于計算多個主機共同的的cpu feature集合,從而得到可以保證遷移成功的虛擬機cpu feature配置。
  • virsh cpu-compare: 給出一個cpu feature的集合,將它與主機的cpu feature作集合的比較,得到兩個集合的關系(超集、子集、相等或者非包含關系)。該接口可以用于判斷遠端的一個host上的虛機是否可以遷移到本地。
    • 下面分別介紹三個virsh命令的內部實現流程,三個virsh命令的前半段代碼路徑是類似的。

    capabilities

    • virsh capabilities命令用于顯示主機的能力,其中一項顯示主機cpu支持的feature集合。這個feature以model加feature的形式顯示出來,以下面的一個命令輸出為例,Model為SandyBridge的cpu本身包含了一個feature集合,這個集合可以隱式地通過/usr/share/libvirt/cpu_map/x86_SandyBridge.xml查詢得到,同時通過顯示地也指定了一個feature集合。兩個集合取并集,就是整個cpu包含的feature集合。
    <capabilities><host><cpu><!-- 主機cpu匹配到的架構 --><arch>x86_64</arch><!-- 主機cpu匹配到的Model,該Model隱式地包含一系列的feature集合 --><model>SandyBridge</model><!-- 主機cpu的生產廠商 --><vendor>Intel</vendor>......<!-- 顯示指定的主機包含的feature --><feature name='vme'/><feature name='ds'/><feature name='acpi'/>......</cpu>......</host>...... </capabilities>
    • capabilities流程首先通過cpuid命令查詢主機包含的所有feature,然后遍歷Libvirt在xml中預定義的所有Model,找到最匹配主機的那一個Model,最后用主機的feature集合減去Model包含的feature集合,得到需要顯示指定的feature集合或者顯示禁止的feature集合,將其增加到virCPUDef的features域中。所謂最匹配的model,就是簽名和主機cpu相同,同時包含盡可能多的feature的那個預定義model。
    1. 總體流程 cmdCapabilities......qemuConnectGetCapabilities....../* 調用x86上的driver接口搜集host相關的capabilities */cpuDriverX86.getHostvirCPUx86GetHost/* 首先通過嵌入式匯編執行cpuid指令* 獲取主機cpu feature,之后的所有操作* 就是要在預定義的model之中找到最匹配* 該feature集合的model,將其封裝成virCPUDef* 作為結果輸出,所有通過cpuid查詢得到的信息被存放到cpuData中 */cpuidSet(CPUX86_BASIC, cpuData)cpuidSet(CPUX86_EXTENDED, cpuData)/* 解析cpuid查詢得到的數據集合cpudata,找到最合適的model* 將其封裝到指向virCPUDef的cpu指針中 */x86DecodeCPUData(cpu, cpuData, models)x86Decode/* 加載/usr/share/libvirt/cpu_map下的所有預定義信息 */map = virCPUx86GetMap()/* 根據data中的vendor信息,在map中找到對應的預定義vendor信息* 比如主機的vendor信息是intel,那么map中找到的就是x86_vendors.xml中* <vendor name='Intel' string='GenuineIntel'/>這一個item加載到內存中的信息 */vendor = x86DataToVendor(&data, map)/* 從cpudata中找到簽名信息,我們知道簽名信息是通過eax_in=1,ecx_in=0查詢cpuid得到* 因此x86DataToSignature的核心內容就是從data中查找匹配eax_in=1,ecx_in=0的這個item* 返回對應的結果 */signature = x86DataToSignature(&data)/* 簽名找到之后,解析出其中的family,model和stepping信息,因為在之后遍歷預定義Model* 的過程中,會使用Model信息和簽名信息來做判斷,獲取最匹配的預定義Model */virCPUx86SignatureFromCPUID(signature, &sigFamily, &sigModel, &sigStepping)/* 遍歷預定義的cpu model */for (i = map->nmodels - 1; i >= 0; i--) {/* 取出候選的model */candidate = map->models[i]/* 如果主機的vendro和預定義model的vendor不同,首先pass掉 */if (vendor && candidate->vendor && vendor != candidate->vendor) {VIR_DEBUG("CPU vendor %s of model %s differs from %s; ignoring",candidate->vendor->name, candidate->name, vendor->name);continue;}/* 將data中的feature集合'變成'預定義的model+feature集合的形式* 存放在virCPUDef指針cpuCandidate中,其中virCPUDef.model存放預定義的* model名字,virCPUDef.features存放需要顯示指明的主機cpu feature集合 */cpuCandidate = x86DataToCPU(&data, candidate, map, hvModel)/* 判斷預定義的model是否是最匹配主機cpu feature的 */if ((rc = x86DecodeUseCandidate(model, cpuModel,candidate, cpuCandidate,signature, preferred,cpu->type == VIR_CPU_TYPE_HOST))) {virCPUDefFree(cpuModel);cpuModel = cpuCandidate;model = candidate;if (rc == 2)break;}} 2. 具體算法: 2.1 怎么把主機查詢得到的cpu feature集合轉化成預定義的model+feature集合? x86DataToCPU(const virCPUx86Data *data,virCPUx86ModelPtr model,virCPUx86MapPtr map,virDomainCapsCPUModelPtr hvModel,virCPUType cpuType) data:主機cpuid數據 model:預定義的model map:libvirt預定義的所有信息/* 首先拷貝一份cpuid查詢得到的數據到copy中 */x86DataCopy(&copy, data)/* 再拷貝一份預定義的model包含的cpuid數據到modelData中 */x86DataCopy(&modelData, &model->data)/* 核心步驟:* 讓主機包含的feature集合減去預定義model包含的feature集合* copy是主機feature集合,modelData是預定義model包含的feature集合* 操作完成后,copy中剩下的就是不包含model的feature集合* 這些集合會被添加到virCPUDef.features中,這些feature都是主機* 擁有的,因此虛機vcpu可以使用,policy被設置為VIR_CPU_FEATURE_REQUIRE */x86DataSubtract(&copy, &modelData)/* 同上,將model包含的feature集合減去data包含的feature集合* 操作完成后,model中剩下的集合就是data中不包含的,這些feature是主機* 不具備的feature,因此如果使用預定義的model,需要顯示的禁止掉這些feature* policy需要被設置為VIR_CPU_FEATURE_DISABLE */x86DataSubtract(&modelData, data)/* 將主機包含的、但預定義model中不包含的feature集合顯示地添加到virCPUDef.features中* 策略是vcpu可以使用:VIR_CPU_FEATURE_REQUIRE */x86DataToCPUFeatures(cpu, VIR_CPU_FEATURE_REQUIRE, &copy, map)/* 將預定義model包含的、但主機cpu不包含的feature集合顯示地添加到virCPUDef.features中* 顯示地禁止:VIR_CPU_FEATURE_DISABLE */x86DataToCPUFeatures(cpu, VIR_CPU_FEATURE_DISABLE, &modelData, map) 2.2 怎么選擇最優的預定義model x86DecodeUseCandidate/* 首先判斷簽名:首先要找到和主機cpu相同簽名的預定義model *//* Ideally we want to select a model with family/model equal to* family/model of the real CPU. Once we found such model, we only* consider candidates with matching family/model.*/if (signature &&virCPUx86SignaturesMatch(current->signatures, signature) &&!virCPUx86SignaturesMatch(candidate->signatures, signature)) {VIR_DEBUG("%s differs in signature from matching %s",cpuCandidate->model, cpuCurrent->model);return 0;}/* 如果存在簽名相同的多個model情況:選取包含feature集合多的那個預定義model */if (cpuCurrent->nfeatures > cpuCandidate->nfeatures) {VIR_DEBUG("%s results in shorter feature list than %s",cpuCandidate->model, cpuCurrent->model);return 1;}

    cpu-baseline

    • cpu-baseline命令接受多個feature集合作為輸入(以model+顯式feature集的形式給出),它從這些feature集合中,在預定義的model中找到最合適的哪一個model,將其作為基線返回,如果多個feature集合沒有任何交集,那么返回不兼容的cpu model。
    1. 總體流程 cmdCPUBaseline......qemuConnectBaselineCPU......cpuDriverX86.baselinevirCPUx86Baseline/* 首先取第一個feature集作為輸入,將其'變成'Libvirt預定義的model,* 將此model作為基線model */base_model = x86ModelFromCPU(cpus[0], map, -1)modelName = cpus[0]->model/* 處理所有輸入cpu model隱式包含的feature集合 */for (i = 1; i < ncpus; i++) {/* 依次取出所有的輸入feature集合,將其'變成'預定義的model形式 */model = x86ModelFromCPU(cpus[i], map, -1)/* 對所有這些feature集合進行取交集操作,base_model中最后剩下的就是所有feature集合的交集 */x86DataIntersect(&base_model->data, &model->data)}/* 處理所有輸入cpu 顯示指定的feature集合 */if (features) {for (i = 0; features[i]; i++) {if ((feat = x86FeatureFind(map, features[i])) &&x86DataAdd(&featData->data.x86, &feat->data) < 0)goto cleanup;}x86DataIntersect(&base_model->data, &featData->data.x86)}/* 最終,將所有的這些輸入feature集合取交集后,base model沒有數據了* 說明這些feature集合之間沒有任何交集,返回不兼容的cpu */if (x86DataIsEmpty(&base_model->data)) {virReportError(VIR_ERR_OPERATION_FAILED,"%s", _("CPUs are incompatible"));goto error;}/* 如果輸入feature集合有交集,將這些數據轉化成virCPUDef作為輸出 */x86Decode(cpu, &base_model->data, models, modelName, migratable) 2. 具體算法 怎么將輸入的feature集合轉化成預定義的cpu model? x86ModelFromCPU/* 首先處理model中隱式包含的feature集合* 取出要比較的cpu的model,該model隱式指明了feature的集合* 在預定義的model中查找是否有相同的model,沒有則報錯 */if (cpu->model &&(policy == VIR_CPU_FEATURE_REQUIRE || policy == -1)) {if (!(model = x86ModelFind(map, cpu->model))) {virReportError(VIR_ERR_INTERNAL_ERROR,_("Unknown CPU model %s"), cpu->model);return NULL;}/* 拷貝匹配到的預定義model的cpuid數據 */model = x86ModelCopy(model);/* 處理輸入中顯式指定的feature */for (i = 0; i < cpu->nfeatures; i++) {/* 如果輸入的feature沒有指定使用策略,默認使用require策略 */if (cpu->features[i].policy == -1)fpol = VIR_CPU_FEATURE_REQUIRE;/* 否則使用指定的策略 */elsefpol = cpu->features[i].policy;/* 首先在預定義的feature中檢查,是否有輸入的feature,如果沒有報錯 */if (!(feature = x86FeatureFind(map, cpu->features[i].name))) {virReportError(VIR_ERR_INTERNAL_ERROR,_("Unknown CPU feature %s"), cpu->features[i].name);return NULL;}/* 根據feature的策略,將輸入的feature添加到model中,或者從model刪除 */ switch (fpol) {case VIR_CPU_FEATURE_FORCE:case VIR_CPU_FEATURE_REQUIRE:/* 如果策略是require,添加到model包含的feature集合中 */if (x86DataAdd(&model->data, &feature->data) < 0)return NULL;break;case VIR_CPU_FEATURE_DISABLE:case VIR_CPU_FEATURE_FORBID:/* 將feature從model包含的feature集合中刪除 */x86DataSubtract(&model->data, &feature->data);break;/* coverity[dead_error_condition] */case VIR_CPU_FEATURE_OPTIONAL:case VIR_CPU_FEATURE_LAST:break;}

    cpu-compare

    • virsh cpu-compare工具用于將輸入的feature集合與主機cpu的feature集合比較,輸出的結果有4個:
    typedef enum {VIR_CPU_COMPARE_ERROR = -1, /* 比較出錯 *//* 與主機不兼容,這里包含兩種集合關系:* 一是兩者不相關* 二是主機cpu feature是輸入cpu feature的子集* 子集說明輸入cpu feature集合中包含了主機無法提供的feature* 因此無法遷移,返回不兼容 */VIR_CPU_COMPARE_INCOMPATIBLE = 0, VIR_CPU_COMPARE_IDENTICAL = 1, /* 與主機相同 *//* 主機的cpu feature是輸入cpu feature的超集,說明可以遷移*/VIR_CPU_COMPARE_SUPERSET = 2, } virCPUCompareResult; 1. 總體流程 cmdCPUCompare......qemuConnectCompareCPU/* 獲取主機的cpu feature集合 */cpu = virQEMUDriverGetHostCPU(driver)/* 將主機的cpu feature集合與xmlDesc中描述的cpu feature集合比較 */virCPUCompareXML(driver->hostarch, cpu, xmlDesc, failIncompatible, validateXML) ......cpuDriverX86.comparevirCPUx86Comparex86Compute(host, cpu, NULL, &message)/* 如果做比較的cpu與主機的cpu廠商不同,報錯不兼容 */if (cpu->vendor &&(!host->vendor || STRNEQ(cpu->vendor, host->vendor))) {VIR_DEBUG("host CPU vendor does not match required CPU vendor %s",cpu->vendor);return VIR_CPU_COMPARE_INCOMPATIBLE;}/* 加載Libvirt預定義的信息,并且根據要比較的cpu feature集合* 在預定義的model中組裝各個屬性的feature集合* 將要比較的要求提供的cpu feature集合存放在cpu_require中 */if (!(map = virCPUx86GetMap()) ||!(host_model = x86ModelFromCPU(host, map, -1)) ||!(cpu_force = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_FORCE)) ||!(cpu_require = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_REQUIRE)) ||!(cpu_optional = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_OPTIONAL)) ||!(cpu_disable = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_DISABLE)) ||!(cpu_forbid = x86ModelFromCPU(cpu, map, VIR_CPU_FEATURE_FORBID))) return VIR_CPU_COMPARE_ERROR;/* 如果主機的cpu feature集合中包含了要比較的cpu feature中禁止提供的feature* 返回不兼容 */x86DataIntersect(&cpu_forbid->data, &host_model->data)if (!x86DataIsEmpty(&cpu_forbid->data)) {virX86CpuIncompatible(N_("Host CPU provides forbidden features"),&cpu_forbid->data); return VIR_CPU_COMPARE_INCOMPATIBLE;}/* 首先獲取比較cpu feature集合中實際需要提供的feature集合 */x86DataSubtract(&cpu_require->data, &cpu_force->data);x86DataSubtract(&cpu_require->data, &cpu_optional->data);x86DataSubtract(&cpu_require->data, &cpu_disable->data);/* 比較host_model與cpu_require包含的集合關系 */result = x86ModelCompare(host_model, cpu_require);/* 如果host_model是cpu_require的子集,返回不兼容* 說明要比較的cpu feature集合中包含了主機中無法提供的feature */if (result == SUBSET || result == UNRELATED) {x86DataSubtract(&cpu_require->data, &host_model->data);virX86CpuIncompatible(N_("Host CPU does not provide required ""features"),&cpu_require->data);return VIR_CPU_COMPARE_INCOMPATIBLE;}/* 至此,host與輸入cpu的集合關系只有兩種:* 一是相同、二是host是cpu的超集* 下面的步驟就是判斷應該屬于那種情況 *//* 首先取出host的所有feature集合 */diff = x86ModelCopy(host_model);/* 減去輸入cpu中涉及到的所有feature集合 */x86DataSubtract(&diff->data, &cpu_optional->data);x86DataSubtract(&diff->data, &cpu_require->data);x86DataSubtract(&diff->data, &cpu_disable->data);x86DataSubtract(&diff->data, &cpu_force->data);/* 減去之后,如果還存在一些feature* 說明這些feature與輸入的cpu feature集合無關的* host就是輸入cpu feature的超集* 如果不存在feature了,說明兩者一樣 */if (x86DataIsEmpty(&diff->data))ret = VIR_CPU_COMPARE_IDENTICAL;elseret = VIR_CPU_COMPARE_SUPERSET; 2. 具體算法 怎么比較兩個集合model1、model2的關系 x86ModelCompare(virCPUx86ModelPtr model1,virCPUx86ModelPtr model2)virCPUx86CompareResult result = EQUAL;/* 首先遍歷model1中的feature,將其與model2集合比較* 如果它不存在于model2中,那model1肯定不是model2的子集了* 如果是的話,model1的任何元素都應該存在于model2中 */while ((item1 = virCPUx86DataNext(&iter1))) {virCPUx86CompareResult match = SUPERSET;if ((item2 = virCPUx86DataGet(&model2->data, item1))) {if (virCPUx86DataItemMatch(item1, item2))continue;else if (!virCPUx86DataItemMatchMasked(item1, item2))match = SUBSET;}/* 如果result為初始值,將其賦值為SUPERSET */if (result == EQUAL)result = match;/* 如果model1中存在model2中沒有的元素* match肯定被賦值為SUBSET,兩者不相同返回不相關 */else if (result != match)return UNRELATED;}/* 如果程序走到這里,model2集合必然大于等于model1* 判斷具體的兩種情況:等于或者大于 */while ((item2 = virCPUx86DataNext(&iter2))) {virCPUx86CompareResult match = SUBSET;if ((item1 = virCPUx86DataGet(&model1->data, item2))) {if (virCPUx86DataItemMatch(item2, item1))continue;else if (!virCPUx86DataItemMatchMasked(item2, item1))match = SUPERSET;}if (result == EQUAL)result = match;else if (result != match)return UNRELATED;}

    總結

    以上是生活随笔為你收集整理的Libvirt CPU Feature的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。