2017-02-15 24 views
7

因此,我正在使用qemu kvm一段时间,现在我需要传递PCI设备。我做了所有必要的程序,使这项工作:启用iommu,modprobed vfio模块,绑定到vfio设备,并检查确认vfio组确实创建了,等等... 但是,当我启动与任何PCI设备qemu我收到错误消息:无法在vfio pci设备的文件描述符上使用pread

vfio:无法读取设备配置空间

我深入到QEMU的代码,看看有什么问题可能是,结果发现,在PREAD的设备出现问题。即使偏移量为0,也会发生这种情况,并且对文件描述符执行正常读操作时没有问题,因为我更改了代码以对其进行测试。 检查errno的pread失败的原因给了我'非法查找'错误消息。

我写了一些代码,看看这是否发生在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 '非法寻求'

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

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

在这里,我尝试阅读并看到在sysfs共同文件PREAD如果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; 
} 

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

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

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

+0

[pread](https://github.com/lattera/glibc/blob/master/sysdeps/posix/pread.c)包括(1)寻找(2)寻找请求偏移量,(3)读取,(4)最终寻找回原始偏移量(old_offset),以获得当前偏移量(并将其保存为old_offset)。显然,你看到的是至少有一个这样的搜索是非法的。我想知道这是否工作'pread(fd,buf,nbytes,SEEK_CUR)'或者'long int ftell(FILE * stream)'的当前偏移量的值是多少? – Arash

+0

请张贴您的QEMU命令行(尤其是您的部分config vfio devices) – Codeguard

+0

这是我使用的qemu命令:qemu-system-x86_64 -enable-kvm -m 1024 -device vfio-pci,host = 01:00.0 -drive file =/disk0/vdisk.qcow2,id =盘,格式= qcow2。这在ubuntu中正常工作 – igalvez

回答