virtio block device
PATCH 3/3 virtio block device
Subject: [PATCH 3/3] virtio block device
Cc: Rusty Russell <[email protected]>
Cc: Avi Kivity <[email protected]>
Cc: Dor Laor <[email protected]>
This patch implements the backend support for the virtio block device. It's designed to support in-order queueing of a virtually unlimited size so it will be able to perform better than SCSI. Besides performance, the virtio block interface passes through guest SCSI commands so it can be used to expose any type of block device (although only normal disks are supported in this patch).
Makefile.target +1 -1
@@ -436,7 +436,7 @@ VL_OBJS += rtl8139.o # virtio devices -VL_OBJS += virtio.o virtio-net.o +VL_OBJS += virtio.o virtio-net.o virtio-blk.o ifeq ($(TARGET_BASE_ARCH), i386) # Hardware support
hw/virtio-blk.c new file +163 -0
163 lines not on this page · raw message
sysemu.h +1 -1
@@ -117,7 +117,7 @@#endif typedef enum { -IF_IDE ,IF_SCSI ,IF_FLOPPY ,IF_PFLASH ,IF_MTD ,IF_SD +IF_IDE ,IF_SCSI ,IF_FLOPPY ,IF_PFLASH ,IF_MTD ,IF_SD ,IF_VIRTIO }BlockInterfaceType ; typedef struct DriveInfo {
hw/pc.c +12 -0
@@ -1008,6 +1008,18 @@ } } } + +/* Add virtio block devices */ +if (pci_enabled) { +int index; +int unit_id =0 ; + +while ((index =drive_get_index (IF_VIRTIO ,0 , unit_id)) !=-1 ) { +virtio_blk_init (pci_bus,0x5002 ,0x2258 , + drives_table[index].bdrv); + unit_id++; + } + } } static void pc_init_pci(int ram_size, int vga_ram_size,
hw/pc.h +4 -0
@@ -147,4 +147,8 @@void *virtio_net_init (PCIBus *bus,NICInfo *nd,int devfn); +/* virtio-blk.h */ +void *virtio_blk_init (PCIBus *bus,uint16_t vendor,uint16_t device, +BlockDriverState *bs); +#endif
vl.c +4 -0
@@ -4918,6 +4918,9 @@ }else if (!strcmp (buf,"sd" )) { interface =IF_SD ; max_devs =0 ; + }else if (!strcmp (buf,"virtio" )) { + interface =IF_VIRTIO ; + max_devs =0 ; }else {fprintf (stderr,"qemu: '%s' unsupported bus type '%s'\n" , str, buf);return -1 ;
@@ -5088,6 +5091,7 @@break ;case IF_PFLASH :case IF_MTD : +case IF_VIRTIO :break ; }if (!file[0 ])
Anthony Liguori wrote:
Subject: [PATCH 3/3] virtio block device
+
+static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{
+ VirtIOBlock *s = to_virtio_blk(vdev); + VirtQueueElement elem; + unsigned int count;
+
+ while ((count = virtqueue_pop(vq, &elem)) != 0) {
+ struct virtio_blk_inhdr *in;
+ struct virtio_blk_outhdr *out;
+ unsigned int wlen;
+ off_t off;
+ int i;
+
+ out = (void *)elem.out_sg[0].iov_base;
+ in = (void *)elem.in_sg[elem.in_num - 1].iov_base;
+ off = out->sector;
+
+ if (out->type & VIRTIO_BLK_T_SCSI_CMD) {
+ wlen = sizeof(*in);
+ in->status = VIRTIO_BLK_S_UNSUPP;
+ } else if (out->type & VIRTIO_BLK_T_OUT) {
+ wlen = sizeof(*in);
+
+ for (i = 1; i < elem.out_num; i++) {
+ bdrv_write(s->bs, off,
+ elem.out_sg[i].iov_base,
+ elem.out_sg[i].iov_len / 512);
+ off += elem.out_sg[i].iov_len / 512;
+ }
+
+ in->status = VIRTIO_BLK_S_OK;
+ } else {
+ wlen = sizeof(*in);
+
+ for (i = 0; i < elem.in_num - 1; i++) {
+ bdrv_read(s->bs, off,
+ elem.in_sg[i].iov_base,
+ elem.in_sg[i].iov_len / 512);
+ off += elem.in_sg[i].iov_len / 512;
+ wlen += elem.in_sg[i].iov_len;
+ }
+
+ in->status = VIRTIO_BLK_S_OK;
+ }
+
+ virtqueue_push(vq, &elem, wlen);
+ virtio_notify(vdev, vq);
+ }
The notify here should also be outside the while loop.
===================================================================
sysemu.h +0 -0
@@ -117,7 +117,7 @@
#endif
typedef enum {
- IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD
+ IF_IDE, IF_SCSI, IF_FLOPPY, IF_PFLASH, IF_MTD, IF_SD, IF_VIRTIO
} BlockInterfaceType;
typedef struct DriveInfo {
hw/pc.c +0 -0
@@ -1008,6 +1008,18 @@
}
}
}+
+ /* Add virtio block devices */
+ if (pci_enabled) {
+ int index;
+ int unit_id = 0;
+
+ while ((index = drive_get_index(IF_VIRTIO, 0, unit_id)) != -1) {
+ virtio_blk_init(pci_bus, 0x5002, 0x2258,
The pci vendor id should be identical to the network (0x6900) regards,
Dor.
Quoted 55 lines · Dor Laor
Anthony Liguori wrote:
Subject: [PATCH 3/3] virtio block device
(quoted diff) +49
-0+ +static void virtio_blk_handle_output(VirtIODevice *vdev, VirtQueue *vq) +{ + VirtIOBlock *s = to_virtio_blk(vdev); + VirtQueueElement elem; + unsigned int count; + + while ((count = virtqueue_pop(vq, &elem)) != 0) { + struct virtio_blk_inhdr *in; + struct virtio_blk_outhdr *out; + unsigned int wlen; + off_t off; + int i; + + out = (void *)elem.out_sg[0].iov_base; + in = (void *)elem.in_sg[elem.in_num - 1].iov_base; + off = out->sector; + + if (out->type & VIRTIO_BLK_T_SCSI_CMD) { + wlen = sizeof(*in); + in->status = VIRTIO_BLK_S_UNSUPP; + } else if (out->type & VIRTIO_BLK_T_OUT) { + wlen = sizeof(*in); + + for (i = 1; i < elem.out_num; i++) { + bdrv_write(s->bs, off, + elem.out_sg[i].iov_base, + elem.out_sg[i].iov_len / 512); + off += elem.out_sg[i].iov_len / 512; + } + + in->status = VIRTIO_BLK_S_OK; + } else { + wlen = sizeof(*in); + + for (i = 0; i < elem.in_num - 1; i++) { + bdrv_read(s->bs, off, + elem.in_sg[i].iov_base, + elem.in_sg[i].iov_len / 512); + off += elem.in_sg[i].iov_len / 512; + wlen += elem.in_sg[i].iov_len; + } + + in->status = VIRTIO_BLK_S_OK; + } + + virtqueue_push(vq, &elem, wlen); + virtio_notify(vdev, vq); + }
Quoted 2 lines
The notify here should also be outside the while loop.
Yeah, that's in the optimization patch.
Quoted 32 lines
===================================================================
sysemu.h +1
-1@@ -117,7 +117,7 @@#endif typedef enum { -IF_IDE ,IF_SCSI ,IF_FLOPPY ,IF_PFLASH ,IF_MTD ,IF_SD +IF_IDE ,IF_SCSI ,IF_FLOPPY ,IF_PFLASH ,IF_MTD ,IF_SD ,IF_VIRTIO }BlockInterfaceType ; typedef struct DriveInfo {Index:
qemu/hw/pc.c
===================================================================hw/pc.c +8
-0@@ -1008,6 +1008,18 @@ } } } + +/* Add virtio block devices */ +if (pci_enabled) { +int index; +int unit_id =0 ; + +while ((index =drive_get_index (IF_VIRTIO ,0 , unit_id)) !=-1 ) { + virtio_blk_init(pci_bus, 0x5002, 0x2258,
Quoted 2 lines
The pci vendor id should be identical to the network (
0x6900)
Indeed! That's for catching that.
Regards,
Anthony Liguori
Quoted 4 lines
regards,
Dor.