用户态驱动程序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

认识DPDK的UIO驱动(一)相关推荐

  1. DPDK uio驱动实现(二十)

    一.dpdk uio驱动框架 在系统加载igb_uio驱动后,每当有网卡和igb_uio驱动进行绑定时, 就会在/dev目录下创建一个uio设备,例如/dev/uio1.uio设备是一个接口层,用于将 ...

  2. dpdk报文收发流程--理解dma控制器、UIO驱动、描述符空间、mbuf空间、KNI

    1. dpdk报文收发流程 1.1 报文接收流程 传统方式接收报文时,当网卡接收到报文后会产生硬件中断,进而报文会通过协议栈,最后到达应用层,这个过程需要内核协议栈的处理. 和传统报文接收不同,当应用 ...

  3. dpdk 18 ixgbe驱动初始化分析

    rte_log_set_global_level rte_log_set_global_level(uint32_t level) pci bus注册 TE_REGISTER_BUS(pci, rte ...

  4. DPDK网卡PMD驱动

    以/home/user/dpdk-stable-18.11.11/drivers/net/i40e目录下的驱动为例 源代码文件有 # ls base i40e_ethdev_vf.c i40e_log ...

  5. DPDK(Data Plane Development Kit)快速处理数据包 开发平台及接口 简介

    一.网络IO的处境和趋势 从我们用户的使用就可以感受到网速一直在提升,而网络技术的发展也从1GE/10GE/25GE/40GE/100GE的演变,从中可以得出单机的网络IO能力必须跟上时代的发展. 1 ...

  6. 初学者也能看懂的DPDK解析

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由Willko发表于云+社区专栏 一.网络IO的处境和趋势 从我们用户的使用就可以感受到网速一直在提升,而网络技术的发展也从1GE/10 ...

  7. linux报文高速捕获技术对比--napi/libpcap/afpacket/pfring/dpdk/xdp

    1. 传统linux网络协议栈流程和性能分析 Linux网络协议栈是处理网络数据包的典型系统,它包含了从物理层直到应用层的全过程. 数据包到达网卡设备. 网卡设备依据配置进行DMA操作.(第1次拷贝: ...

  8. Linux网络报文捕获/抓包技术对比:napi、libpcap、afpacket、PF_RING、PACKET_MMAP、DPDK、XDP(eXpress Data Path)

    Table of Contents 1.传统linux网络协议栈流程和性能分析 协议栈的主要问题 针对单个数据包级别的资源分配和释放 流量的串行访问 从驱动到用户态的数据拷贝 内核到用户空间的上下文切 ...

  9. 绝对干货!初学者也能看懂的DPDK解析

    欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由Willko发表于云+社区专栏 一.网络IO的处境和趋势 从我们用户的使用就可以感受到网速一直在提升,而网络技术的发展也从1GE/10 ...

最新文章

  1. 复杂场景下的复杂缺陷检测方法--深度学习算法综述
  2. python语言使用什么语句实现上下文管理协议_Python 技巧探究:上下文管理器和with语句...
  3. 4.1.4 文件的物理结构(上下)
  4. hbase shell-dml(数据管理指令)
  5. windows上安装mysql5.7.24
  6. BeetleX之Websocket服务使用
  7. 完美解决html中select的option不能隐藏的问题。
  8. 谷粒商城:02. 数据库初始化以及SQL语句
  9. 全国地级市坐标、名称、编码获取 / 全球城市坐标位置
  10. MySQL数据库索引教程(超详细)
  11. 16进制颜色码对照表
  12. 分享9个加快houdini渲染的技巧,快来学习一下
  13. 【观察】星环科技“七剑下天山”,做数据世界超高速引擎
  14. 家庭花卉养殖技巧及技术收集
  15. Java动态导出word文档内容及图片
  16. (笔记)yolov5自适应anchors
  17. JAVA实现页面上传图片或文件
  18. Sentinel-1A卫星数据下载
  19. Linux中ssh登录跳过RSA key fingerprint输入yes/no
  20. ubuntu12.10安装NCL问题

热门文章

  1. AppTransition
  2. 超级电容器-为你的设计提供一个峰值电源或二次电源
  3. mobl:针对移动Web开发的DSL
  4. 苹果签名服务商必看!苹果企业签名去哪里找客户?
  5. 对偶理论和灵敏度分析---线性规划的对偶理论
  6. 又一个加密PHP脚本的解码方法
  7. 千兆/万兆单向传输网卡
  8. SSC 扩频时钟技术(4):基于systemverilog语言实现ssc扩频时钟模型设计
  9. 《惢客创业日记》2019.05.09(周四)给美女让座的大叔
  10. 短视频平台盈利模式深度解析