我正在测试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的错误。
答案 0 :(得分:1)
我刚刚发现为什么它不起作用:
使用kmalloc
代替vmalloc
来分配缓冲区以传递给sg_init_one
。这就是原因:
scatterlist将缓冲区存储为页面。
因此,必须使用page_to_virt
将缓冲区转换为页面,这基本上会将偏移应用于虚拟地址以获取页面物理地址。
现在,vmalloc
并没有像kmalloc
那样在连续的物理地址空间中映射内存。因此,它更改页表以将非连续的物理地址映射到连续的虚拟地址。所以函数page_to_virt
似乎不适用于vmalloc
,因为它需要的不仅仅是应用偏移来进入物理地址空间。