不能在vfio pci设备的文件描述符上使用pread

所以我正在用qemu kvm一段时间,现在我需要通过PCI设备。 我做了所有必要的程序,使这项工作:启用iommu,modprobed vfio模块,绑定设备vfio,并检查vfio组确实创build,等等…但是,当我启动与任何PCI设备qemu我收到错误消息:

vfio:无法读取设备configuration空间

我深入了解qemu的代码,看看问题可能是什么,并发现问题出现在设备的前导。 即使偏移量为0,也会发生这种情况,对文件描述符执行正常读取时可以正常工作,因为我更改了代码以进行testing。 检查errno的pread故障的原因给了我一个“ 非法寻求 ”的错误消息。

我写了一些代码,看看这是否发生在qemu环境之外(认为这可能是qemu的代码中的干扰设备的东西),并具有相同的问题。 我也试着用pread读取一个正常的文件,而且完美的工作…这是我写的testing代码,我把它分解了一下,能够指出更相关的部分:

#define BUF_SIZE 4096 int main(){ char buf[BUF_SIZE], buf1[BUF_SIZE], buf2[BUF_SIZE]; int ret,group_fd, fd, fd2; size_t nbytes = 4096; ssize_t bytes_read; int iommu1, iommu2; int container, group, device, i; struct vfio_group_status group_status = { .argsz = sizeof(group_status) }; struct vfio_iommu_type1_info iommu_info = { .argsz = sizeof(iommu_info) }; struct vfio_iommu_type1_dma_map dma_map = { .argsz = sizeof(dma_map) }; struct vfio_device_info device_info = { .argsz = sizeof(device_info) }; container = open("/dev/vfio/vfio",O_RDWR); if(ioctl(container,VFIO_GET_API_VERSION)!=VFIO_API_VERSION){ printf("Unknown api version: %m\n"); } group_fd = open("/dev/vfio/22",O_RDWR); printf("Group fd = %d\n", group_fd); ioctl(group_fd, VFIO_GROUP_GET_STATUS, &group_status); if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)){ printf("Group not viable\n"); return 1; } ret = ioctl(group_fd, VFIO_GROUP_SET_CONTAINER,&container); ret = ioctl(container,VFIO_SET_IOMMU,VFIO_TYPE1_IOMMU); ioctl(container, VFIO_IOMMU_GET_INFO, &iommu_info); /* Allocate some space and setup a DMA mapping */ dma_map.vaddr = (unsigned long int) mmap(0, 1024 * 1024, PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); dma_map.size = 1024 * 1024; dma_map.iova = 0; /* 1MB starting at 0x0 from device view */ dma_map.flags = VFIO_DMA_MAP_FLAG_READ | VFIO_DMA_MAP_FLAG_WRITE; ioctl(container, VFIO_IOMMU_MAP_DMA, &dma_map); printf("\n\nGETTING DEVICE FD\n"); fd = ioctl(group_fd,VFIO_GROUP_GET_DEVICE_FD,"0000:08:00.0"); printf("Fd = %d\n",fd); printf("VFIO_GROUP_GET_DEV_ID = %lu\n",VFIO_GROUP_GET_DEVICE_FD); 

这个阅读工作正常,给我一个nbytes的ret代码

  ret = read(fd,buf,nbytes); if(ret<1){ printf("ERROR: %m \n"); } 

此pread失败,ret code -1和errno'非法查找'

  ret = pread(fd,buf,nbytes,0); if(ret<0){ printf("ERROR: %m \n"); } 

在这里,我尝试读取和pread在sysfs中的一个公共文件,以查看是否pread失败,在这种情况下读取和pread工作就好了:

  printf("TESTING PREAD ON A COMMON FILE\n"); fd2 = open("/sys/bus/pci/devices/0000:08:00.0/device",O_RDONLY); ret = read(fd2,buf1,nbytes); if(ret<0){ printf("ERROR: %m\n"); } printf("Result from read: ret = %d, content = %s\n",ret,buf1); ret = pread(fd2,buf2,nbytes,2); if(ret<0){ printf("ERROR: %m\n"); # } printf("Result from pread: ret = %d, content = %s\n",ret,buf2); close(fd2); getchar(); close(fd); close(container); close(group_fd); return 0; } 

我正在使用uClibc编译embedded式系统的通用Linux内核v4.7.8 ….任何人有任何想法,为什么这可能会发生? 我现在无能为力! TT

更新:我在同一台机器上安装了ubuntu 16.04(kernel v4.4.0),并重复了这些步骤,并且pci passthrough工作正常,我的testing代码上的pread也可以正常工作。 所以我不确定自定义通用内核出了什么问题。

按照arash的build议,我尝试了pread(fd,buf,nbytes,SEEK_CUR) ,它给了我相同的“非法寻求”错误。 我从ftell得到的偏移量在ubuntu和通用内核中都是0xffffffff

我发现这个问题是什么,并且有意将这个问题留在这里一段时间,以便任何可能碰到这堵墙的人。 事实证明,uClibc 0.9.33版本的pread和pwrite函数被破坏,导致这些函数无法处理大于4G的偏移量。 从下面的链接补丁修复了我的问题: http : //uclibc.10924.n7.nabble.com/backport-pread-pwrite-fix-for-0-9-33-branch-td11921.html