【问题标题】:Can't use pread on a file descriptor for a vfio pci device无法在 vfio pci 设备的文件描述符上使用 pread
【发布时间】:2017-02-15 10:43:55
【问题描述】:

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

vfio: 读取设备配置空间失败

我深入研究了 qemu 的代码以查看问题可能是什么,并发现问题发生在设备的预加载上。即使偏移量为 0,也会发生这种情况,并且对文件描述符进行正常读取没有问题,因为我更改了代码来测试它。 检查 errno 是否因预读失败给我一个“Illegal seek”错误消息。

我编写了一些代码来查看这是否发生在 qemu 上下文之外(认为可能是 qemu 代码中的某些东西干扰了设备),并且遇到了同样的问题。我还尝试使用 pread 读取一个普通文件,并且效果很好......这是我编写的测试它的代码,我将其分解了一下以便能够指出更相关的部分:

#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 代码 -1 和 errno 'Illegal seek'

    ret = pread(fd,buf,nbytes,0);

    if(ret<0){      
       printf("ERROR: %m \n");  
    }   

在这里,我尝试在 sysfs 中读取和预读一个公共文件,以查看预读是否失败,在这种情况下,读和预读都可以正常工作:

    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 为嵌入式系统编译的通用 linux 内核 v4.7.8.... 有人知道为什么会发生这种情况吗?我现在一头雾水!! T.T

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

根据 arash 的建议,我尝试了 pread(fd,buf,nbytes,SEEK_CUR),它给了我同样的“非法搜索”错误。我从 ftell 得到的偏移量在 ubuntu 和通用内核中都是 0xffffffff

【问题讨论】:

  • pread 包括 (1) 查找文件开头以获取当前偏移量(并将其保存为 old_offset),(2) 查找请求偏移量,(3) 读取, (4) 最后寻回原来的偏移量(old_offset)。显然,您所看到的是,这些搜索中至少有一个是非法的。我想知道这是否有效pread(fd,buf,nbytes,SEEK_CUR) 或者long int ftell (FILE *stream) 的当前偏移值是多少
  • 请同时发布您的 QEMU 命令行(尤其是配置 vfio 设备的部分)
  • 这是我正在使用的 qemu 命令:qemu-system-x86_64 -enable-kvm -m 1024 -device vfio-pci,host=01:00.0 -drive file=/disk0/vdisk.qcow2 ,id=磁盘,格式=qcow2。这在 ubuntu 中运行良好

标签: c linux virtualization qemu vfio


【解决方案1】:

我发现了问题所在,并且一直想在此处发布一段时间,以供任何可能碰壁的人使用。事实证明,uClibc 0.9.33 版的 pread 和 pwrite 函数被破坏,导致这些函数无法在大于 4G 的偏移量上工作。下面链接中的补丁为我解决了这个问题: http://uclibc.10924.n7.nabble.com/backport-pread-pwrite-fix-for-0-9-33-branch-td11921.html

【讨论】:

    猜你喜欢
    • 2012-08-12
    • 1970-01-01
    • 2018-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-18
    相关资源
    最近更新 更多