网卡插口绑定驱动
在我的虚拟机中,有如下网路插口:
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
这儿第一个网路插口为virtiowps for linux,后三个都是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是解绑驱动过程中会使用到的文件。具体的绑定与解绑的过程就是向这几个文件中写入规定格式的数据完成的。
linuxkernel源码目录中的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列表中的硬件。
写入这个文件的格式中,VendorId与DeviceId数组是必须的,其它的数组可以不指定。
成功添加一个设备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网卡对应的VendorId与DeviceId在上述列表中,在驱动初始化的时侯添加到了系统中,这样我们就可以绑定82574L网卡到e1000e驱动上。
先写入数据到new_id添加设备id之后进行绑定
为了成功绑定插口到igb_uio上linux 网卡 驱动,我们首先须要在igb_uio中添加支持的设备linux 下载工具,这个可以通过写入数据到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("x x" % (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到dpdkdrivers(比如igb_uio)中,之后写入bind文件。
这样确保了首先有注册的设备id,有了这个设备id总线才才能match到驱动执行probe操作。没有注册的设备idlinux 网卡 驱动,pci总线不会匹配到指定的驱动,也未能将设备绑定到相应的驱动上。
echo“Vendoriddeviceid”>new_id的时侯会scan,用new_id中的设备id匹配系统中的插口,将未绑定到任何驱动上的插口绑定到对应的驱动上。
new_id的写入的参数中没有pci号,因而不能指定只绑定相同机型网卡的单个口到驱动中。除非其它口早已绑定到了其它驱动,不然这种口就会被绑定。
绑定失败的情况new_id没有添加,不会match到指定的驱动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。
文章评论