淺談linux設(shè)備中virtio組織關(guān)系
當(dāng)我們查看Linux kernel.C的Virtio代碼時,我們會發(fā)現(xiàn)許多相關(guān)的文件。那么,verdio,我們只有Virtio\UC',Virtio\ubk。C、 美德\ubalon。C、 等等。與這些文件相關(guān)聯(lián)的是什么?其次,有許多相應(yīng)的程序功能。這個電話怎么說是真的,比如說通過互聯(lián)網(wǎng)。從哪里開始?為了澄清這些關(guān)系,必須考慮模式Linux.DiesIn本文分析了linux-kernel-3.10中病毒組織與設(shè)備初始化的關(guān)系。
總線及驅(qū)動的注冊
linux設(shè)備驅(qū)動模型的核心有三個概念:設(shè)備(device),驅(qū)動(driver),總線(bus)。而如果我們把virtio的相關(guān)關(guān)系梳理清楚后,以網(wǎng)絡(luò)virtio_net為例映射到設(shè)備驅(qū)動模型,就得到了下圖。我們這個小節(jié)后面就以下圖為背景展開。
圖1
linux將virtio實(shí)現(xiàn)分離成兩部分:和物理總線標(biāo)準(zhǔn)相關(guān)的(如pci,scsi等),和物理總線標(biāo)準(zhǔn)無關(guān)的。
圖中左側(cè)部分即和物理總線相關(guān)的實(shí)現(xiàn),這里以pci為了,當(dāng)然virtio也支持其他總線類型,如scsi。virtio-pci是virtio對應(yīng)pci的驅(qū)動實(shí)現(xiàn),所以virtio-pci是一個pci總線上的一個驅(qū)動。它通過如下方式注冊到pci總線上去。
l??virtio總線的注冊
點(diǎn)擊(此處)折疊或打開
/*virtio.c*/
static struct bus_type virtio_bus?=?{
.name?=?"virtio",
.match?=?virtio_dev_match,
.dev_attrs?=?virtio_dev_attrs,
.uevent?=?virtio_uevent,
.probe?=?virtio_dev_probe,
.remove?=?virtio_dev_remove,
};
static?int?virtio_init(void)
{
if?(bus_register(&virtio_bus)?!=?0)
panic("virtio bus registration failed");
return 0;
}
core_initcall(virtio_init);
如代碼所示,這個總線的名字叫”virtio”,通過bus_register就將virtio總線注冊進(jìn)系統(tǒng),可以在sys文件系統(tǒng)中查看。
l??virtio-net驅(qū)動注冊
最后我們看我們經(jīng)常接觸到設(shè)備驅(qū)動的初始化,我們以網(wǎng)絡(luò)驅(qū)動virtio_net為例,其對應(yīng)的驅(qū)動為virtio-net。其注冊過程如下。
點(diǎn)擊(此處)折疊或打開
/*?virtio-net.c?*/
static struct virtio_device_id id_table[]?=?{
{?VIRTIO_ID_NET,?VIRTIO_DEV_ANY_ID?},
{?0?},
};
static struct virtio_driver virtio_net_driver?=?{
.feature_table?=?features,
.feature_table_size?=?ARRAY_SIZE(features),
.driver.name?=????KBUILD_MODNAME,
.driver.owner?=????THIS_MODULE,
.id_table?=????id_table,
.probe?=????virtnet_probe,
.remove?=????virtnet_remove,
.config_changed?=?virtnet_config_changed,
#ifdef CONFIG_PM
.freeze?=????virtnet_freeze,
.restore?=????virtnet_restore,
#endif
};
module_virtio_driver(virtio_net_driver);
#define module_virtio_driver(__virtio_driver)?\
module_driver(__virtio_driver,?register_virtio_driver,?\
unregister_virtio_driver)
int?register_virtio_driver(struct virtio_driver?*driver)
{
/*?Catch this early.?*/
BUG_ON(driver->feature_table_size?&&?!driver->feature_table);
driver->driver.bus?=?&virtio_bus;
return driver_register(&driver->driver);
}
最終通過register_virtio_driver函數(shù)將驅(qū)動的bus設(shè)置為之前注冊的virtio總線,完成總線的注冊。這樣我們就能在sys文件系統(tǒng)對應(yīng)virtio總線下的drivers目錄看到這個驅(qū)動了。
所以我們再回頭來看圖1,可以看到virtio設(shè)備是橫跨兩類總線及驅(qū)動的。
virtio設(shè)備的初始化流程
梳理清楚virtio相關(guān)設(shè)備,總線及驅(qū)動關(guān)系后我們看下virtio設(shè)備的初始化過程,我們還是以網(wǎng)絡(luò)virtio_net設(shè)備為例子。這個初始化過程如下圖2中的黃色部分所示。
圖2
首先是系統(tǒng)啟動kernel初始化階段,pci子系統(tǒng)調(diào)用pci_scan_device發(fā)現(xiàn)pci網(wǎng)卡設(shè)備,并初始化對應(yīng)pci_dev結(jié)構(gòu),然后將去注冊到pci總線上(dev->dev.bus=&pci_bus_type)。同時設(shè)置device的vendor_id為0x1AF4(virtio的pci vendor_id),device_id為1
然后當(dāng)我們加載virtio-pci驅(qū)動時,當(dāng)調(diào)用module_pci_driver(virtio_pci_driver)將virtio-pci驅(qū)動注冊在pci總線上時,在linux設(shè)備驅(qū)動模型中,這會導(dǎo)致對pci總線設(shè)備鏈表上未被驅(qū)動綁定的每個設(shè)備調(diào)用pci總線的match回調(diào)函數(shù),即pci_bus_match函數(shù)。原型如下:
static int pci_bus_match(struct device *dev, struct device_driver *drv)
pci_bus_match函數(shù)將linux設(shè)備驅(qū)動模型核心出入的device結(jié)構(gòu)轉(zhuǎn)換為pci_dev結(jié)構(gòu),將device_driver結(jié)構(gòu)轉(zhuǎn)換為pci_driver結(jié)構(gòu),之后調(diào)用pci_match_device函數(shù)判斷pci設(shè)備結(jié)構(gòu)是否有匹配的pci設(shè)備ID結(jié)構(gòu)。如果有則判斷設(shè)備的pci ID和驅(qū)動設(shè)置的id_table中是否一樣,如果一樣說明設(shè)備和驅(qū)動匹配(這里設(shè)備的vendor_id和virtio-pci的virtio_pci_id_table匹配),將struct device的driver指針指向驅(qū)動,然后調(diào)用pci總線的probe函數(shù),即pci_deivce_probe函數(shù)。這個函數(shù)再次將struct device強(qiáng)制轉(zhuǎn)換成struct pci_dev,將設(shè)置在設(shè)備中的driver結(jié)構(gòu)強(qiáng)制轉(zhuǎn)換為struct pci_derver。它再次校驗(yàn)這個驅(qū)動能否支持這個設(shè)備,遞增設(shè)備的引用計(jì)數(shù),然后調(diào)用pci驅(qū)動probe函數(shù)(即virtio-pci的probe函數(shù)virtio_pci_probe),傳入它應(yīng)該綁定到的struct pci_dev結(jié)構(gòu)體指針。這就進(jìn)入到了圖2中黃色部分的函數(shù)調(diào)用鏈了。
在開始梳理virtio_net初始化調(diào)用鏈前我們先看其對應(yīng)的結(jié)構(gòu)struct virtio_pci_device,將其展開得到圖3。
圖3
我們看到virtio_pci_device可以分為兩部分,一部分是和pci總線相關(guān)的設(shè)備對應(yīng)struct pci_dev,另一部分是和virtio總線相關(guān)的設(shè)備對應(yīng)struct?virtio_device。
virtio_pci_probe函數(shù)主要負(fù)責(zé)完成pci_dev部分的初始化,已經(jīng)virtio_device部分初始化,然后調(diào)用register_virtio_device函數(shù)。
register_virtio_device函數(shù)將virtio_device的設(shè)備總線設(shè)置為virtio總線,然后調(diào)用device_register將virtio_device對應(yīng)的設(shè)備添加到virtio總線上。這個添加總線的動作,會觸發(fā)virtio總線的match函數(shù)即virtio_dev_match調(diào)用,同樣該函數(shù)會比較設(shè)備dev的pci id和驅(qū)動id (virtio net的devid為1),如果匹配則virtio bus的probe函數(shù)virtio_dev_probe將被調(diào)用。其中又會調(diào)用對應(yīng)驅(qū)動的probe函數(shù),即virtnet_probe。而virtnet_probe將會完成virtio net設(shè)備struct?virtio_device剩余部分的初始化。
到此,virtio net的初始化流程就已經(jīng)梳理清楚了。virtio net設(shè)備創(chuàng)建完成后也會分別出現(xiàn)在pci總線和virtio總線的drvices目錄下。
最后附上virtio的其他類型設(shè)備id:
點(diǎn)擊(此處)折疊或打開
#define VIRTIO_ID_NET????????1?/*?virtio net?*/
#define VIRTIO_ID_BLOCK????????2?/*?virtio block?*/
#define VIRTIO_ID_CONSOLE????3?/*?virtio console?*/
#define VIRTIO_ID_RNG????????4?/*?virtio rng?*/
#define VIRTIO_ID_BALLOON????5?/*?virtio balloon?*/
#define VIRTIO_ID_RPMSG????????7?/*?virtio remote processor messaging?*/
#define VIRTIO_ID_SCSI????????8?/*?virtio scsi?*/
#define VIRTIO_ID_9P????????9?/*?9p virtio console?*/
#define VIRTIO_ID_RPROC_SERIAL 11?/*?virtio remoteproc serial?link?*/
355定義virtio u id u caif-12/*virtio caif*/