Rex_Zhang

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理


1.1为什么出现了UIO?
   硬件设备可以根据功能分为网络设备,块设备,字符设备,或者根据与CPU相连的方式分为PCI设备,USB设备等。它们被不同的内核子系统支持。这些标准的设备的驱动编写较为容易而且容易维护。很容易加入主内核源码树。但是,又有很多设备难以划分到这些子系统中,比如I/O卡,现场总线接口或者定制的FPGA。通常这些非标准设备的驱动被实现为字符驱动。这些驱动使用了很多内核内部函数和宏。而这些内部函数和宏是变化的。这样驱动的编写者必须编写一个完全的内核驱动,而且一直维护这些代码。而且这些驱动进不了主内核源码。于是就出现了用户空间I/O框架(Userspace I/O framework)。

1.2 dpdk的uio使用

在基于kernel的IO模型中,所有的设备IO都要经过内核处理,在高并发的网络数据包收发的情况下,大量硬件中断会降低内核数据包处理能力,内核和用户空间的数据拷贝也会造成大量的计算资源浪费。所以,作为高并发大流量网络开发框架的DPDK,必须要找到一个能够避免内核中断爆炸和大量数据拷贝的方法,在用户空间能够直接和硬件进行交互。Linux的UIO就是这样一个将硬件操作映射到用户空间的kernel bypass方案。

下图展示了UIO驱动的内核部分,用户空间部分,和UIO框架以及内核的关系:

1.3 dpdk的网口绑定干了啥?

绑定操作:

./dpdk-devbind.py -b igb_uio 0000:00:07.0

既然是设备驱动程序一定会创建设备,/dev下查看:多出了uio0设备;

vm65:/dev # ll |grep uio
crw-------  1 root root    246,   0 Feb 18 07:41 uio0
vm65:/dev #

查看/sys/class/uio/uio:

vm65:/sys/class/uio/uio0 # ll
total 0
-r--r--r-- 1 root root 4096 Feb 18 06:42 dev
lrwxrwxrwx 1 root root    0 Feb 18 06:42 device -> ../../../0000:00:07.0
-r--r--r-- 1 root root 4096 Feb 18 06:42 event
drwxr-xr-x 4 root root    0 Feb 18 06:42 maps
-r--r--r-- 1 root root 4096 Feb 18 06:42 name
drwxr-xr-x 2 root root    0 Feb 18 06:42 power
lrwxrwxrwx 1 root root    0 Feb 18 06:42 subsystem -> ../../../../../class/uio
-rw-r--r-- 1 root root 4096 Feb 18 06:41 uevent
-r--r--r-- 1 root root 4096 Feb 18 06:42 version

同时,查看dmesg,多出了以下几行信息:

<6>[171282.211083] igb_uio 0000:00:07.0: mapping 1K dma=0x123fb9b000 host=ffff88123fb9b000
<6>[171282.211088] igb_uio 0000:00:07.0: unmapping 1K dma=0x123fb9b000 host=ffff88123fb9b000

可以看到。绑定过程中的kernel part会通过uio_register_device 注册对应的uio设备,获取pcie的配置空间BAR信息包括addr、name、size、offset填充到map/map0中,这些在用户态会将其读出,并mmap至用户态进程空间,这样用户态便可直接操作设备的内存空间。

除了内存管理,第二个任务,对于设备中断的应答必须在内核空间进行。所以在内核空间有一小部分代码用来应答和禁止中断,中断的其他任务由用户态完成;

假设用户空间要等待一个设备中断,它仅仅须要简单的堵塞在对 /dev/uioX的read()操作上。 当设备产生中断时,read()操作马上返回。

UIO 也实现了poll()系统调用。你能够使用 select()来等待中断的发生。select()有一个超时參数能够用来实现有限时间内等待中断。

 

1.4 用户态驱动

常用的用户态驱动开发有两种方式可选:

1、打开/dev/mem 后,使用MMAP映射出芯片物理地址对应的虚拟地址,在用户态访问虚拟地址加偏移就能访问到芯片的寄存器;

2、采用UIO方式,在内核态映射地址后,在用户态通过打开/dev/uioxxx 方式,再使用MMAP映射一次,就可采用跟方式1 相同的方式访问芯片reg或是memory;

这两种方式各有优缺点:

方式1:优点是实现简单,如果不需要用到中断,可以不用管内核态的内容,关注用户态代码就好。缺点是代码运行的进程要有root权限,不然无法访问/dev/mem,如果要使用中断还是会涉及到内核态的驱动;

方式2:优点是无需要root权限,中断可以直接在用户态获取。缺点是必须要在内核态做初始化;即需要在内核态通过uio_register_device注册对应的uio设备,这样user space就可以通过/sys/class/uio/uioX/mapsX来访问这个uio设备;

关键结构体&函数:

注册uio设备前需要初始化对应的uio_info结构体:

struct uio_info {  
    struct uio_device   *uio_dev; // 在__uio_register_device中初始化 
    const char      *name; // 调用__uio_register_device之前必须初始化 
    const char      *version; //调用__uio_register_device之前必须初始化 
    struct uio_mem      mem[MAX_UIO_MAPS]; 
    struct uio_port     port[MAX_UIO_PORT_REGIONS]; 
    long            irq; //分配给uio设备的中断号,调用__uio_register_device之前必须初始化 
    unsigned long       irq_flags;// 调用__uio_register_device之前必须初始化 
    void            *priv; // 
    irqreturn_t (*handler)(int irq, struct uio_info *dev_info); //uio_interrupt中调用,用于中断处理 
                                                                                                   // 调用__uio_register_device之前必须初始化 
    int (*mmap)(struct uio_info *info, struct vm_area_struct *vma); //在uio_mmap中被调用, 
                                                                                                                // 执行设备打开特定操作 
    int (*open)(struct uio_info *info, struct inode *inode);//在uio_open中被调用,执行设备打开特定操作 
    int (*release)(struct uio_info *info, struct inode *inode);//在uio_device中被调用,执行设备打开特定操作 
    int (*irqcontrol)(struct uio_info *info, s32 irq_on);//在uio_write方法中被调用,执行用户驱动的 
                                                                                       //特定操作。 
}; 

设备注册函数:

#define uio_register_device(parent, info) __uio_register_device(THIS_MODULE, parent, info)
int __uio_register_device(struct module *owner,
              struct device *parent,
              struct uio_info *info)

用户态驱动示例:

https://blog.csdn.net/ganggexiongqi/article/details/6751798

https://blog.csdn.net/ganggexiongqi/article/details/6748103

posted on 2019-02-19 09:27  Rex_Zhang  阅读(3411)  评论(0编辑  收藏  举报