认识DPDK的UIO驱动(一)

news/2024/11/18 0:31:45/

用户态驱动程序UIO

UIO(Userspace I/O)是运行在用户空间的I/O技术。Linux系统中一般的驱动设备都是运行在内核空间,而在用户空间用应用程序调用即可。

UIO的内核部分和用户空间的工作

内核空间

UIO的少量运行在内核空间的驱动所做的工作有哪些呢?

(1)分配和记录设备需要的资源和注册uio设备

在设备的探测函数中:

-使能PCI 设备

-申请资源

-读取并记录配置信息

-注册uio设备// uio_register_device()

// uio_8139d_pci_probe & uio_8139d_handler

(2)必须在内核空间实现的小部分中断应答函数

用户空间的关键操作

(1)关键操作

(2)响应硬件中断

有什么优势?

1. 用户空间驱动程序的优点

  1. 可以和整个C库链接。
  2. 在驱动中可以使用浮点数,在某些特殊的硬件中,可能需要使用浮点数,而linux内核并不提供浮点数的支持。如果能在用户态实现驱动,就可以轻松解决这一问题。
  3. 驱动问题不会导致整个系统挂起。内核态驱动的一些错误常常导致整个系统挂起。
  4. 用户态的驱动调试方便。
  5. 可以给出封闭源码的驱动程序,不必采用GPL,更为灵活。

源码简单分析

与其他内核PCI模块开发一样代码结构,UIO驱动实现部分:

关键数据结构:

   //dpdk定义的uio pci设备描述结构
struct rte_uio_pci_dev {struct uio_info info; //uio 通用结构struct pci_dev *pdev;  //pci设备描述结构enum rte_intr_mode mode; //中断模式
};
struct uio_info {struct uio_device    *uio_dev; //uio设备属于const char        *name; //名称const char        *version; //版本号struct uio_mem        mem[MAX_UIO_MAPS];//可映射的内存区域列表,size == 0表示列表结束struct uio_port        port[MAX_UIO_PORT_REGIONS]; //网口区域列表long            irq; //UIO_IRQ_CUSTOM 中断号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); //中断控制操作 关闭/打开 当向/dev/uioX中写入值时
};
关键处理函数:
static int __init
igbuio_pci_init_module(void)
{int ret;ret = igbuio_config_intr_mode(intr_mode); //内核insmod时带的参数,中断模式if (ret < 0)return ret;return pci_register_driver(&igbuio_pci_driver);//注册PCI设备,实际调用pci_module_init。
}
关键的pci驱动操作函数,主要是探测和删除
static struct pci_driver igbuio_pci_driver = {.name = "igb_uio", //名称.id_table = NULL,.probe = igbuio_pci_probe, //探测回调函数.remove = igbuio_pci_remove,//删除回调函数
};
关键看下igbuio_pci_probe:
//根据内核版本不同,返回类型不同
#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0)
static int __devinit
#else
static int
#endif
igbuio_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{struct rte_uio_pci_dev *udev;struct msix_entry msix_entry;int err;//分配内核空间内存,rte_uio_pci_dev一个设备类型大小udev = kzalloc(sizeof(struct rte_uio_pci_dev), GFP_KERNEL);if (!udev)return -ENOMEM;/** 使能设备: 调用更底层的PCI代码使能设备的内存和I/O区域*/err = pci_enable_device(dev);if (err != 0) {dev_err(&dev->dev, "Cannot enable PCI device\n");goto fail_free;}/*预留PCI设备的i/o或内存区域,pci_request_regions这个函数封装了一些PCI驱动相关的内存操作,不深入理解;*/err = pci_request_regions(dev, "igb_uio");if (err != 0) {dev_err(&dev->dev, "Cannot request regions\n");goto fail_disable;}/* 将设备设置层DMA总线主模式 */pci_set_master(dev);/* 重新映射I/O内存,同样详细的封装不做具体理解 */err = igbuio_setup_bars(dev, &udev->info);if (err != 0)goto fail_release_iomem;/* 设定 64-bit DMA mask 若函数返回成功,可以在位于该函数所带参数范围内的任意地址进行DMA操作。*/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;}/* 填充uio信息 */udev->info.name = "igb_uio";udev->info.version = "0.1";udev->info.handler = igbuio_pci_irqhandler;udev->info.irqcontrol = igbuio_pci_irqcontrol;
#ifdef CONFIG_XEN_DOM0/* check if the driver run on Xen Dom0 */if (xen_initial_domain())udev->info.mmap = igbuio_dom0_pci_mmap;
#endifudev->info.priv = udev;udev->pdev = dev;switch (igbuio_intr_mode_preferred) {case RTE_INTR_MODE_MSIX:/* Only 1 msi-x vector needed */msix_entry.entry = 0;if (pci_enable_msix(dev, &msix_entry, 1) == 0) {dev_dbg(&dev->dev, "using MSI-X");udev->info.irq = msix_entry.vector;udev->mode = RTE_INTR_MODE_MSIX;break;}/* fall back to INTX */case RTE_INTR_MODE_LEGACY:if (pci_intx_mask_supported(dev)) {dev_dbg(&dev->dev, "using INTX");udev->info.irq_flags = IRQF_SHARED;udev->info.irq = dev->irq;udev->mode = RTE_INTR_MODE_LEGACY;break;}dev_notice(&dev->dev, "PCI INTX mask not supported\n");/* fall back to no IRQ */case RTE_INTR_MODE_NONE:udev->mode = RTE_INTR_MODE_NONE;udev->info.irq = 0;break;default:dev_err(&dev->dev, "invalid IRQ mode %u",igbuio_intr_mode_preferred);err = -EINVAL;goto fail_release_iomem;}//用特定属性创建sysfs节点组err = sysfs_create_group(&dev->dev.kobj, &dev_attr_grp);if (err != 0)goto fail_release_iomem;/* 注册uio设备 */err = uio_register_device(&dev->dev, &udev->info);if (err != 0)goto fail_remove_group;pci_set_drvdata(dev, udev);dev_info(&dev->dev, "uio device registered with irq %lx\n",udev->info.irq);return 0;
fail_remove_group:sysfs_remove_group(&dev->dev.kobj, &dev_attr_grp);
fail_release_iomem:igbuio_pci_release_iomem(&udev->info);if (udev->mode == RTE_INTR_MODE_MSIX)pci_disable_msix(udev->pdev);pci_release_regions(dev);
fail_disable:pci_disable_device(dev);
fail_free:kfree(udev);return err;
}

DPDK应用层实现

网卡驱动模型一般包含三层,即PCI总线设备、网卡设备以及网卡设备的私有数据结构,即将设备的共性一层层的抽象,PCI总线设备包含网卡设备,网卡设备又包含其私有数据结构。在DPDK中,首先会注册设备驱动,然后查找当前系统有哪些PCI设备,并通过PCI_ID为PCI设备找到对应的驱动,最后调用驱动初始化设备。

一、网卡驱动注册

以e1000网卡驱动为例说明。

在1.8.0版本中,网卡驱动的注册使用了一种奇技淫巧的方法,使用GCC attribute扩展属性的constructor属性,使得网卡驱动的注册在程序MAIN函数之前就执行了。

   staticstruct rte_driver pmd_igb_drv ={.type =PMD_PDEV,.init =rte_igb_pmd_init,};staticstruct rte_driver pmd_igbvf_drv ={.type =PMD_PDEV,.init =rte_igbvf_pmd_init,};PMD_REGISTER_DRIVER(pmd_igb_drv);PMD_REGISTER_DRIVER(pmd_igbvf_drv);

其中PMD_REGISTER_DRIVER()宏的定义如下:

   #define PMD_REGISTER_DRIVER(d)\void devinitfn_ ##d(void);\void __attribute__((constructor, used)) devinitfn_ ##d(void)\{\rte_eal_driver_register(&d);\}

使用attribute的constructor属性,在MAIN函数执行前,就执行rte_eal_driver_register()函数,将pmd_igb_drv驱动挂到全局dev_driver_list链表上。

原文链接:https://blog.csdn.net/pangyemeng/article/details/78457599

更多DPDK学习资料有需要的可以自行添加进入学习交流君 羊 793599096 免费获取,或自行报名学习,免费订阅,永久学习,关注我持续更新哦!!!

学习地址:http://ke.qq.com/course/5066203?flowToken=1043717


http://www.ppmy.cn/news/146649.html

相关文章

三相电机智能栅极驱动芯片DRV83053与DRV8343S找不同

2021-08-11 DRV8343SPHPRQ1 DRV83053PHP 写在前头&#xff0c;8343s这款芯片现在是真tm的难找&#xff0c;从上到下遍历了淘宝二十几家店加上微信的几个老板&#xff0c;一家一家问价&#xff0c;只有一家回复可以买到样片&#xff0c;订货&#xff0c;七天货期。还有一家让我…

GC8837 DFN8 12V直流电机驱动芯片 完美替代TI DRV8837

GC8837是一款12V直流电机驱动芯片&#xff0c;为摄像机、消费类产品、玩具和其他低压或者电池供电的运动控制类应用提供了集成的电机驱动解决方案。芯片一般用了驱动一个直流电机或者使用两颗来驱动步进电机。   GC8837可以工作在0~12V的电源电压上&#xff0c;能提供高达1.5…

DC-DC升压芯片MP9185

一、芯片特性 MP9185是一款固定频率600kHz&#xff0c;宽输入范围&#xff0c;高度集成的升压转换器。 MP9185从低至2.7V的输入电压开始&#xff0c;通过集成低R DS&#xff08;ON&#xff09;功率MOSFET的1节电池支持高达30W的负载功率。 MP9185采用恒定关断时间&#xff…

remark #8291: Recommended relationship between field width ‘W‘ and the number of fractional digits ‘

remark #8291: Recommended relationship between field width ‘W’ and the number of fractional digits ‘D’ in this edit descriptor is ‘W>D7’. fortran .f 程序文件编译问题 remark #8291: Recommended relationship between field width ‘W’ and the number …

RZ7889 是一款DC 双向马达驱动电路IC

产品说明 电路简述 RZ7889 是一款DC 双向马达驱动电路&#xff0c;它适用于玩具等类的电机驱动、自动阀门电机驱动、电磁门锁驱动等。它有两个逻辑输入端子用来控制电机前进、 后退及制动。该电路具有良好的抗干扰性&#xff0c;微小的待机电流、低的输出内阻&#xff0c;同时&…

图解网卡硬件

网络中最基础的部件是什么?不是交换机也不是路由器,而是小小的不起眼但又无处不在的网卡。如果在5年前,或许网卡与您无关,但在如今这网络的时代,无论是上网冲浪还是联网玩游戏,都离不开网卡,更何况,就算您不食人间烟火,多数主板上也会为您集成一块板载网卡。所以,对于…

8139d网卡工作原理 [不断更新]

工欲善其事&#xff0c;必先利其器。想学习8139网卡驱动程序&#xff0c;但是连它怎么工作的都不知道看代码看的我头都大了&#xff01;&#xff01;&#xff01;特地看了不少文档总结出如下几点&#xff0c;便于学习&#xff01; 8139d网卡发送一个数据包的过程中&#xff1a;…

神奇的开关 — 可控硅

带大家认识下可控硅&#xff0c;以及可控硅的工作原理 ...... by 矜辰所致目录 前言一、什么是可控硅1.1 基本概念1.2 可控硅的结构1.2.1 单向可控硅1.2.2 双向可控硅 二、可控硅的工作原理2.1 可控硅是如何工作的导通条件维持条件关断条件 2.2 工作原理 三、可控硅的参数四、…