如何多次加载同一个网卡驱动

今天在跟同事看一个偶现的问题时,需要用更高版本的 linux 内核驱动进行测试。编译了一个高版本的内核驱动后发现出问题的内核驱动引用计数异常,导致内核模块不能正常卸载,rmmod -f 也无法卸载。

按照我的经验可能只剩下了重启,但是我们看的这个问题有一定的偶然性,一旦重启可能又需要过很长时间才能出现。

我们需要一种不重启也能加载新版本内核驱动的方法,我一开始觉得可能需要写个外部模块修改出问题的内核驱动的引用计数,但是仔细想了想这种方式可能实现不了。那难道只能重启机器了吗?

多次加载(同一)驱动

下班回家的路上,我想到了驱动加载的过程,之前我有系统的研究过,知道每个内核驱动由驱动名称唯一标识,突然灵机一动,想到或许改个名字重新编译一下应该就可以加载了,相当于让驱动改头换面,既换汤也换药。

不过考虑到一些驱动可能会用 EXPORT_SYMBOL、EXPORT_SYMBOL_GPL 来导出符号,这样如果已经加载了一个驱动,再次加载就会报符号重复的错误,对于这种情况,可能只有将这些 EXPORT 的符号名也修改下了。

回家后我使用虚拟机尝试多次加载 e1000e 网卡驱动,我对驱动源码进行了如下修改:

1. 修改 Makefile 中的模块名称

# SPDX-License-Identifier: GPL-2.0
# Copyright(c) 1999 - 2018 Intel Corporation.

#
# Makefile for the Intel(R) PRO/1000 ethernet driver
#

#obj-$(CONFIG_E1000E) += e1000e.o
obj-m += e1000e_1.o
e1000e_1-objs := 82571.o ich8lan.o 80003es2lan.o \
	       mac.o manage.o nvm.o phy.o \
	       param.o ethtool.o netdev.o ptp.o

上面的修改将 e1000e 修改为 e1000e_1。

2. 修改 pci 驱动注册中的驱动名称

char e1000e_driver_name[] = "e1000e_1";

这个名称在 pci_driver 结构体中被使用,相关的代码如下:

./netdev.c-7557-static struct pci_driver e1000_driver = {
./netdev.c:7558:	.name     = e1000e_driver_name,
./netdev.c-7559-	.id_table = e1000_pci_tbl,
./netdev.c-7560-	.probe    = e1000_probe,
./netdev.c-7561-	.remove   = e1000_remove,
./netdev.c-7562-	.driver   = {
./netdev.c-7563-		.pm = &e1000_pm_ops,
./netdev.c-7564-	},
./netdev.c-7565-	.shutdown = e1000_shutdown,
./netdev.c-7566-	.err_handler = &e1000_err_handler

如果不修改这个名字,当加载时注册 pci 驱动时系统会根据名称查找系统中 pci 总线驱动链表,找到已经存在的项目后会报 pci 驱动已经注册的错误。

与这个逻辑相关的代码如下:

int driver_register(struct device_driver *drv)
{
	int ret;
	struct device_driver *other;

	if (!drv->bus->p) {
		pr_err("Driver '%s' was unable to register with bus_type '%s' because the bus was not initialized.\n",
			   drv->name, drv->bus->name);
		return -EINVAL;
	}

	if ((drv->bus->probe && drv->probe) ||
	    (drv->bus->remove && drv->remove) ||
	    (drv->bus->shutdown && drv->shutdown))
		printk(KERN_WARNING "Driver '%s' needs updating - please use "
			"bus_type methods\n", drv->name);

	other = driver_find(drv->name, drv->bus);
	if (other) {
		printk(KERN_ERR "Error: Driver '%s' is already registered, "
			"aborting...\n", drv->name);
		return -EBUSY;
	}
...................

driver_register 在网卡驱动的 init 函数中被调用以注册驱动,driver_register 中会以驱动名称在总线中已经注册的驱动链表中查找,如果找到一个已经存在的驱动则会终止注册,如果没有找到则继续执行注册过程并将驱动最终添加到总线注册驱动链表中。

修改了以上两点后重新编译,后生成一个 e1000e_1.ko 的驱动,可以成功加载,也可以成功绑定驱动。

加载后 lsmod 查看 e1000e 驱动信息如下:

longyu@virt-debian10:~/linux-source-4.19/drivers/net/ethernet/intel/e1000e$ lsmod | grep e1000e
e1000e_1              282624  0
e1000e                282624  0

使用 dpdk 中提供的 dpdk-devbind.py 脚本绑定虚拟网卡到 e1000e_1 中,能够绑定成功,绑定成功后执行 dpdk-devbind.py -s 有如下输出信息:

longyu@virt-debian10:~/dpdk-stable-17.02.1/usertools$ python dpdk-devbind.py -s

Network devices using DPDK-compatible driver
============================================
<none>

Network devices using kernel driver
===================================
0000:01:00.0 'Virtio network device' if=enp1s0 drv=virtio-pci unused=virtio_pci *Active*
0000:04:00.0 '82574L Gigabit Network Connection' if=enp4s0 drv=e1000e unused= 
0000:08:00.0 '82574L Gigabit Network Connection' if=enp8s0 drv=e1000e unused= 
0000:09:00.0 '82574L Gigabit Network Connection' if=enp9s0 drv=e1000e_1 unused=e1000e *Active*

可以看到 09:00.0 绑定到了 e1000e_1 驱动,其它的两个 e1000e 的网口绑定到了 e1000e 驱动。

配置 ip 后能够看到接口正常收发包,ifconfig 查看接口信息得到了如下输出:

longyu@virt-debian10:~/linux-source-4.19/drivers/net/ethernet/intel/e1000e$ sudo ifconfig enp9s0
enp9s0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.122.30  netmask 255.255.255.0  broadcast 192.168.122.255
        inet6 fe80::5054:ff:fe08:3fef  prefixlen 64  scopeid 0x20<link>
        ether 52:54:00:08:3f:ef  txqueuelen 1000  (Ethernet)
        RX packets 63777  bytes 6480718 (6.1 MiB)
        RX errors 554  dropped 0  overruns 0  frame 554
        TX packets 29  bytes 27028 (26.3 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
        device interrupt 23  memory 0xfc240000-fc260000  

可以看出 tx 与 rx 都有数据,表明收发包正常。

总结

上面这种方法对于网卡驱动这种不会 export 任何符号的模块来说是可行的,对于那些需要 export 符号的驱动,需要修改符号的名称以避免重复导出符号的错误。

同时需要注意的是这里由于使用的是 pci 驱动,我们可以单独绑定某个 pci 设备接口到指定的驱动上,对于其它类型的驱动,这种方式就不适用了。

DriverStudio 3.2是迄今为止最完备的驱动开发工具套件,它所提供的工具覆盖了 驱动开发的方方面面。DriverStudio套件中的所有工具都与Visual Studio IDE环 境集成在一起。开发人员可以在Visual Studio .NET 2002和2003环境中编写并测 试驱动程序,并且通过使用Microsoft C++编译器与Microsoft DDK完全保持兼容。 在这个版本中,所有工具的功能都得到了加强并且让开发人员可以更容易的开发优 质的驱动程序,这个版本的DriverStudio包括下列工具: DriverWorks DriverNetworks Visual SoftICE SoftICE BoundsChecker Driver Edition TrueTime Driver Edition TrueCoverage Driver Edition DriverWorks和DriverNetworks是DriverStudio的两个主要框架工具,可以提高开发 驱动程序的速度。DriverWorks简化了开发NT和WDM驱动程序的工作;DriverNetworks 则帮助开发人员毫不费力的创建和定制网络驱动程序。目前的版本可以让驱动程序 开发人员来回切换开发工具而无需改变开发环境。 DriverWorks和DriverNetworks还都提供了全新的DriverWizard,可以用C语言来创建 Windows设备驱动程序。另外,DriverWorks和DriverNetworks还为Visual Studio .NET 提供了全新的菜单和工具条,让开发人员可以使用Windows DDK编译器和链接器来build 驱动程序。 DriverWorks的类库和实例现在已经支持开发虚拟COM端口的驱动程序了。 Visual SoftICE是一个双机的系统级调试器,提供了多窗口,可配置的图形化用户界面 来帮助开发人员调试核心级的驱动程序,应用程序甚至于整个操作系统。 Visual SoftICE的最新版本在性能,功能以及用户界面上都有很大提高,其他新特性还 包括: 完全支持AMD 64位的Opteron和Athlon64处理器所提供的10个新命令: SYMLINK, DEVMGR, DP, TDIR, TMKDIR, TMOVE, TRENAME, TRMDIR, TRMFILE和TVOL。 SoftICE支持单机,用串行线连接的双机或是通过TCP/IP连接的远程计算机上的核心 级和用户级的调试。在DriverStudio 3.1版本中,SoftICE可以: 支持VMware 4.0 在"DriverStudio Config"中提供了一个新的反编译选项页 符号加载器提供了改进后的新界面 BoundsChecker Driver Edition是一个错误侦测工具,可以实施分析和侦测设备驱动 程序的错误。开发人员可以马上发现并修正这些错误,不用像以前那样要在数小时后 才能完成。新增的改进还有: 开发人员修改程序设置之后无需重启计算机 让开发人员可以记录驱动程序中的user-written函数。开发人员可以清楚地看到开发 中驱动程序在运行时的情况 可以查看DriverWorkbench内置的事件页汇总信息,包括记录了多少错误和泄漏问题 提供SoftICE事件命令的新开关参数,用来标示BoundsChecker当前监视的驱动程序 改进的自旋锁(spinlock)错误侦测 TrueTime Driver Edition可以用来改善驱动程序的性能问题,它可以准确地指出导致 速度变慢的代码和性能瓶颈。在DriverStudio 3.1版本中,用户可以将两个或多个测试 数据文件合并为一个,用来比较不同条件下改变代码所带来的性能提高情况。新增的改 进还有: 从系统性能计数器中收集数据 全新的"Custom Chart"为所有的函数,IRP和性能数据提供了集成可定制的显示方式 在文件系统可用之前即可开始收集性能数据 显示的数据可以导出为以逗号分割的文本文件,以备后续处理 TrueCoverage Driver Edition是一个代码辅助工具,它可以在没有源代码和符号文件的 情况下收集代码的辅助信息。驱动开发人员可以以图形化的方式查看代码结构,另外: 同时支持NMS和PDB符号文件 可以获取以下辅助数据:每个线程的基本信息,符合条件的辅助信息和部分执行的代码
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值