網卡接口綁定驅動

網卡接口綁定驅動

在我的虛擬機中,有如下網絡接口:

longyu@virt-debian10:~$ lspci | grep 'Eth'
01:00.0 Ethernet controller: Red Hat, Inc Virtio network device (rev 01)
04:00.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection
08:00.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection
09:00.0 Ethernet controller: Intel Corporation 82574L Gigabit Network Connection

這裏第一個網絡接口爲 virtio,後三個都是 e1000e 虛擬網卡 82574L。

在dpdk 的使用過程中我們常常需要將網卡綁定到不同的驅動上,這一般是通過 dpdk_nic_bind.py 腳本來完成的,這個腳本具體的用法我不在這裏贅述,感興趣的讀者可以研究研究。

注意這裏的 01:00.0、04:00.0、08:00.0、09:00.0 表示的是網絡接口對應的 pci 號,這些 pci 號唯一表示一個接口,在綁定驅動與解綁驅動時會使用到。

綁定網卡接口驅動的具體過程

網卡接口綁定主要與 bind 與 new_id 兩個特殊的文件有關。在我的系統上,我搜索 /sys 下的名爲 bind 的文件,搜索到了不同驅動的 bind 文件,截取部分信息如下:

longyu@virt-debian10:~$ sudo find /sys -name 'bind'
/sys/devices/virtual/vtconsole/vtcon0/bind
/sys/devices/virtual/vtconsole/vtcon1/bind
/sys/bus/serio/drivers/serio_raw/bind
/sys/bus/serio/drivers/atkbd/bind
/sys/bus/pci/drivers/shpchp/bind
/sys/bus/pci/drivers/agpgart-sis/bind
/sys/bus/pci/drivers/e1000e/bind

這裏我以 e1000e 驅動爲例,看看 /sys/bus/pci/dirvers 目錄下有那些東東。

執行 ls 命令查看 /sys/bus/pci/drivers 目錄的內容,輸出如下:

longyu@virt-debian10:~$ ls /sys/bus/pci/drivers/e1000e
0000:04:00.0  0000:08:00.0  0000:09:00.0  bind  module  new_id  remove_id  uevent  unbind

這裏的 0000:04:00.0、0000:08:00.0、0000:09:00.0 表示綁定到 e1000e 驅動上的 pci 接口的 pci 號。

bind 與 new_id 是綁定驅動過程中會使用到的文件,unbind 是解綁驅動過程中會使用到的文件。具體的綁定與解綁的過程就是向這幾個文件中寫入規定格式的數據完成的。

linux kernel 源碼目錄中的 ABI/testing/sysfs-bus-pci 對這幾個文件的描述信息如下:

1. /sys/bus/pci/drivers/…/bind

		Writing a device location to this file will cause
		the driver to attempt to bind to the device found at
		this location.	This is useful for overriding default
		bindings.  The format for the location is: DDDD:BB:DD.F.
		That is Domain:Bus:Device.Function and is the same as
		found in /sys/bus/pci/devices/.  For example:
		# echo 0000:00:19.0 > /sys/bus/pci/drivers/foo/bind
		(Note: kernels before 2.6.28 may require echo -n).

這裏寫入的 0000:00:19.0 就是上面我們提到過的 pci 號。對 bind 文件寫入每一個接口的 pci 號意味着我們可以將一個網卡上的不同口綁定到不同的驅動上。

2. /sys/bus/pci/drivers/…/unbind

		Writing a device location to this file will cause the
		driver to attempt to unbind from the device found at
		this location.	This may be useful when overriding default
		bindings.  The format for the location is: DDDD:BB:DD.F.
		That is Domain:Bus:Device.Function and is the same as
		found in /sys/bus/pci/devices/. For example:
		# echo 0000:00:19.0 > /sys/bus/pci/drivers/foo/unbind
		(Note: kernels before 2.6.28 may require echo -n).

這裏向 unbind 文件寫入接口的 pci 號就會解除當前綁定的驅動。一個接口可以不綁定到任何驅動上面,不過我們常常不會這樣去做。

3. /sys/bus/pci/drivers/…/new_id

		Writing a device ID to this file will attempt to
		dynamically add a new device ID to a PCI device driver.
		This may allow the driver to support more hardware than
		was included in the driver's static device ID support
		table at compile time.  The format for the device ID is:
		VVVV DDDD SVVV SDDD CCCC MMMM PPPP.  That is Vendor ID,
		Device ID, Subsystem Vendor ID, Subsystem Device ID,
		Class, Class Mask, and Private Driver Data.  The Vendor ID
		and Device ID fields are required, the rest are optional.
		Upon successfully adding an ID, the driver will probe
		for the device and attempt to bind to it.  For example:
		# echo "8086 10f5" > /sys/bus/pci/drivers/foo/new_id

向 new_id 中寫入設備 id,將會動態的在 pci 設備驅動中添加一個新的設備 id。這種功能允許驅動添加更多的硬件而非僅有在編譯時包含到驅動中的靜態支持設備 ID 列表中的硬件。

寫入這個文件的格式中,Vendor Id 與 Device Id 字段是必須的,其它的字段可以不指定。

成功添加一個設備 ID 時,驅動會嘗試 probe 系統中匹配到的設備並嘗試綁定到它之上。

4. /sys/bus/pci/drivers/…/remove_id

		Writing a device ID to this file will remove an ID
		that was dynamically added via the new_id sysfs entry.
		The format for the device ID is:
		VVVV DDDD SVVV SDDD CCCC MMMM.	That is Vendor ID, Device
		ID, Subsystem Vendor ID, Subsystem Device ID, Class,
		and Class Mask.  The Vendor ID and Device ID fields are
		required, the rest are optional.  After successfully
		removing an ID, the driver will no longer support the
		device.  This is useful to ensure auto probing won't
		match the driver to the device.  For example:
		# echo "8086 10f5" > /sys/bus/pci/drivers/foo/remove_id

remove_id 中寫入的格式與 new_id 的寫入格式相同。寫入 remove_id 可以用來確保內核不會自動 probe 匹配到這個驅動的設備。

dpdk 綁定、解綁網卡接口時的一些問題

dpdk 中最常使用的驅動是 igb_uio,我們經常需要將網卡接口綁定到 igb_uio 上。我們必須瞭解的是 igb_uio 驅動並沒有添加任何的靜態設備 id 列表,這表明初始狀態它是不支持任何設備的。

igb_uio 驅動與 pci 驅動類似,在其源碼中可以找到如下 pci_driver 結構體。

608 static struct pci_driver igbuio_pci_driver = {
609     .name = "igb_uio",
610     .id_table = NULL,
611     .probe = igbuio_pci_probe,
612     .remove = igbuio_pci_remove,
613 };

這裏 id_table 設置爲 NULL 表示驅動中沒有靜態添加任何支持的設備 id 列表,這意味着加載了 igb_uio 驅動後我們不能直接寫入 bind 文件綁定驅動。

爲了更清楚的說明這個 id_table,我是用 e1000e 驅動中的相關數據結構進行對比。

下面是 e1000e 驅動中 netdev.c 中定義的 pci_driver 結構體的內容:

7556 /* PCI Device API Driver */
7557 static struct pci_driver e1000_driver = {
7558     .name     = e1000e_driver_name,
7559     .id_table = e1000_pci_tbl,
7560     .probe    = e1000_probe,
7561     .remove   = e1000_remove,
7562     .driver   = {
7563         .pm = &e1000_pm_ops,
7564     },
7565     .shutdown = e1000_shutdown,
7566     .err_handler = &e1000_err_handler
7567 };

這裏的 id_table 與 igb_uio 不同,它指向了 e1000_pci_tbl 這個數組。e1000_pci_tbl 數組的部分內容截取如下:

static const struct pci_device_id e1000_pci_tbl[] = {
	{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_COPPER), board_82571 },
	{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_FIBER), board_82571 },
	{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER), board_82571 },
	{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82571EB_QUAD_COPPER_LP),
	  board_82571 },
	........
	{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82572EI), board_82572 },
	{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82572EI_COPPER), board_82572 },
	{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82572EI_FIBER), board_82572 },
	{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82572EI_SERDES), board_82572 },

	{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82573E), board_82573 },
	{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82573E_IAMT), board_82573 },
	{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82573L), board_82573 },

	{ PCI_VDEVICE(INTEL, E1000_DEV_ID_82574L), board_82574 },

我們看到在 e1000_pci_tbl 中有很多型號的網卡設備。82574L 也是其中的一款。82574L 網卡對應的 Vendor Id 與 Device Id 在上述列表中,在驅動初始化的時候添加到了系統中,這樣我們就可以綁定 82574L 網卡到 e1000e 驅動上。

先寫入數據到 new_id 添加設備 id 然後進行綁定

爲了成功綁定接口到 igb_uio 上,我們首先需要在 igb_uio 中添加支持的設備,這個可以通過寫入數據到 new_id 添加設備 id 後寫入 bind 文件來完成。注意同一個設備 id 可以寫入多次到 new_id 中,要移除也需要寫入相同的次數到 remove_id 中。注意寫入到 remove_id 並不會解除綁定。

dpdk-17.04 中 dpdk-devbind.py 腳本中相關的代碼如下:

    if driver in dpdk_drivers:
        filename = "/sys/bus/pci/drivers/%s/new_id" % driver
        try:
            f = open(filename, "w")
        except:
            print("Error: bind failed for %s - Cannot open %s"
                  % (dev_id, filename))
            return
        try:
            f.write("%04x %04x" % (dev["Vendor"], dev["Device"]))
            f.close()
        except:
            print("Error: bind failed for %s - Cannot write new PCI ID to "
                  "driver %s" % (dev_id, driver))
            return

    # do the bind by writing to /sys
    filename = "/sys/bus/pci/drivers/%s/bind" % driver
    try:
        f = open(filename, "a")
    except:
        print("Error: bind failed for %s - Cannot open %s"
              % (dev_id, filename))
        if saved_driver is not None:  # restore any previous driver
            bind_one(dev_id, saved_driver, force)
        return
    try:
        f.write(dev_id)
        f.close()

上述代碼首先寫入 new_id 中添加設備 id 到 dpdk drivers(例如 igb_uio)中,然後寫入 bind 文件。

這樣確保了首先有註冊的設備 id,有了這個設備 id 總線才能夠 match 到驅動執行 probe 操作。沒有註冊的設備 id,pci 總線不會匹配到指定的驅動,也無法將設備綁定到相應的驅動上。

echo “Vendor id device id” > new_id 的時候會 scan,用 new_id 中的設備 id 匹配系統中的接口,將未綁定到任何驅動上的接口綁定到對應的驅動上。

new_id 的寫入的參數中沒有 pci 號,因此不能指定只綁定相同型號網卡的單個口到驅動中。除非其它口已經綁定到了其它驅動,不然這些口都會被綁定。

綁定失敗的情況

  1. new_id 沒有添加,不會 match 到指定的驅動
  2. probe 過程異常,綁定失敗
    這種情況可以通過查看 dmesg 信息來分析定位。

寫入 new_id 設備 id 觸發總線匹配驅動自動 probe 問題

上文中提到過當寫入設備 id 到 new_id 文件中會出觸發總線匹配系統中的接口,屬於寫入的設備 id 的設備並且沒有綁定到任何驅動上的接口將會全部會被綁定到 new_id 所屬的驅動。

例如系統中有兩個 82574L 網卡接口,都沒有綁定驅動,這時我們寫入 82574L 的設備 id 到 igb_uio 驅動對應的 new_id 文件中,會導致這兩個口都綁定到 igb_uio 上。

如果這種行爲對功能有所影響,那麼你可以選擇在綁定到 igb_uio 之前先將接口綁定到其它驅動上(一般是官方驅動),這樣在寫入 new_id 文件時,已經綁定到其它驅動的接口就會被 skip。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章