nvme用户态驱动-day01-用户态pci设备访问

814 阅读2分钟

用户态pci设备访问(nvme设备)

  • 安装用户态pci访问开发包
apt install -y libpciaccess-dev
  • 用户态pci设备访问
#include <stdio.h>
#include <pciaccess.h>

// class code for nvme
#define NVME_CLASS_CODE 0x10802

int main(void)
{
    struct pci_device_iterator *pci_dev_iter;
    struct pci_device *pci_dev;
    struct pci_id_match match;

    // 初始化pci系统
    pci_system_init();

    match.vendor_id = PCI_MATCH_ANY;
    match.subvendor_id = PCI_MATCH_ANY;
    match.subdevice_id = PCI_MATCH_ANY;
    match.device_id = PCI_MATCH_ANY;
    match.device_class = NVME_CLASS_CODE;
    match.device_class_mask = 0xFFFFFF;

    // 创建一个迭代器,用于迭代所有的pci nvme设备
    pci_dev_iter = pci_id_match_iterator_create(&match);
    
    // 打印出所有nvme pci设备的bdf号
    while (pci_dev = pci_device_next(pci_dev_iter)) {
        printf("pci bdf %x:%x:%x\n", pci_dev->bus, pci_dev->dev, pci_dev->func);
    }

    return 0;
}
  • 加载uio驱动
modprobe uio_pci_generic

uio驱动加载成功后可以在/sys/module路径下找到uio_pci_generic,代表uio驱动加载成功。也可以通过lsmod命令来确认uio驱动是否加载成功.

  • 绑定/解绑nvme linux内核驱动 (1) 手动实现

解绑内核驱动:将pci设备号写入该设备的unbind文件/sys/bus/pci/drivers/nvme/unbind, 例如:

echo 0000:0b:00.0 > /sys/bus/pci/drivers/nvme/unbind

绑定内核驱动: 与解绑类似,将pci设备号写入设备bind文件/sys/bus/pci/drivers/nvme/bind, 例如:

echo 0000:0b:00.0 > /sys/bus/pci/drivers/nvme/bind

(2)代码实现

int pci_device_unbind_kernel_driver(struct pci_device *dev)
{
    int n;
    FILE *fd;
    char filename[PATH_MAX];
    char buf[256];

    // SYSFS_PCI_DEVICES: /sys/bus/pci/devices
    // PCI_PRI_FMT: %04x:%02x:%02x.%1u
    snprintf(filename, sizeof(filename),
         SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/driver/unbind",
         dev->domain, dev->bus, dev->dev, dev->func);

    fd = fopen(filename, "w");
    if (!fd)
        return 0;

    n = snprintf(buf, sizeof(buf), PCI_PRI_FMT,
             dev->domain, dev->bus, dev->dev, dev->func);

    if (fwrite(buf, n, 1, fd) == 0)
        goto error;

    fclose(fd);
    return 0;

error:
    fclose(fd);
    return -1;
}
  • 将nvme设备绑定到uio驱动 (1) 手动绑定
nvme_vendor_id=`cat /sys/bus/pci/devices/0000\:0b\:00.0/vendor`
nvme_device_id=07f0=`cat /sys/bus/pci/devices/0000\:0b\:00.0/device`
echo "$nvme_vendor_id $nvme_device_id" > /sys/bus/pci/drivers/uio_pci_generic/new_id

(2) 通过代码绑定

// 检查uio驱动是否加载    
static int check_modules(char *driver_name)  // uio的驱动名称是uio_pci_generic
{
    FILE *fd;
    const char *proc_modules = "/proc/modules";
    char buffer[256];

    fd = fopen(proc_modules, "r");
    if (!fd)
        return -1;

    while (fgets(buffer, sizeof(buffer), fd)) {
        if (strstr(buffer, driver_name) == NULL)
            continue;
        else {
            fclose(fd);
            return 0;
        }
    }
    fclose(fd);

    return -1;
}

int pci_device_bind_uio_driver(struct pci_device *dev, char *driver_name)
{
    int err, n;
    FILE *fd;
    char filename[PATH_MAX];
    char buf[256];

    err = check_modules(driver_name);
    if (err < 0) {
        fprintf(stderr, "No %s module loaded\n", driver_name);
        return err;
    }

    // SYSFS_PCI_DRIVERS    "/sys/bus/pci/drivers"
    snprintf(filename, sizeof(filename),
         SYSFS_PCI_DRIVERS "/" "%s" "/new_id", driver_name);

    fd = fopen(filename, "w");
    if (!fd) {
        return -1;
    }

    // 根据vendor_id 和 device_id绑定nvme设备到uio驱动
    n = snprintf(buf, sizeof(buf), "%04x %04x",
             dev->vendor_id, dev->device_id);

    if (fwrite(buf, n, 1, fd) == 0)
        goto error;

    fclose(fd);
    return 0;

error:
    fclose(fd);
    return -1;
}
  • 检查pci设备是否绑定uio驱动 (1) 手动确定

查看如下目录/sys/bus/pci/devices/[pci设备bdf]/ 是否有uio子目录,例如下面这样表示uio驱动绑定成功。

root@cjhjy-virtual-machine:/Images/git/nvme# ll /sys/bus/pci/devices/0000\:0b\:00.0/uio
total 0
drwxr-xr-x 3 root root 0  6月 26 14:52 ./
drwxr-xr-x 5 root root 0  6月 18 23:54 ../
drwxr-xr-x 3 root root 0  6月 26 14:52 uio0/

(2) 代码确定

int pci_device_has_uio_driver(struct pci_device *dev)
{
    struct dirent *e;
    DIR *dir;
    char dirname[PATH_MAX];

    // SYSFS_PCI_DEVICES: /sys/bus/pci/devices
    // PCI_PRI_FMT: %04x:%02x:%02x.%1u
    snprintf(dirname, sizeof(dirname),
         SYSFS_PCI_DEVICES "/" PCI_PRI_FMT "/uio",
         dev->domain, dev->bus, dev->dev, dev->func);

    dir = opendir(dirname);
    if (!dir) {
        snprintf(dirname, sizeof(dirname),
             SYSFS_PCI_DEVICES "/" PCI_PRI_FMT,
             dev->domain, dev->bus, dev->dev, dev->func);
        dir = opendir(dirname);
        if (!dir)
            return 0;
    }

    while ((e = readdir(dir)) != NULL) {
        if (strncmp(e->d_name, "uio", 3) == 0) {
            break;
        }
    }

    closedir(dir);

    if (!e)
        return 0;

    return 1;
}

Reference

t.zoukankan.com/styshoo-p-6… (linux内核驱动解绑)