DPDK创建UIO设备节点

DPDK 专栏收录该内容
38 篇文章 2 订阅

根据DPDK的官方文档说明,其提示IGB_UIO内核驱动程序可以自动创建/dev/uioX设备,但是没有提到uio_pci_generic驱动和vfio-pci驱动的情况。由于目前项目使用的是uio_pci_generic驱动,就遇到如下问题,UIO设备没有创建:

EAL:   Invalid NUMA socket, default to 0
EAL:   probe driver: 8086:1533 net_e1000_igb
EAL: Cannot open /dev/uio0: No such file or directory
EAL: Requested device 0000:0e:00.0 cannot be used
EAL: Error - exiting with code: 1

解决方法是,为EAL添加选项--create-uio-dev,要求创建UIO设备。此选项将DPDK内部的配置结构internal_config的成员变量create_uio_dev设置为真。

int rte_eal_create_uio_dev(void)
{
    return internal_config.create_uio_dev;
}

以上的不能打开/dev/uio0的错误信息来自于函数pci_uio_alloc_resource中,系统调用open失败返回的错误码。原因是由于上一步的函数pci_get_uio_dev未能创建出/dev/uio0设备节点而导致。

int pci_uio_alloc_resource(struct rte_pci_device *dev, struct mapped_pci_resource **uio_res)
{
    uio_num = pci_get_uio_dev(dev, dirname, sizeof(dirname), 1);
    if (uio_num < 0) {
        RTE_LOG(WARNING, EAL, "  "PCI_PRI_FMT" not managed by UIO driver, "
                "skipping\n", loc->domain, loc->bus, loc->devid, loc->function);
        return 1;
    }

    snprintf(devname, sizeof(devname), "/dev/uio%u", uio_num);
    dev->intr_handle.fd = open(devname, O_RDWR);
    if (dev->intr_handle.fd < 0) {
        RTE_LOG(ERR, EAL, "Cannot open %s: %s\n", devname, strerror(errno));
        goto error;
    }
}

函数pci_uio_alloc_resource为在PCI总线的probe探测过程中所调用,假设当前探测的PCI设备节点为0000:0e:00.0,即域为0000,总线号为0e,设备ID为00,功能号为0。遍历如下的对应目录:/sys/bus/pci/devices/0000:0e:00.0/uio/,或者如果以上目录不存在,遍历目录:/sys/bus/pci/devices/0000:0e:00.0/。遍历的目的是为找到以uio或者uio:uio开头的子目录,例如uio0或者uio:uio0,不同的内核版本有所不同,优先选择单独以uio开头的子目录。找到此类子目录可获得此PCI设备对应的UIO索引号,如以上的uio0子目录对应的索引号0,此索引用于创建UIO设备节点的名称中的索引,如设备节点/dev/uio0。

static int pci_get_uio_dev(struct rte_pci_device *dev, char *dstbuf, unsigned int buflen, int create)
{
    struct rte_pci_addr *loc = &dev->addr;

    snprintf(dirname, sizeof(dirname), "%s/" PCI_PRI_FMT "/uio", rte_pci_get_sysfs_path(),
            loc->domain, loc->bus, loc->devid, loc->function);
    dir = opendir(dirname);
    if (dir == NULL) {        /* retry with the parent directory */
        snprintf(dirname, sizeof(dirname), "%s/" PCI_PRI_FMT, rte_pci_get_sysfs_path(), loc->domain, loc->bus, loc->devid, loc->function);
        dir = opendir(dirname);
        if (dir == NULL) {
            RTE_LOG(ERR, EAL, "Cannot opendir %s\n", dirname);
            return -1;
        }
    }
    while ((e = readdir(dir)) != NULL) {             /* take the first file starting with "uio" */
        int shortprefix_len = sizeof("uio") - 1;     /* format could be uio%d ...*/
        int longprefix_len = sizeof("uio:uio") - 1;  /* ... or uio:uio%d */

        if (strncmp(e->d_name, "uio", 3) != 0)
            continue;
        uio_num = strtoull(e->d_name + shortprefix_len, &endptr, 10);   /* first try uio%d */
        if (errno == 0 && endptr != (e->d_name + shortprefix_len)) {
            snprintf(dstbuf, buflen, "%s/uio%u", dirname, uio_num);
            break;
        }
        uio_num = strtoull(e->d_name + longprefix_len, &endptr, 10);    /* then try uio:uio%d */
        if (errno == 0 && endptr != (e->d_name + longprefix_len)) {
            snprintf(dstbuf, buflen, "%s/uio:uio%u", dirname, uio_num);
            break;
        }
    }
    closedir(dir);
    if (e == NULL)   /* No uio resource found */
        return -1;

    /* create uio device if we've been asked to */
    if (rte_eal_create_uio_dev() && create && pci_mknod_uio_dev(dstbuf, uio_num) < 0)
        RTE_LOG(WARNING, EAL, "Cannot create /dev/uio%u\n", uio_num);

    return uio_num;
}

最终UIO设备节点是否创建,由函数rte_eal_create_uio_dev的返回值和create变量决定。由开始前的介绍可知,如果指定了--create-uio-dev选项,第一个条件返回真。在函数pci_uio_alloc_resource调用pci_get_uio_dev函数时create参数固定为1,所以只要指定了--create-uio-dev选项,此处就会创建UIO设备节点。具体的创建工作由函数pci_mknod_uio_dev完成。

static int pci_mknod_uio_dev(const char *sysfs_uio_path, unsigned uio_num)
{
    /* get the name of the sysfs file that contains the major and minor of the uio device and read its content */
    snprintf(filename, sizeof(filename), "%s/dev", sysfs_uio_path);
    f = fopen(filename, "r");
    if (f == NULL) {
        RTE_LOG(ERR, EAL, "%s(): cannot open sysfs to get major:minor\n",  __func__);
        return -1;
    }

    ret = fscanf(f, "%u:%u", &major, &minor);
    if (ret != 2) {
        RTE_LOG(ERR, EAL, "%s(): cannot parse sysfs to get major:minor\n",  __func__);
        fclose(f);
        return -1;
    }
    fclose(f);

    /* create the char device "mknod /dev/uioX c major minor" */
    snprintf(filename, sizeof(filename), "/dev/uio%u", uio_num);
    dev = makedev(major, minor);
    ret = mknod(filename, S_IFCHR | S_IRUSR | S_IWUSR, dev);
    if (ret != 0) {
        RTE_LOG(ERR, EAL, "%s(): mknod() failed %s\n", __func__, strerror(errno));
        return -1;
    }
}

UIO设备节点的名称格式固定为uio%u,其中结尾的数字为pci_get_uio_dev变量目录时得到的UIO索引值。设备的主次设备号由遍历到的子目录中的dev文件中获得。例如对于PCI设备0000:0e:00.0,其目录如下。

/ # cat /sys/bus/pci/devices/0000:0e:00.0/uio/uio0/dev  
244:0
/ # 

 

END

  • 0
    点赞
  • 0
    评论
  • 1
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值