c语言 - 不能对 vfio pci 设备的文件描述符使用读取



所以我正在与Qemu KVM合作一段时间,现在我需要通过PCI设备。我做了所有必需的过程来完成这项工作:启用了Iommu,Modprobed VFIO模块,将设备绑定到VFIO,并检查了VFIO组的确创建了,等等...但是,当我使用任何PCI设备启动QEMU时,我会收到错误消息:

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

i挖掘QEMU的代码,以查看问题可能是什么,并发现该问题发生在设备的前提下。即使偏移为0,也会发生这种情况,并且在更改代码进行测试时,对文件描述符进行正常读取。检查Errno的出现故障的原因,我会给我一个'非法seek '错误消息。

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

#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: %mn");    
    }   
    group_fd = open("/dev/vfio/22",O_RDWR);     printf("Group fd = %dn", group_fd);
    ioctl(group_fd, VFIO_GROUP_GET_STATUS, &group_status);
    if (!(group_status.flags & VFIO_GROUP_FLAGS_VIABLE)){
        printf("Group not viablen");
        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("nnGETTING DEVICE FDn");      
    fd = ioctl(group_fd,VFIO_GROUP_GET_DEVICE_FD,"0000:08:00.0");

    printf("Fd = %dn",fd);     
    printf("VFIO_GROUP_GET_DEV_ID = %lun",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");  
    }   

在这里,我尝试在SYSF中的一个常见文件上阅读并pread,以查看PREAD是否失败,并且在这种情况下,阅读和pread工作正常:

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

我使用的是通用的Linux内核v4.7.8与UCLIBC编译的嵌入式系统。...有人对为什么会发生这种情况有任何想法吗?我现在毫无头绪!T.T

更新:我在同一台计算机上安装了Ubuntu 16.04(内核v4.4.0),并重复了步骤,并且PCI PassThrough效果很好,并且在我的测试代码上的pread也可以很好地工作。所以我不确定自定义通用内核怎么了。

根据Arash的建议,我尝试了 PREAD(FD,BUF,NBYTES,SEEK_CUR),它给了我同样的"非法寻求"错误。我从ftell获得的偏移量是 0xffffffff 在Ubuntu和通用核中。

我找到了问题所在,并意思是在此处将其发布一段时间,以供任何可能撞到这堵墙的人。事实证明,UCLIBC版本0.9.33的pread和Pwrite功能被损坏,导致这些功能无法在大于4G的偏移量上工作。下面链接的补丁为我解决了问题:http://uclibc.10924.n7.nabble.com/backport-port-pread-pwrite-forite-for-0-9-33-branch-td11921.html

最新更新