qemu-kvm: move virtio-pci.o to near pci.o
Subsystems (3)
A message that is not in the archive (sent off-list or not yet received): [email protected]
Michael S. Tsirkin
maintainerPATCH v3 1/4 qemu-kvm: move virtio-pci.o to near pci.o
virtio-pci depends, and will always depend, on pci.c so it makes sense to keep it in the same makefile, (unlike the rest of virtio files which should eventually
be moved out to Makefile.hw).
--- Makefile.hw | 2 +- Makefile.target | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
Makefile.hw +1 -1
@@ -11,7 +11,7 @@ VPATH=$(SRC_PATH):$(SRC_PATH)/hw QEMU_CFLAGS+=-I.. -I$(SRC_PATH)/fpu obj-y = -obj-y += virtio.o virtio-pci.o +obj-y += virtio.o obj-y += fw_cfg.o obj-y += watchdog.o obj-y += nand.o ecc.o
Makefile.target +1 -1
@@ -170,7 +170,7 @@ obj-y = vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o \ gdbstub.o gdbstub-xml.o msix.o ioport.o qemu-config.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly -obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o +obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o LIBS+=-lz
Signature · 1 line
1.6.2.5
Michael S. Tsirkin
maintainerPATCH v3 1/4 qemu-kvm: move virtio-pci.o to near pci.o
In reply to a message not in the archivevirtio-pci depends, and will always depend, on pci.c so it makes sense to keep it in the same makefile, (unlike the rest of virtio files which should eventually
be moved out to Makefile.hw).
--- Makefile.hw | 2 +- Makefile.target | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
Makefile.hw +1 -1
@@ -11,7 +11,7 @@ VPATH=$(SRC_PATH):$(SRC_PATH)/hw QEMU_CFLAGS+=-I.. -I$(SRC_PATH)/fpu obj-y = -obj-y += virtio.o virtio-pci.o +obj-y += virtio.o obj-y += fw_cfg.o obj-y += watchdog.o obj-y += nand.o ecc.o
Makefile.target +1 -1
@@ -170,7 +170,7 @@ obj-y = vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o \ gdbstub.o gdbstub-xml.o msix.o ioport.o qemu-config.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly -obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o +obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o LIBS+=-lz
Signature · 1 line
1.6.2.5
Michael S. Tsirkin
maintainerPATCH v3 2/4 virtio: move features to an inline function
In reply to a message not in the archivedevices should have the final say over which virtio features they support. E.g. indirect entries may or may not make sense in the context of virtio-console. Move the common bits from virtio-pci to an inline function and let each device call it.
No functional changes.
--- hw/virtio-balloon.c | 2 +- hw/virtio-blk.c | 2 +- hw/virtio-console.c | 2 +- hw/virtio-net.c | 2 +- hw/virtio-pci.c | 3 --- hw/virtio.h | 10 ++++++++++ 6 files changed, 14 insertions(+), 7 deletions(-)
hw/virtio-balloon.c +1 -1
@@ -127,7 +127,7 @@ static void virtio_balloon_set_config(VirtIODevice *vdev,static uint32_t virtio_balloon_get_features (VirtIODevice *vdev) { -return 0 ; +return virtio_common_features (); }static ram_addr_t virtio_balloon_to_target (void *opaque,ram_addr_t target)
hw/virtio-blk.c +1 -1
@@ -378,7 +378,7 @@ static uint32_t virtio_blk_get_features(VirtIODevice *vdev)if (strcmp (s->serial_str,"0" )) features |=1 <<VIRTIO_BLK_F_IDENTIFY ; -return features; +return features |virtio_common_features (); }static void virtio_blk_save (QEMUFile *f,void *opaque)
hw/virtio-console.c +1 -1
@@ -53,7 +53,7 @@ static void virtio_console_handle_input(VirtIODevice *vdev, VirtQueue *vq)static uint32_t virtio_console_get_features (VirtIODevice *vdev) { -return 0 ; +return virtio_common_features (); }static int vcon_can_read (void *opaque)
hw/virtio-net.c +1 -1
@@ -154,7 +154,7 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev) }#endif -return features; +return features |virtio_common_features (); }static uint32_t virtio_net_bad_features (VirtIODevice *vdev)
hw/virtio-pci.c +0 -3
@@ -230,9 +230,6 @@ static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr)switch (addr) {case VIRTIO_PCI_HOST_FEATURES : ret = vdev->get_features (vdev); - ret |= (1 <<VIRTIO_F_NOTIFY_ON_EMPTY ); - ret |= (1 <<VIRTIO_RING_F_INDIRECT_DESC ); - ret |= (1 <<VIRTIO_F_BAD_FEATURE );break ;case VIRTIO_PCI_GUEST_FEATURES : ret = vdev->features;
hw/virtio.h +10 -0
@@ -167,4 +167,14 @@ VirtIODevice *virtio_net_init(DeviceState *dev);VirtIODevice *virtio_console_init (DeviceState *dev);VirtIODevice *virtio_balloon_init (DeviceState *dev); +static inline uint32_t virtio_common_features (void ) +{ +uint32_t features =0 ; + features |= (1 <<VIRTIO_F_NOTIFY_ON_EMPTY ); + features |= (1 <<VIRTIO_RING_F_INDIRECT_DESC ); + features |= (1 <<VIRTIO_F_BAD_FEATURE ); + +return features; +} +#endif
Signature · 1 line
1.6.2.5
Michael S. Tsirkin
maintainerPATCH v3 2/4 virtio: move features to an inline function
In reply to a message not in the archivedevices should have the final say over which virtio features they support. E.g. indirect entries may or may not make sense in the context of virtio-console. Move the common bits from virtio-pci to an inline function and let each device call it.
No functional changes.
--- hw/virtio-balloon.c | 2 +- hw/virtio-blk.c | 2 +- hw/virtio-console.c | 2 +- hw/virtio-net.c | 2 +- hw/virtio-pci.c | 3 --- hw/virtio.h | 10 ++++++++++ 6 files changed, 14 insertions(+), 7 deletions(-)
hw/virtio-balloon.c +1 -1
@@ -127,7 +127,7 @@ static void virtio_balloon_set_config(VirtIODevice *vdev,static uint32_t virtio_balloon_get_features (VirtIODevice *vdev) { -return 0 ; +return virtio_common_features (); }static ram_addr_t virtio_balloon_to_target (void *opaque,ram_addr_t target)
hw/virtio-blk.c +1 -1
@@ -378,7 +378,7 @@ static uint32_t virtio_blk_get_features(VirtIODevice *vdev)if (strcmp (s->serial_str,"0" )) features |=1 <<VIRTIO_BLK_F_IDENTIFY ; -return features; +return features |virtio_common_features (); }static void virtio_blk_save (QEMUFile *f,void *opaque)
hw/virtio-console.c +1 -1
@@ -53,7 +53,7 @@ static void virtio_console_handle_input(VirtIODevice *vdev, VirtQueue *vq)static uint32_t virtio_console_get_features (VirtIODevice *vdev) { -return 0 ; +return virtio_common_features (); }static int vcon_can_read (void *opaque)
hw/virtio-net.c +1 -1
@@ -154,7 +154,7 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev) }#endif -return features; +return features |virtio_common_features (); }static uint32_t virtio_net_bad_features (VirtIODevice *vdev)
hw/virtio-pci.c +0 -3
@@ -230,9 +230,6 @@ static uint32_t virtio_ioport_read(VirtIOPCIProxy *proxy, uint32_t addr)switch (addr) {case VIRTIO_PCI_HOST_FEATURES : ret = vdev->get_features (vdev); - ret |= (1 <<VIRTIO_F_NOTIFY_ON_EMPTY ); - ret |= (1 <<VIRTIO_RING_F_INDIRECT_DESC ); - ret |= (1 <<VIRTIO_F_BAD_FEATURE );break ;case VIRTIO_PCI_GUEST_FEATURES : ret = vdev->features;
hw/virtio.h +10 -0
@@ -167,4 +167,14 @@ VirtIODevice *virtio_net_init(DeviceState *dev);VirtIODevice *virtio_console_init (DeviceState *dev);VirtIODevice *virtio_balloon_init (DeviceState *dev); +static inline uint32_t virtio_common_features (void ) +{ +uint32_t features =0 ; + features |= (1 <<VIRTIO_F_NOTIFY_ON_EMPTY ); + features |= (1 <<VIRTIO_RING_F_INDIRECT_DESC ); + features |= (1 <<VIRTIO_F_BAD_FEATURE ); + +return features; +} +#endif
Signature · 1 line
1.6.2.5
Michael S. Tsirkin
maintainerPATCH v3 3/4 qemu-kvm: vhost-net implementation
In reply to a message not in the archiveThis adds support for vhost-net virtio kernel backend. To enable (assuming device eth2):
1. enable promisc mode or program guest mac in device eth2
2. disable tso, gso, lro, jumbo frames on the card (disabling lro + jumbo frames should be sufficient, haven't tested this)
3. add vhost=eth2 to -net flag
4. run with CAP_NET_ADMIN priviledge (e.g. root)
This patch is RFC, but works without issues for me.
It still needs to be split up, tested and benchmarked properly, but posting it here in case people want to test drive the kernel bits I posted.
--- Makefile.target | 3 +- hw/vhost_net.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/vhost_net.h | 30 +++++++++ hw/virtio-net.c | 32 ++++++++++- hw/virtio-pci.c | 40 ++++++++++++ hw/virtio.c | 19 ------ hw/virtio.h | 28 ++++++++- net.c | 6 ++- net.h | 1 + qemu-kvm.c | 8 --- qemu-kvm.h | 9 +++ 11 files changed, 324 insertions(+), 33 deletions(-) create mode 100644 hw/vhost_net.c create mode 100644 hw/vhost_net.h
Makefile.target +2 -1
@@ -170,7 +170,8 @@ obj-y = vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o \ gdbstub.o gdbstub-xml.o msix.o ioport.o qemu-config.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly -obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o +obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o \ + vhost_net.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o LIBS+=-lz
hw/vhost_net.c new file +181 -0
181 lines not on this page · raw message
hw/vhost_net.h new file +30 -0
@@ -0,0 +1,30 @@ +#ifndef VHOST_NET_H +#define VHOST_NET_H + +#include "hw/virtio.h" + +struct vhost_virtqueue { +int kick; +int call; +void *desc; +void *avail; +void *used; +}; + +struct vhost_dev { +int control; +struct vhost_virtqueue *vqs; +int nvqs; +}; + +struct vhost_net { +struct vhost_dev dev; +struct vhost_virtqueue vqs[2 ]; +int sock; +}; + +int vhost_net_init (struct vhost_net *net, +VirtIODevice *dev, +char *vhost_device); + +#endif
hw/virtio-net.c +31 -1
@@ -19,6 +19,8 @@#include "qemu-kvm.h" #endif +#include "vhost_net.h" +#define TAP_VNET_HDR #define VIRTIO_NET_VM_VERSION 10
@@ -56,6 +58,8 @@ typedef struct VirtIONetuint8_t *macs; } mac_table;uint32_t *vlans; +char *vhost_device; +struct vhost_net vhost; } VirtIONet;/* TODO
@@ -134,9 +138,12 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev) (1 <<VIRTIO_NET_F_CTRL_RX ) | (1 <<VIRTIO_NET_F_CTRL_VLAN ) | (1 <<VIRTIO_NET_F_CTRL_RX_EXTRA ); +VirtIONet *n =to_virtio_net (vdev); + +if (n->vhost_device) +return 1 <<VIRTIO_NET_F_MAC ;#ifdef TAP_VNET_HDR -VirtIONet *n =to_virtio_net (vdev);VLANClientState *host = n->vc->vlan->first_client;if (tap_has_vnet_hdr (host)) {
@@ -175,6 +182,9 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev)static void virtio_net_set_features (VirtIODevice *vdev,uint32_t features) {VirtIONet *n =to_virtio_net (vdev); +/* vhost net supports no features */ +if (n->vhost_device) +return ;#ifdef TAP_VNET_HDR VLANClientState *host = n->vc->vlan->first_client;#endif
@@ -351,6 +361,9 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)static int do_virtio_net_can_receive (VirtIONet *n,int bufsize) { +if (n->vhost_device) +return 0 ; +if (!virtio_queue_ready (n->rx_vq) || !(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK ))return 0 ;
@@ -411,6 +424,7 @@ static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count)while (offset < count && i < iovcnt) {int len =MIN (iov[i].iov_len, count - offset);memcpy (iov[i].iov_base, buf + offset, len); + offset += len; i++; }
@@ -610,6 +624,8 @@ static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)#else int has_vnet_hdr =0 ;#endif +if (n->vhost_device) +return ;if (!(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK ))return ;
@@ -822,6 +838,18 @@ static void virtio_net_cleanup(VLANClientState *vc)virtio_cleanup (&n->vdev); } +static void virtio_net_driver_ok (VirtIODevice *vdev) +{ +VirtIONet *n =to_virtio_net (vdev); +int r; + + r =vhost_net_init (&n->vhost, vdev, n->vhost_device); +if (r) { +fprintf (stderr,"\nvhost_net_init returned %d\n" , r); +exit (-r); + } +} +VirtIODevice *virtio_net_init (DeviceState *dev) {VirtIONet *n;
@@ -837,6 +865,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->vdev.set_features = virtio_net_set_features; n->vdev.bad_features = virtio_net_bad_features; n->vdev.reset = virtio_net_reset; + n->vdev.driver_ok = virtio_net_driver_ok; n->rx_vq =virtio_add_queue (&n->vdev,256 , virtio_net_handle_rx); n->tx_vq =virtio_add_queue (&n->vdev,256 , virtio_net_handle_tx); n->ctrl_vq =virtio_add_queue (&n->vdev,64 , virtio_net_handle_ctrl);
@@ -863,6 +892,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->vdev.nvectors =3 ; else n->vdev.nvectors = dev->nd->nvectors; + n->vhost_device = dev->nd->vhost_device;register_savevm ("virtio-net" , virtio_net_id++,VIRTIO_NET_VM_VERSION , virtio_net_save, virtio_net_load, n);
hw/virtio-pci.c +40 -0
@@ -15,11 +15,13 @@#include <inttypes.h> +#include <linux/kvm.h> #include "virtio.h" #include "pci.h" #include "sysemu.h" #include "msix.h" #include "net.h" +#include "qemu-kvm.h" /* from Linux's linux/virtio_pci.h */
@@ -199,6 +201,8 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) vdev->status = val &0xFF ;if (vdev->status ==0 )virtio_pci_reset (proxy); +if ((val &VIRTIO_CONFIG_S_DRIVER_OK ) && vdev->driver_ok) + vdev->driver_ok (vdev);break ;case VIRTIO_MSI_CONFIG_VECTOR :msix_vector_unuse (&proxy->pci_dev, vdev->config_vector);
@@ -365,12 +369,48 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,msix_write_config (pci_dev, address, val, len); } +static int virtio_pci_irqfd (void * opaque,uint16_t vector,int fd) +{ +VirtIOPCIProxy *proxy = opaque; +struct kvm_irqfd call = { }; +int r; + +if (vector >= proxy->pci_dev.msix_entries_nr) +return -EINVAL ; +if (!proxy->pci_dev.msix_entry_used[vector]) +return -ENOENT ; + call.fd = fd; + call.gsi = proxy->pci_dev.msix_irq_entries[vector].gsi; + r =kvm_vm_ioctl (kvm_state,KVM_IRQFD , &call); +if (r <0 ) +return r; +return 0 ; +} + +static int virtio_pci_queuefd (void * opaque,int n,int fd) +{ +VirtIOPCIProxy *proxy = opaque; +struct kvm_ioeventfd kick = { + .datamatch = n, + .addr = proxy->addr +VIRTIO_PCI_QUEUE_NOTIFY , + .len =2 , + .flags =KVM_IOEVENTFD_FLAG_DATAMATCH |KVM_IOEVENTFD_FLAG_PIO , + .fd = fd, + }; +int r =kvm_vm_ioctl (kvm_state,KVM_IOEVENTFD , &kick); +if (r <0 ) +return r; +return 0 ; +} +static const VirtIOBindings virtio_pci_bindings = { .notify = virtio_pci_notify, .save_config = virtio_pci_save_config, .load_config = virtio_pci_load_config, .save_queue = virtio_pci_save_queue, .load_queue = virtio_pci_load_queue, + .irqfd = virtio_pci_irqfd, + .queuefd = virtio_pci_queuefd, }; static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,
hw/virtio.c +0 -19
@@ -54,24 +54,6 @@ typedef struct VRingUsedVRingUsedElem ring[0 ]; } VRingUsed; -typedef struct VRing -{ -unsigned int num; -target_phys_addr_t desc; -target_phys_addr_t avail; -target_phys_addr_t used; -}VRing ; - -struct VirtQueue -{ -VRing vring; -target_phys_addr_t pa; -uint16_t last_avail_idx; -int inuse; -uint16_t vector; -void (*handle_output)(VirtIODevice *vdev,VirtQueue *vq); -}; -#define VIRTIO_PCI_QUEUE_MAX 16/* virt queue functions */
@@ -401,7 +383,6 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) sg->iov_base =cpu_physical_memory_map (vring_desc_addr (desc_pa, i), &len, is_write); -if (sg->iov_base ==NULL || len != sg->iov_len) {fprintf (stderr,"virtio: trying to map MMIO memory\n" );exit (1 );
hw/virtio.h +25 -3
@@ -54,15 +54,34 @@struct VirtQueue ; +typedef struct VRing +{ +unsigned int num; +target_phys_addr_t desc; +target_phys_addr_t avail; +target_phys_addr_t used; +}VRing ; + +typedef struct VirtQueue VirtQueue ; +struct VirtIODevice ; +typedef struct VirtIODevice VirtIODevice ; + +struct VirtQueue +{ +VRing vring; +target_phys_addr_t pa; +uint16_t last_avail_idx; +int inuse; +uint16_t vector; +void (*handle_output)(VirtIODevice *vdev,VirtQueue *vq); +}; +static inline target_phys_addr_t vring_align (target_phys_addr_t addr,unsigned long align) {return (addr + align -1 ) & ~(align -1 ); } -typedef struct VirtQueue VirtQueue ; -typedef struct VirtIODevice VirtIODevice ; -#define VIRTQUEUE_MAX_SIZE 1024 typedef struct VirtQueueElement
@@ -81,6 +100,8 @@ typedef struct {void (*save_queue)(void * opaque,int n,QEMUFile *f);int (*load_config)(void * opaque,QEMUFile *f);int (*load_queue)(void * opaque,int n,QEMUFile *f); +int (*irqfd)(void * opaque,uint16_t vector,int fd); +int (*queuefd)(void * opaque,int n,int fd); } VirtIOBindings;#define VIRTIO_PCI_QUEUE_MAX 16
@@ -104,6 +125,7 @@ struct VirtIODevicevoid (*get_config)(VirtIODevice *vdev,uint8_t *config);void (*set_config)(VirtIODevice *vdev,const uint8_t *config);void (*reset)(VirtIODevice *vdev); +void (*driver_ok)(VirtIODevice *vdev);VirtQueue *vq;const VirtIOBindings *binding;void *binding_opaque;
net.c +5 -1
@@ -2588,7 +2588,8 @@ int net_client_init(Monitor *mon, const char *device, const char *p) }if (!strcmp (device,"nic" )) {static const char *const nic_params[] = { -"vlan" ,"name" ,"macaddr" ,"model" ,"addr" ,"id" ,"vectors" ,NULL +"vlan" ,"name" ,"macaddr" ,"model" ,"addr" ,"id" ,"vectors" , +"vhost" ,NULL };NICInfo *nd;uint8_t *macaddr;
@@ -2620,6 +2621,9 @@ int net_client_init(Monitor *mon, const char *device, const char *p) goto out; } } +if (get_param_value (buf,sizeof (buf),"vhost" , p)) { + nd->vhost_device =strdup (buf); + }if (get_param_value (buf,sizeof (buf),"model" , p)) { nd->model =strdup (buf); }
net.h +1 -0
@@ -110,6 +110,7 @@ struct NICInfo {int used;int bootable;int nvectors; +char *vhost_device; };extern int nb_nics;
qemu-kvm.c +0 -8
@@ -136,14 +136,6 @@ static inline void clear_gsi(kvm_context_t kvm, unsigned int gsi)DPRINTF ("Invalid GSI %d\n" ); } -struct slot_info { -unsigned long phys_addr; -unsigned long len; -unsigned long userspace_addr; -unsigned flags; -int logging_count; -}; -struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS ];static void init_slots (void )
qemu-kvm.h +9 -0
@@ -1215,6 +1215,15 @@ int kvm_ioctl(KVMState *s, int type, ...);int kvm_vm_ioctl (KVMState *s,int type, ...);int kvm_check_extension (KVMState *s,unsigned int ext); +struct slot_info { +unsigned long phys_addr; +unsigned long len; +unsigned long userspace_addr; +unsigned flags; +int logging_count; +}; + +extern struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS ];#endif #endif
Signature · 1 line
1.6.2.5
Michael S. Tsirkin
maintainerPATCH v3 3/4 qemu-kvm: vhost-net implementation
In reply to a message not in the archiveThis adds support for vhost-net virtio kernel backend. To enable (assuming device eth2):
1. enable promisc mode or program guest mac in device eth2
2. disable tso, gso, lro, jumbo frames on the card (disabling lro + jumbo frames should be sufficient, haven't tested this)
3. add vhost=eth2 to -net flag
4. run with CAP_NET_ADMIN priviledge (e.g. root)
This patch is RFC, but works without issues for me.
It still needs to be split up, tested and benchmarked properly, but posting it here in case people want to test drive the kernel bits I posted.
--- Makefile.target | 3 +- hw/vhost_net.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/vhost_net.h | 30 +++++++++ hw/virtio-net.c | 32 ++++++++++- hw/virtio-pci.c | 40 ++++++++++++ hw/virtio.c | 19 ------ hw/virtio.h | 28 ++++++++- net.c | 6 ++- net.h | 1 + qemu-kvm.c | 8 --- qemu-kvm.h | 9 +++ 11 files changed, 324 insertions(+), 33 deletions(-) create mode 100644 hw/vhost_net.c create mode 100644 hw/vhost_net.h
Makefile.target +2 -1
@@ -170,7 +170,8 @@ obj-y = vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o \ gdbstub.o gdbstub-xml.o msix.o ioport.o qemu-config.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly -obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o +obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o \ + vhost_net.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o LIBS+=-lz
hw/vhost_net.c new file +181 -0
181 lines not on this page · raw message
hw/vhost_net.h new file +30 -0
@@ -0,0 +1,30 @@ +#ifndef VHOST_NET_H +#define VHOST_NET_H + +#include "hw/virtio.h" + +struct vhost_virtqueue { +int kick; +int call; +void *desc; +void *avail; +void *used; +}; + +struct vhost_dev { +int control; +struct vhost_virtqueue *vqs; +int nvqs; +}; + +struct vhost_net { +struct vhost_dev dev; +struct vhost_virtqueue vqs[2 ]; +int sock; +}; + +int vhost_net_init (struct vhost_net *net, +VirtIODevice *dev, +char *vhost_device); + +#endif
hw/virtio-net.c +31 -1
@@ -19,6 +19,8 @@#include "qemu-kvm.h" #endif +#include "vhost_net.h" +#define TAP_VNET_HDR #define VIRTIO_NET_VM_VERSION 10
@@ -56,6 +58,8 @@ typedef struct VirtIONetuint8_t *macs; } mac_table;uint32_t *vlans; +char *vhost_device; +struct vhost_net vhost; } VirtIONet;/* TODO
@@ -134,9 +138,12 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev) (1 <<VIRTIO_NET_F_CTRL_RX ) | (1 <<VIRTIO_NET_F_CTRL_VLAN ) | (1 <<VIRTIO_NET_F_CTRL_RX_EXTRA ); +VirtIONet *n =to_virtio_net (vdev); + +if (n->vhost_device) +return 1 <<VIRTIO_NET_F_MAC ;#ifdef TAP_VNET_HDR -VirtIONet *n =to_virtio_net (vdev);VLANClientState *host = n->vc->vlan->first_client;if (tap_has_vnet_hdr (host)) {
@@ -175,6 +182,9 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev)static void virtio_net_set_features (VirtIODevice *vdev,uint32_t features) {VirtIONet *n =to_virtio_net (vdev); +/* vhost net supports no features */ +if (n->vhost_device) +return ;#ifdef TAP_VNET_HDR VLANClientState *host = n->vc->vlan->first_client;#endif
@@ -351,6 +361,9 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)static int do_virtio_net_can_receive (VirtIONet *n,int bufsize) { +if (n->vhost_device) +return 0 ; +if (!virtio_queue_ready (n->rx_vq) || !(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK ))return 0 ;
@@ -411,6 +424,7 @@ static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count)while (offset < count && i < iovcnt) {int len =MIN (iov[i].iov_len, count - offset);memcpy (iov[i].iov_base, buf + offset, len); + offset += len; i++; }
@@ -610,6 +624,8 @@ static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)#else int has_vnet_hdr =0 ;#endif +if (n->vhost_device) +return ;if (!(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK ))return ;
@@ -822,6 +838,18 @@ static void virtio_net_cleanup(VLANClientState *vc)virtio_cleanup (&n->vdev); } +static void virtio_net_driver_ok (VirtIODevice *vdev) +{ +VirtIONet *n =to_virtio_net (vdev); +int r; + + r =vhost_net_init (&n->vhost, vdev, n->vhost_device); +if (r) { +fprintf (stderr,"\nvhost_net_init returned %d\n" , r); +exit (-r); + } +} +VirtIODevice *virtio_net_init (DeviceState *dev) {VirtIONet *n;
@@ -837,6 +865,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->vdev.set_features = virtio_net_set_features; n->vdev.bad_features = virtio_net_bad_features; n->vdev.reset = virtio_net_reset; + n->vdev.driver_ok = virtio_net_driver_ok; n->rx_vq =virtio_add_queue (&n->vdev,256 , virtio_net_handle_rx); n->tx_vq =virtio_add_queue (&n->vdev,256 , virtio_net_handle_tx); n->ctrl_vq =virtio_add_queue (&n->vdev,64 , virtio_net_handle_ctrl);
@@ -863,6 +892,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->vdev.nvectors =3 ; else n->vdev.nvectors = dev->nd->nvectors; + n->vhost_device = dev->nd->vhost_device;register_savevm ("virtio-net" , virtio_net_id++,VIRTIO_NET_VM_VERSION , virtio_net_save, virtio_net_load, n);
hw/virtio-pci.c +40 -0
@@ -15,11 +15,13 @@#include <inttypes.h> +#include <linux/kvm.h> #include "virtio.h" #include "pci.h" #include "sysemu.h" #include "msix.h" #include "net.h" +#include "qemu-kvm.h" /* from Linux's linux/virtio_pci.h */
@@ -199,6 +201,8 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) vdev->status = val &0xFF ;if (vdev->status ==0 )virtio_pci_reset (proxy); +if ((val &VIRTIO_CONFIG_S_DRIVER_OK ) && vdev->driver_ok) + vdev->driver_ok (vdev);break ;case VIRTIO_MSI_CONFIG_VECTOR :msix_vector_unuse (&proxy->pci_dev, vdev->config_vector);
@@ -365,12 +369,48 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,msix_write_config (pci_dev, address, val, len); } +static int virtio_pci_irqfd (void * opaque,uint16_t vector,int fd) +{ +VirtIOPCIProxy *proxy = opaque; +struct kvm_irqfd call = { }; +int r; + +if (vector >= proxy->pci_dev.msix_entries_nr) +return -EINVAL ; +if (!proxy->pci_dev.msix_entry_used[vector]) +return -ENOENT ; + call.fd = fd; + call.gsi = proxy->pci_dev.msix_irq_entries[vector].gsi; + r =kvm_vm_ioctl (kvm_state,KVM_IRQFD , &call); +if (r <0 ) +return r; +return 0 ; +} + +static int virtio_pci_queuefd (void * opaque,int n,int fd) +{ +VirtIOPCIProxy *proxy = opaque; +struct kvm_ioeventfd kick = { + .datamatch = n, + .addr = proxy->addr +VIRTIO_PCI_QUEUE_NOTIFY , + .len =2 , + .flags =KVM_IOEVENTFD_FLAG_DATAMATCH |KVM_IOEVENTFD_FLAG_PIO , + .fd = fd, + }; +int r =kvm_vm_ioctl (kvm_state,KVM_IOEVENTFD , &kick); +if (r <0 ) +return r; +return 0 ; +} +static const VirtIOBindings virtio_pci_bindings = { .notify = virtio_pci_notify, .save_config = virtio_pci_save_config, .load_config = virtio_pci_load_config, .save_queue = virtio_pci_save_queue, .load_queue = virtio_pci_load_queue, + .irqfd = virtio_pci_irqfd, + .queuefd = virtio_pci_queuefd, }; static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,
hw/virtio.c +0 -19
@@ -54,24 +54,6 @@ typedef struct VRingUsedVRingUsedElem ring[0 ]; } VRingUsed; -typedef struct VRing -{ -unsigned int num; -target_phys_addr_t desc; -target_phys_addr_t avail; -target_phys_addr_t used; -}VRing ; - -struct VirtQueue -{ -VRing vring; -target_phys_addr_t pa; -uint16_t last_avail_idx; -int inuse; -uint16_t vector; -void (*handle_output)(VirtIODevice *vdev,VirtQueue *vq); -}; -#define VIRTIO_PCI_QUEUE_MAX 16/* virt queue functions */
@@ -401,7 +383,6 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) sg->iov_base =cpu_physical_memory_map (vring_desc_addr (desc_pa, i), &len, is_write); -if (sg->iov_base ==NULL || len != sg->iov_len) {fprintf (stderr,"virtio: trying to map MMIO memory\n" );exit (1 );
hw/virtio.h +25 -3
@@ -54,15 +54,34 @@struct VirtQueue ; +typedef struct VRing +{ +unsigned int num; +target_phys_addr_t desc; +target_phys_addr_t avail; +target_phys_addr_t used; +}VRing ; + +typedef struct VirtQueue VirtQueue ; +struct VirtIODevice ; +typedef struct VirtIODevice VirtIODevice ; + +struct VirtQueue +{ +VRing vring; +target_phys_addr_t pa; +uint16_t last_avail_idx; +int inuse; +uint16_t vector; +void (*handle_output)(VirtIODevice *vdev,VirtQueue *vq); +}; +static inline target_phys_addr_t vring_align (target_phys_addr_t addr,unsigned long align) {return (addr + align -1 ) & ~(align -1 ); } -typedef struct VirtQueue VirtQueue ; -typedef struct VirtIODevice VirtIODevice ; -#define VIRTQUEUE_MAX_SIZE 1024 typedef struct VirtQueueElement
@@ -81,6 +100,8 @@ typedef struct {void (*save_queue)(void * opaque,int n,QEMUFile *f);int (*load_config)(void * opaque,QEMUFile *f);int (*load_queue)(void * opaque,int n,QEMUFile *f); +int (*irqfd)(void * opaque,uint16_t vector,int fd); +int (*queuefd)(void * opaque,int n,int fd); } VirtIOBindings;#define VIRTIO_PCI_QUEUE_MAX 16
@@ -104,6 +125,7 @@ struct VirtIODevicevoid (*get_config)(VirtIODevice *vdev,uint8_t *config);void (*set_config)(VirtIODevice *vdev,const uint8_t *config);void (*reset)(VirtIODevice *vdev); +void (*driver_ok)(VirtIODevice *vdev);VirtQueue *vq;const VirtIOBindings *binding;void *binding_opaque;
net.c +5 -1
@@ -2588,7 +2588,8 @@ int net_client_init(Monitor *mon, const char *device, const char *p) }if (!strcmp (device,"nic" )) {static const char *const nic_params[] = { -"vlan" ,"name" ,"macaddr" ,"model" ,"addr" ,"id" ,"vectors" ,NULL +"vlan" ,"name" ,"macaddr" ,"model" ,"addr" ,"id" ,"vectors" , +"vhost" ,NULL };NICInfo *nd;uint8_t *macaddr;
@@ -2620,6 +2621,9 @@ int net_client_init(Monitor *mon, const char *device, const char *p) goto out; } } +if (get_param_value (buf,sizeof (buf),"vhost" , p)) { + nd->vhost_device =strdup (buf); + }if (get_param_value (buf,sizeof (buf),"model" , p)) { nd->model =strdup (buf); }
net.h +1 -0
@@ -110,6 +110,7 @@ struct NICInfo {int used;int bootable;int nvectors; +char *vhost_device; };extern int nb_nics;
qemu-kvm.c +0 -8
@@ -136,14 +136,6 @@ static inline void clear_gsi(kvm_context_t kvm, unsigned int gsi)DPRINTF ("Invalid GSI %d\n" ); } -struct slot_info { -unsigned long phys_addr; -unsigned long len; -unsigned long userspace_addr; -unsigned flags; -int logging_count; -}; -struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS ];static void init_slots (void )
qemu-kvm.h +9 -0
@@ -1215,6 +1215,15 @@ int kvm_ioctl(KVMState *s, int type, ...);int kvm_vm_ioctl (KVMState *s,int type, ...);int kvm_check_extension (KVMState *s,unsigned int ext); +struct slot_info { +unsigned long phys_addr; +unsigned long len; +unsigned long userspace_addr; +unsigned flags; +int logging_count; +}; + +extern struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS ];#endif #endif
Signature · 1 line
1.6.2.5
Re: [PATCHv3 3/4] qemu-kvm: vhost-net implementation
Hi Michael,
Some random high-level comments:
- I had expected this to be available as:
-net raw,ifname=eth2 -net nic,model=virtio
I'd prefer it this way, because it means you can use this mode even without vhost and it's ties in better with the way all other qemu networking modes work.
Like the VNET_HDR support, the "detect that there is only one backend on the vlan, and that it is a raw socket" code will be a hack until we have command line options that don't involve vlans.
Open question whether we the user should explicitly have to request vhost acceleration or we use it if it is available. The latter sounds nice, but it hasn't turned out great for kvm.
- CAP_NET_ADMIN is needed for raw sockets, so for e.g. libvirt I think we need to be able to support passing the raw socket fd via the command line and the monitor interface. I don't think we need that for the vhost fd, it should be safe to allow unprivileged users access to that, I think.
- I thought this version supports migration, but I don't see where that is handled in the code?
- You're not using the errors eventfd?
I've taken a first look through the kernel patch, hopefully I'll post decent comments for that soon, but in the meantime:
- I think /dev/vhost makes more sense - we shouldn't need to add another character device if we implement kernel backends for other virtio devices
- I'd really like vhost to support a 'tap' mode, so that we can still use a bridge if a NIC isn't available to be assigned. It would result in this stuff getting much more testing. Options I see:
1) Add tap-like functionality to vhost
2) Add VHOST_NET_SET_TAP
3) Just tell people to set up a tap and bind a raw socket too it
IMHO, (2) makes the most sense - it should be much less exta kernel
code than (1), and it would be much more convenient than (3)
- Distros will need a udev rule to set the device perms to 0666
- Distros will also need some way to have the module loaded if its not built-in; best I can think of in Fedora is to stick it in /etc/sysconfig/modules/kvm.modules
What would be nicer is if loading the kvm module could cause vhost to be loaded. It's nice that vhost can be used without kvm, but I think if kvm is loaded it's just very convenient to load vhost too. See also the problems we've cause with needing mkinitrd/dracut hacks by not having KVM_GUEST require VIRTIO_PCI
Quoted 3 lines · Michael S. Tsirkin
This adds support for vhost-net virtio kernel backend. To enable (assuming device eth2):
1. enable promisc mode or program guest mac in device eth2
Why can't vhost do this itself?
Quoted 3 lines
2. disable tso, gso, lro, jumbo frames on the card (disabling lro + jumbo frames should be sufficient, haven't tested this)
And this.
If we leave that up to the user or the management app, we need to expose to them what features vhost supports so that they can know in future to stop disabling them.
Quoted 55 lines
3. add vhost=eth2 to -net flag
4. run withCAP_NET_ADMINpriviledge (e.g. root)This patch is RFC, but works without issues for me.
It still needs to be split up, tested and benchmarked properly, but posting it here in case people want to test drive the kernel bits I posted.
Signed-off-by Michael S. Tsirkin <[email protected]>--- Makefile.target | 3 +- hw/vhost_net.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/vhost_net.h | 30 +++++++++ hw/virtio-net.c | 32 ++++++++++- hw/virtio-pci.c | 40 ++++++++++++ hw/virtio.c | 19 ------ hw/virtio.h | 28 ++++++++- net.c | 6 ++- net.h | 1 + qemu-kvm.c | 8 --- qemu-kvm.h | 9 +++ 11 files changed, 324 insertions(+), 33 deletions(-) create mode 100644 hw/vhost_net.c create mode 100644 hw/vhost_net.hMakefile.target +2
-1@@ -170,7 +170,8 @@ obj-y = vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o \ gdbstub.o gdbstub-xml.o msix.o ioport.o qemu-config.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly -obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o +obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o \ + vhost_net.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o LIBS+=-lzhw/vhost_net.c new file +9
-0@@ -0,0 +1,181 @@ +#include <sys/eventfd.h> +#include <sys/socket.h> +#include <linux/kvm.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/vhost.h> +#include <linux/virtio_ring.h> +#include <netpacket/packet.h> +#include <net/ethernet.h>
hw/vhost_net.c line 19
Quoted 10 lines
hw/vhost_net.c new file +10
-0+#include <net/if.h> +#include <netinet/in.h> + +#include <stdio.h> + +#include "qemu-kvm.h" + +#include "vhost_net.h" + +const char *vhost_net_device;
Seems to be unused
Quoted 13 lines
hw/vhost_net.c new file +13
-0+ +static int vhost_virtqueue_init (struct vhost_dev *dev, +struct VirtIODevice *vdev, +struct vhost_virtqueue *vq, +struct VirtQueue *q, +unsigned idx) +{ +target_phys_addr_t s, l; +int r; +struct vhost_vring_addr addr = { + .index = idx, + }; +struct vhost_vring_file file = {
hw/vhost_net.c line 42
Quoted 10 lines
hw/vhost_net.c new file +10
-0+ .index = idx, + }; +struct vhost_vring_state size = { + .index = idx, + }; + + size.num = q->vring.num; + r =ioctl (dev->control,VHOST_SET_VRING_NUM , &size); +if (r) +return -errno;
VHOST_SET_VRING_SIZE is a better name
Also, qemu coding style is:
if (r) {
return -errno;
}Yes, I hate it too
hw/vhost_net.c line 44
Quoted 2 lines
hw/vhost_net.c new file +2
-0+ + file.fd = vq->kick =eventfd (0 ,0 );
Handle error
Do we not want CLOEXEC and NONBLOCK ?
hw/vhost_net.c line 57
Quoted 13 lines
hw/vhost_net.c new file +13
-0+ r =ioctl (dev->control,VHOST_SET_VRING_KICK , &file); +if (r) +return -errno; + file.fd = vq->call =eventfd (0 ,0 ); + r =ioctl (dev->control,VHOST_SET_VRING_CALL , &file); +if (r) +return -errno; + + s = l =sizeof (struct vring_desc ) * q->vring.num; + vq->desc =cpu_physical_memory_map (q->vring.desc, &l,0 ); +if (!vq->desc || l != s) +return -ENOMEM ; + addr.user_addr = (u_int64_t )(unsigned long )vq->desc;
Easier to read if it was re-factored to:
vq->desc = vhost_map(q->vring.desc,
sizeof(struct vring_desc) * q->vring.num,
&addr.user_addr);
if (!vq->desc)
return -ENOMEM;hw/vhost_net.c line 66
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ r =ioctl (dev->control,VHOST_SET_VRING_DESC , &addr); +if (r <0 ) +return -errno; + s = l = offsetof(struct vring_avail , ring) + +sizeof (u_int64_t) * q->vring.num; + vq->avail =cpu_physical_memory_map (q->vring.avail, &l,0 ); +if (!vq->avail || l != s) +return -ENOMEM ; + addr.user_addr = (u_int64_t )(unsigned long )vq->avail;
And re-used here
hw/vhost_net.c line 75
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ r =ioctl (dev->control,VHOST_SET_VRING_AVAIL , &addr); +if (r <0 ) +return -errno; + s = l = offsetof(struct vring_used , ring) + +sizeof (struct vring_used_elem ) * q->vring.num; + vq->used =cpu_physical_memory_map (q->vring.used, &l,1 ); +if (!vq->used || l != s) +return -ENOMEM ; + addr.user_addr = (u_int64_t )(unsigned long )vq->used;
and here
Need to unmap these too at some point
hw/vhost_net.c line 86
Quoted 11 lines
hw/vhost_net.c new file +11
-0+ r =ioctl (dev->control,VHOST_SET_VRING_USED , &addr); +if (r <0 ) +return -errno; + + r = vdev->binding->irqfd (vdev->binding_opaque, q->vector, vq->call); +if (r <0 ) +return -errno; + + r = vdev->binding->queuefd (vdev->binding_opaque, idx, vq->kick); +if (r <0 ) +return -errno;
Naming is a little confused here too
kick vs. call
irqfd vs. queuefd
irqfd vs. ioeventfd
How about just keeping it simple, sticking with the latter and making it VHOST_SET_VRING_IRQFD and VHOST_SET_VRING_IOEVENTFD ?
Quoted 5 lines
hw/vhost_net.c new file +5
-0+ +return 0 ; +} + +static int vhost_dev_init(struct vhost_dev *hdev,
hw/vhost_net.c line 101
Quoted 10 lines
hw/vhost_net.c new file +10
-0+ VirtIODevice *vdev) +{ +int i, r, n =0 ; +struct vhost_memory *mem; + hdev->control =open ("/dev/vhost-net" ,O_RDWR ); +if (hdev->control <0 ) +return -errno; + r =ioctl (hdev->control,VHOST_SET_OWNER ,NULL ); +if (r <0 ) +return -errno;
Should close the fd if we fail here or later
(Yep, I know you exit on error but ...)
hw/vhost_net.c line 107
Quoted 6 lines
hw/vhost_net.c new file +6
-0+for (i =0 ; i <KVM_MAX_NUM_MEM_REGIONS ; ++i) { +if (!slots[i].len || (slots[i].flags &KVM_MEM_LOG_DIRTY_PAGES )) { +continue ; + } + ++n; + }
I don't love making a "slots" variable global, it'd be nicer to have:
slots = kvm_get_slots(&n_slots);
naming confusion here between "slots" and "regions" too
Also, max regions is 32, so I'd just allocate max regions and if some are unused it's no big deal
hw/vhost_net.c line 109
Quoted 1 line
hw/vhost_net.c new file +1
-0+ mem = qemu_mallocz(offsetof(struct vhost_memory, regions) +
sizeof(*mem) would suffice here
hw/vhost_net.c line 118
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ n * sizeof(struct vhost_memory_region)); +if (!mem) +return -ENOMEM ; + mem->nregions = n; + n =0 ; +for (i =0 ; i <KVM_MAX_NUM_MEM_REGIONS ; ++i) { +if (!slots[i].len || (slots[i].flags &KVM_MEM_LOG_DIRTY_PAGES )) { +continue ; + }
Ignoring the LOG_DIRTY_PAGES regions could do with an explanatory comment
hw/vhost_net.c line 127
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ mem->regions[n].guest_phys_addr = slots[i].phys_addr; + mem->regions[n].memory_size = slots[i].len; + mem->regions[n].userspace_addr = slots[i].userspace_addr; + ++n; + } + + r =ioctl (hdev->control,VHOST_SET_MEM_TABLE , mem); +if (r <0 ) +return -errno;
Need to free the table (on error too)
hw/vhost_net.c line 136
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ +for (i =0 ; i < hdev->nvqs; ++i) { + r =vhost_virtqueue_init (hdev, + vdev, + hdev->vqs + i, + vdev->vq + i, + i); +if (r <0 ) +return r;
Need to handle errors better, e.g. unmapping
hw/vhost_net.c line 144
Quoted 8 lines
hw/vhost_net.c new file +8
-0+ } + +return 0 ; +} + +int vhost_net_init (struct vhost_net *net, +VirtIODevice *dev, +char *vhost_device)
const char *vhost_device
hw/vhost_net.c line 151
Quoted 7 lines
hw/vhost_net.c new file +7
-0+{ +struct sockaddr_ll lladdr; +struct ifreq req; +int r; +const char *ifname = vhost_device; +if (!ifname) +return 0 ;
Why the ifname variable?
Quoted 13 lines
hw/vhost_net.c new file +13
-0+ net->dev.nvqs =2 ; + net->dev.vqs = net->vqs; + r =vhost_dev_init (&net->dev, dev); +if (r <0 ) +return r; + + net->sock =socket (PF_PACKET ,SOCK_RAW ,htons (ETH_P_ALL )); +if (net->sock <0 ) +return -errno; + +memset (&req,0 ,sizeof (req)); +strncpy (req.ifr_name, ifname,IFNAMSIZ -1 ); + r =ioctl (net->sock,SIOCGIFINDEX , &req);
hw/vhost_net.c line 175
Quoted 10 lines
hw/vhost_net.c new file +10
-0+if (r <0 ) +return -errno; + +memset (&lladdr,0 ,sizeof (lladdr)); + lladdr.sll_family =AF_PACKET ; + lladdr.sll_protocol =htons (ETH_P_ALL ); + lladdr.sll_ifindex = req.ifr_ifindex; + r =bind (net->sock, (const struct sockaddr *)&lladdr,sizeof (lladdr)); +if (r <0 ) +return -errno;
You've get the point at this stage, but need to close the socket here on error too
Quoted 25 lines
hw/vhost_net.c new file +5
-0+ r =ioctl (net->dev.control,VHOST_NET_SET_SOCKET , &net->sock); +if (r <0 ) +return -errno; +return 0 ; +}hw/vhost_net.h new file +14
-0@@ -0,0 +1,30 @@ +#ifndef VHOST_NET_H +#define VHOST_NET_H + +#include "hw/virtio.h" + +struct vhost_virtqueue { +int kick; +int call; +void *desc; +void *avail; +void *used; +}; + +struct vhost_dev {
hw/vhost_net.h line 24
Quoted 10 lines
hw/vhost_net.h new file +10
-0+int control; +struct vhost_virtqueue *vqs; +int nvqs; +}; + +struct vhost_net { +struct vhost_dev dev; +struct vhost_virtqueue vqs[2 ]; +int sock; +};
I think I'd just malloc the vqs - having two vqs pointers in the two structs is a little confusing. Maybe it's worth it, though
Quoted 28 lines
hw/vhost_net.h new file +5
-0+int vhost_net_init (struct vhost_net *net, +VirtIODevice *dev, +char *vhost_device); + +#endif hw/virtio-net.c +4
-0@@ -19,6 +19,8 @@#include "qemu-kvm.h" #endif +#include "vhost_net.h" +#define TAP_VNET_HDR #define VIRTIO_NET_VM_VERSION 10@@ -56,6 +58,8 @@ typedef struct VirtIONetuint8_t *macs; } mac_table;uint32_t *vlans; +char *vhost_device; +struct vhost_net vhost; } VirtIONet;/* TODO
hw/virtio-net.c static uint32_t virtio_net_get_features(VirtIODevice *vdev) · line 144
Quoted 7 lines
hw/virtio-net.c +4
-0@@ -134,9 +138,12 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev) (1 <<VIRTIO_NET_F_CTRL_RX ) | (1 <<VIRTIO_NET_F_CTRL_VLAN ) | (1 <<VIRTIO_NET_F_CTRL_RX_EXTRA ); +VirtIONet *n =to_virtio_net (vdev); + +if (n->vhost_device) +return 1 <<VIRTIO_NET_F_MAC ;
features = MAC;
if (n->vhost_device)
return features;
features |= ...;
Quoted 26 lines
hw/virtio-net.c +6
-1#ifdef TAP_VNET_HDR -VirtIONet *n =to_virtio_net (vdev);VLANClientState *host = n->vc->vlan->first_client;if (tap_has_vnet_hdr (host)) {@@ -175,6 +182,9 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev)static void virtio_net_set_features (VirtIODevice *vdev,uint32_t features) {VirtIONet *n =to_virtio_net (vdev); +/* vhost net supports no features */ +if (n->vhost_device) +return ;#ifdef TAP_VNET_HDR VLANClientState *host = n->vc->vlan->first_client;#endif @@ -351,6 +361,9 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)static int do_virtio_net_can_receive (VirtIONet *n,int bufsize) { +if (n->vhost_device) +return 0 ; +if (!virtio_queue_ready (n->rx_vq) || !(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK ))return 0 ;
hw/virtio-net.c static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count) · line 427
Quoted 4 lines
hw/virtio-net.c +1
-0@@ -411,6 +424,7 @@ static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count)while (offset < count && i < iovcnt) {int len =MIN (iov[i].iov_len, count - offset);memcpy (iov[i].iov_base, buf + offset, len); +
Spurious
Quoted 13 lines
hw/virtio-net.c +2
-0offset += len; i++; }@@ -610,6 +624,8 @@ static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)#else int has_vnet_hdr =0 ;#endif +if (n->vhost_device) +return ;if (!(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK ))return ;
hw/virtio-net.c static void virtio_net_cleanup(VLANClientState *vc) · line 846
Quoted 9 lines
hw/virtio-net.c +6
-0@@ -822,6 +838,18 @@ static void virtio_net_cleanup(VLANClientState *vc)virtio_cleanup (&n->vdev); } +static void virtio_net_driver_ok (VirtIODevice *vdev) +{ +VirtIONet *n =to_virtio_net (vdev); +int r; + + r =vhost_net_init (&n->vhost, vdev, n->vhost_device);
Might be worth a comment as to why we need to wait until here to setup vhost; it's not immediately obvious
hw/virtio-net.c static void virtio_net_cleanup(VLANClientState *vc) · line 850
Quoted 4 lines
hw/virtio-net.c +4
-0+if (r) { +fprintf (stderr,"\nvhost_net_init returned %d\n" , r); +exit (-r); + }
I think I'd like us to be able to fallback to normal userspace virtio-net with raw socket if we can't init vhost
Also, the guest is up and running at this point, right? With a linux guest, the root filesystem is probably mounted. It's not polite to just exit at this point if we can't open a raw socket or initialize vhost
Quoted 47 lines
hw/virtio-net.c +2
-0VirtIODevice *virtio_net_init (DeviceState *dev) {VirtIONet *n;@@ -837,6 +865,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->vdev.set_features = virtio_net_set_features; n->vdev.bad_features = virtio_net_bad_features; n->vdev.reset = virtio_net_reset; + n->vdev.driver_ok = virtio_net_driver_ok; n->rx_vq =virtio_add_queue (&n->vdev,256 , virtio_net_handle_rx); n->tx_vq =virtio_add_queue (&n->vdev,256 , virtio_net_handle_tx); n->ctrl_vq =virtio_add_queue (&n->vdev,64 , virtio_net_handle_ctrl);@@ -863,6 +892,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->vdev.nvectors =3 ; else n->vdev.nvectors = dev->nd->nvectors; + n->vhost_device = dev->nd->vhost_device;register_savevm ("virtio-net" , virtio_net_id++,VIRTIO_NET_VM_VERSION , virtio_net_save, virtio_net_load, n);hw/virtio-pci.c +4
-0@@ -15,11 +15,13 @@#include <inttypes.h> +#include <linux/kvm.h> #include "virtio.h" #include "pci.h" #include "sysemu.h" #include "msix.h" #include "net.h" +#include "qemu-kvm.h" /* from Linux's linux/virtio_pci.h */ @@ -199,6 +201,8 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) vdev->status = val &0xFF ;if (vdev->status ==0 )virtio_pci_reset (proxy); +if ((val &VIRTIO_CONFIG_S_DRIVER_OK ) && vdev->driver_ok) + vdev->driver_ok (vdev);break ;case VIRTIO_MSI_CONFIG_VECTOR :msix_vector_unuse (&proxy->pci_dev, vdev->config_vector);
hw/virtio-pci.c static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, · line 375
Quoted 7 lines
hw/virtio-pci.c +4
-0@@ -365,12 +369,48 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,msix_write_config (pci_dev, address, val, len); } +static int virtio_pci_irqfd (void * opaque,uint16_t vector,int fd) +{ +VirtIOPCIProxy *proxy = opaque; +struct kvm_irqfd call = { };
Does the {}; init do anything?
hw/virtio-pci.c static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, · line 381
Quoted 6 lines
hw/virtio-pci.c +6
-0+int r; + +if (vector >= proxy->pci_dev.msix_entries_nr) +return -EINVAL ; +if (!proxy->pci_dev.msix_entry_used[vector]) +return -ENOENT ;
Does this mean if the guest doesn't have MSI support, we exit?
hw/virtio-pci.c static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, · line 387
Quoted 6 lines
hw/virtio-pci.c +6
-0+ call.fd = fd; + call.gsi = proxy->pci_dev.msix_irq_entries[vector].gsi; + r =kvm_vm_ioctl (kvm_state,KVM_IRQFD , &call); +if (r <0 ) +return r; +return 0 ;
Have you plans to implement a non-kvm mode for this too?
Quoted 59 lines
hw/virtio-pci.c +20
-0+} + +static int virtio_pci_queuefd (void * opaque,int n,int fd) +{ +VirtIOPCIProxy *proxy = opaque; +struct kvm_ioeventfd kick = { + .datamatch = n, + .addr = proxy->addr +VIRTIO_PCI_QUEUE_NOTIFY , + .len =2 , + .flags =KVM_IOEVENTFD_FLAG_DATAMATCH |KVM_IOEVENTFD_FLAG_PIO , + .fd = fd, + }; +int r =kvm_vm_ioctl (kvm_state,KVM_IOEVENTFD , &kick); +if (r <0 ) +return r; +return 0 ; +} +static const VirtIOBindings virtio_pci_bindings = { .notify = virtio_pci_notify, .save_config = virtio_pci_save_config, .load_config = virtio_pci_load_config, .save_queue = virtio_pci_save_queue, .load_queue = virtio_pci_load_queue, + .irqfd = virtio_pci_irqfd, + .queuefd = virtio_pci_queuefd, }; static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,hw/virtio.c +0
-18@@ -54,24 +54,6 @@ typedef struct VRingUsedVRingUsedElem ring[0 ]; } VRingUsed; -typedef struct VRing -{ -unsigned int num; -target_phys_addr_t desc; -target_phys_addr_t avail; -target_phys_addr_t used; -}VRing ; - -struct VirtQueue -{ -VRing vring; -target_phys_addr_t pa; -uint16_t last_avail_idx; -int inuse; -uint16_t vector; -void (*handle_output)(VirtIODevice *vdev,VirtQueue *vq); -}; -#define VIRTIO_PCI_QUEUE_MAX 16/* virt queue functions */
hw/virtio.c int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) · line 404
Quoted 4 lines
hw/virtio.c +0
-1@@ -401,7 +383,6 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) sg->iov_base =cpu_physical_memory_map (vring_desc_addr (desc_pa, i), &len, is_write); -
Spurious
You appear to have a vendetta against whitespace :-)
Quoted 23 lines
hw/virtio.c +0
-0if (sg->iov_base ==NULL || len != sg->iov_len) {fprintf (stderr,"virtio: trying to map MMIO memory\n" );exit (1 );hw/virtio.h +12
-0@@ -54,15 +54,34 @@struct VirtQueue ; +typedef struct VRing +{ +unsigned int num; +target_phys_addr_t desc; +target_phys_addr_t avail; +target_phys_addr_t used; +}VRing ; + +typedef struct VirtQueue VirtQueue ; +struct VirtIODevice ; +typedef struct VirtIODevice VirtIODevice ; +
hw/virtio.h line 78
Quoted 10 lines
hw/virtio.h +10
-0+struct VirtQueue +{ +VRing vring; +target_phys_addr_t pa; +uint16_t last_avail_idx; +int inuse; +uint16_t vector; +void (*handle_output)(VirtIODevice *vdev,VirtQueue *vq); +}; +
Might be better to add accessors for the bits we need?
Quoted 94 lines
hw/virtio.h +3
-3static inline target_phys_addr_t vring_align (target_phys_addr_t addr,unsigned long align) {return (addr + align -1 ) & ~(align -1 ); } -typedef struct VirtQueue VirtQueue ; -typedef struct VirtIODevice VirtIODevice ; -#define VIRTQUEUE_MAX_SIZE 1024 typedef struct VirtQueueElement@@ -81,6 +100,8 @@ typedef struct {void (*save_queue)(void * opaque,int n,QEMUFile *f);int (*load_config)(void * opaque,QEMUFile *f);int (*load_queue)(void * opaque,int n,QEMUFile *f); +int (*irqfd)(void * opaque,uint16_t vector,int fd); +int (*queuefd)(void * opaque,int n,int fd); } VirtIOBindings;#define VIRTIO_PCI_QUEUE_MAX 16@@ -104,6 +125,7 @@ struct VirtIODevicevoid (*get_config)(VirtIODevice *vdev,uint8_t *config);void (*set_config)(VirtIODevice *vdev,const uint8_t *config);void (*reset)(VirtIODevice *vdev); +void (*driver_ok)(VirtIODevice *vdev);VirtQueue *vq;const VirtIOBindings *binding;void *binding_opaque;net.c +5
-1@@ -2588,7 +2588,8 @@ int net_client_init(Monitor *mon, const char *device, const char *p) }if (!strcmp (device,"nic" )) {static const char *const nic_params[] = { -"vlan" ,"name" ,"macaddr" ,"model" ,"addr" ,"id" ,"vectors" ,NULL +"vlan" ,"name" ,"macaddr" ,"model" ,"addr" ,"id" ,"vectors" , +"vhost" ,NULL };NICInfo *nd;uint8_t *macaddr;@@ -2620,6 +2621,9 @@ int net_client_init(Monitor *mon, const char *device, const char *p) goto out; } } +if (get_param_value (buf,sizeof (buf),"vhost" , p)) { + nd->vhost_device =strdup (buf); + }if (get_param_value (buf,sizeof (buf),"model" , p)) { nd->model =strdup (buf); }net.h +1
-0@@ -110,6 +110,7 @@ struct NICInfo {int used;int bootable;int nvectors; +char *vhost_device; };extern int nb_nics;qemu-kvm.c +0
-8@@ -136,14 +136,6 @@ static inline void clear_gsi(kvm_context_t kvm, unsigned int gsi)DPRINTF ("Invalid GSI %d\n" ); } -struct slot_info { -unsigned long phys_addr; -unsigned long len; -unsigned long userspace_addr; -unsigned flags; -int logging_count; -}; -struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS ];static void init_slots (void )qemu-kvm.h +2
-0@@ -1215,6 +1215,15 @@ int kvm_ioctl(KVMState *s, int type, ...);int kvm_vm_ioctl (KVMState *s,int type, ...);int kvm_check_extension (KVMState *s,unsigned int ext); +struct slot_info { +unsigned long phys_addr;
qemu-kvm.h int kvm_ioctl(KVMState *s, int type, ...); · line 1229
Quoted 10 lines
qemu-kvm.h +7
-0+unsigned long len; +unsigned long userspace_addr; +unsigned flags; +int logging_count; +}; + +extern struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS ];#endif #endif
Cheers,
Mark.
Re: [PATCHv3 3/4] qemu-kvm: vhost-net implementation
In reply to Michael S. TsirkinHi Michael,
Some random high-level comments:
- I had expected this to be available as:
-net raw,ifname=eth2 -net nic,model=virtio
I'd prefer it this way, because it means you can use this mode even without vhost and it's ties in better with the way all other qemu networking modes work.
Like the VNET_HDR support, the "detect that there is only one backend on the vlan, and that it is a raw socket" code will be a hack until we have command line options that don't involve vlans.
Open question whether we the user should explicitly have to request vhost acceleration or we use it if it is available. The latter sounds nice, but it hasn't turned out great for kvm.
- CAP_NET_ADMIN is needed for raw sockets, so for e.g. libvirt I think we need to be able to support passing the raw socket fd via the command line and the monitor interface. I don't think we need that for the vhost fd, it should be safe to allow unprivileged users access to that, I think.
- I thought this version supports migration, but I don't see where that is handled in the code?
- You're not using the errors eventfd?
I've taken a first look through the kernel patch, hopefully I'll post decent comments for that soon, but in the meantime:
- I think /dev/vhost makes more sense - we shouldn't need to add another character device if we implement kernel backends for other virtio devices
- I'd really like vhost to support a 'tap' mode, so that we can still use a bridge if a NIC isn't available to be assigned. It would result in this stuff getting much more testing. Options I see:
1) Add tap-like functionality to vhost
2) Add VHOST_NET_SET_TAP
3) Just tell people to set up a tap and bind a raw socket too it
IMHO, (2) makes the most sense - it should be much less exta kernel
code than (1), and it would be much more convenient than (3)
- Distros will need a udev rule to set the device perms to 0666
- Distros will also need some way to have the module loaded if its not built-in; best I can think of in Fedora is to stick it in /etc/sysconfig/modules/kvm.modules
What would be nicer is if loading the kvm module could cause vhost to be loaded. It's nice that vhost can be used without kvm, but I think if kvm is loaded it's just very convenient to load vhost too. See also the problems we've cause with needing mkinitrd/dracut hacks by not having KVM_GUEST require VIRTIO_PCI
Quoted 3 lines · Michael S. Tsirkin
This adds support for vhost-net virtio kernel backend. To enable (assuming device eth2):
1. enable promisc mode or program guest mac in device eth2
Why can't vhost do this itself?
Quoted 3 lines
2. disable tso, gso, lro, jumbo frames on the card (disabling lro + jumbo frames should be sufficient, haven't tested this)
And this.
If we leave that up to the user or the management app, we need to expose to them what features vhost supports so that they can know in future to stop disabling them.
Quoted 55 lines
3. add vhost=eth2 to -net flag
4. run withCAP_NET_ADMINpriviledge (e.g. root)This patch is RFC, but works without issues for me.
It still needs to be split up, tested and benchmarked properly, but posting it here in case people want to test drive the kernel bits I posted.
Signed-off-by Michael S. Tsirkin <[email protected]>--- Makefile.target | 3 +- hw/vhost_net.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/vhost_net.h | 30 +++++++++ hw/virtio-net.c | 32 ++++++++++- hw/virtio-pci.c | 40 ++++++++++++ hw/virtio.c | 19 ------ hw/virtio.h | 28 ++++++++- net.c | 6 ++- net.h | 1 + qemu-kvm.c | 8 --- qemu-kvm.h | 9 +++ 11 files changed, 324 insertions(+), 33 deletions(-) create mode 100644 hw/vhost_net.c create mode 100644 hw/vhost_net.hMakefile.target +2
-1@@ -170,7 +170,8 @@ obj-y = vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o \ gdbstub.o gdbstub-xml.o msix.o ioport.o qemu-config.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly -obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o +obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o \ + vhost_net.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o LIBS+=-lzhw/vhost_net.c new file +9
-0@@ -0,0 +1,181 @@ +#include <sys/eventfd.h> +#include <sys/socket.h> +#include <linux/kvm.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/vhost.h> +#include <linux/virtio_ring.h> +#include <netpacket/packet.h> +#include <net/ethernet.h>
hw/vhost_net.c line 19
Quoted 10 lines
hw/vhost_net.c new file +10
-0+#include <net/if.h> +#include <netinet/in.h> + +#include <stdio.h> + +#include "qemu-kvm.h" + +#include "vhost_net.h" + +const char *vhost_net_device;
Seems to be unused
Quoted 13 lines
hw/vhost_net.c new file +13
-0+ +static int vhost_virtqueue_init (struct vhost_dev *dev, +struct VirtIODevice *vdev, +struct vhost_virtqueue *vq, +struct VirtQueue *q, +unsigned idx) +{ +target_phys_addr_t s, l; +int r; +struct vhost_vring_addr addr = { + .index = idx, + }; +struct vhost_vring_file file = {
hw/vhost_net.c line 42
Quoted 10 lines
hw/vhost_net.c new file +10
-0+ .index = idx, + }; +struct vhost_vring_state size = { + .index = idx, + }; + + size.num = q->vring.num; + r =ioctl (dev->control,VHOST_SET_VRING_NUM , &size); +if (r) +return -errno;
VHOST_SET_VRING_SIZE is a better name
Also, qemu coding style is:
if (r) {
return -errno;
}Yes, I hate it too
hw/vhost_net.c line 44
Quoted 2 lines
hw/vhost_net.c new file +2
-0+ + file.fd = vq->kick =eventfd (0 ,0 );
Handle error
Do we not want CLOEXEC and NONBLOCK ?
hw/vhost_net.c line 57
Quoted 13 lines
hw/vhost_net.c new file +13
-0+ r =ioctl (dev->control,VHOST_SET_VRING_KICK , &file); +if (r) +return -errno; + file.fd = vq->call =eventfd (0 ,0 ); + r =ioctl (dev->control,VHOST_SET_VRING_CALL , &file); +if (r) +return -errno; + + s = l =sizeof (struct vring_desc ) * q->vring.num; + vq->desc =cpu_physical_memory_map (q->vring.desc, &l,0 ); +if (!vq->desc || l != s) +return -ENOMEM ; + addr.user_addr = (u_int64_t )(unsigned long )vq->desc;
Easier to read if it was re-factored to:
vq->desc = vhost_map(q->vring.desc,
sizeof(struct vring_desc) * q->vring.num,
&addr.user_addr);
if (!vq->desc)
return -ENOMEM;hw/vhost_net.c line 66
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ r =ioctl (dev->control,VHOST_SET_VRING_DESC , &addr); +if (r <0 ) +return -errno; + s = l = offsetof(struct vring_avail , ring) + +sizeof (u_int64_t) * q->vring.num; + vq->avail =cpu_physical_memory_map (q->vring.avail, &l,0 ); +if (!vq->avail || l != s) +return -ENOMEM ; + addr.user_addr = (u_int64_t )(unsigned long )vq->avail;
And re-used here
hw/vhost_net.c line 75
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ r =ioctl (dev->control,VHOST_SET_VRING_AVAIL , &addr); +if (r <0 ) +return -errno; + s = l = offsetof(struct vring_used , ring) + +sizeof (struct vring_used_elem ) * q->vring.num; + vq->used =cpu_physical_memory_map (q->vring.used, &l,1 ); +if (!vq->used || l != s) +return -ENOMEM ; + addr.user_addr = (u_int64_t )(unsigned long )vq->used;
and here
Need to unmap these too at some point
hw/vhost_net.c line 86
Quoted 11 lines
hw/vhost_net.c new file +11
-0+ r =ioctl (dev->control,VHOST_SET_VRING_USED , &addr); +if (r <0 ) +return -errno; + + r = vdev->binding->irqfd (vdev->binding_opaque, q->vector, vq->call); +if (r <0 ) +return -errno; + + r = vdev->binding->queuefd (vdev->binding_opaque, idx, vq->kick); +if (r <0 ) +return -errno;
Naming is a little confused here too
kick vs. call
irqfd vs. queuefd
irqfd vs. ioeventfd
How about just keeping it simple, sticking with the latter and making it VHOST_SET_VRING_IRQFD and VHOST_SET_VRING_IOEVENTFD ?
Quoted 5 lines
hw/vhost_net.c new file +5
-0+ +return 0 ; +} + +static int vhost_dev_init(struct vhost_dev *hdev,
hw/vhost_net.c line 101
Quoted 10 lines
hw/vhost_net.c new file +10
-0+ VirtIODevice *vdev) +{ +int i, r, n =0 ; +struct vhost_memory *mem; + hdev->control =open ("/dev/vhost-net" ,O_RDWR ); +if (hdev->control <0 ) +return -errno; + r =ioctl (hdev->control,VHOST_SET_OWNER ,NULL ); +if (r <0 ) +return -errno;
Should close the fd if we fail here or later
(Yep, I know you exit on error but ...)
hw/vhost_net.c line 107
Quoted 6 lines
hw/vhost_net.c new file +6
-0+for (i =0 ; i <KVM_MAX_NUM_MEM_REGIONS ; ++i) { +if (!slots[i].len || (slots[i].flags &KVM_MEM_LOG_DIRTY_PAGES )) { +continue ; + } + ++n; + }
I don't love making a "slots" variable global, it'd be nicer to have:
slots = kvm_get_slots(&n_slots);
naming confusion here between "slots" and "regions" too
Also, max regions is 32, so I'd just allocate max regions and if some are unused it's no big deal
hw/vhost_net.c line 109
Quoted 1 line
hw/vhost_net.c new file +1
-0+ mem = qemu_mallocz(offsetof(struct vhost_memory, regions) +
sizeof(*mem) would suffice here
hw/vhost_net.c line 118
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ n * sizeof(struct vhost_memory_region)); +if (!mem) +return -ENOMEM ; + mem->nregions = n; + n =0 ; +for (i =0 ; i <KVM_MAX_NUM_MEM_REGIONS ; ++i) { +if (!slots[i].len || (slots[i].flags &KVM_MEM_LOG_DIRTY_PAGES )) { +continue ; + }
Ignoring the LOG_DIRTY_PAGES regions could do with an explanatory comment
hw/vhost_net.c line 127
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ mem->regions[n].guest_phys_addr = slots[i].phys_addr; + mem->regions[n].memory_size = slots[i].len; + mem->regions[n].userspace_addr = slots[i].userspace_addr; + ++n; + } + + r =ioctl (hdev->control,VHOST_SET_MEM_TABLE , mem); +if (r <0 ) +return -errno;
Need to free the table (on error too)
hw/vhost_net.c line 136
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ +for (i =0 ; i < hdev->nvqs; ++i) { + r =vhost_virtqueue_init (hdev, + vdev, + hdev->vqs + i, + vdev->vq + i, + i); +if (r <0 ) +return r;
Need to handle errors better, e.g. unmapping
hw/vhost_net.c line 144
Quoted 8 lines
hw/vhost_net.c new file +8
-0+ } + +return 0 ; +} + +int vhost_net_init (struct vhost_net *net, +VirtIODevice *dev, +char *vhost_device)
const char *vhost_device
hw/vhost_net.c line 151
Quoted 7 lines
hw/vhost_net.c new file +7
-0+{ +struct sockaddr_ll lladdr; +struct ifreq req; +int r; +const char *ifname = vhost_device; +if (!ifname) +return 0 ;
Why the ifname variable?
Quoted 13 lines
hw/vhost_net.c new file +13
-0+ net->dev.nvqs =2 ; + net->dev.vqs = net->vqs; + r =vhost_dev_init (&net->dev, dev); +if (r <0 ) +return r; + + net->sock =socket (PF_PACKET ,SOCK_RAW ,htons (ETH_P_ALL )); +if (net->sock <0 ) +return -errno; + +memset (&req,0 ,sizeof (req)); +strncpy (req.ifr_name, ifname,IFNAMSIZ -1 ); + r =ioctl (net->sock,SIOCGIFINDEX , &req);
hw/vhost_net.c line 175
Quoted 10 lines
hw/vhost_net.c new file +10
-0+if (r <0 ) +return -errno; + +memset (&lladdr,0 ,sizeof (lladdr)); + lladdr.sll_family =AF_PACKET ; + lladdr.sll_protocol =htons (ETH_P_ALL ); + lladdr.sll_ifindex = req.ifr_ifindex; + r =bind (net->sock, (const struct sockaddr *)&lladdr,sizeof (lladdr)); +if (r <0 ) +return -errno;
You've get the point at this stage, but need to close the socket here on error too
Quoted 25 lines
hw/vhost_net.c new file +5
-0+ r =ioctl (net->dev.control,VHOST_NET_SET_SOCKET , &net->sock); +if (r <0 ) +return -errno; +return 0 ; +}hw/vhost_net.h new file +14
-0@@ -0,0 +1,30 @@ +#ifndef VHOST_NET_H +#define VHOST_NET_H + +#include "hw/virtio.h" + +struct vhost_virtqueue { +int kick; +int call; +void *desc; +void *avail; +void *used; +}; + +struct vhost_dev {
hw/vhost_net.h line 24
Quoted 10 lines
hw/vhost_net.h new file +10
-0+int control; +struct vhost_virtqueue *vqs; +int nvqs; +}; + +struct vhost_net { +struct vhost_dev dev; +struct vhost_virtqueue vqs[2 ]; +int sock; +};
I think I'd just malloc the vqs - having two vqs pointers in the two structs is a little confusing. Maybe it's worth it, though
Quoted 28 lines
hw/vhost_net.h new file +5
-0+int vhost_net_init (struct vhost_net *net, +VirtIODevice *dev, +char *vhost_device); + +#endif hw/virtio-net.c +4
-0@@ -19,6 +19,8 @@#include "qemu-kvm.h" #endif +#include "vhost_net.h" +#define TAP_VNET_HDR #define VIRTIO_NET_VM_VERSION 10@@ -56,6 +58,8 @@ typedef struct VirtIONetuint8_t *macs; } mac_table;uint32_t *vlans; +char *vhost_device; +struct vhost_net vhost; } VirtIONet;/* TODO
hw/virtio-net.c static uint32_t virtio_net_get_features(VirtIODevice *vdev) · line 144
Quoted 7 lines
hw/virtio-net.c +4
-0@@ -134,9 +138,12 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev) (1 <<VIRTIO_NET_F_CTRL_RX ) | (1 <<VIRTIO_NET_F_CTRL_VLAN ) | (1 <<VIRTIO_NET_F_CTRL_RX_EXTRA ); +VirtIONet *n =to_virtio_net (vdev); + +if (n->vhost_device) +return 1 <<VIRTIO_NET_F_MAC ;
features = MAC;
if (n->vhost_device)
return features;
features |= ...;
Quoted 26 lines
hw/virtio-net.c +6
-1#ifdef TAP_VNET_HDR -VirtIONet *n =to_virtio_net (vdev);VLANClientState *host = n->vc->vlan->first_client;if (tap_has_vnet_hdr (host)) {@@ -175,6 +182,9 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev)static void virtio_net_set_features (VirtIODevice *vdev,uint32_t features) {VirtIONet *n =to_virtio_net (vdev); +/* vhost net supports no features */ +if (n->vhost_device) +return ;#ifdef TAP_VNET_HDR VLANClientState *host = n->vc->vlan->first_client;#endif @@ -351,6 +361,9 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)static int do_virtio_net_can_receive (VirtIONet *n,int bufsize) { +if (n->vhost_device) +return 0 ; +if (!virtio_queue_ready (n->rx_vq) || !(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK ))return 0 ;
hw/virtio-net.c static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count) · line 427
Quoted 4 lines
hw/virtio-net.c +1
-0@@ -411,6 +424,7 @@ static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count)while (offset < count && i < iovcnt) {int len =MIN (iov[i].iov_len, count - offset);memcpy (iov[i].iov_base, buf + offset, len); +
Spurious
Quoted 13 lines
hw/virtio-net.c +2
-0offset += len; i++; }@@ -610,6 +624,8 @@ static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)#else int has_vnet_hdr =0 ;#endif +if (n->vhost_device) +return ;if (!(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK ))return ;
hw/virtio-net.c static void virtio_net_cleanup(VLANClientState *vc) · line 846
Quoted 9 lines
hw/virtio-net.c +6
-0@@ -822,6 +838,18 @@ static void virtio_net_cleanup(VLANClientState *vc)virtio_cleanup (&n->vdev); } +static void virtio_net_driver_ok (VirtIODevice *vdev) +{ +VirtIONet *n =to_virtio_net (vdev); +int r; + + r =vhost_net_init (&n->vhost, vdev, n->vhost_device);
Might be worth a comment as to why we need to wait until here to setup vhost; it's not immediately obvious
hw/virtio-net.c static void virtio_net_cleanup(VLANClientState *vc) · line 850
Quoted 4 lines
hw/virtio-net.c +4
-0+if (r) { +fprintf (stderr,"\nvhost_net_init returned %d\n" , r); +exit (-r); + }
I think I'd like us to be able to fallback to normal userspace virtio-net with raw socket if we can't init vhost
Also, the guest is up and running at this point, right? With a linux guest, the root filesystem is probably mounted. It's not polite to just exit at this point if we can't open a raw socket or initialize vhost
Quoted 47 lines
hw/virtio-net.c +2
-0VirtIODevice *virtio_net_init (DeviceState *dev) {VirtIONet *n;@@ -837,6 +865,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->vdev.set_features = virtio_net_set_features; n->vdev.bad_features = virtio_net_bad_features; n->vdev.reset = virtio_net_reset; + n->vdev.driver_ok = virtio_net_driver_ok; n->rx_vq =virtio_add_queue (&n->vdev,256 , virtio_net_handle_rx); n->tx_vq =virtio_add_queue (&n->vdev,256 , virtio_net_handle_tx); n->ctrl_vq =virtio_add_queue (&n->vdev,64 , virtio_net_handle_ctrl);@@ -863,6 +892,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->vdev.nvectors =3 ; else n->vdev.nvectors = dev->nd->nvectors; + n->vhost_device = dev->nd->vhost_device;register_savevm ("virtio-net" , virtio_net_id++,VIRTIO_NET_VM_VERSION , virtio_net_save, virtio_net_load, n);hw/virtio-pci.c +4
-0@@ -15,11 +15,13 @@#include <inttypes.h> +#include <linux/kvm.h> #include "virtio.h" #include "pci.h" #include "sysemu.h" #include "msix.h" #include "net.h" +#include "qemu-kvm.h" /* from Linux's linux/virtio_pci.h */ @@ -199,6 +201,8 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) vdev->status = val &0xFF ;if (vdev->status ==0 )virtio_pci_reset (proxy); +if ((val &VIRTIO_CONFIG_S_DRIVER_OK ) && vdev->driver_ok) + vdev->driver_ok (vdev);break ;case VIRTIO_MSI_CONFIG_VECTOR :msix_vector_unuse (&proxy->pci_dev, vdev->config_vector);
hw/virtio-pci.c static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, · line 375
Quoted 7 lines
hw/virtio-pci.c +4
-0@@ -365,12 +369,48 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,msix_write_config (pci_dev, address, val, len); } +static int virtio_pci_irqfd (void * opaque,uint16_t vector,int fd) +{ +VirtIOPCIProxy *proxy = opaque; +struct kvm_irqfd call = { };
Does the {}; init do anything?
hw/virtio-pci.c static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, · line 381
Quoted 6 lines
hw/virtio-pci.c +6
-0+int r; + +if (vector >= proxy->pci_dev.msix_entries_nr) +return -EINVAL ; +if (!proxy->pci_dev.msix_entry_used[vector]) +return -ENOENT ;
Does this mean if the guest doesn't have MSI support, we exit?
hw/virtio-pci.c static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, · line 387
Quoted 6 lines
hw/virtio-pci.c +6
-0+ call.fd = fd; + call.gsi = proxy->pci_dev.msix_irq_entries[vector].gsi; + r =kvm_vm_ioctl (kvm_state,KVM_IRQFD , &call); +if (r <0 ) +return r; +return 0 ;
Have you plans to implement a non-kvm mode for this too?
Quoted 59 lines
hw/virtio-pci.c +20
-0+} + +static int virtio_pci_queuefd (void * opaque,int n,int fd) +{ +VirtIOPCIProxy *proxy = opaque; +struct kvm_ioeventfd kick = { + .datamatch = n, + .addr = proxy->addr +VIRTIO_PCI_QUEUE_NOTIFY , + .len =2 , + .flags =KVM_IOEVENTFD_FLAG_DATAMATCH |KVM_IOEVENTFD_FLAG_PIO , + .fd = fd, + }; +int r =kvm_vm_ioctl (kvm_state,KVM_IOEVENTFD , &kick); +if (r <0 ) +return r; +return 0 ; +} +static const VirtIOBindings virtio_pci_bindings = { .notify = virtio_pci_notify, .save_config = virtio_pci_save_config, .load_config = virtio_pci_load_config, .save_queue = virtio_pci_save_queue, .load_queue = virtio_pci_load_queue, + .irqfd = virtio_pci_irqfd, + .queuefd = virtio_pci_queuefd, }; static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,hw/virtio.c +0
-18@@ -54,24 +54,6 @@ typedef struct VRingUsedVRingUsedElem ring[0 ]; } VRingUsed; -typedef struct VRing -{ -unsigned int num; -target_phys_addr_t desc; -target_phys_addr_t avail; -target_phys_addr_t used; -}VRing ; - -struct VirtQueue -{ -VRing vring; -target_phys_addr_t pa; -uint16_t last_avail_idx; -int inuse; -uint16_t vector; -void (*handle_output)(VirtIODevice *vdev,VirtQueue *vq); -}; -#define VIRTIO_PCI_QUEUE_MAX 16/* virt queue functions */
hw/virtio.c int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) · line 404
Quoted 4 lines
hw/virtio.c +0
-1@@ -401,7 +383,6 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) sg->iov_base =cpu_physical_memory_map (vring_desc_addr (desc_pa, i), &len, is_write); -
Spurious
You appear to have a vendetta against whitespace :-)
Quoted 23 lines
hw/virtio.c +0
-0if (sg->iov_base ==NULL || len != sg->iov_len) {fprintf (stderr,"virtio: trying to map MMIO memory\n" );exit (1 );hw/virtio.h +12
-0@@ -54,15 +54,34 @@struct VirtQueue ; +typedef struct VRing +{ +unsigned int num; +target_phys_addr_t desc; +target_phys_addr_t avail; +target_phys_addr_t used; +}VRing ; + +typedef struct VirtQueue VirtQueue ; +struct VirtIODevice ; +typedef struct VirtIODevice VirtIODevice ; +
hw/virtio.h line 78
Quoted 10 lines
hw/virtio.h +10
-0+struct VirtQueue +{ +VRing vring; +target_phys_addr_t pa; +uint16_t last_avail_idx; +int inuse; +uint16_t vector; +void (*handle_output)(VirtIODevice *vdev,VirtQueue *vq); +}; +
Might be better to add accessors for the bits we need?
Quoted 94 lines
hw/virtio.h +3
-3static inline target_phys_addr_t vring_align (target_phys_addr_t addr,unsigned long align) {return (addr + align -1 ) & ~(align -1 ); } -typedef struct VirtQueue VirtQueue ; -typedef struct VirtIODevice VirtIODevice ; -#define VIRTQUEUE_MAX_SIZE 1024 typedef struct VirtQueueElement@@ -81,6 +100,8 @@ typedef struct {void (*save_queue)(void * opaque,int n,QEMUFile *f);int (*load_config)(void * opaque,QEMUFile *f);int (*load_queue)(void * opaque,int n,QEMUFile *f); +int (*irqfd)(void * opaque,uint16_t vector,int fd); +int (*queuefd)(void * opaque,int n,int fd); } VirtIOBindings;#define VIRTIO_PCI_QUEUE_MAX 16@@ -104,6 +125,7 @@ struct VirtIODevicevoid (*get_config)(VirtIODevice *vdev,uint8_t *config);void (*set_config)(VirtIODevice *vdev,const uint8_t *config);void (*reset)(VirtIODevice *vdev); +void (*driver_ok)(VirtIODevice *vdev);VirtQueue *vq;const VirtIOBindings *binding;void *binding_opaque;net.c +5
-1@@ -2588,7 +2588,8 @@ int net_client_init(Monitor *mon, const char *device, const char *p) }if (!strcmp (device,"nic" )) {static const char *const nic_params[] = { -"vlan" ,"name" ,"macaddr" ,"model" ,"addr" ,"id" ,"vectors" ,NULL +"vlan" ,"name" ,"macaddr" ,"model" ,"addr" ,"id" ,"vectors" , +"vhost" ,NULL };NICInfo *nd;uint8_t *macaddr;@@ -2620,6 +2621,9 @@ int net_client_init(Monitor *mon, const char *device, const char *p) goto out; } } +if (get_param_value (buf,sizeof (buf),"vhost" , p)) { + nd->vhost_device =strdup (buf); + }if (get_param_value (buf,sizeof (buf),"model" , p)) { nd->model =strdup (buf); }net.h +1
-0@@ -110,6 +110,7 @@ struct NICInfo {int used;int bootable;int nvectors; +char *vhost_device; };extern int nb_nics;qemu-kvm.c +0
-8@@ -136,14 +136,6 @@ static inline void clear_gsi(kvm_context_t kvm, unsigned int gsi)DPRINTF ("Invalid GSI %d\n" ); } -struct slot_info { -unsigned long phys_addr; -unsigned long len; -unsigned long userspace_addr; -unsigned flags; -int logging_count; -}; -struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS ];static void init_slots (void )qemu-kvm.h +2
-0@@ -1215,6 +1215,15 @@ int kvm_ioctl(KVMState *s, int type, ...);int kvm_vm_ioctl (KVMState *s,int type, ...);int kvm_check_extension (KVMState *s,unsigned int ext); +struct slot_info { +unsigned long phys_addr;
qemu-kvm.h int kvm_ioctl(KVMState *s, int type, ...); · line 1229
Quoted 10 lines
qemu-kvm.h +7
-0+unsigned long len; +unsigned long userspace_addr; +unsigned flags; +int logging_count; +}; + +extern struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS ];#endif #endif
Cheers,
Mark.
Re: [PATCHv3 3/4] qemu-kvm: vhost-net implementation
Quoted 7 lines · Mark McLoughlin
- I had expected this to be available as:
-net raw,ifname=eth2 -net nic,model=virtio
I'd prefer it this way, because it means you can use this mode even without vhost and it's ties in better with the way all other qemu networking modes work.
Agreed, I made a similar comment in the thread on the kernel interface.
Quoted 5 lines
-
CAP_NET_ADMINis needed for raw sockets, so for e.g. libvirt I think we need to be able to support passing the raw socket fd via the command line and the monitor interface. I don't think we need that for the vhost fd, it should be safe to allow unprivileged users access to that, I think.
Agreed on both points. The raw packet socket patch for qemu from Or Gerlitz addresses the option of passing in a file descriptor for the socket, IIRC.
Quoted 3 lines
- I think
/dev/vhostmakes more sense - we shouldn't need to add another character device if we implement kernel backends for other virtio devices
I believe we should separate them the way that Michael has done. A character device per logical interface is much more straightforward that one for different interfaces that you first need to select and probe. You might also want to give permissions for one kind of interface to a user or group, but not for another interface.
Quoted 10 lines
- I'd really like vhost to support a 'tap' mode, so that we can still use a bridge if a NIC isn't available to be assigned. It would result in this stuff getting much more testing. Options I see:
1) Add tap-like functionality to vhost
2) AddVHOST_NET_SET_TAP
3) Just tell people to set up a tap and bind a raw socket too itIMHO, (2) makes the most sense - it should be much less exta kernel
code than (1), and it would be much more convenient than (3)
Yes, see the thread between Michael and me about this. I suggested a variation of VHOST_NET_SET_TAP, Michael suggested a TAP_GET_SOCKET ioctl addition to the tap driver.
Quoted 3 lines
What would be nicer is if loading the kvm module could cause vhost to be loaded. It's nice that vhost can be used without kvm, but I think if kvm is loaded it's just very convenient to load vhost too.
You can do that with modprobe.conf rules.
Quoted 6 lines · Michael S. Tsirkin
Quoted 3 lines
This adds support for vhost-net virtio kernel backend. To enable (assuming device eth2):
1. enable promisc mode or program guest mac in device eth2Why can't vhost do this itself?
I think it should not. You might want to extend the interface to allow passing in UDP and TCP sockets in addition to the raw sockets, so we can use the same driver for doing in-kernel handling for the other qemu network options. The kernel should assume as little as possible about the sockets.
Quoted 9 lines
Quoted 3 lines
2. disable tso, gso, lro, jumbo frames on the card (disabling lro + jumbo frames should be sufficient, haven't tested this)
And this.
If we leave that up to the user or the management app, we need to expose to them what features vhost supports so that they can know in future to stop disabling them.
Yes, but it still seems cleaner to do it this in user space for the reason I mention above.
Arnd <><
Re: [PATCHv3 3/4] qemu-kvm: vhost-net implementation
In reply to Mark McLoughlinQuoted 7 lines · Mark McLoughlin
- I had expected this to be available as:
-net raw,ifname=eth2 -net nic,model=virtio
I'd prefer it this way, because it means you can use this mode even without vhost and it's ties in better with the way all other qemu networking modes work.
Agreed, I made a similar comment in the thread on the kernel interface.
Quoted 5 lines
-
CAP_NET_ADMINis needed for raw sockets, so for e.g. libvirt I think we need to be able to support passing the raw socket fd via the command line and the monitor interface. I don't think we need that for the vhost fd, it should be safe to allow unprivileged users access to that, I think.
Agreed on both points. The raw packet socket patch for qemu from Or Gerlitz addresses the option of passing in a file descriptor for the socket, IIRC.
Quoted 3 lines
- I think
/dev/vhostmakes more sense - we shouldn't need to add another character device if we implement kernel backends for other virtio devices
I believe we should separate them the way that Michael has done. A character device per logical interface is much more straightforward that one for different interfaces that you first need to select and probe. You might also want to give permissions for one kind of interface to a user or group, but not for another interface.
Quoted 10 lines
- I'd really like vhost to support a 'tap' mode, so that we can still use a bridge if a NIC isn't available to be assigned. It would result in this stuff getting much more testing. Options I see:
1) Add tap-like functionality to vhost
2) AddVHOST_NET_SET_TAP
3) Just tell people to set up a tap and bind a raw socket too itIMHO, (2) makes the most sense - it should be much less exta kernel
code than (1), and it would be much more convenient than (3)
Yes, see the thread between Michael and me about this. I suggested a variation of VHOST_NET_SET_TAP, Michael suggested a TAP_GET_SOCKET ioctl addition to the tap driver.
Quoted 3 lines
What would be nicer is if loading the kvm module could cause vhost to be loaded. It's nice that vhost can be used without kvm, but I think if kvm is loaded it's just very convenient to load vhost too.
You can do that with modprobe.conf rules.
Quoted 6 lines · Michael S. Tsirkin
Quoted 3 lines
This adds support for vhost-net virtio kernel backend. To enable (assuming device eth2):
1. enable promisc mode or program guest mac in device eth2Why can't vhost do this itself?
I think it should not. You might want to extend the interface to allow passing in UDP and TCP sockets in addition to the raw sockets, so we can use the same driver for doing in-kernel handling for the other qemu network options. The kernel should assume as little as possible about the sockets.
Quoted 9 lines
Quoted 3 lines
2. disable tso, gso, lro, jumbo frames on the card (disabling lro + jumbo frames should be sufficient, haven't tested this)
And this.
If we leave that up to the user or the management app, we need to expose to them what features vhost supports so that they can know in future to stop disabling them.
Yes, but it still seems cleaner to do it this in user space for the reason I mention above.
Arnd <><
Michael S. Tsirkin
maintainerRe: [PATCHv3 3/4] qemu-kvm: vhost-net implementation
In reply to Mark McLoughlinQuoted 3 lines · Mark McLoughlin
Hi Michael,
Some random high-level comments:
Note this code is posted very much just so that people can test vhost, not really for merging, but thanks for review!
Quoted 11 lines
- I had expected this to be available as:
-net raw,ifname=eth2 -net nic,model=virtio
I'd prefer it this way, because it means you can use this mode even without vhost and it's ties in better with the way all other qemu networking modes work.
Like the
VNET_HDRsupport, the "detect that there is only one backend on the vlan, and that it is a raw socket" code will be a hack until we have command line options that don't involve vlans.
Hmm. If we were to push the hack upstream I'd go for it, if we intend to rewrite it anyway - why bother with how it looks for now?
Quoted 3 lines
Open question whether we the user should explicitly have to request vhost acceleration or we use it if it is available. The latter sounds nice, but it hasn't turned out great for kvm.
Reason I didn't it's still pretty experimental at this point.
Quoted 8 lines
-
CAP_NET_ADMINis needed for raw sockets, so for e.g. libvirt I think we need to be able to support passing the raw socket fd via the command line and the monitor interface. I don't think we need that for the vhost fd, it should be safe to allow unprivileged users access to that, I think.- I thought this version supports migration, but I don't see where that is handled in the code?
Not in this version. Kernel code's already there though.
Quoted 1 line
- You're not using the errors eventfd?
I should, but it kind of works without, as long as the guest does not do anything strange.
Quoted 6 lines
I've taken a first look through the kernel patch, hopefully I'll post decent comments for that soon, but in the meantime:
- I think
/dev/vhostmakes more sense - we shouldn't need to add another character device if we implement kernel backends for other virtio devices
Why not?
Quoted 3 lines
- I'd really like vhost to support a 'tap' mode, so that we can still use a bridge if a NIC isn't available to be assigned.
It would result in this stuff getting much more testing.
BTW, there are currently some options available if you do not want to assign a NIC:
- set NIC to promisc mode
- use macvlan
Quoted 7 lines
Options I see:
1) Add tap-like functionality to vhost
2) AddVHOST_NET_SET_TAP
3) Just tell people to set up a tap and bind a raw socket too it
IMHO, (2) makes the most sense - it should be much less exta kernel
code than (1), and it would be much more convenient than (3)
I agree, an issue I see here is that we have no good way to check it's really tap and not some other random device. I'm looking at a similar option which is adding an ioctl to tap to expose it's underlying socket.
Quoted 23 lines
- Distros will need a udev rule to set the device perms to 0666
- Distros will also need some way to have the module loaded if its not built-in; best I can think of in Fedora is to stick it in
/etc/sysconfig/modules/kvm.modulesWhat would be nicer is if loading the kvm module could cause vhost to be loaded. It's nice that vhost can be used without kvm, but I think if kvm is loaded it's just very convenient to load vhost too. See also the problems we've cause with needing mkinitrd/dracut hacks by not having
KVM_GUESTrequireVIRTIO_PCIQuoted 3 lines · Michael S. Tsirkin
This adds support for vhost-net virtio kernel backend. To enable (assuming device eth2):
1. enable promisc mode or program guest mac in device eth2Why can't vhost do this itself?
Quoted 3 lines
2. disable tso, gso, lro, jumbo frames on the card (disabling lro + jumbo frames should be sufficient, haven't tested this)
Quoted 5 lines
And this.
If we leave that up to the user or the management app, we need to expose to them what features vhost supports so that they can know in future to stop disabling them.
That's what I did. There's a get features ioctl that returns 0 for now, when TSO is added I'll add it there.
Quoted 59 lines
Quoted 59 lines
3. add vhost=eth2 to -net flag
4. run withCAP_NET_ADMINpriviledge (e.g. root)This patch is RFC, but works without issues for me.
It still needs to be split up, tested and benchmarked properly, but posting it here in case people want to test drive the kernel bits I posted.
Signed-off-by Michael S. Tsirkin <[email protected]>--- Makefile.target | 3 +- hw/vhost_net.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/vhost_net.h | 30 +++++++++ hw/virtio-net.c | 32 ++++++++++- hw/virtio-pci.c | 40 ++++++++++++ hw/virtio.c | 19 ------ hw/virtio.h | 28 ++++++++- net.c | 6 ++- net.h | 1 + qemu-kvm.c | 8 --- qemu-kvm.h | 9 +++ 11 files changed, 324 insertions(+), 33 deletions(-) create mode 100644 hw/vhost_net.c create mode 100644 hw/vhost_net.hMakefile.target +2
-1@@ -170,7 +170,8 @@ obj-y = vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o \ gdbstub.o gdbstub-xml.o msix.o ioport.o qemu-config.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly -obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o +obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o \ + vhost_net.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o LIBS+=-lzhw/vhost_net.c new file +13
-0@@ -0,0 +1,181 @@ +#include <sys/eventfd.h> +#include <sys/socket.h> +#include <linux/kvm.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/vhost.h> +#include <linux/virtio_ring.h> +#include <netpacket/packet.h> +#include <net/ethernet.h> +#include <net/if.h> +#include <netinet/in.h> + +#include <stdio.h>
Quoted 8 lines
hw/vhost_net.c line 19
Quoted 6 lines
hw/vhost_net.c new file +6
-0+ +#include "qemu-kvm.h" + +#include "vhost_net.h" + +const char *vhost_net_device;Seems to be unused
Good catch
Quoted 32 lines
Quoted 13 lines
hw/vhost_net.c new file +13
-0+ +static int vhost_virtqueue_init (struct vhost_dev *dev, +struct VirtIODevice *vdev, +struct vhost_virtqueue *vq, +struct VirtQueue *q, +unsigned idx) +{ +target_phys_addr_t s, l; +int r; +struct vhost_vring_addr addr = { + .index = idx, + }; +struct vhost_vring_file file = {hw/vhost_net.c line 42
Quoted 10 lines
hw/vhost_net.c new file +10
-0+ .index = idx, + }; +struct vhost_vring_state size = { + .index = idx, + }; + + size.num = q->vring.num; + r =ioctl (dev->control,VHOST_SET_VRING_NUM , &size); +if (r) +return -errno;
VHOST_SET_VRING_SIZEis a better nameAlso, qemu coding style is:
if (r) { return -errno; }
Quoted 8 lines
Yes, I hate it too
hw/vhost_net.c line 44
Quoted 2 lines
hw/vhost_net.c new file +2
-0+ + file.fd = vq->kick =eventfd (0 ,0 );Handle error
Do we not want CLOEXEC and NONBLOCK ?
Why do we?
Quoted 46 lines
hw/vhost_net.c line 57
Quoted 13 lines
hw/vhost_net.c new file +13
-0+ r =ioctl (dev->control,VHOST_SET_VRING_KICK , &file); +if (r) +return -errno; + file.fd = vq->call =eventfd (0 ,0 ); + r =ioctl (dev->control,VHOST_SET_VRING_CALL , &file); +if (r) +return -errno; + + s = l =sizeof (struct vring_desc ) * q->vring.num; + vq->desc =cpu_physical_memory_map (q->vring.desc, &l,0 ); +if (!vq->desc || l != s) +return -ENOMEM ; + addr.user_addr = (u_int64_t )(unsigned long )vq->desc;Easier to read if it was re-factored to:
vq->desc= vhost_map(q->vring.desc,sizeof(struct vring_desc) * q->vring.num, &addr.user_addr); if (!vq->desc) return -ENOMEM;hw/vhost_net.c line 66
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ r =ioctl (dev->control,VHOST_SET_VRING_DESC , &addr); +if (r <0 ) +return -errno; + s = l = offsetof(struct vring_avail , ring) + +sizeof (u_int64_t) * q->vring.num; + vq->avail =cpu_physical_memory_map (q->vring.avail, &l,0 ); +if (!vq->avail || l != s) +return -ENOMEM ; + addr.user_addr = (u_int64_t )(unsigned long )vq->avail;And re-used here
hw/vhost_net.c line 75
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ r =ioctl (dev->control,VHOST_SET_VRING_AVAIL , &addr); +if (r <0 ) +return -errno; + s = l = offsetof(struct vring_used , ring) + +sizeof (struct vring_used_elem ) * q->vring.num; + vq->used =cpu_physical_memory_map (q->vring.used, &l,1 ); +if (!vq->used || l != s) +return -ENOMEM ; + addr.user_addr = (u_int64_t )(unsigned long )vq->used;and here
Quoted 1 line
Need to unmap these too at some point
Yes, cleanup is completely broken
Quoted 14 lines
hw/vhost_net.c line 86
Quoted 11 lines
hw/vhost_net.c new file +11
-0+ r =ioctl (dev->control,VHOST_SET_VRING_USED , &addr); +if (r <0 ) +return -errno; + + r = vdev->binding->irqfd (vdev->binding_opaque, q->vector, vq->call); +if (r <0 ) +return -errno; + + r = vdev->binding->queuefd (vdev->binding_opaque, idx, vq->kick); +if (r <0 ) +return -errno;Naming is a little confused here too
Quoted 8 lines
kick vs. call
irqfd vs. queuefd
irqfd vs. ioeventfd
How about just keeping it simple, sticking with the latter and making it
VHOST_SET_VRING_IRQFDandVHOST_SET_VRING_IOEVENTFD?
vhost tries to stick to virtio terminology.
virtio has kick and call so vhost copied that.
I agree call is a bit generic, but kick + irqfd
would be just confusing, right?
Quoted 274 lines
Quoted 5 lines
hw/vhost_net.c new file +5
-0+ +return 0 ; +} + +static int vhost_dev_init(struct vhost_dev *hdev,hw/vhost_net.c line 101
Quoted 10 lines
hw/vhost_net.c new file +10
-0+ VirtIODevice *vdev) +{ +int i, r, n =0 ; +struct vhost_memory *mem; + hdev->control =open ("/dev/vhost-net" ,O_RDWR ); +if (hdev->control <0 ) +return -errno; + r =ioctl (hdev->control,VHOST_SET_OWNER ,NULL ); +if (r <0 ) +return -errno;Should close the fd if we fail here or later
(Yep, I know you exit on error but ...)
hw/vhost_net.c line 107
Quoted 6 lines
hw/vhost_net.c new file +6
-0+for (i =0 ; i <KVM_MAX_NUM_MEM_REGIONS ; ++i) { +if (!slots[i].len || (slots[i].flags &KVM_MEM_LOG_DIRTY_PAGES )) { +continue ; + } + ++n; + }I don't love making a "slots" variable global, it'd be nicer to have:
slots =
kvm_get_slots(&n_slots);naming confusion here between "slots" and "regions" too
Also, max regions is 32, so I'd just allocate max regions and if some are unused it's no big deal
hw/vhost_net.c line 109
Quoted 1 line
hw/vhost_net.c new file +1
-0+ mem = qemu_mallocz(offsetof(struct vhost_memory, regions) +
sizeof(*mem)would suffice herehw/vhost_net.c line 118
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ n * sizeof(struct vhost_memory_region)); +if (!mem) +return -ENOMEM ; + mem->nregions = n; + n =0 ; +for (i =0 ; i <KVM_MAX_NUM_MEM_REGIONS ; ++i) { +if (!slots[i].len || (slots[i].flags &KVM_MEM_LOG_DIRTY_PAGES )) { +continue ; + }Ignoring the
LOG_DIRTY_PAGESregions could do with an explanatory commenthw/vhost_net.c line 127
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ mem->regions[n].guest_phys_addr = slots[i].phys_addr; + mem->regions[n].memory_size = slots[i].len; + mem->regions[n].userspace_addr = slots[i].userspace_addr; + ++n; + } + + r =ioctl (hdev->control,VHOST_SET_MEM_TABLE , mem); +if (r <0 ) +return -errno;Need to free the table (on error too)
hw/vhost_net.c line 136
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ +for (i =0 ; i < hdev->nvqs; ++i) { + r =vhost_virtqueue_init (hdev, + vdev, + hdev->vqs + i, + vdev->vq + i, + i); +if (r <0 ) +return r;Need to handle errors better, e.g. unmapping
hw/vhost_net.c line 144
Quoted 8 lines
hw/vhost_net.c new file +8
-0+ } + +return 0 ; +} + +int vhost_net_init (struct vhost_net *net, +VirtIODevice *dev, +char *vhost_device)const char
*vhost_devicehw/vhost_net.c line 151
Quoted 7 lines
hw/vhost_net.c new file +7
-0+{ +struct sockaddr_ll lladdr; +struct ifreq req; +int r; +const char *ifname = vhost_device; +if (!ifname) +return 0 ;Why the ifname variable?
Quoted 13 lines
hw/vhost_net.c new file +13
-0+ net->dev.nvqs =2 ; + net->dev.vqs = net->vqs; + r =vhost_dev_init (&net->dev, dev); +if (r <0 ) +return r; + + net->sock =socket (PF_PACKET ,SOCK_RAW ,htons (ETH_P_ALL )); +if (net->sock <0 ) +return -errno; + +memset (&req,0 ,sizeof (req)); +strncpy (req.ifr_name, ifname,IFNAMSIZ -1 ); + r =ioctl (net->sock,SIOCGIFINDEX , &req);hw/vhost_net.c line 175
Quoted 10 lines
hw/vhost_net.c new file +10
-0+if (r <0 ) +return -errno; + +memset (&lladdr,0 ,sizeof (lladdr)); + lladdr.sll_family =AF_PACKET ; + lladdr.sll_protocol =htons (ETH_P_ALL ); + lladdr.sll_ifindex = req.ifr_ifindex; + r =bind (net->sock, (const struct sockaddr *)&lladdr,sizeof (lladdr)); +if (r <0 ) +return -errno;You've get the point at this stage, but need to close the socket here on error too
Quoted 25 lines
hw/vhost_net.c new file +5
-0+ r =ioctl (net->dev.control,VHOST_NET_SET_SOCKET , &net->sock); +if (r <0 ) +return -errno; +return 0 ; +}hw/vhost_net.h new file +14
-0@@ -0,0 +1,30 @@ +#ifndef VHOST_NET_H +#define VHOST_NET_H + +#include "hw/virtio.h" + +struct vhost_virtqueue { +int kick; +int call; +void *desc; +void *avail; +void *used; +}; + +struct vhost_dev {hw/vhost_net.h line 24
Quoted 10 lines
hw/vhost_net.h new file +10
-0+int control; +struct vhost_virtqueue *vqs; +int nvqs; +}; + +struct vhost_net { +struct vhost_dev dev; +struct vhost_virtqueue vqs[2 ]; +int sock; +};I think I'd just malloc the vqs - having two vqs pointers in the two structs is a little confusing. Maybe it's worth it, though
Quoted 28 lines
hw/vhost_net.h new file +5
-0+int vhost_net_init (struct vhost_net *net, +VirtIODevice *dev, +char *vhost_device); + +#endif hw/virtio-net.c +4
-0@@ -19,6 +19,8 @@#include "qemu-kvm.h" #endif +#include "vhost_net.h" +#define TAP_VNET_HDR #define VIRTIO_NET_VM_VERSION 10@@ -56,6 +58,8 @@ typedef struct VirtIONetuint8_t *macs; } mac_table;uint32_t *vlans; +char *vhost_device; +struct vhost_net vhost; } VirtIONet;/* TODO hw/virtio-net.c static uint32_t virtio_net_get_features(VirtIODevice *vdev) · line 144
Quoted 7 lines
hw/virtio-net.c +4
-0@@ -134,9 +138,12 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev) (1 <<VIRTIO_NET_F_CTRL_RX ) | (1 <<VIRTIO_NET_F_CTRL_VLAN ) | (1 <<VIRTIO_NET_F_CTRL_RX_EXTRA ); +VirtIONet *n =to_virtio_net (vdev); + +if (n->vhost_device) +return 1 <<VIRTIO_NET_F_MAC ;features = MAC;if (n->vhost_device) return features; features |= ...;Quoted 26 lines
hw/virtio-net.c +6
-1#ifdef TAP_VNET_HDR -VirtIONet *n =to_virtio_net (vdev);VLANClientState *host = n->vc->vlan->first_client;if (tap_has_vnet_hdr (host)) {@@ -175,6 +182,9 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev)static void virtio_net_set_features (VirtIODevice *vdev,uint32_t features) {VirtIONet *n =to_virtio_net (vdev); +/* vhost net supports no features */ +if (n->vhost_device) +return ;#ifdef TAP_VNET_HDR VLANClientState *host = n->vc->vlan->first_client;#endif @@ -351,6 +361,9 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)static int do_virtio_net_can_receive (VirtIONet *n,int bufsize) { +if (n->vhost_device) +return 0 ; +if (!virtio_queue_ready (n->rx_vq) || !(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK ))return 0 ;hw/virtio-net.c static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count) · line 427
Quoted 4 lines
hw/virtio-net.c +1
-0@@ -411,6 +424,7 @@ static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count)while (offset < count && i < iovcnt) {int len =MIN (iov[i].iov_len, count - offset);memcpy (iov[i].iov_base, buf + offset, len); +Spurious
Quoted 13 lines
hw/virtio-net.c +2
-0offset += len; i++; }@@ -610,6 +624,8 @@ static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)#else int has_vnet_hdr =0 ;#endif +if (n->vhost_device) +return ;if (!(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK ))return ;hw/virtio-net.c static void virtio_net_cleanup(VLANClientState *vc) · line 846
Quoted 9 lines
hw/virtio-net.c +6
-0@@ -822,6 +838,18 @@ static void virtio_net_cleanup(VLANClientState *vc)virtio_cleanup (&n->vdev); } +static void virtio_net_driver_ok (VirtIODevice *vdev) +{ +VirtIONet *n =to_virtio_net (vdev); +int r; + + r =vhost_net_init (&n->vhost, vdev, n->vhost_device);Might be worth a comment as to why we need to wait until here to setup vhost; it's not immediately obvious
hw/virtio-net.c static void virtio_net_cleanup(VLANClientState *vc) · line 850
Quoted 4 lines
hw/virtio-net.c +4
-0+if (r) { +fprintf (stderr,"\nvhost_net_init returned %d\n" , r); +exit (-r); + }I think I'd like us to be able to fallback to normal userspace virtio-net with raw socket if we can't init vhost
Quoted 3 lines
Also, the guest is up and running at this point, right? With a linux guest, the root filesystem is probably mounted. It's not polite to just exit at this point if we can't open a raw socket or initialize vhost
Hmm, I know for testing exit on error is easier ... But maybe fprintf to stderr is enough.
Quoted 51 lines
Quoted 51 lines
hw/virtio-net.c +2
-0VirtIODevice *virtio_net_init (DeviceState *dev) {VirtIONet *n;@@ -837,6 +865,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->vdev.set_features = virtio_net_set_features; n->vdev.bad_features = virtio_net_bad_features; n->vdev.reset = virtio_net_reset; + n->vdev.driver_ok = virtio_net_driver_ok; n->rx_vq =virtio_add_queue (&n->vdev,256 , virtio_net_handle_rx); n->tx_vq =virtio_add_queue (&n->vdev,256 , virtio_net_handle_tx); n->ctrl_vq =virtio_add_queue (&n->vdev,64 , virtio_net_handle_ctrl);@@ -863,6 +892,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->vdev.nvectors =3 ; else n->vdev.nvectors = dev->nd->nvectors; + n->vhost_device = dev->nd->vhost_device;register_savevm ("virtio-net" , virtio_net_id++,VIRTIO_NET_VM_VERSION , virtio_net_save, virtio_net_load, n);hw/virtio-pci.c +4
-0@@ -15,11 +15,13 @@#include <inttypes.h> +#include <linux/kvm.h> #include "virtio.h" #include "pci.h" #include "sysemu.h" #include "msix.h" #include "net.h" +#include "qemu-kvm.h" /* from Linux's linux/virtio_pci.h */ @@ -199,6 +201,8 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) vdev->status = val &0xFF ;if (vdev->status ==0 )virtio_pci_reset (proxy); +if ((val &VIRTIO_CONFIG_S_DRIVER_OK ) && vdev->driver_ok) + vdev->driver_ok (vdev);break ;case VIRTIO_MSI_CONFIG_VECTOR :msix_vector_unuse (&proxy->pci_dev, vdev->config_vector);@@ -365,12 +369,48 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,msix_write_config (pci_dev, address, val, len); }
Quoted 6 lines
hw/virtio-pci.c static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, · line 375
Quoted 4 lines
hw/virtio-pci.c +4
-0+static int virtio_pci_irqfd (void * opaque,uint16_t vector,int fd) +{ +VirtIOPCIProxy *proxy = opaque; +struct kvm_irqfd call = { };Does the {}; init do anything?
Yes, memset to 0.
Quoted 8 lines
hw/virtio-pci.c static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, · line 381
Quoted 6 lines
hw/virtio-pci.c +6
-0+int r; + +if (vector >= proxy->pci_dev.msix_entries_nr) +return -EINVAL ; +if (!proxy->pci_dev.msix_entry_used[vector]) +return -ENOENT ;Does this mean if the guest doesn't have MSI support, we exit?
Yes
Quoted 8 lines
hw/virtio-pci.c static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, · line 387
Quoted 6 lines
hw/virtio-pci.c +6
-0+ call.fd = fd; + call.gsi = proxy->pci_dev.msix_irq_entries[vector].gsi; + r =kvm_vm_ioctl (kvm_state,KVM_IRQFD , &call); +if (r <0 ) +return r; +return 0 ;Have you plans to implement a non-kvm mode for this too?
With tcg? I could but I don't expect it to be better than userspace, maybe worse.
Quoted 95 lines
Quoted 59 lines
hw/virtio-pci.c +20
-0+} + +static int virtio_pci_queuefd (void * opaque,int n,int fd) +{ +VirtIOPCIProxy *proxy = opaque; +struct kvm_ioeventfd kick = { + .datamatch = n, + .addr = proxy->addr +VIRTIO_PCI_QUEUE_NOTIFY , + .len =2 , + .flags =KVM_IOEVENTFD_FLAG_DATAMATCH |KVM_IOEVENTFD_FLAG_PIO , + .fd = fd, + }; +int r =kvm_vm_ioctl (kvm_state,KVM_IOEVENTFD , &kick); +if (r <0 ) +return r; +return 0 ; +} +static const VirtIOBindings virtio_pci_bindings = { .notify = virtio_pci_notify, .save_config = virtio_pci_save_config, .load_config = virtio_pci_load_config, .save_queue = virtio_pci_save_queue, .load_queue = virtio_pci_load_queue, + .irqfd = virtio_pci_irqfd, + .queuefd = virtio_pci_queuefd, }; static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,hw/virtio.c +0
-18@@ -54,24 +54,6 @@ typedef struct VRingUsedVRingUsedElem ring[0 ]; } VRingUsed; -typedef struct VRing -{ -unsigned int num; -target_phys_addr_t desc; -target_phys_addr_t avail; -target_phys_addr_t used; -}VRing ; - -struct VirtQueue -{ -VRing vring; -target_phys_addr_t pa; -uint16_t last_avail_idx; -int inuse; -uint16_t vector; -void (*handle_output)(VirtIODevice *vdev,VirtQueue *vq); -}; -#define VIRTIO_PCI_QUEUE_MAX 16/* virt queue functions */ hw/virtio.c int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) · line 404
Quoted 4 lines
hw/virtio.c +0
-1@@ -401,7 +383,6 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) sg->iov_base =cpu_physical_memory_map (vring_desc_addr (desc_pa, i), &len, is_write); -Spurious
You appear to have a vendetta against whitespace :-)
Quoted 27 lines
hw/virtio.c +0
-0if (sg->iov_base ==NULL || len != sg->iov_len) {fprintf (stderr,"virtio: trying to map MMIO memory\n" );exit (1 );hw/virtio.h +16
-0@@ -54,15 +54,34 @@struct VirtQueue ; +typedef struct VRing +{ +unsigned int num; +target_phys_addr_t desc; +target_phys_addr_t avail; +target_phys_addr_t used; +}VRing ; + +typedef struct VirtQueue VirtQueue ; +struct VirtIODevice ; +typedef struct VirtIODevice VirtIODevice ; + +struct VirtQueue +{ +VRing vring; +target_phys_addr_t pa;
Quoted 8 lines
hw/virtio.h line 78
Quoted 6 lines
hw/virtio.h +6
-0+uint16_t last_avail_idx; +int inuse; +uint16_t vector; +void (*handle_output)(VirtIODevice *vdev,VirtQueue *vq); +}; +Might be better to add accessors for the bits we need?
Quite
Quoted 107 lines
Quoted 94 lines
hw/virtio.h +3
-3static inline target_phys_addr_t vring_align (target_phys_addr_t addr,unsigned long align) {return (addr + align -1 ) & ~(align -1 ); } -typedef struct VirtQueue VirtQueue ; -typedef struct VirtIODevice VirtIODevice ; -#define VIRTQUEUE_MAX_SIZE 1024 typedef struct VirtQueueElement@@ -81,6 +100,8 @@ typedef struct {void (*save_queue)(void * opaque,int n,QEMUFile *f);int (*load_config)(void * opaque,QEMUFile *f);int (*load_queue)(void * opaque,int n,QEMUFile *f); +int (*irqfd)(void * opaque,uint16_t vector,int fd); +int (*queuefd)(void * opaque,int n,int fd); } VirtIOBindings;#define VIRTIO_PCI_QUEUE_MAX 16@@ -104,6 +125,7 @@ struct VirtIODevicevoid (*get_config)(VirtIODevice *vdev,uint8_t *config);void (*set_config)(VirtIODevice *vdev,const uint8_t *config);void (*reset)(VirtIODevice *vdev); +void (*driver_ok)(VirtIODevice *vdev);VirtQueue *vq;const VirtIOBindings *binding;void *binding_opaque;net.c +5
-1@@ -2588,7 +2588,8 @@ int net_client_init(Monitor *mon, const char *device, const char *p) }if (!strcmp (device,"nic" )) {static const char *const nic_params[] = { -"vlan" ,"name" ,"macaddr" ,"model" ,"addr" ,"id" ,"vectors" ,NULL +"vlan" ,"name" ,"macaddr" ,"model" ,"addr" ,"id" ,"vectors" , +"vhost" ,NULL };NICInfo *nd;uint8_t *macaddr;@@ -2620,6 +2621,9 @@ int net_client_init(Monitor *mon, const char *device, const char *p) goto out; } } +if (get_param_value (buf,sizeof (buf),"vhost" , p)) { + nd->vhost_device =strdup (buf); + }if (get_param_value (buf,sizeof (buf),"model" , p)) { nd->model =strdup (buf); }net.h +1
-0@@ -110,6 +110,7 @@ struct NICInfo {int used;int bootable;int nvectors; +char *vhost_device; };extern int nb_nics;qemu-kvm.c +0
-8@@ -136,14 +136,6 @@ static inline void clear_gsi(kvm_context_t kvm, unsigned int gsi)DPRINTF ("Invalid GSI %d\n" ); } -struct slot_info { -unsigned long phys_addr; -unsigned long len; -unsigned long userspace_addr; -unsigned flags; -int logging_count; -}; -struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS ];static void init_slots (void )qemu-kvm.h +2
-0@@ -1215,6 +1215,15 @@ int kvm_ioctl(KVMState *s, int type, ...);int kvm_vm_ioctl (KVMState *s,int type, ...);int kvm_check_extension (KVMState *s,unsigned int ext); +struct slot_info { +unsigned long phys_addr;qemu-kvm.h int kvm_ioctl(KVMState *s, int type, ...); · line 1229
Quoted 10 lines
qemu-kvm.h +7
-0+unsigned long len; +unsigned long userspace_addr; +unsigned flags; +int logging_count; +}; + +extern struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS ];#endif #endif Cheers,
Mark.
Michael S. Tsirkin
maintainerRe: [PATCHv3 3/4] qemu-kvm: vhost-net implementation
In reply to Mark McLoughlinQuoted 3 lines · Mark McLoughlin
Hi Michael,
Some random high-level comments:
Note this code is posted very much just so that people can test vhost, not really for merging, but thanks for review!
Quoted 11 lines
- I had expected this to be available as:
-net raw,ifname=eth2 -net nic,model=virtio
I'd prefer it this way, because it means you can use this mode even without vhost and it's ties in better with the way all other qemu networking modes work.
Like the
VNET_HDRsupport, the "detect that there is only one backend on the vlan, and that it is a raw socket" code will be a hack until we have command line options that don't involve vlans.
Hmm. If we were to push the hack upstream I'd go for it, if we intend to rewrite it anyway - why bother with how it looks for now?
Quoted 3 lines
Open question whether we the user should explicitly have to request vhost acceleration or we use it if it is available. The latter sounds nice, but it hasn't turned out great for kvm.
Reason I didn't it's still pretty experimental at this point.
Quoted 8 lines
-
CAP_NET_ADMINis needed for raw sockets, so for e.g. libvirt I think we need to be able to support passing the raw socket fd via the command line and the monitor interface. I don't think we need that for the vhost fd, it should be safe to allow unprivileged users access to that, I think.- I thought this version supports migration, but I don't see where that is handled in the code?
Not in this version. Kernel code's already there though.
Quoted 1 line
- You're not using the errors eventfd?
I should, but it kind of works without, as long as the guest does not do anything strange.
Quoted 6 lines
I've taken a first look through the kernel patch, hopefully I'll post decent comments for that soon, but in the meantime:
- I think
/dev/vhostmakes more sense - we shouldn't need to add another character device if we implement kernel backends for other virtio devices
Why not?
Quoted 3 lines
- I'd really like vhost to support a 'tap' mode, so that we can still use a bridge if a NIC isn't available to be assigned.
It would result in this stuff getting much more testing.
BTW, there are currently some options available if you do not want to assign a NIC:
- set NIC to promisc mode
- use macvlan
Quoted 7 lines
Options I see:
1) Add tap-like functionality to vhost
2) AddVHOST_NET_SET_TAP
3) Just tell people to set up a tap and bind a raw socket too it
IMHO, (2) makes the most sense - it should be much less exta kernel
code than (1), and it would be much more convenient than (3)
I agree, an issue I see here is that we have no good way to check it's really tap and not some other random device. I'm looking at a similar option which is adding an ioctl to tap to expose it's underlying socket.
Quoted 23 lines
- Distros will need a udev rule to set the device perms to 0666
- Distros will also need some way to have the module loaded if its not built-in; best I can think of in Fedora is to stick it in
/etc/sysconfig/modules/kvm.modulesWhat would be nicer is if loading the kvm module could cause vhost to be loaded. It's nice that vhost can be used without kvm, but I think if kvm is loaded it's just very convenient to load vhost too. See also the problems we've cause with needing mkinitrd/dracut hacks by not having
KVM_GUESTrequireVIRTIO_PCIQuoted 3 lines · Michael S. Tsirkin
This adds support for vhost-net virtio kernel backend. To enable (assuming device eth2):
1. enable promisc mode or program guest mac in device eth2Why can't vhost do this itself?
Quoted 3 lines
2. disable tso, gso, lro, jumbo frames on the card (disabling lro + jumbo frames should be sufficient, haven't tested this)
Quoted 5 lines
And this.
If we leave that up to the user or the management app, we need to expose to them what features vhost supports so that they can know in future to stop disabling them.
That's what I did. There's a get features ioctl that returns 0 for now, when TSO is added I'll add it there.
Quoted 59 lines
Quoted 59 lines
3. add vhost=eth2 to -net flag
4. run withCAP_NET_ADMINpriviledge (e.g. root)This patch is RFC, but works without issues for me.
It still needs to be split up, tested and benchmarked properly, but posting it here in case people want to test drive the kernel bits I posted.
Signed-off-by Michael S. Tsirkin <[email protected]>--- Makefile.target | 3 +- hw/vhost_net.c | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/vhost_net.h | 30 +++++++++ hw/virtio-net.c | 32 ++++++++++- hw/virtio-pci.c | 40 ++++++++++++ hw/virtio.c | 19 ------ hw/virtio.h | 28 ++++++++- net.c | 6 ++- net.h | 1 + qemu-kvm.c | 8 --- qemu-kvm.h | 9 +++ 11 files changed, 324 insertions(+), 33 deletions(-) create mode 100644 hw/vhost_net.c create mode 100644 hw/vhost_net.hMakefile.target +2
-1@@ -170,7 +170,8 @@ obj-y = vl.o osdep.o monitor.o pci.o loader.o isa_mmio.o machine.o \ gdbstub.o gdbstub-xml.o msix.o ioport.o qemu-config.o # virtio has to be here due to weird dependency between PCI and virtio-net. # need to fix this properly -obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o +obj-y += virtio-blk.o virtio-balloon.o virtio-net.o virtio-console.o virtio-pci.o \ + vhost_net.o obj-$(CONFIG_KVM) += kvm.o kvm-all.o LIBS+=-lzhw/vhost_net.c new file +13
-0@@ -0,0 +1,181 @@ +#include <sys/eventfd.h> +#include <sys/socket.h> +#include <linux/kvm.h> +#include <fcntl.h> +#include <sys/ioctl.h> +#include <linux/vhost.h> +#include <linux/virtio_ring.h> +#include <netpacket/packet.h> +#include <net/ethernet.h> +#include <net/if.h> +#include <netinet/in.h> + +#include <stdio.h>
Quoted 8 lines
hw/vhost_net.c line 19
Quoted 6 lines
hw/vhost_net.c new file +6
-0+ +#include "qemu-kvm.h" + +#include "vhost_net.h" + +const char *vhost_net_device;Seems to be unused
Good catch
Quoted 32 lines
Quoted 13 lines
hw/vhost_net.c new file +13
-0+ +static int vhost_virtqueue_init (struct vhost_dev *dev, +struct VirtIODevice *vdev, +struct vhost_virtqueue *vq, +struct VirtQueue *q, +unsigned idx) +{ +target_phys_addr_t s, l; +int r; +struct vhost_vring_addr addr = { + .index = idx, + }; +struct vhost_vring_file file = {hw/vhost_net.c line 42
Quoted 10 lines
hw/vhost_net.c new file +10
-0+ .index = idx, + }; +struct vhost_vring_state size = { + .index = idx, + }; + + size.num = q->vring.num; + r =ioctl (dev->control,VHOST_SET_VRING_NUM , &size); +if (r) +return -errno;
VHOST_SET_VRING_SIZEis a better nameAlso, qemu coding style is:
if (r) { return -errno; }
Quoted 8 lines
Yes, I hate it too
hw/vhost_net.c line 44
Quoted 2 lines
hw/vhost_net.c new file +2
-0+ + file.fd = vq->kick =eventfd (0 ,0 );Handle error
Do we not want CLOEXEC and NONBLOCK ?
Why do we?
Quoted 46 lines
hw/vhost_net.c line 57
Quoted 13 lines
hw/vhost_net.c new file +13
-0+ r =ioctl (dev->control,VHOST_SET_VRING_KICK , &file); +if (r) +return -errno; + file.fd = vq->call =eventfd (0 ,0 ); + r =ioctl (dev->control,VHOST_SET_VRING_CALL , &file); +if (r) +return -errno; + + s = l =sizeof (struct vring_desc ) * q->vring.num; + vq->desc =cpu_physical_memory_map (q->vring.desc, &l,0 ); +if (!vq->desc || l != s) +return -ENOMEM ; + addr.user_addr = (u_int64_t )(unsigned long )vq->desc;Easier to read if it was re-factored to:
vq->desc= vhost_map(q->vring.desc,sizeof(struct vring_desc) * q->vring.num, &addr.user_addr); if (!vq->desc) return -ENOMEM;hw/vhost_net.c line 66
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ r =ioctl (dev->control,VHOST_SET_VRING_DESC , &addr); +if (r <0 ) +return -errno; + s = l = offsetof(struct vring_avail , ring) + +sizeof (u_int64_t) * q->vring.num; + vq->avail =cpu_physical_memory_map (q->vring.avail, &l,0 ); +if (!vq->avail || l != s) +return -ENOMEM ; + addr.user_addr = (u_int64_t )(unsigned long )vq->avail;And re-used here
hw/vhost_net.c line 75
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ r =ioctl (dev->control,VHOST_SET_VRING_AVAIL , &addr); +if (r <0 ) +return -errno; + s = l = offsetof(struct vring_used , ring) + +sizeof (struct vring_used_elem ) * q->vring.num; + vq->used =cpu_physical_memory_map (q->vring.used, &l,1 ); +if (!vq->used || l != s) +return -ENOMEM ; + addr.user_addr = (u_int64_t )(unsigned long )vq->used;and here
Quoted 1 line
Need to unmap these too at some point
Yes, cleanup is completely broken
Quoted 14 lines
hw/vhost_net.c line 86
Quoted 11 lines
hw/vhost_net.c new file +11
-0+ r =ioctl (dev->control,VHOST_SET_VRING_USED , &addr); +if (r <0 ) +return -errno; + + r = vdev->binding->irqfd (vdev->binding_opaque, q->vector, vq->call); +if (r <0 ) +return -errno; + + r = vdev->binding->queuefd (vdev->binding_opaque, idx, vq->kick); +if (r <0 ) +return -errno;Naming is a little confused here too
Quoted 8 lines
kick vs. call
irqfd vs. queuefd
irqfd vs. ioeventfd
How about just keeping it simple, sticking with the latter and making it
VHOST_SET_VRING_IRQFDandVHOST_SET_VRING_IOEVENTFD?
vhost tries to stick to virtio terminology.
virtio has kick and call so vhost copied that.
I agree call is a bit generic, but kick + irqfd
would be just confusing, right?
Quoted 274 lines
Quoted 5 lines
hw/vhost_net.c new file +5
-0+ +return 0 ; +} + +static int vhost_dev_init(struct vhost_dev *hdev,hw/vhost_net.c line 101
Quoted 10 lines
hw/vhost_net.c new file +10
-0+ VirtIODevice *vdev) +{ +int i, r, n =0 ; +struct vhost_memory *mem; + hdev->control =open ("/dev/vhost-net" ,O_RDWR ); +if (hdev->control <0 ) +return -errno; + r =ioctl (hdev->control,VHOST_SET_OWNER ,NULL ); +if (r <0 ) +return -errno;Should close the fd if we fail here or later
(Yep, I know you exit on error but ...)
hw/vhost_net.c line 107
Quoted 6 lines
hw/vhost_net.c new file +6
-0+for (i =0 ; i <KVM_MAX_NUM_MEM_REGIONS ; ++i) { +if (!slots[i].len || (slots[i].flags &KVM_MEM_LOG_DIRTY_PAGES )) { +continue ; + } + ++n; + }I don't love making a "slots" variable global, it'd be nicer to have:
slots =
kvm_get_slots(&n_slots);naming confusion here between "slots" and "regions" too
Also, max regions is 32, so I'd just allocate max regions and if some are unused it's no big deal
hw/vhost_net.c line 109
Quoted 1 line
hw/vhost_net.c new file +1
-0+ mem = qemu_mallocz(offsetof(struct vhost_memory, regions) +
sizeof(*mem)would suffice herehw/vhost_net.c line 118
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ n * sizeof(struct vhost_memory_region)); +if (!mem) +return -ENOMEM ; + mem->nregions = n; + n =0 ; +for (i =0 ; i <KVM_MAX_NUM_MEM_REGIONS ; ++i) { +if (!slots[i].len || (slots[i].flags &KVM_MEM_LOG_DIRTY_PAGES )) { +continue ; + }Ignoring the
LOG_DIRTY_PAGESregions could do with an explanatory commenthw/vhost_net.c line 127
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ mem->regions[n].guest_phys_addr = slots[i].phys_addr; + mem->regions[n].memory_size = slots[i].len; + mem->regions[n].userspace_addr = slots[i].userspace_addr; + ++n; + } + + r =ioctl (hdev->control,VHOST_SET_MEM_TABLE , mem); +if (r <0 ) +return -errno;Need to free the table (on error too)
hw/vhost_net.c line 136
Quoted 9 lines
hw/vhost_net.c new file +9
-0+ +for (i =0 ; i < hdev->nvqs; ++i) { + r =vhost_virtqueue_init (hdev, + vdev, + hdev->vqs + i, + vdev->vq + i, + i); +if (r <0 ) +return r;Need to handle errors better, e.g. unmapping
hw/vhost_net.c line 144
Quoted 8 lines
hw/vhost_net.c new file +8
-0+ } + +return 0 ; +} + +int vhost_net_init (struct vhost_net *net, +VirtIODevice *dev, +char *vhost_device)const char
*vhost_devicehw/vhost_net.c line 151
Quoted 7 lines
hw/vhost_net.c new file +7
-0+{ +struct sockaddr_ll lladdr; +struct ifreq req; +int r; +const char *ifname = vhost_device; +if (!ifname) +return 0 ;Why the ifname variable?
Quoted 13 lines
hw/vhost_net.c new file +13
-0+ net->dev.nvqs =2 ; + net->dev.vqs = net->vqs; + r =vhost_dev_init (&net->dev, dev); +if (r <0 ) +return r; + + net->sock =socket (PF_PACKET ,SOCK_RAW ,htons (ETH_P_ALL )); +if (net->sock <0 ) +return -errno; + +memset (&req,0 ,sizeof (req)); +strncpy (req.ifr_name, ifname,IFNAMSIZ -1 ); + r =ioctl (net->sock,SIOCGIFINDEX , &req);hw/vhost_net.c line 175
Quoted 10 lines
hw/vhost_net.c new file +10
-0+if (r <0 ) +return -errno; + +memset (&lladdr,0 ,sizeof (lladdr)); + lladdr.sll_family =AF_PACKET ; + lladdr.sll_protocol =htons (ETH_P_ALL ); + lladdr.sll_ifindex = req.ifr_ifindex; + r =bind (net->sock, (const struct sockaddr *)&lladdr,sizeof (lladdr)); +if (r <0 ) +return -errno;You've get the point at this stage, but need to close the socket here on error too
Quoted 25 lines
hw/vhost_net.c new file +5
-0+ r =ioctl (net->dev.control,VHOST_NET_SET_SOCKET , &net->sock); +if (r <0 ) +return -errno; +return 0 ; +}hw/vhost_net.h new file +14
-0@@ -0,0 +1,30 @@ +#ifndef VHOST_NET_H +#define VHOST_NET_H + +#include "hw/virtio.h" + +struct vhost_virtqueue { +int kick; +int call; +void *desc; +void *avail; +void *used; +}; + +struct vhost_dev {hw/vhost_net.h line 24
Quoted 10 lines
hw/vhost_net.h new file +10
-0+int control; +struct vhost_virtqueue *vqs; +int nvqs; +}; + +struct vhost_net { +struct vhost_dev dev; +struct vhost_virtqueue vqs[2 ]; +int sock; +};I think I'd just malloc the vqs - having two vqs pointers in the two structs is a little confusing. Maybe it's worth it, though
Quoted 28 lines
hw/vhost_net.h new file +5
-0+int vhost_net_init (struct vhost_net *net, +VirtIODevice *dev, +char *vhost_device); + +#endif hw/virtio-net.c +4
-0@@ -19,6 +19,8 @@#include "qemu-kvm.h" #endif +#include "vhost_net.h" +#define TAP_VNET_HDR #define VIRTIO_NET_VM_VERSION 10@@ -56,6 +58,8 @@ typedef struct VirtIONetuint8_t *macs; } mac_table;uint32_t *vlans; +char *vhost_device; +struct vhost_net vhost; } VirtIONet;/* TODO hw/virtio-net.c static uint32_t virtio_net_get_features(VirtIODevice *vdev) · line 144
Quoted 7 lines
hw/virtio-net.c +4
-0@@ -134,9 +138,12 @@ static uint32_t virtio_net_get_features(VirtIODevice *vdev) (1 <<VIRTIO_NET_F_CTRL_RX ) | (1 <<VIRTIO_NET_F_CTRL_VLAN ) | (1 <<VIRTIO_NET_F_CTRL_RX_EXTRA ); +VirtIONet *n =to_virtio_net (vdev); + +if (n->vhost_device) +return 1 <<VIRTIO_NET_F_MAC ;features = MAC;if (n->vhost_device) return features; features |= ...;Quoted 26 lines
hw/virtio-net.c +6
-1#ifdef TAP_VNET_HDR -VirtIONet *n =to_virtio_net (vdev);VLANClientState *host = n->vc->vlan->first_client;if (tap_has_vnet_hdr (host)) {@@ -175,6 +182,9 @@ static uint32_t virtio_net_bad_features(VirtIODevice *vdev)static void virtio_net_set_features (VirtIODevice *vdev,uint32_t features) {VirtIONet *n =to_virtio_net (vdev); +/* vhost net supports no features */ +if (n->vhost_device) +return ;#ifdef TAP_VNET_HDR VLANClientState *host = n->vc->vlan->first_client;#endif @@ -351,6 +361,9 @@ static void virtio_net_handle_rx(VirtIODevice *vdev, VirtQueue *vq)static int do_virtio_net_can_receive (VirtIONet *n,int bufsize) { +if (n->vhost_device) +return 0 ; +if (!virtio_queue_ready (n->rx_vq) || !(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK ))return 0 ;hw/virtio-net.c static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count) · line 427
Quoted 4 lines
hw/virtio-net.c +1
-0@@ -411,6 +424,7 @@ static int iov_fill(struct iovec *iov, int iovcnt, const void *buf, int count)while (offset < count && i < iovcnt) {int len =MIN (iov[i].iov_len, count - offset);memcpy (iov[i].iov_base, buf + offset, len); +Spurious
Quoted 13 lines
hw/virtio-net.c +2
-0offset += len; i++; }@@ -610,6 +624,8 @@ static void virtio_net_flush_tx(VirtIONet *n, VirtQueue *vq)#else int has_vnet_hdr =0 ;#endif +if (n->vhost_device) +return ;if (!(n->vdev.status &VIRTIO_CONFIG_S_DRIVER_OK ))return ;hw/virtio-net.c static void virtio_net_cleanup(VLANClientState *vc) · line 846
Quoted 9 lines
hw/virtio-net.c +6
-0@@ -822,6 +838,18 @@ static void virtio_net_cleanup(VLANClientState *vc)virtio_cleanup (&n->vdev); } +static void virtio_net_driver_ok (VirtIODevice *vdev) +{ +VirtIONet *n =to_virtio_net (vdev); +int r; + + r =vhost_net_init (&n->vhost, vdev, n->vhost_device);Might be worth a comment as to why we need to wait until here to setup vhost; it's not immediately obvious
hw/virtio-net.c static void virtio_net_cleanup(VLANClientState *vc) · line 850
Quoted 4 lines
hw/virtio-net.c +4
-0+if (r) { +fprintf (stderr,"\nvhost_net_init returned %d\n" , r); +exit (-r); + }I think I'd like us to be able to fallback to normal userspace virtio-net with raw socket if we can't init vhost
Quoted 3 lines
Also, the guest is up and running at this point, right? With a linux guest, the root filesystem is probably mounted. It's not polite to just exit at this point if we can't open a raw socket or initialize vhost
Hmm, I know for testing exit on error is easier ... But maybe fprintf to stderr is enough.
Quoted 51 lines
Quoted 51 lines
hw/virtio-net.c +2
-0VirtIODevice *virtio_net_init (DeviceState *dev) {VirtIONet *n;@@ -837,6 +865,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->vdev.set_features = virtio_net_set_features; n->vdev.bad_features = virtio_net_bad_features; n->vdev.reset = virtio_net_reset; + n->vdev.driver_ok = virtio_net_driver_ok; n->rx_vq =virtio_add_queue (&n->vdev,256 , virtio_net_handle_rx); n->tx_vq =virtio_add_queue (&n->vdev,256 , virtio_net_handle_tx); n->ctrl_vq =virtio_add_queue (&n->vdev,64 , virtio_net_handle_ctrl);@@ -863,6 +892,7 @@ VirtIODevice *virtio_net_init(DeviceState *dev) n->vdev.nvectors =3 ; else n->vdev.nvectors = dev->nd->nvectors; + n->vhost_device = dev->nd->vhost_device;register_savevm ("virtio-net" , virtio_net_id++,VIRTIO_NET_VM_VERSION , virtio_net_save, virtio_net_load, n);hw/virtio-pci.c +4
-0@@ -15,11 +15,13 @@#include <inttypes.h> +#include <linux/kvm.h> #include "virtio.h" #include "pci.h" #include "sysemu.h" #include "msix.h" #include "net.h" +#include "qemu-kvm.h" /* from Linux's linux/virtio_pci.h */ @@ -199,6 +201,8 @@ static void virtio_ioport_write(void *opaque, uint32_t addr, uint32_t val) vdev->status = val &0xFF ;if (vdev->status ==0 )virtio_pci_reset (proxy); +if ((val &VIRTIO_CONFIG_S_DRIVER_OK ) && vdev->driver_ok) + vdev->driver_ok (vdev);break ;case VIRTIO_MSI_CONFIG_VECTOR :msix_vector_unuse (&proxy->pci_dev, vdev->config_vector);@@ -365,12 +369,48 @@ static void virtio_write_config(PCIDevice *pci_dev, uint32_t address,msix_write_config (pci_dev, address, val, len); }
Quoted 6 lines
hw/virtio-pci.c static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, · line 375
Quoted 4 lines
hw/virtio-pci.c +4
-0+static int virtio_pci_irqfd (void * opaque,uint16_t vector,int fd) +{ +VirtIOPCIProxy *proxy = opaque; +struct kvm_irqfd call = { };Does the {}; init do anything?
Yes, memset to 0.
Quoted 8 lines
hw/virtio-pci.c static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, · line 381
Quoted 6 lines
hw/virtio-pci.c +6
-0+int r; + +if (vector >= proxy->pci_dev.msix_entries_nr) +return -EINVAL ; +if (!proxy->pci_dev.msix_entry_used[vector]) +return -ENOENT ;Does this mean if the guest doesn't have MSI support, we exit?
Yes
Quoted 8 lines
hw/virtio-pci.c static void virtio_write_config(PCIDevice *pci_dev, uint32_t address, · line 387
Quoted 6 lines
hw/virtio-pci.c +6
-0+ call.fd = fd; + call.gsi = proxy->pci_dev.msix_irq_entries[vector].gsi; + r =kvm_vm_ioctl (kvm_state,KVM_IRQFD , &call); +if (r <0 ) +return r; +return 0 ;Have you plans to implement a non-kvm mode for this too?
With tcg? I could but I don't expect it to be better than userspace, maybe worse.
Quoted 95 lines
Quoted 59 lines
hw/virtio-pci.c +20
-0+} + +static int virtio_pci_queuefd (void * opaque,int n,int fd) +{ +VirtIOPCIProxy *proxy = opaque; +struct kvm_ioeventfd kick = { + .datamatch = n, + .addr = proxy->addr +VIRTIO_PCI_QUEUE_NOTIFY , + .len =2 , + .flags =KVM_IOEVENTFD_FLAG_DATAMATCH |KVM_IOEVENTFD_FLAG_PIO , + .fd = fd, + }; +int r =kvm_vm_ioctl (kvm_state,KVM_IOEVENTFD , &kick); +if (r <0 ) +return r; +return 0 ; +} +static const VirtIOBindings virtio_pci_bindings = { .notify = virtio_pci_notify, .save_config = virtio_pci_save_config, .load_config = virtio_pci_load_config, .save_queue = virtio_pci_save_queue, .load_queue = virtio_pci_load_queue, + .irqfd = virtio_pci_irqfd, + .queuefd = virtio_pci_queuefd, }; static void virtio_init_pci(VirtIOPCIProxy *proxy, VirtIODevice *vdev,hw/virtio.c +0
-18@@ -54,24 +54,6 @@ typedef struct VRingUsedVRingUsedElem ring[0 ]; } VRingUsed; -typedef struct VRing -{ -unsigned int num; -target_phys_addr_t desc; -target_phys_addr_t avail; -target_phys_addr_t used; -}VRing ; - -struct VirtQueue -{ -VRing vring; -target_phys_addr_t pa; -uint16_t last_avail_idx; -int inuse; -uint16_t vector; -void (*handle_output)(VirtIODevice *vdev,VirtQueue *vq); -}; -#define VIRTIO_PCI_QUEUE_MAX 16/* virt queue functions */ hw/virtio.c int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) · line 404
Quoted 4 lines
hw/virtio.c +0
-1@@ -401,7 +383,6 @@ int virtqueue_pop(VirtQueue *vq, VirtQueueElement *elem) sg->iov_base =cpu_physical_memory_map (vring_desc_addr (desc_pa, i), &len, is_write); -Spurious
You appear to have a vendetta against whitespace :-)
Quoted 27 lines
hw/virtio.c +0
-0if (sg->iov_base ==NULL || len != sg->iov_len) {fprintf (stderr,"virtio: trying to map MMIO memory\n" );exit (1 );hw/virtio.h +16
-0@@ -54,15 +54,34 @@struct VirtQueue ; +typedef struct VRing +{ +unsigned int num; +target_phys_addr_t desc; +target_phys_addr_t avail; +target_phys_addr_t used; +}VRing ; + +typedef struct VirtQueue VirtQueue ; +struct VirtIODevice ; +typedef struct VirtIODevice VirtIODevice ; + +struct VirtQueue +{ +VRing vring; +target_phys_addr_t pa;
Quoted 8 lines
hw/virtio.h line 78
Quoted 6 lines
hw/virtio.h +6
-0+uint16_t last_avail_idx; +int inuse; +uint16_t vector; +void (*handle_output)(VirtIODevice *vdev,VirtQueue *vq); +}; +Might be better to add accessors for the bits we need?
Quite
Quoted 107 lines
Quoted 94 lines
hw/virtio.h +3
-3static inline target_phys_addr_t vring_align (target_phys_addr_t addr,unsigned long align) {return (addr + align -1 ) & ~(align -1 ); } -typedef struct VirtQueue VirtQueue ; -typedef struct VirtIODevice VirtIODevice ; -#define VIRTQUEUE_MAX_SIZE 1024 typedef struct VirtQueueElement@@ -81,6 +100,8 @@ typedef struct {void (*save_queue)(void * opaque,int n,QEMUFile *f);int (*load_config)(void * opaque,QEMUFile *f);int (*load_queue)(void * opaque,int n,QEMUFile *f); +int (*irqfd)(void * opaque,uint16_t vector,int fd); +int (*queuefd)(void * opaque,int n,int fd); } VirtIOBindings;#define VIRTIO_PCI_QUEUE_MAX 16@@ -104,6 +125,7 @@ struct VirtIODevicevoid (*get_config)(VirtIODevice *vdev,uint8_t *config);void (*set_config)(VirtIODevice *vdev,const uint8_t *config);void (*reset)(VirtIODevice *vdev); +void (*driver_ok)(VirtIODevice *vdev);VirtQueue *vq;const VirtIOBindings *binding;void *binding_opaque;net.c +5
-1@@ -2588,7 +2588,8 @@ int net_client_init(Monitor *mon, const char *device, const char *p) }if (!strcmp (device,"nic" )) {static const char *const nic_params[] = { -"vlan" ,"name" ,"macaddr" ,"model" ,"addr" ,"id" ,"vectors" ,NULL +"vlan" ,"name" ,"macaddr" ,"model" ,"addr" ,"id" ,"vectors" , +"vhost" ,NULL };NICInfo *nd;uint8_t *macaddr;@@ -2620,6 +2621,9 @@ int net_client_init(Monitor *mon, const char *device, const char *p) goto out; } } +if (get_param_value (buf,sizeof (buf),"vhost" , p)) { + nd->vhost_device =strdup (buf); + }if (get_param_value (buf,sizeof (buf),"model" , p)) { nd->model =strdup (buf); }net.h +1
-0@@ -110,6 +110,7 @@ struct NICInfo {int used;int bootable;int nvectors; +char *vhost_device; };extern int nb_nics;qemu-kvm.c +0
-8@@ -136,14 +136,6 @@ static inline void clear_gsi(kvm_context_t kvm, unsigned int gsi)DPRINTF ("Invalid GSI %d\n" ); } -struct slot_info { -unsigned long phys_addr; -unsigned long len; -unsigned long userspace_addr; -unsigned flags; -int logging_count; -}; -struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS ];static void init_slots (void )qemu-kvm.h +2
-0@@ -1215,6 +1215,15 @@ int kvm_ioctl(KVMState *s, int type, ...);int kvm_vm_ioctl (KVMState *s,int type, ...);int kvm_check_extension (KVMState *s,unsigned int ext); +struct slot_info { +unsigned long phys_addr;qemu-kvm.h int kvm_ioctl(KVMState *s, int type, ...); · line 1229
Quoted 10 lines
qemu-kvm.h +7
-0+unsigned long len; +unsigned long userspace_addr; +unsigned flags; +int logging_count; +}; + +extern struct slot_info slots[KVM_MAX_NUM_MEM_REGIONS ];#endif #endif Cheers,
Mark.
Re: [PATCHv3 3/4] qemu-kvm: vhost-net implementation
In reply to Mark McLoughlinQuoted 4 lines · Mark McLoughlin
- I think
/dev/vhostmakes more sense - we shouldn't need to add another character device if we implement kernel backends for other virtio devices
I'm of the opposite opinion - vhost-net and vhost-blk are related, but they're a lot more different than they are similar.
Quoted 11 lines
- I'd really like vhost to support a 'tap' mode, so that we can still use a bridge if a NIC isn't available to be assigned. It would result in this stuff getting much more testing. Options I see:
1) Add tap-like functionality to vhost
2) AddVHOST_NET_SET_TAP
3) Just tell people to set up a tap and bind a raw socket too itIMHO, (2) makes the most sense - it should be much less exta kernel
code than (1), and it would be much more convenient than (3)
What about connecting vhost-net to one end of a veth pair, and conencting the other end to a bridge?
Quoted 10 lines
- Distros will also need some way to have the module loaded if its not built-in; best I can think of in Fedora is to stick it in
/etc/sysconfig/modules/kvm.modulesWhat would be nicer is if loading the kvm module could cause vhost to be loaded. It's nice that vhost can be used without kvm, but I think if kvm is loaded it's just very convenient to load vhost too. See also the problems we've cause with needing mkinitrd/dracut hacks by not having
KVM_GUESTrequireVIRTIO_PCI
Given that new userspace and a new kernel is needed, I don't see an issue with the /etc/sysconfig fix.
Quoted 10 lines
Quoted 4 lines
2. disable tso, gso, lro, jumbo frames on the card (disabling lro + jumbo frames should be sufficient, haven't tested this)
And this.
If we leave that up to the user or the management app, we need to expose to them what features vhost supports so that they can know in future to stop disabling them.
I imagine the merged version will support all those goodies.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCHv3 3/4] qemu-kvm: vhost-net implementation
In reply to Mark McLoughlinQuoted 4 lines · Mark McLoughlin
- I think
/dev/vhostmakes more sense - we shouldn't need to add another character device if we implement kernel backends for other virtio devices
I'm of the opposite opinion - vhost-net and vhost-blk are related, but they're a lot more different than they are similar.
Quoted 11 lines
- I'd really like vhost to support a 'tap' mode, so that we can still use a bridge if a NIC isn't available to be assigned. It would result in this stuff getting much more testing. Options I see:
1) Add tap-like functionality to vhost
2) AddVHOST_NET_SET_TAP
3) Just tell people to set up a tap and bind a raw socket too itIMHO, (2) makes the most sense - it should be much less exta kernel
code than (1), and it would be much more convenient than (3)
What about connecting vhost-net to one end of a veth pair, and conencting the other end to a bridge?
Quoted 10 lines
- Distros will also need some way to have the module loaded if its not built-in; best I can think of in Fedora is to stick it in
/etc/sysconfig/modules/kvm.modulesWhat would be nicer is if loading the kvm module could cause vhost to be loaded. It's nice that vhost can be used without kvm, but I think if kvm is loaded it's just very convenient to load vhost too. See also the problems we've cause with needing mkinitrd/dracut hacks by not having
KVM_GUESTrequireVIRTIO_PCI
Given that new userspace and a new kernel is needed, I don't see an issue with the /etc/sysconfig fix.
Quoted 10 lines
Quoted 4 lines
2. disable tso, gso, lro, jumbo frames on the card (disabling lro + jumbo frames should be sufficient, haven't tested this)
And this.
If we leave that up to the user or the management app, we need to expose to them what features vhost supports so that they can know in future to stop disabling them.
I imagine the merged version will support all those goodies.
Signature · 1 line
error compiling committee.c: too many arguments to function
Michael S. Tsirkin
maintainerPATCH v3 4/4 qemu-kvm: add compat eventfd
In reply to a message not in the archiveSupport build on rhel 5.3 where we have syscall for eventfd but not userspace wrapper.
--- compat/sys/eventfd.h | 13 +++++++++++++ configure | 1 + 2 files changed, 14 insertions(+), 0 deletions(-) create mode 100644 compat/sys/eventfd.h
compat/sys/eventfd.h new file +13 -0
@@ -0,0 +1,13 @@ +#ifndef _COMPAT_SYS_EVENTFD +#define _COMPAT_SYS_EVENTFD + +#include <unistd.h> +#include <syscall.h> + + +static inline int eventfd (int count,int flags) +{ +return syscall (SYS_eventfd, count, flags); +} + +#endif
configure +1 -0
@@ -866,6 +866,7 @@ if test "$kvm" = "yes" ; then kvm_cflags="-I$source_path/kvm/include" kvm_cflags="$kvm_cflags -I$source_path/kvm/include/$kvm_arch" +kvm_cflags="$kvm_cflags -idirafter $source_path/compat" # test for KVM_CAP_PIT
Signature · 1 line
1.6.2.5
Michael S. Tsirkin
maintainerPATCH v3 4/4 qemu-kvm: add compat eventfd
In reply to a message not in the archiveSupport build on rhel 5.3 where we have syscall for eventfd but not userspace wrapper.
--- compat/sys/eventfd.h | 13 +++++++++++++ configure | 1 + 2 files changed, 14 insertions(+), 0 deletions(-) create mode 100644 compat/sys/eventfd.h
compat/sys/eventfd.h new file +13 -0
@@ -0,0 +1,13 @@ +#ifndef _COMPAT_SYS_EVENTFD +#define _COMPAT_SYS_EVENTFD + +#include <unistd.h> +#include <syscall.h> + + +static inline int eventfd (int count,int flags) +{ +return syscall (SYS_eventfd, count, flags); +} + +#endif
configure +1 -0
@@ -866,6 +866,7 @@ if test "$kvm" = "yes" ; then kvm_cflags="-I$source_path/kvm/include" kvm_cflags="$kvm_cflags -I$source_path/kvm/include/$kvm_arch" +kvm_cflags="$kvm_cflags -idirafter $source_path/compat" # test for KVM_CAP_PIT
Signature · 1 line
1.6.2.5