2017-02-15 30 views
7

Итак, я работаю с qemu kvm некоторое время, и теперь мне нужно переходить через PCI-устройства. Я сделал все необходимые процедуры для выполнения этой работы: включил iommu, modprobed vfio module, связал устройство с vfio и проверил, что группа vfio действительно создана и т. Д. Но когда я запускаю qemu с любыми устройствами pci, я получаю сообщение об ошибке:Невозможно использовать pread в файловом дескрипторе для устройства vfio pci

vfio: Не удалось прочитать устройство конфигурации пространства

копаюсь в код из QEMU, чтобы увидеть, что может быть проблемой, и выяснил, что проблема возникает на pread к устройству. Это происходит даже тогда, когда смещение равно 0, и нормальное чтение в файловом дескрипторе работает без проблем, поскольку я изменил код для его проверки. Проверка errno по причине отказа pread дает мне сообщение об ошибке «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); 

Это чтение прекрасно работает, дает мне в отставке код-байт

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

Это pread терпит неудачу с кодом -1 в отставке и ERRNO 'Illegal искать'

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; 
} 

Я использую стандартное ядро ​​Линукса v4.7.8, скомпилированный с uClibc для встроенной системы ... У кого-нибудь есть идеи, почему это может произойти? Я сейчас не знаю! T.T

UPDATE: Я установил ubuntu 16.04 (ядро 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) поиск к началу файла, чтобы получить текущее смещение (и сохранить его как old_offset), (2) искать смещение запроса, (3) прочитать, (4), наконец, вернуться к исходному смещению (old_offset).Видимо, вы видите, что хотя бы один из этих иска незаконен. Интересно, работает ли это 'pread (fd, buf, nbytes, SEEK_CUR)' или какое значение текущего смещения от 'long int ftell (поток FILE *)' – Arash

+0

Пожалуйста, отправьте также свою командную строку QEMU (особенно ту часть, где вы config vfio) – 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

ответ

1

Я нашел, что было проблемой, и имел смысл опубликовать его здесь некоторое время для всех, кто мог бы попасть в эту стену. Оказывается, нарушены функции pread и pwrite версии 0,3.33 uClibc, в результате чего эти функции не работают при смещениях больше 4G. Патчи из приведенной ниже ссылки исправили проблему для меня: http://uclibc.10924.n7.nabble.com/backport-pread-pwrite-fix-for-0-9-33-branch-td11921.html