goon
goon

Reputation:

Empty buffer while from host to guest using virtqueue with virtio device/driver

I'm testing the virtio driver to communicate from a guest machine to the host machine in Qemu. For now I just want to send a single buffer.

My problem is that the device receives an empty buffer when the callback associated to the virtual queue is called.

The weird thing is that the length of the buffer received in the device changes when the length of the buffer sent from driver changes.

In my driver :

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

And in my QEMU device :

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

I'm using an arm64 linux. The value of the buffer seem to be consistent through the qemu's call. So I guess the problem is coming from the linux kernel or something else. I checked that the scatterlist goes ok, from what I can tell, and I receive no error from add_output_buf.

Upvotes: 2

Views: 628

Answers (1)

watts
watts

Reputation: 127

I just found out why it wouldn't work : Use kmalloc instead of vmalloc to allocate the buffer to pass to sg_init_one. Here's why :

The scatterlist stores the buffer as a page. So it has to turns the buffer into a page using page_to_virt, which basically applies an offset to the virtual adress to get the page physical address.

Now, vmalloc doesn't map memory in a contiguous physical address space, as kmalloc does. So it changes the page tables to map non contiguous physical addresses to contiguous virtual addresses. So the function page_to_virt doesn't seem to work with vmalloc as it requires more than applying an offset to get into physical address space.

Upvotes: 1

Related Questions