uio是如何与dpdk对接的(一)

本文介绍了如何加载设备驱动模块,特别是UIO如何与DPDK对接。在Linux内核中,通过pci_register_driver接口注册PCI驱动,如igbuio_pci_driver,该驱动在probe函数中接管网卡。在网卡驱动初始化时,会申请数据存储缓存并与网卡DMA对接。而在DPDK的UIO模块中,UIO获取网卡资源并保存,使得用户态驱动可以操作网卡寄存器,实现用户态接管网卡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

加载设备驱动模块
设备驱动模块被编译成.o或者.ko,类似elf程序有一个入口main函数,.ko模块入口函数是module_init(igbuio_pci_init_module),igbuio_pci_init_module( )在insmod后首先执行。代码片段如下:

static int __init
igbuio_pci_init_module(void)
{
    return pci_register_driver(&igbuio_pci_driver);
}

pci_register_driver是一个linux内核提供用来进行pci注册的标准接口,将igbuio_pci_driver 作为作为参数传入,这个结构在驱动程序里定义,作为驱动程序和PCI设联系的纽带。

static struct pci_driver igbuio_pci_driver = {
    .name = "igb_uio",
    .id_table = NULL,    // uio设备驱动加载后不主动接管任何设备
    .probe = igbuio_pci_probe,
    .remove = igbuio_pci_remove,
};

.name:驱动名
.id_table:网卡id,驱动注册后接管的网卡id
.probe:注册处理函数
.remove:注销处理函数

常规网卡驱动,在open dev时候在申请数据存储的缓存(即数据包缓存队列),挂在adapter结构上,adapter结构是各个驱动的私有数据,为了统一管理约定位置紧挨着net_dev结构尾部,例如e1000网卡驱动的adapter:

struct e1000_adapter *adapter = netdev_priv(netdev);

/**
*    netdev_priv - access network device private data
*    @dev: network device
*
* Get network device private data
*/
static inline void *netdev_priv(const struct net_device *dev)
{
    return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN);
}

adapter(即privata是不同网卡驱动的差异体现的地方)
缓存队列申请之后,需要和网卡dma对接,网卡才知道收到网络数据后DMA将数据考到哪里。申请对接代码:

static inline void *
dma_alloc_coherent(struct device *dev, size_t size, dma_addr_t *dma_handle,
        gfp_t gfp)
{
    struct dma_map_ops *ops = get_dma_ops(dev);
    void *memory;

    gfp &= ~(__GFP_DMA | __GFP_HIGHMEM | __GFP_DMA32);

  /* dma_handle:TSAD寄存器地址 */
    if (dma_alloc_from_coherent(dev, size, dma_handle, &memory))  
        return memory;

    if (!dev)
        dev = &x86_dma_fallback_dev;

    if (!is_device_dma_capable(dev))
        return NULL;

    if (!ops->alloc_coherent)
        return NULL;

  /* 申请内存获取虚拟地址以及物理地址,填充网卡数据发送地址寄存器TSAD */
    memory = ops->alloc_coherent(dev, size, dma_handle,
                     dma_alloc_coherent_gfp_flags(dev, gfp));
    debug_dma_alloc_coherent(dev, size, *dma_handle, memory);

    return memory;
}

dma访问的是物理地址,它是怎么知道这块内存的物理地址呢?
1. 网卡数据发送地址寄存器 TSAD ,含义是”这个寄存器里的地址,是网卡缓存数据拷贝的对端地址”。BOIS在给PCI设备的寄存器分配了网卡寄存器的基地址,写进设备的配置空间。操作系统初始化时,为每个PCI分配了pci_dev结构,并把BIOS获取的并写到了配置空间的地址读出来写到resource字段中;
2. 网卡驱动在open设备的时候申请缓冲区内存ring,得到这块内存的虚拟地址(用于内核访问)和物理地址(用于DMA访问),然后将这个物理地址写到TSAD中。

此时,这个网卡驱动就接管了这个网络设备(网卡)。
那么,dpdk何时接管网卡呢?

在dpdk的uio模块中,uio获取网卡资源(resource)并保存到uio_info中,为用户态驱动准备资源。

/* Remap pci resources described by bar #pci_bar in uio resource n. */
static int
igbuio_pci_setup_iomem(struct pci_dev *dev, struct uio_info *info,
               int n, int pci_bar, const char *name)
{
    unsigned long addr, len;
    void *internal_addr;

    if (n >= ARRAY_SIZE(info->mem))
        return -EINVAL;

    addr = pci_resource_start(dev, pci_bar);
    len = pci_resource_len(dev, pci_bar);
    if (addr == 0 || len == 0)
        return -1;
    internal_addr = ioremap(addr, len);
    if (internal_addr == NULL)
        return -1;
    info->mem[n].name = name;
    info->mem[n].addr = addr; /* 网卡资源起始地址 物理地址 */
    info->mem[n].internal_addr = internal_addr; /* 网卡资源起始地址,虚拟地址 */
    info->mem[n].size = len;
    info->mem[n].memtype = UIO_MEM_PHYS;
    return 0;
}

以上代码为用户态驱动准备好了网卡资源,用户态通过internal_addr即可操作网卡寄存器。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值