博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
dpdk uio驱动
阅读量:4204 次
发布时间:2019-05-26

本文共 6056 字,大约阅读时间需要 20 分钟。

关于dpdk的驱动层,一直以来都没有理的很清楚。一是因为本人不是开发驱动的,对驱动知识相当匮乏,二来用dpdk来开发,貌似也不需要过多关注底层驱动逻辑。但是这块不懂的话,总会感觉对dpdk一知半解的,不踏实。所以这篇博客就是通过查阅资料和阅读源码总结出来的,如有理解错误的地方还望各位指正。因为uio是对IO设备而言的,因此本博客中的设备指的是IO设备。

1.linux设备驱动uio机制

        大家都知道,linux操作系统分为两个层级,一个是内核态,一个是用户态。平时编写的软件都是运行在用户态。以一个简单的udp socket通信举例,client1准备向client2发送数据,用户态的程序首先要将数据准备好,然后需要将数据传给网卡设备。但是这个时候问题就来了,我们如何将数据传给网卡设备呢?写过socket的知道,要先创建一个socket句柄,然后bind绑定ip+port,最后再sendto发送数据。这里面的bind和sendto就是内核给我们提供是api,调用这些函数就进行了一次系统调用。系统调用可以简单的理解成一次软中断,内核收到这个中断之后,会执行相应的动作,最终会调用到网卡驱动提供的send等函数。最后将结果返回给用户态的进程。client2如果想接收到client1发送过来的数据,也要进行bind和recvfrom。当网卡收到数据后,产生一次中断,通知内核将网卡的数据转到用户态。这个过程中还包含了内核检查报文头部,判断这些数据是否为client2希望收到的数据。

       系统调用是很耗费cpu性能的,这也就是为什么I/O密集型任务并不适合传统linux架构的原因。幸运的是,一些前辈大牛搞出了uio机制,让驱动大部分功能运行在用户态,只有一小部分运行在内核态,比如中断等。值得注意的是,仅仅是uio驱动还不能实现网卡的收发包,因为这个驱动并没有提供网卡的配置函数和收发包函数。uio驱动的作用是让你在用户态就可以操作网卡设备的内存。dpdk同时还提供了pmd用户态驱动,用户态pmd驱动就是通过uio机制,通过操作网卡的寄存器实现在用户态收发报文。关于用户态pmd驱动,下一篇文章再叙述。

       下面贴一个几乎每个讲解uio机制的文章都会有的一张图:

       注册到uio驱动上的设备,uio驱动会在/sys/class/uio目录下生成相对应的文件夹来记录设备的一些信息,同时会在/dev目录下生成相对应的设备文件。这样用户态就可以通过/sys/class/uio/uio0/maps/map0和/sys/class/uio/uio0/portio/port0来访问这个uio设备。注意/sys/class/uio/uio0/maps/map0下的各个文件,addr文件对应的就是该设备内存的物理地址。后续开发对应网卡的驱动,会读取这个地址,然后通过offset文件的值,获取到设备寄存器的地址。关于这个地址是如何产生的,有兴趣的读者可以参考这篇文章。关于如何写一个简单的uio驱动,可以参考这篇文章。

2.dpdk igb_uio驱动的实现

dpdk自己实现了一个uio驱动,名称叫igb_uio驱动。源码路径在lib\librte_eal\linuxapp\igb_uio\igb_uio.c。

定义一个struct pci_deiver的结构体。

static struct pci_driver igbuio_pci_driver = {	.name = "igb_uio",	.id_table = NULL,	.probe = igbuio_pci_probe,	.remove = igbuio_pci_remove,};

简单的说明下struct pci_deiver这个结构体。在linux系统中,每个pci驱动都有一个pci_driver实例,用以描述驱动名称,支持的设备信息,以及对应的操作函数;

/*    描述一个pci设备,每个pci驱动必须创建一个pci_driver实例*/struct pci_driver {    struct list_head node;    const char *name;  /* 驱动程序名,内核中所有pci驱动程序名都是唯一的 */        const struct pci_device_id *id_table;    /* must be non-NULL for probe to be called  pci设备配置信息数组 */        int  (*probe)  (struct pci_dev *dev, const struct pci_device_id *id);    /* New device inserted  设备插入内核时调用 */        void (*remove) (struct pci_dev *dev);    /* Device removed (NULL if not a hot-plug capable driver)  设备从内核移除时调用 */*/        int  (*suspend) (struct pci_dev *dev, pm_message_t state);    /* Device suspended */    int  (*suspend_late) (struct pci_dev *dev, pm_message_t state);    int  (*resume_early) (struct pci_dev *dev);       int  (*resume) (struct pci_dev *dev);                    /* Device woken up */        void (*shutdown) (struct pci_dev *dev);    int (*sriov_configure) (struct pci_dev *dev, int num_vfs); /* PF pdev */        const struct pci_error_handlers *err_handler;    struct device_driver    driver;    struct pci_dynids dynids;};

在内核中注册一个pci驱动,要调用内核提供的api:pci_register_driver,入参就是struct pci_driver。当有设备绑定到这个pci驱动且设备的struct pci_device_id在id_table里的时候,内核就会调用probe函数。 igb_uio驱动中,id_table设置为空,所以当我们在内核中加载igb_uio.ko的时候,并不会调用probe函数。只有在我们运行dpdk提供的dpdk-devbind.py脚本绑定网卡的时候,probe函数才会被调用。

static int __initigbuio_pci_init_module(void){	int ret;	ret = igbuio_config_intr_mode(intr_mode);	if (ret < 0)		return ret;	return pci_register_driver(&igbuio_pci_driver);}static void __exitigbuio_pci_exit_module(void){	pci_unregister_driver(&igbuio_pci_driver);}module_init(igbuio_pci_init_module);module_exit(igbuio_pci_exit_module);

module_init是注册模块初始化函数,模块加载时会执行这个函数。pci_register_driver就是向内核注册一个pci驱动,名称是struct pci_driver指定的,这里为igb_uio,probe函数为igbuio_pci_probe。

我们来关注下igbuio_pci_probe这个函数。

static int __devinit#elsestatic int#endifigbuio_pci_probe(struct pci_dev *dev, const struct pci_device_id *id){	struct rte_uio_pci_dev *udev;	dma_addr_t map_dma_addr;	void *map_addr;	int err;	udev = kzalloc(sizeof(struct rte_uio_pci_dev), GFP_KERNEL);	if (!udev)		return -ENOMEM;	mutex_init(&udev->lock);	/*	 * enable device: ask low-level code to enable I/O and	 * memory	 */	err = pci_enable_device(dev);  //开启设备,	if (err != 0) {		dev_err(&dev->dev, "Cannot enable PCI device\n");		goto fail_free;	}	/* enable bus mastering on the device */	pci_set_master(dev);	/* remap IO memory */	err = igbuio_setup_bars(dev, &udev->info);  /* 在这个函数中,会对设备进行一系列配置,最终的结果就是访问设备寄存器不需要持有寄存器的地址,而直接访问内存地址就可以了 */	if (err != 0)		goto fail_release_iomem;	/* set 64-bit DMA mask */	err = pci_set_dma_mask(dev,  DMA_BIT_MASK(64));	if (err != 0) {		dev_err(&dev->dev, "Cannot set DMA mask\n");		goto fail_release_iomem;	}	err = pci_set_consistent_dma_mask(dev, DMA_BIT_MASK(64));	if (err != 0) {		dev_err(&dev->dev, "Cannot set consistent DMA mask\n");		goto fail_release_iomem;	}	/* fill uio infos */	udev->info.name = "igb_uio";	udev->info.version = "0.1";	udev->info.irqcontrol = igbuio_pci_irqcontrol;	udev->info.open = igbuio_pci_open;	udev->info.release = igbuio_pci_release;	udev->info.priv = udev;	udev->pdev = dev;	err = sysfs_create_group(&dev->dev.kobj, &dev_attr_grp);	if (err != 0)		goto fail_release_iomem;	/* register uio driver 应该是源码注释写错了,这里注册的是uio设备而非驱动*/	err = uio_register_device(&dev->dev, &udev->info);    /* 注册完成之后,可以发现在/dev目录下出现了uioX,在/sys/class/uio目录下出现了uioX文件夹。             * 用户态进程可以通过读取/sys/class/uio/uioX/maps/map0目录下的文件来操作设备*/	if (err != 0)		goto fail_remove_group;	pci_set_drvdata(dev, udev);	/*	 * Doing a harmless dma mapping for attaching the device to	 * the iommu identity mapping if kernel boots with iommu=pt.	 * Note this is not a problem if no IOMMU at all.	 */	map_addr = dma_alloc_coherent(&dev->dev, 1024, &map_dma_addr,			GFP_KERNEL);	if (map_addr)		memset(map_addr, 0, 1024);	if (!map_addr)		dev_info(&dev->dev, "dma mapping failed\n");	else {		dev_info(&dev->dev, "mapping 1K dma=%#llx host=%p\n",			 (unsigned long long)map_dma_addr, map_addr);		dma_free_coherent(&dev->dev, 1024, map_addr, map_dma_addr);		dev_info(&dev->dev, "unmapping 1K dma=%#llx host=%p\n",			 (unsigned long long)map_dma_addr, map_addr);	}	return 0;fail_remove_group:	sysfs_remove_group(&dev->dev.kobj, &dev_attr_grp);fail_release_iomem:	igbuio_pci_release_iomem(&udev->info);	pci_disable_device(dev);fail_free:	kfree(udev);	return err;}

        函数执行完成之后,设备就被绑定到igb_uio驱动上了,后续就是通过pmd驱动来对网卡进行配置、报文读取等。

3.关于外设的物理地址

        有关dpdk uio机制其实已经解读完成,心中还有一个疑惑,就是关于如何理解外设的物理地址。这篇文章做了一个比较详细的说明,在此mark一下,学习学习~

转载地址:http://jyali.baihongyu.com/

你可能感兴趣的文章
性能测试一般过程与LR性能测试过程
查看>>
Software Security Testing软件安全测试
查看>>
SQL注入漏洞全接触--进阶篇
查看>>
SQL注入漏洞全接触--高级篇
查看>>
SQL注入法攻击一日通
查看>>
菜鸟入门级:SQL注入攻击
查看>>
用vbs来写sql注入等80端口的攻击脚本
查看>>
C# 检查字符串,防SQL注入攻击
查看>>
关于对SQL注入80004005 及其它错误消息分析
查看>>
即时通软件性能测试(与宴宾的对话)
查看>>
应用软件性能测试的艺术(翻译)——序
查看>>
高级性能测试(翻译)
查看>>
Web安全测试解决方案
查看>>
今天开始上班
查看>>
开源测试研究方案泡汤了
查看>>
晒一下我培训的课程——应用系统性能测试规划、实施与分析
查看>>
利用 STAF 实现程序更新包的自动部署测试
查看>>
周末参加“北京干部管理职业技术学院”关于高职课程改革的专家讨论会
查看>>
软件自动化测试框架的发展
查看>>
实现haproxy+LNMT负载均衡架构
查看>>