2017-05-15 2 views
1

Je teste le pilote virtio pour communiquer à partir d'un ordinateur invité vers la machine hôte de Qemu. Pour l'instant, je veux juste envoyer un seul tampon.Vider le tampon de l'hôte à l'invité en utilisant virtqueue avec le périphérique virtio/driver

Mon problème est que le périphérique reçoit un tampon vide lorsque le rappel associé à la file d'attente virtuelle est appelé. La chose étrange est que la longueur du tampon reçu dans le périphérique change lorsque la longueur du tampon envoyé par le pilote change.

Dans mon chauffeur:

static int virt_test_probe(struct virtio_device *vdev) 
{ 
    struct virt_test_info *vi; 
    int retvalue = 0; 
    struct scatterlist sg[1]; 
    char *str = vmalloc(1000*sizeof(char)); 
    int err; 
    memset(str, 121, 1000*sizeof(char)); 
    vi = kzalloc(sizeof(struct virt_test_info), GFP_KERNEL); 
    if (!vi) 
     return -ENOMEM; 

    printk(KERN_ERR "TEST VIRTIO LINUX: %s. Value of str: %s\n", __FUNCTION__, str); 

    vi->vq = virtio_find_single_vq(vdev, protector_recv_done, "input"); 
    if (IS_ERR(vi->vq)) { 
     retvalue = PTR_ERR(vi->vq); 
     goto err_free; 
    } 

    vdev->priv = vi; 
    virtio_device_ready(vdev); 
    sg_init_one(sg, str, 1000*sizeof(char));  
    err = virtqueue_add_outbuf(vi->vq, sg, 1, str, GFP_ATOMIC); 
    virtqueue_kick(vi->vq); 

    return 0; 

err_free: 
    kfree(vi); 
    return retvalue; 
} 

Et dans mon appareil QEMU:

static void handle_output(VirtIODevice *vdev, VirtQueue *vq) 
{ 
    //VirtIOTEST *vtest = VIRTIO_TEST(vdev); 
    char *buf = malloc(1000*sizeof(char)); 
    VirtQueueElement *elem; 
    int len; 
    printf("TEST VIRTIO: %s\n", __FUNCTION__); 

    for(;;) { 
     elem = virtqueue_pop(vq, sizeof(VirtQueueElement)); 
     if (elem) 
      break; 
    } 
    memset(buf,122, 1000*sizeof(char)); // I nitialised the buffer with y char 

    len = iov_to_buf(elem->out_sg, elem->out_num, 0, buf, 1000*sizeof(char)); 

    printf("TEST VIRTIO: %s: content %s, len:%i\n", __FUNCTION__, buf, len); //the value length here changes with the the length of the driver's buffer 
    for(len=0; len<10; len++) { 
     printf("%02x",buf[len]); // here we're printing 0s 
    } 
} 

J'utilise un linux arm64. La valeur du tampon semble être cohérente à travers l'appel de qemu. Donc je suppose que le problème vient du noyau Linux ou d'autre chose. J'ai vérifié que la liste de diffusion va bien, d'après ce que je peux dire, et je ne reçois aucune erreur de la part de add_output_buf.

Répondre

1

Je viens de découvrir pourquoi cela ne fonctionnerait pas: Utiliser kmalloc au lieu de vmalloc d'allouer la mémoire tampon pour passer à sg_init_one. Voici pourquoi:

La liste de diffusion stocke le tampon en tant que page. Il doit donc transformer le tampon en une page utilisant page_to_virt, qui applique essentiellement un décalage à l'adresse virtuelle pour obtenir l'adresse physique de la page.

Maintenant, vmalloc ne mappe pas la mémoire dans un espace d'adressage physique contigu, comme le fait kmalloc. Il modifie donc les tables de pages pour mapper les adresses physiques non contiguës aux adresses virtuelles contiguës. Donc, la fonction page_to_virt ne semble pas fonctionner avec vmalloc car il faut plus que d'appliquer un décalage pour entrer dans l'espace d'adressage physique.