用户态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内核驱动解绑)