igb_uio 是 dpdk 内部实现的将网卡映射到用户态的内核模块,它是 uio 模块的一个实例。
igb_uio 是一种 pci 驱动,将网卡绑定到 igb_uio 隔离了网卡的内核驱动,同时 igb_uio 完成网卡中断内核态初始化并将中断信号映射到用户态。
igb_uio 与 uio 模块密切相关,我将从 uio 模块着手分析 igb_uio 模块的工作原理。
uio 模块分析
uio 是一种字符设备驱动,在此驱动中注册了单独的 file_operations 函数表,uio 设备可以看做是一种独立的设备类型。
file_operations 函数内容如下:
static const struct file_operations uio_fops = {
.owner = THIS_MODULE,
.open = uio_open,
.release = uio_release,
.read = uio_read,
.write = uio_write,
.mmap = uio_mmap,
.poll = uio_poll,
.fasync = uio_fasync,
.llseek = noop_llseek,
};
复制
该函树表在 uio_major_init 中初始化 cdev 结构体时使用,相关代码如下:
cdev->owner = THIS_MODULE;
cdev->ops = &uio_fops;
kobject_set_name(&cdev->kobj, "%s", name);
result = cdev_add(cdev, uio_dev, UIO_MAX_DEVICES);
复制
我们对 /dev/uioxx 文件的操作最终都会对应到对 uio_fops 的不同方法的调用上。
uio_info 结构体及其实例化过程
uio 模块中的 idev 变量是一个指向 struct uio_device 的指针,struct uio_device 中又包含 一个指向 struct uio_info 的指针,struct uio_info 结构体内容如下:
struct uio_info {
struct uio_device *uio_dev;
const char *name;
const char *version;
struct uio_mem mem[MAX_UIO_MAPS];
struct uio_port port[MAX_UIO_PORT_REGIONS];
long irq;
unsigned long irq_flags;
void *priv;
irqreturn_t (*handler)(int irq, struct uio_info *dev_info);
int (*mmap)(struct uio_info *info, struct vm_area_struct *vma);
int (*open)(struct uio_info *info, struct inode *inode);
int (*release)(struct uio_info *info, struct inode *inode);
int (*irqcontrol)(struct uio_info *info, s32 irq_on);
};
复制
每一个 uio 设备都会实例化一个 uio_info 结构体,uio 驱动自身不会实例化 uio_info 结构体,它只提供一个框架,可以在其它模块中调用 uio_register_device 来实例化 uio_info 结构体,在 dpdk 中,常见方式是在驱动绑定 igb_uio 的时候调用 uio_register_device 进行实例化。
igb_uio.c 中初始化当前设备 uio_info 结构过程
可以在 igb_uio.c 的 probe 函数 igbuio_pci_probe 中找到实例化的相关代码,摘录如下:
410 /* remap IO memory */
411 err = igbuio_setup_bars(dev, &udev->info);
.....................................................
428 /* fill uio infos */
429 udev->info.name = "igb_uio";
430 udev->info.version = "0.1";
431 udev->info.handler = igbuio_pci_irqhandler;
432 udev->info.irqcontrol = igbuio_pci_irqcontrol;
433 #ifdef CONFIG_XEN_DOM0
434 /* check if the driver run on Xen Dom0 */
435 if (xen_initial_domain())
436 udev->info.mmap = igbuio_dom0_pci_mmap;
437 #endif
438 udev->info.priv = udev;
...........................................................
478 /* register uio driver */
479 err = uio_register_device(&dev->dev, &udev->info);
复制
411 行调用 igbuio_setup_bars 映射 pci 设备 bar 中的内存区域,此函数代码如下:
332 static int
333 igbuio_setup_bars(struct pci_dev *dev, struct uio_info *info)
334 {
335 int i, iom, iop, ret;
336 unsigned long flags;
337 static const char *bar_names[PCI_STD_RESOURCE_END + 1] = {
338 "BAR0",
339 "BAR1",
340 "BAR2",
341 "BAR3",
342 "BAR4",
343 "BAR5",
344 };
345
346 iom = 0;
347 iop = 0;
348
349 for (i = 0; i < ARRAY_SIZE(bar_names); i++) {
350 if (pci_resource_len(dev, i) != 0 &&
351 pci_resource_start(dev, i) != 0) {
352 flags = pci_resource_flags(dev, i);
353 if (flags &