使用带有virtio设备/驱动程序的virtqueue从主机到guest的空缓冲区

时间:2017-05-15 17:10:46

标签: c qemu drivers

我正在测试virtio驱动程序,以便在客户机上与Qemu中的主机进行通信。现在我只想发送一个缓冲区。

我的问题是,当调用与虚拟队列关联的回调时,设备会收到空缓冲区。

奇怪的是,当驱动程序发送的缓冲区长度发生变化时,设备中收到的缓冲区长度会发生变化。

在我的司机中:

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

在我的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
    }
}

我正在使用arm64 linux。 通过qemu的调用,缓冲区的值似乎是一致的。所以我猜问题来自Linux内核或其他东西。我检查了散点列表是否正常,从我所知道的,我没有收到add_output_buf的错误。

1 个答案:

答案 0 :(得分:1)

我刚刚发现为什么它不起作用: 使用kmalloc代替vmalloc来分配缓冲区以传递给sg_init_one。这就是原因:

scatterlist将缓冲区存储为页面。 因此,必须使用page_to_virt将缓冲区转换为页面,这基本上会将偏移应用于虚拟地址以获取页面物理地址。

现在,vmalloc并没有像kmalloc那样在连续的物理地址空间中映射内存。因此,它更改页表以将非连续的物理地址映射到连续的虚拟地址。所以函数page_to_virt似乎不适用于vmalloc,因为它需要的不仅仅是应用偏移来进入物理地址空间。