我們看linux kernel中virtio驅動相關代碼,會發現有很多相關文件。首先有virtio.c這種文件,其次還有virtio_pci.c,virtio_scsi.c等這些文件,還有virtio_net.c,virtio_blk.c,virtio_balloon.c等這些。那么這些文件是什么關系呢?其次里面很多還有各自probe函數,到底是如何調用的,例如以網絡的virtio_net到底是從哪里開始初始化的?要理清這些關系需要以linux設備驅動模型為背景展開討論。這篇文章,我們以linux kernel 3.10代碼為例,分析一下virtio的相關組織關系,以及設備初始化調用流程。
總線及驅動的注冊
linux設備驅動模型的核心有三個概念:設備(device),驅動(driver),總線(bus)。而如果我們把virtio的相關關系梳理清楚后,以網絡virtio_net為例映射到設備驅動模型,就得到了下圖。我們這個小節后面就以下圖為背景展開。
圖1
linux將virtio實現分離成兩部分:和物理總線標準相關的(如pci,scsi等),和物理總線標準無關的。
圖中左側部分即和物理總線相關的實現,這里以pci為了,當然virtio也支持其他總線類型,如scsi。virtio-pci是virtio對應pci的驅動實現,所以virtio-pci是一個pci總線上的一個驅動。它通過如下方式注冊到pci總線上去。
lvirtio總線的注冊
點擊(此處)折疊或打開
/*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,
};
staticintvirtio_init(void)
{
if(bus_register(&virtio_bus)!=0)
panic("virtio bus registration failed");
return 0;
}
core_initcall(virtio_init);
如代碼所示,這個總線的名字叫”virtio”,通過bus_register就將virtio總線注冊進系統,可以在sys文件系統中查看。
lvirtio-net驅動注冊
最后我們看我們經常接觸到設備驅動的初始化,我們以網絡驅動virtio_net為例,其對應的驅動為virtio-net。其注冊過程如下。
點擊(此處)折疊或打開
/*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)
intregister_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函數將驅動的bus設置為之前注冊的virtio總線,完成總線的注冊。這樣我們就能在sys文件系統對應virtio總線下的drivers目錄看到這個驅動了。
所以我們再回頭來看圖1,可以看到virtio設備是橫跨兩類總線及驅動的。
virtio設備的初始化流程
梳理清楚virtio相關設備,總線及驅動關系后我們看下virtio設備的初始化過程,我們還是以網絡virtio_net設備為例子。這個初始化過程如下圖2中的黃色部分所示。
圖2
首先是系統啟動kernel初始化階段,pci子系統調用pci_scan_device發現pci網卡設備,并初始化對應pci_dev結構,然后將去注冊到pci總線上(dev->dev.bus=&pci_bus_type)。同時設置device的vendor_id為0x1AF4(virtio的pci vendor_id),device_id為1
然后當我們加載virtio-pci驅動時,當調用module_pci_driver(virtio_pci_driver)將virtio-pci驅動注冊在pci總線上時,在linux設備驅動模型中,這會導致對pci總線設備鏈表上未被驅動綁定的每個設備調用pci總線的match回調函數,即pci_bus_match函數。原型如下:
static int pci_bus_match(struct device *dev, struct device_driver *drv)
pci_bus_match函數將linux設備驅動模型核心出入的device結構轉換為pci_dev結構,將device_driver結構轉換為pci_driver結構,之后調用pci_match_device函數判斷pci設備結構是否有匹配的pci設備ID結構。如果有則判斷設備的pci ID和驅動設置的id_table中是否一樣,如果一樣說明設備和驅動匹配(這里設備的vendor_id和virtio-pci的virtio_pci_id_table匹配),將struct device的driver指針指向驅動,然后調用pci總線的probe函數,即pci_deivce_probe函數。這個函數再次將struct device強制轉換成struct pci_dev,將設置在設備中的driver結構強制轉換為struct pci_derver。它再次校驗這個驅動能否支持這個設備,遞增設備的引用計數,然后調用pci驅動probe函數(即virtio-pci的probe函數virtio_pci_probe),傳入它應該綁定到的struct pci_dev結構體指針。這就進入到了圖2中黃色部分的函數調用鏈了。
在開始梳理virtio_net初始化調用鏈前我們先看其對應的結構struct virtio_pci_device,將其展開得到圖3。
圖3
我們看到virtio_pci_device可以分為兩部分,一部分是和pci總線相關的設備對應struct pci_dev,另一部分是和virtio總線相關的設備對應structvirtio_device。
virtio_pci_probe函數主要負責完成pci_dev部分的初始化,已經virtio_device部分初始化,然后調用register_virtio_device函數。
register_virtio_device函數將virtio_device的設備總線設置為virtio總線,然后調用device_register將virtio_device對應的設備添加到virtio總線上。這個添加總線的動作,會觸發virtio總線的match函數即virtio_dev_match調用,同樣該函數會比較設備dev的pci id和驅動id (virtio net的devid為1),如果匹配則virtio bus的probe函數virtio_dev_probe將被調用。其中又會調用對應驅動的probe函數,即virtnet_probe。而virtnet_probe將會完成virtio net設備structvirtio_device剩余部分的初始化。
到此,virtio net的初始化流程就已經梳理清楚了。virtio net設備創建完成后也會分別出現在pci總線和virtio總線的drvices目錄下。
最后附上virtio的其他類型設備id:
點擊(此處)折疊或打開
#define VIRTIO_ID_NET1/*virtio net*/
#define VIRTIO_ID_BLOCK2/*virtio block*/
#define VIRTIO_ID_CONSOLE3/*virtio console*/
#define VIRTIO_ID_RNG4/*virtio rng*/
#define VIRTIO_ID_BALLOON5/*virtio balloon*/
#define VIRTIO_ID_RPMSG7/*virtio remote processor messaging*/
#define VIRTIO_ID_SCSI8/*virtio scsi*/
#define VIRTIO_ID_9P9/*9p virtio console*/
#define VIRTIO_ID_RPROC_SERIAL 11/*virtio remoteproc seriallink*/
#define VIRTIO_ID_CAIF 12/*Virtio caif*/
-
Linux
+關注
關注
87文章
11232瀏覽量
208957 -
總線
+關注
關注
10文章
2869瀏覽量
87995 -
設備驅動
+關注
關注
0文章
68瀏覽量
10879
發布評論請先 登錄
相關推薦
評論