• dpdk PMD


    PMD是Poll Mode Driver的缩写,即基于用户态的轮询机制的驱动

    在不考虑vfio的情况下,PMD的结构图如下

       虽然PMD是在用户态实现设备驱动,但还是依赖于内核提供的策略。其中uio模块,是内核提供的用户态驱动框架,而igb_uio是DPDK kit中拥有与uio交互,bind指定网卡的内核模块;

      当使用DPDK脚本dpdk-devbind来bind网卡时,会通过sysfs与内核交互,让内核使用指定驱动来匹配网卡。具体的行为向/sys/bus/pci/devices/(pci id)/driver_override写入指定驱动名称,或者向/sys/bus/pci/drivers/igb_uio(驱动名称)/new_id写入要绑定网卡的PCI ID。前者是配置设备,让其选择驱动。后者是是配置驱动,让其支持新的PCI设备。按照内核的文档https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-bus-pci,这两个动作都会促使驱动bind新设备。

      但是在dpdk-devbind脚本中,还是通过向/sys/bus/pci/drivers/igb_uio(驱动名称)/bind写入pci id来保证bind;

      当使用igb_uio bind指定设备后,内核会调用igb_uio注册的struct pci_driver的probe函数,即igbuio_pci_probe;

    其会调用uio_register_device,注册uio设备

    打开uio设备

     应用层DPDK已经可以使用uio设备了。DPDK的应用层代码,会打开uioX设备,在函数pci_uio_alloc_resource中;

       当open对应的uio设备时,对应的内核操作为uio_open,其又会调用igb_uio的open函数,流程图如下

     

     igb_uio的默认中断模式为RTE_INTR_MODE_MSIX,在igbuio_pci_enable_interrupts的关键代码如下

    static int
    igbuio_pci_enable_interrupts(struct rte_uio_pci_dev *udev)
    {
        int err = 0;
    
        switch (igbuio_intr_mode_preferred) {
        case RTE_INTR_MODE_MSIX:
            /* Only 1 msi-x vector needed */
    #ifndef HAVE_ALLOC_IRQ_VECTORS
            msix_entry.entry = 0;
            if (pci_enable_msix(udev->pdev, &msix_entry, 1) == 0) {
                dev_dbg(&udev->pdev->dev, "using MSI-X");
                udev->info.irq_flags = IRQF_NO_THREAD;
                udev->info.irq = msix_entry.vector;
                udev->mode = RTE_INTR_MODE_MSIX;
                break;
            }
    #else
            if (pci_alloc_irq_vectors(udev->pdev, 1, 1, PCI_IRQ_MSIX) == 1) {
                dev_dbg(&udev->pdev->dev, "using MSI-X");
                udev->info.irq_flags = IRQF_NO_THREAD;
                udev->info.irq = pci_irq_vector(udev->pdev, 0);
                udev->mode = RTE_INTR_MODE_MSIX;
                break;
            }
    #endif
    
    default:
            dev_err(&udev->pdev->dev, "invalid IRQ mode %u",
                igbuio_intr_mode_preferred);
            udev->info.irq = UIO_IRQ_NONE;
            err = -EINVAL;
        }
    
        if (udev->info.irq != UIO_IRQ_NONE)
            err = request_irq(udev->info.irq, igbuio_pci_irqhandler,
                      udev->info.irq_flags, udev->info.name,
                      udev);
        dev_info(&udev->pdev->dev, "uio device registered with irq %ld\n",
             udev->info.irq);
    
        return err;
    }

       当打开uio设备时,igb_uio注册了一个中断。这时大家应该有个疑问,PMD不是用户态轮询设备吗?为什么还要申请中断,注册中断处理函数呢?这是因为,即使应用层可以通过uio来实现设备驱动,但是设备的某些事件还是需要内核进行响应,然后通知应用层;相当于中断的上半部 和下半部

     上半部中断服务函数: 主要通知一个event到user比如: 唤醒在idev->wait等待队列中的task

    /**
     * This is interrupt handler which will check if the interrupt is for the right device.
     * If yes, disable it here and will be enable later.
     */
    static irqreturn_t
    igbuio_pci_irqhandler(int irq, void *dev_id)
    {
        struct rte_uio_pci_dev *udev = (struct rte_uio_pci_dev *)dev_id;
        struct uio_info *info = &udev->info;
    
        /* Legacy mode need to mask in hardware */
        if (udev->mode == RTE_INTR_MODE_LEGACY &&
            !pci_check_and_mask_intx(udev->pdev))
            return IRQ_NONE;
    
        uio_event_notify(info);
    
        /* Message signal mode, no share IRQ and automasked */
        return IRQ_HANDLED;
    }

    当DPDK的app启动时,会进行EAL初始化,如下图:

     

    以ixgbe驱动为例详细说明:

    学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂
    更多DPDK相关学习资料有需要的可以自行报名学习,免费订阅,久学习,或点击这里加qun免费
    领取,关注我持续更新哦! !  

    RTE_PMD_REGISTER_PCI(net_ixgbe, rte_ixgbe_pmd);
    
    #define RTE_PMD_EXPORT_NAME(name, idx) \
    static const char RTE_PMD_EXPORT_NAME_ARRAY(this_pmd_name, idx) \
    __attribute__((used)) = RTE_STR(name)
    
    /** Helper for PCI device registration from driver (eth, crypto) instance */
    #define RTE_PMD_REGISTER_PCI(nm, pci_drv) \
    RTE_INIT(pciinitfn_ ##nm) \
    {\
        (pci_drv).driver.name = RTE_STR(nm);\
        rte_pci_register(&pci_drv); \
    } \
    RTE_PMD_EXPORT_NAME(nm, __COUNTER__)
    
    /* register a driver */
    void
    rte_pci_register(struct rte_pci_driver *driver)
    {
        TAILQ_INSERT_TAIL(&rte_pci_bus.driver_list, driver, next);
        driver->bus = &rte_pci_bus;
    }

        RTE_PMD_REGISTER_PCI为dpdk定义的宏,使用了GNU C提供的“__attribute__(constructor)”机制,使得注册设备驱动的过程在main函数执行之前完成。 
      这样就有了设备驱动类型、设备驱动的初始化函数

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

    2、通过读取/sys/bus/pci/devices/目录下的信息,扫描当前系统的PCI设备,并初始化,并按照PCI地址从大到小的顺序挂在到pci_debice_list上。

    3、rte_bu

  • 相关阅读:
    【Go】fatal error: concurrent map writes 问题记录
    STM32内部Flash的使用
    基于Boost库的在线搜索引擎
    Flink 作业提交
    宕机了, redis如何保证数据不丢?
    安装kubesphere3.3
    截图python ffmpeg
    频谱和信道基础
    【MindInsight】【Summary】报错“Hyper config is not in system envi
    支持jesd204b协议高速DAC芯片AD9144-FMC-EBZ配置笔记
  • 原文地址:https://blog.csdn.net/lingshengxiyou/article/details/127868955