编写Udev规则文件开机设置网卡SR-IOV
在虚拟化场景下,SR-IOV
(Single Root I/O Virtualization
)是一个很常用的功能,通过SR-IOV,一个物理的设备(Physical Function
),可以派生出很多虚拟设备(Virtual Function
),这些虚拟设备具有简单的PCIe功能。以网卡为例,通过SR-IOV
,我们可以将一块网卡,虚拟化成很多块网卡,这些虚拟出来的网卡,有自己独立的PCIe地址,中断,配置空间等,这些虚拟出来的网卡,可以作为单独的PCIe设备被attach到虚拟机中,实现网络功能,当然,场景并不局限于VM。
这里先不关注SR-IOV
的应用场景,或者其实现原理,而是关心一个简单的问题:如何设置SR-IOV
,并且能稳定的实现开机启动时就设置好呢?
这里以网卡为例,假设要开启SR-IOV
的网卡名字为eth0
,文档里会提示你echo 7 > /sys/class/net/eth0/device/sriov_numvfs
。在系统启动完成之后,这么做肯定是没有问题。问题是,在系统启动阶段,这么做是不是可以呢?红帽的文档第 8 章 配置 SR-IOV 网络通过rc.local
来实现:
# chmod +x /etc/rc.d/rc.local
# echo "echo 7 > /sys/class/net/enp4s0f1/device/sriov_numvfs" >> /etc/rc.local
但是呢,文档里也提示了:
注意
因为额外的 systemd,Red Hat Enterprise Linux 将会并行启动服务,而不是依次启动它们。这意味着,rc.local 在引导过程中被执行的位置是不固定的。因此,一些不可预见的情况可能会出现,我们不推荐使用这个方法。
也就是说,因为systemd
的并行特性,可能这么做不一定能获得预期的结果。
同样是这篇文档,还提供了驱动options的方式:
[root@compute ~]# echo "options igb max_vfs=7" >>/etc/modprobe.d/igb.conf
然而不巧的是,并不是所有的网卡驱动都支持这个option,在例子里使用的igb
驱动,这个驱动对应的主要是Intel的千兆网卡芯片比如I350
,而我们使用的是X700
系列的网卡,加载的驱动是i40e
,这个驱动并没有对应的max_vfs
参数,只提供了一个debug
参数:
# modinfo i40e
filename: /lib/modules/3.10.0-957.21.3.el7.x86_64/updates/drivers/net/ethernet/intel/i40e/i40e.ko
version: 2.8.43
license: GPL
description: Intel(R) 40-10 Gigabit Ethernet Connection Network Driver
author: Intel Corporation, <e1000-devel@lists.sourceforge.net>
retpoline: Y
rhelversion: 7.6
srcversion: F8774F317E76D8D6B699043
...
depends: ptp
vermagic: 3.10.0-957.21.3.el7.x86_64 SMP mod_unload modversions
parm: debug:Debug level (0=none,...,16=all) (int)
那怎么办呢?经历了一番折腾,算是找到了一个比较稳定的办法,利用Udev,通过编写Udev Rule文件,实现开机自动打开SR-IOV
功能,这么做还额外带来另一个好处,还记得之前写的文章使用udev重命名网卡么,这里顺便也把网卡名字改成统一的了,既实现了SR-IOV
的开机配置,又统一了网卡名字,一举两得!
既然是Udev,那就需要一些匹配规则了,针对网卡来说,最稳定的匹配规则之一,就是网卡的Mac地址,所以,可以确定的是,肯定会根据网卡Mac地址去匹配:
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:11:22:33:44:01", NAME="eth0"
然后呢,这里仅仅是设置了网卡名字,SR-IOV
怎么开呢?我们先进到网卡的目录看一眼:
[root@compute ~]# ls /sys/class/net/eth0/
addr_assign_type carrier dev_port gro_flush_timeout link_mode phys_port_id proto_down subsystem
address carrier_changes dormant ifalias mtu phys_port_name queues tx_queue_len
addr_len device duplex ifindex netdev_group phys_switch_id speed type
broadcast dev_id flags iflink operstate power statistics uevent
可以这么理解:规则里的ATTR{address}
,相当于/sys/class/net/eth0/address
文件,那么也就是说ATTR所代表的目录就是/sys/class/net/eth0/
,那/sys/class/net/eth0/device/sriov_numvfs
这个配置文件不就是ATTR{device/sriov_numvfs}
么?是这样么?是的,没错,那在规则文件里,使用=
号赋值就行了吧:
SUBSYSTEM=="net", ACTION=="add", DRIVERS=="?*", ATTR{address}=="00:11:22:33:44:01", ATTR{device/sriov_numvfs}="7", NAME="eth0"
是不是真的可以呢?可以测试一下,把上面的内容保存到/etc/udev/rules.d/70-persistent-net.rules
:执行udevadm test /sys/class/net/eth0
,从一大堆输出里找到:
[root@compute ~]# udevadm test /sys/class/net/eth0
...
ATTR '/sys/devices/pci0000:17/0000:17:02.0/0000:19:00.1/net/eth0/device/sriov_numvfs' writing '7' 70-persistent-net.rules:1
NAME 'eth0' /etc/udev/rules.d/70-persistent-net.rules:1
...
确实可以!重启试试,依然能工作。问题解决。
当然,这自然不是唯一的办法,目前呢,还能想到的一个办法,就是写个Oneshot的Systemd Service,在network-pre.target这个阶段执行,这样可以比较稳定的确定执行的时间,不至于像rc.local
那样不知道什么时候被执行了。
最后感慨一下:Udev真的是个强大的工具!