export cpu_tlbstate to modules
Subsystems (14)
A message that is not in the archive (sent off-list or not yet received): [email protected]
Michael S. Tsirkin
maintainerPATCH 1/2 export cpu_tlbstate to modules
vhost net module wants to do copy to/from user from a kernel thread, which needs switch_mm (like what fs/aio has).
export cpu_tlbstate to make this possible
--- arch/x86/mm/tlb.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
arch/x86/mm/tlb.c +1 -0
@@ -13,6 +13,7 @@ DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = { &init_mm,0 , }; +EXPORT_PER_CPU_SYMBOL_GPL (cpu_tlbstate);/* * Smarter SMP flushing macros.
Signature · 1 line
1.6.2.5
H. Peter Anvin
reviewerQuoted 14 lines · Michael S. Tsirkin
vhost net module wants to do copy to/from user from a kernel thread, which needs
switch_mm(like what fs/aio has).
exportcpu_tlbstateto make this possibleSigned-off-by Michael S. Tsirkin <[email protected]>--- arch/x86/mm/tlb.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)arch/x86/mm/tlb.c +0
-0
arch/x86/mm/tlb.c line 19
Quoted 7 lines
arch/x86/mm/tlb.c +1
-0@@ -13,6 +13,7 @@ DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = { &init_mm,0 , }; +EXPORT_PER_CPU_SYMBOL_GPL (cpu_tlbstate);/* * Smarter SMP flushing macros.
Wouldn't it be a *lot* better to move use_mm() from fs/aio.c into common code, and export that instead?
-hpa
Michael S. Tsirkin
maintainerQuoted 20 lines · H. Peter Anvin
Quoted 19 lines · Michael S. Tsirkin
vhost net module wants to do copy to/from user from a kernel thread, which needs
switch_mm(like what fs/aio has).
exportcpu_tlbstateto make this possibleSigned-off-by Michael S. Tsirkin <[email protected]>--- arch/x86/mm/tlb.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)arch/x86/mm/tlb.c +1
-0@@ -13,6 +13,7 @@ DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = { &init_mm,0 , }; +EXPORT_PER_CPU_SYMBOL_GPL (cpu_tlbstate);
Quoted 7 lines
arch/x86/mm/tlb.c line 19
Quoted 2 lines
arch/x86/mm/tlb.c +0
-0/* * Smarter SMP flushing macros. Wouldn't it be a *lot* better to move
use_mm()fromfs/aio.cinto common code, and export that instead?-hpa
That's easy too. What would a good place for it be?
H. Peter Anvin
reviewerQuoted 4 lines · Michael S. Tsirkin
Quoted 2 lines
Wouldn't it be a *lot* better to move
use_mm()fromfs/aio.cinto common code, and export that instead?That's easy too. What would a good place for it be?
Somewhere in mm/, presumably. When in doubt, make it a new file...
-hpa
Michael S. Tsirkin
maintainerSubject: [PATCH] mm: export use_mm/unuse_mm to modules
vhost net module wants to do copy to/from user from a kernel thread, which needs use_mm (like what fs/aio has). Move that into mm/ and export to modules.
---
Quoted 9 lines · H. Peter Anvin
Quoted 4 lines · Michael S. Tsirkin
Quoted 2 lines
Wouldn't it be a *lot* better to move
use_mm()fromfs/aio.cinto common code, and export that instead?That's easy too. What would a good place for it be?
Somewhere in
mm/, presumably. When in doubt, make it a new file...-hpa
Like this? Ack?
fs/aio.c | 47 +---------------------------------- include/linux/mmu_context.h | 9 ++++++ mm/Makefile | 2 +- mm/mmu_context.c | 58 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 47 deletions(-) create mode 100644 include/linux/mmu_context.h create mode 100644 mm/mmu_context.c
fs/aio.c +1 -46
@@ -24,6 +24,7 @@#include <linux/file.h> #include <linux/mm.h> #include <linux/mman.h> +#include <linux/mmu_context.h> #include <linux/slab.h> #include <linux/timer.h> #include <linux/aio.h>
@@ -34,7 +35,6 @@#include <asm/kmap_types.h> #include <asm/uaccess.h> -#include <asm/mmu_context.h> #if DEBUG >1 #define dprintk printk
@@ -595,51 +595,6 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id) }/* -* use_mm -* Makes the calling kernel thread take on the specified -* mm context. -* Called by the retry thread execute retries within the -* iocb issuer's mm context, so that copy_from/to_user -* operations work seamlessly for aio. -* (Note: this routine is intended to be called only -* from a kernel thread context) -*/ -static void use_mm (struct mm_struct *mm) -{ -struct mm_struct *active_mm; -struct task_struct *tsk = current; - -task_lock (tsk); - active_mm = tsk->active_mm; -atomic_inc (&mm->mm_count); - tsk->mm = mm; - tsk->active_mm = mm; -switch_mm (active_mm, mm, tsk); -task_unlock (tsk); - -mmdrop (active_mm); -} - -/* -* unuse_mm -* Reverses the effect of use_mm, i.e. releases the -* specified mm context which was earlier taken on -* by the calling kernel thread -* (Note: this routine is intended to be called only -* from a kernel thread context) -*/ -static void unuse_mm (struct mm_struct *mm) -{ -struct task_struct *tsk = current; - -task_lock (tsk); - tsk->mm =NULL ; -/* active_mm is still 'mm' */ -enter_lazy_tlb (mm, tsk); -task_unlock (tsk); -} - -/* * Queue up a kiocb to be retried. Assumes that the kiocb * has already been marked as kicked, and places it on * the retry run list for the corresponding ioctx, if it
include/linux/mmu_context.h new file +9 -0
@@ -0,0 +1,9 @@ +#ifndef _LINUX_MMU_CONTEXT_H +#define _LINUX_MMU_CONTEXT_H + +struct mm_struct ; + +void use_mm (struct mm_struct *mm); +void unuse_mm (struct mm_struct *mm); + +#endif
mm/Makefile +1 -1
@@ -11,7 +11,7 @@ obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ maccess.o page_alloc.o page-writeback.o pdflush.o \ readahead.o swap.o truncate.o vmscan.o shmem.o \ prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ - page_isolation.o mm_init.o $(mmu-y) + page_isolation.o mm_init.o mmu_context.o $(mmu-y) obj-y += init-mm.o obj-$(CONFIG_PROC_PAGE_MONITOR) += pagewalk.o
mm/mmu_context.c new file +58 -0
58 lines not on this page · raw message
Signature · 7 lines
1.6.2.5 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Michael S. Tsirkin
maintainerSubject: [PATCH] mm: export use_mm/unuse_mm to modules
vhost net module wants to do copy to/from user from a kernel thread, which needs use_mm (like what fs/aio has). Move that into mm/ and export to modules.
---
Quoted 9 lines · H. Peter Anvin
Quoted 4 lines · Michael S. Tsirkin
Quoted 2 lines
Wouldn't it be a *lot* better to move
use_mm()fromfs/aio.cinto common code, and export that instead?That's easy too. What would a good place for it be?
Somewhere in
mm/, presumably. When in doubt, make it a new file...-hpa
Like this? Ack?
fs/aio.c | 47 +---------------------------------- include/linux/mmu_context.h | 9 ++++++ mm/Makefile | 2 +- mm/mmu_context.c | 58 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 69 insertions(+), 47 deletions(-) create mode 100644 include/linux/mmu_context.h create mode 100644 mm/mmu_context.c
fs/aio.c +1 -46
@@ -24,6 +24,7 @@#include <linux/file.h> #include <linux/mm.h> #include <linux/mman.h> +#include <linux/mmu_context.h> #include <linux/slab.h> #include <linux/timer.h> #include <linux/aio.h>
@@ -34,7 +35,6 @@#include <asm/kmap_types.h> #include <asm/uaccess.h> -#include <asm/mmu_context.h> #if DEBUG >1 #define dprintk printk
@@ -595,51 +595,6 @@ static struct kioctx *lookup_ioctx(unsigned long ctx_id) }/* -* use_mm -* Makes the calling kernel thread take on the specified -* mm context. -* Called by the retry thread execute retries within the -* iocb issuer's mm context, so that copy_from/to_user -* operations work seamlessly for aio. -* (Note: this routine is intended to be called only -* from a kernel thread context) -*/ -static void use_mm (struct mm_struct *mm) -{ -struct mm_struct *active_mm; -struct task_struct *tsk = current; - -task_lock (tsk); - active_mm = tsk->active_mm; -atomic_inc (&mm->mm_count); - tsk->mm = mm; - tsk->active_mm = mm; -switch_mm (active_mm, mm, tsk); -task_unlock (tsk); - -mmdrop (active_mm); -} - -/* -* unuse_mm -* Reverses the effect of use_mm, i.e. releases the -* specified mm context which was earlier taken on -* by the calling kernel thread -* (Note: this routine is intended to be called only -* from a kernel thread context) -*/ -static void unuse_mm (struct mm_struct *mm) -{ -struct task_struct *tsk = current; - -task_lock (tsk); - tsk->mm =NULL ; -/* active_mm is still 'mm' */ -enter_lazy_tlb (mm, tsk); -task_unlock (tsk); -} - -/* * Queue up a kiocb to be retried. Assumes that the kiocb * has already been marked as kicked, and places it on * the retry run list for the corresponding ioctx, if it
include/linux/mmu_context.h new file +9 -0
@@ -0,0 +1,9 @@ +#ifndef _LINUX_MMU_CONTEXT_H +#define _LINUX_MMU_CONTEXT_H + +struct mm_struct ; + +void use_mm (struct mm_struct *mm); +void unuse_mm (struct mm_struct *mm); + +#endif
mm/Makefile +1 -1
@@ -11,7 +11,7 @@ obj-y := bootmem.o filemap.o mempool.o oom_kill.o fadvise.o \ maccess.o page_alloc.o page-writeback.o pdflush.o \ readahead.o swap.o truncate.o vmscan.o shmem.o \ prio_tree.o util.o mmzone.o vmstat.o backing-dev.o \ - page_isolation.o mm_init.o $(mmu-y) + page_isolation.o mm_init.o mmu_context.o $(mmu-y) obj-y += init-mm.o obj-$(CONFIG_PROC_PAGE_MONITOR) += pagewalk.o
mm/mmu_context.c new file +58 -0
58 lines not on this page · raw message
Signature · 1 line
1.6.2.5
H. Peter Anvin
reviewerQuoted 4 lines · Michael S. Tsirkin
Quoted 2 lines
Wouldn't it be a *lot* better to move
use_mm()fromfs/aio.cinto common code, and export that instead?That's easy too. What would a good place for it be?
Somewhere in mm/, presumably. When in doubt, make it a new file...
-hpa
Michael S. Tsirkin
maintainerQuoted 20 lines · H. Peter Anvin
Quoted 19 lines · Michael S. Tsirkin
vhost net module wants to do copy to/from user from a kernel thread, which needs
switch_mm(like what fs/aio has).
exportcpu_tlbstateto make this possibleSigned-off-by Michael S. Tsirkin <[email protected]>--- arch/x86/mm/tlb.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)arch/x86/mm/tlb.c +1
-0@@ -13,6 +13,7 @@ DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = { &init_mm,0 , }; +EXPORT_PER_CPU_SYMBOL_GPL (cpu_tlbstate);
Quoted 7 lines
arch/x86/mm/tlb.c line 19
Quoted 2 lines
arch/x86/mm/tlb.c +0
-0/* * Smarter SMP flushing macros. Wouldn't it be a *lot* better to move
use_mm()fromfs/aio.cinto common code, and export that instead?-hpa
That's easy too. What would a good place for it be?
H. Peter Anvin
reviewerQuoted 14 lines · Michael S. Tsirkin
vhost net module wants to do copy to/from user from a kernel thread, which needs
switch_mm(like what fs/aio has).
exportcpu_tlbstateto make this possibleSigned-off-by Michael S. Tsirkin <[email protected]>--- arch/x86/mm/tlb.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)arch/x86/mm/tlb.c +0
-0
arch/x86/mm/tlb.c line 19
Quoted 7 lines
arch/x86/mm/tlb.c +1
-0@@ -13,6 +13,7 @@ DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = { &init_mm,0 , }; +EXPORT_PER_CPU_SYMBOL_GPL (cpu_tlbstate);/* * Smarter SMP flushing macros.
Wouldn't it be a *lot* better to move use_mm() from fs/aio.c into common code, and export that instead?
-hpa
Michael S. Tsirkin
maintainerPATCH 1/2 export cpu_tlbstate to modules
In reply to a message not in the archivevhost net module wants to do copy to/from user from a kernel thread, which needs switch_mm (like what fs/aio has).
export cpu_tlbstate to make this possible
--- arch/x86/mm/tlb.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
arch/x86/mm/tlb.c +1 -0
@@ -13,6 +13,7 @@ DEFINE_PER_CPU_SHARED_ALIGNED(struct tlb_state, cpu_tlbstate) = { &init_mm,0 , }; +EXPORT_PER_CPU_SYMBOL_GPL (cpu_tlbstate);/* * Smarter SMP flushing macros.
Signature · 1 line
1.6.2.5
Michael S. Tsirkin
maintainerPATCH 2/2 vhost_net: a kernel-level virtio server
In reply to a message not in the archiveWhat it is: vhost net is a character device that can be used to reduce the number of system calls involved in virtio networking. Existing virtio net code is used in the guest without modification.
There's similarity with vringfd, with some differences and reduced scope
- uses eventfd for signalling
- structures can be moved around in memory at any time (good for migration)
- support memory table and not just an offset (needed for kvm)
common virtio related code has been put in a separate file vhost.c and can be made into a separate module if/when more backend appear. I used Rusty's lguest.c as the source for developing this part : this supplied me with witty comments I wouldn't be able to write myself.
What it is not: vhost net is not a bus, and not a generic new system call. No assumptions are made on how guest performs hypercalls. Userspace hypervisors are supported as well as kvm.
How it works: Basically, we connect virtio frontend (configured by userspace) to a backend. The backend could be a network device, or a tun-like device. In this version I only support raw socket as a backend, which can be bound to e.g. SR IOV, or to macvlan device. Backend is also configured by userspace, including vlan/mac etc.
Status:
This works for me, and I haven't see any crashes.
I have not run any benchmarks yet, compared to userspace, I expect to see improved latency (as I save up to 4 system calls per packet) but not yet bandwidth/CPU (as TSO and interrupt mitigation are not yet supported).
Features that I plan to look at in the future:
- TSO
- interrupt mitigation
- zero copy
--- MAINTAINERS | 10 + arch/x86/kvm/Kconfig | 1 + drivers/Makefile | 1 + drivers/block/virtio_blk.c | 3 + drivers/vhost/Kconfig | 11 + drivers/vhost/Makefile | 2 + drivers/vhost/net.c | 462 ++++++++++++++++++++++++++++++ drivers/vhost/vhost.c | 663 ++++++++++++++++++++++++++++++++++++++++++++ drivers/vhost/vhost.h | 108 +++++++ include/linux/Kbuild | 1 + include/linux/miscdevice.h | 1 + include/linux/vhost.h | 100 +++++++ 12 files changed, 1363 insertions(+), 0 deletions(-) create mode 100644 drivers/vhost/Kconfig create mode 100644 drivers/vhost/Makefile create mode 100644 drivers/vhost/net.c create mode 100644 drivers/vhost/vhost.c create mode 100644 drivers/vhost/vhost.h create mode 100644 include/linux/vhost.h
MAINTAINERS +10 -0
@@ -6312,6 +6312,16 @@ S: Maintained F: Documentation/filesystems/vfat.txt F: fs/fat/ +VIRTIO HOST (VHOST) +P: Michael S. Tsirkin +M: [email protected] +L: [email protected] +L: [email protected] +L: [email protected] +S: Maintained +F: drivers/vhost/ +F: include/linux/vhost.h + VIA RHINE NETWORK DRIVER P: Roger Luethi M: [email protected]
arch/x86/kvm/Kconfig +1 -0
@@ -64,6 +64,7 @@ config KVM_AMD # OK, it's a little counter-intuitive to do this, but it puts it neatly under # the virtualization menu. +source drivers/vhost/Kconfig source drivers/lguest/Kconfig source drivers/virtio/Kconfig
drivers/Makefile +1 -0
@@ -105,6 +105,7 @@ obj-$(CONFIG_HID) += hid/ obj-$(CONFIG_PPC_PS3) += ps3/ obj-$(CONFIG_OF) += of/ obj-$(CONFIG_SSB) += ssb/ +obj-$(CONFIG_VHOST_NET) += vhost/ obj-$(CONFIG_VIRTIO) += virtio/ obj-$(CONFIG_VLYNQ) += vlynq/ obj-$(CONFIG_STAGING) += staging/
drivers/block/virtio_blk.c +3 -0
@@ -293,6 +293,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) err =PTR_ERR (vblk->vq); goto out_free_vblk; } +printk (KERN_ERR "vblk->vq = %p\n" , vblk->vq); vblk->pool =mempool_create_kmalloc_pool (1 ,sizeof (struct virtblk_req ));if (!vblk->pool) {
@@ -383,6 +384,8 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)if (!err)blk_queue_logical_block_size (vblk->disk->queue, blk_size); +printk (KERN_ERR "virtio_config_val returned %d\n" , err); +add_disk (vblk->disk);return 0 ;
drivers/vhost/Kconfig new file +11 -0
@@ -0,0 +1,11 @@ +config VHOST_NET + tristate "Host kernel accelerator for virtio net" + depends on NET && EVENTFD + ---help--- + This kernel module can be loaded in host kernel to accelerate + guest networking with virtio_net. Not to be confused with virtio_net + module itself which needs to be loaded in guest kernel. + + To compile this driver as a module, choose M here: the module will + be called vhost_net. +
drivers/vhost/Makefile new file +2 -0
@@ -0,0 +1,2 @@ +obj-$(CONFIG_VHOST_NET) += vhost_net.o +vhost_net-y := vhost.o net.o
drivers/vhost/net.c new file +462 -0
462 lines not on this page · raw message
drivers/vhost/vhost.c new file +663 -0
663 lines not on this page · raw message
drivers/vhost/vhost.h new file +108 -0
108 lines not on this page · raw message
include/linux/Kbuild +1 -0
@@ -360,6 +360,7 @@ unifdef-y += uio.h unifdef-y += unistd.h unifdef-y += usbdevice_fs.h unifdef-y += utsname.h +unifdef-y += vhost.h unifdef-y += videodev2.h unifdef-y += videodev.h unifdef-y += virtio_config.h
include/linux/miscdevice.h +1 -0
@@ -30,6 +30,7 @@#define HPET_MINOR 228#define FUSE_MINOR 229#define KVM_MINOR 232 +#define VHOST_NET_MINOR 233#define MISC_DYNAMIC_MINOR 255struct device ;
include/linux/vhost.h new file +100 -0
100 lines not on this page · raw message
Signature · 1 line
1.6.2.5
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 3 lines · Michael S. Tsirkin
What it is: vhost net is a character device that can be used to reduce the number of system calls involved in virtio networking. Existing virtio net code is used in the guest without modification.
Very nice, I loved reading it. It's getting rather late in my time zone, so this comments only on the network driver. I'll go through the rest tomorrow.
Quoted 9 lines
drivers/block/virtio_blk.c +1
-0@@ -293,6 +293,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) err =PTR_ERR (vblk->vq); goto out_free_vblk; } +printk (KERN_ERR "vblk->vq = %p\n" , vblk->vq); vblk->pool =mempool_create_kmalloc_pool (1 ,sizeof (struct virtblk_req ));if (!vblk->pool) {
drivers/block/virtio_blk.c static int __devinit virtblk_probe(struct virtio_device *vdev) · line 390
Quoted 7 lines
drivers/block/virtio_blk.c +2
-0@@ -383,6 +384,8 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)if (!err)blk_queue_logical_block_size (vblk->disk->queue, blk_size); +printk (KERN_ERR "virtio_config_val returned %d\n" , err); +add_disk (vblk->disk);return 0 ;
I guess you meant to remove these before submitting.
drivers/vhost/net.c line 52
Quoted 4 lines
drivers/vhost/net.c new file +4
-0+static void handle_tx_kick (struct work_struct *work); +static void handle_rx_kick (struct work_struct *work); +static void handle_tx_net (struct work_struct *work); +static void handle_rx_net (struct work_struct *work);
[style] I think the code gets more readable if you reorder it so that you don't need forward declarations for static functions.
Quoted 6 lines
drivers/vhost/net.c new file +6
-0+static long vhost_net_reset_owner (struct vhost_net *n) +{ +struct socket *sock =NULL ; +long r; +mutex_lock (&n->dev.mutex); + r =vhost_dev_check_owner (&n->dev);
drivers/vhost/net.c line 179
Quoted 10 lines
drivers/vhost/net.c new file +10
-0+if (r) + goto done; + sock =vhost_net_stop (n); + r =vhost_dev_reset_owner (&n->dev); +done: +mutex_unlock (&n->dev.mutex); +if (sock) +fput (sock->file); +return r; +}
what is the difference between vhost_net_reset_owner(n) and vhost_net_set_socket(n, -1)?
drivers/vhost/net.c line 220
Quoted 7 lines
drivers/vhost/net.c new file +7
-0+ +static struct file_operations vhost_net_fops = { + .owner =THIS_MODULE , + .release = vhost_net_release, + .unlocked_ioctl = vhost_net_ioctl, + .open = vhost_net_open, +};
This is missing a compat_ioctl pointer. It should simply be
static long vhost_net_compat_ioctl(struct file *f,
unsigned int ioctl, unsigned long arg)
{
return f, ioctl, (unsigned long)compat_ptr(arg);
}Quoted 16 lines
drivers/vhost/net.c new file +16
-0+/* Bits from fs/aio.c. TODO: export and use from there? */ +/* +* use_mm +* Makes the calling kernel thread take on the specified +* mm context. +* Called by the retry thread execute retries within the +* iocb issuer's mm context, so that copy_from/to_user +* operations work seamlessly for aio. +* (Note: this routine is intended to be called only +* from a kernel thread context) +*/ +static void use_mm (struct mm_struct *mm) +{ +struct mm_struct *active_mm; +struct task_struct *tsk = current; +
drivers/vhost/net.c line 253
Quoted 10 lines
drivers/vhost/net.c new file +10
-0+task_lock (tsk); + active_mm = tsk->active_mm; +atomic_inc (&mm->mm_count); + tsk->mm = mm; + tsk->active_mm = mm; +switch_mm (active_mm, mm, tsk); +task_unlock (tsk); + +mmdrop (active_mm); +}
Why do you need a kernel thread here? If the data transfer functions all get called from a guest intercept, shouldn't you already be in the right mm?
Quoted 45 lines
drivers/vhost/net.c new file +45
-0+static void handle_tx (struct vhost_net *net) +{ +struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_TX ]; +unsigned head, out, in; +struct msghdr msg = { + .msg_name =NULL , + .msg_namelen =0 , + .msg_control =NULL , + .msg_controllen =0 , + .msg_iov = (struct iovec *)vq->iov +1 , + .msg_flags =MSG_DONTWAIT , + }; +size_t len; +int err; +struct socket *sock =rcu_dereference (net->sock); +if (!sock || !sock_writeable (sock->sk)) +return ; + +use_mm (net->dev.mm); +mutex_lock (&vq->mutex); +for (;;) { + head =vhost_get_vq_desc (&net->dev, vq, vq->iov, &out, &in); +if (head == vq->num) +break ; +if (out <=1 || in) { +vq_err (vq,"Unexpected descriptor format for TX: " +"out %d, int %d\n" , out, in); +break ; + } +/* Sanity check */ +if (vq->iov->iov_len !=sizeof (struct virtio_net_hdr )) { +vq_err (vq,"Unexpected header len for TX: " +"%ld expected %zd\n" , vq->iov->iov_len, +sizeof (struct virtio_net_hdr )); +break ; + } +/* Skip header. TODO: support TSO. */ + msg.msg_iovlen = out -1 ; + len =iov_length (vq->iov +1 , out -1 ); +/* TODO: Check specific error and bomb out unless ENOBUFS? */ + err = sock->ops->sendmsg (NULL , sock, &msg, len); +if (err <0 ) { +vhost_discard_vq_desc (vq); +break ; + }
drivers/vhost/net.c line 328
Quoted 10 lines
drivers/vhost/net.c new file +10
-0+if (err != len) +pr_err ("Truncated TX packet: " +" len %d != %zd\n" , err, len); +vhost_add_used_and_trigger (vq, head, + len +sizeof (struct virtio_net_hdr )); + } + +mutex_unlock (&vq->mutex); +unuse_mm (net->dev.mm); +}
I guess that this is where one could plug into macvlan directly, using sock_alloc_send_skb/memcpy_fromiovec/dev_queue_xmit directly, instead of filling a msghdr for each, if we want to combine this with the work I did on that.
Arnd <><
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 21 lines · Arnd Bergmann
Quoted 3 lines · Michael S. Tsirkin
What it is: vhost net is a character device that can be used to reduce the number of system calls involved in virtio networking. Existing virtio net code is used in the guest without modification.
Very nice, I loved reading it. It's getting rather late in my time zone, so this comments only on the network driver. I'll go through the rest tomorrow.
Quoted 12 lines
drivers/block/virtio_blk.c +1
-0@@ -293,6 +293,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) err =PTR_ERR (vblk->vq); goto out_free_vblk; } +printk (KERN_ERR "vblk->vq = %p\n" , vblk->vq); vblk->pool =mempool_create_kmalloc_pool (1 ,sizeof (struct virtblk_req ));if (!vblk->pool) {@@ -383,6 +384,8 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)if (!err)blk_queue_logical_block_size (vblk->disk->queue, blk_size);
Quoted 6 lines
drivers/block/virtio_blk.c static int __devinit virtblk_probe(struct virtio_device *vdev) · line 390
Quoted 4 lines
drivers/block/virtio_blk.c +2
-0+printk (KERN_ERR "virtio_config_val returned %d\n" , err); +add_disk (vblk->disk);return 0 ;I guess you meant to remove these before submitting.
Good catch, thanks!
Quoted 7 lines
drivers/vhost/net.c line 52
Quoted 4 lines
drivers/vhost/net.c new file +4
-0+static void handle_tx_kick (struct work_struct *work); +static void handle_rx_kick (struct work_struct *work); +static void handle_tx_net (struct work_struct *work); +static void handle_rx_net (struct work_struct *work);[style] I think the code gets more readable if you reorder it so that you don't need forward declarations for static functions.
Right.
Quoted 11 lines
Quoted 11 lines
drivers/vhost/net.c new file +11
-0+static long vhost_net_reset_owner (struct vhost_net *n) +{ +struct socket *sock =NULL ; +long r; +mutex_lock (&n->dev.mutex); + r =vhost_dev_check_owner (&n->dev); +if (r) + goto done; + sock =vhost_net_stop (n); + r =vhost_dev_reset_owner (&n->dev); +done:
Quoted 8 lines
drivers/vhost/net.c line 179
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+mutex_unlock (&n->dev.mutex); +if (sock) +fput (sock->file); +return r; +}what is the difference between
vhost_net_reset_owner(n)andvhost_net_set_socket(n, -1)?
set socket to -1 will only stop the device.
reset owner will let another process take over the device. It also needs to reset all parameters to make it safe for that other process, so in particular the device is stopped.
I tried explaining this in the header vhost.h - does the comment there help, or do I need to clarify it?
Quoted 10 lines
drivers/vhost/net.c line 220
Quoted 7 lines
drivers/vhost/net.c new file +7
-0+ +static struct file_operations vhost_net_fops = { + .owner =THIS_MODULE , + .release = vhost_net_release, + .unlocked_ioctl = vhost_net_ioctl, + .open = vhost_net_open, +};This is missing a
compat_ioctlpointer. It should simply be
Quoted 5 lines
static long vhost_net_compat_ioctl(struct file *f,
unsigned int ioctl, unsigned long arg){ return f, ioctl, (unsigned long)compat_ptr(arg); }
I had the impression that if there's no compat_ioctl, unlocked_ioctl will get called automatically. No?
Quoted 22 lines
Quoted 22 lines
drivers/vhost/net.c new file +22
-0+/* Bits from fs/aio.c. TODO: export and use from there? */ +/* +* use_mm +* Makes the calling kernel thread take on the specified +* mm context. +* Called by the retry thread execute retries within the +* iocb issuer's mm context, so that copy_from/to_user +* operations work seamlessly for aio. +* (Note: this routine is intended to be called only +* from a kernel thread context) +*/ +static void use_mm (struct mm_struct *mm) +{ +struct mm_struct *active_mm; +struct task_struct *tsk = current; + +task_lock (tsk); + active_mm = tsk->active_mm; +atomic_inc (&mm->mm_count); + tsk->mm = mm; + tsk->active_mm = mm; +switch_mm (active_mm, mm, tsk);
Quoted 8 lines
drivers/vhost/net.c line 253
Quoted 4 lines
drivers/vhost/net.c new file +4
-0+task_unlock (tsk); + +mmdrop (active_mm); +}Why do you need a kernel thread here? If the data transfer functions all get called from a guest intercept, shouldn't you already be in the right mm?
several reasons :)
- I get called under lock, so can't block
- eventfd can be passed to another process, and I won't be in guest context at all
- this also gets called outside guest context from socket poll
- vcpu is blocked while it's doing i/o. it is better to free it up
as all the packet copying might take a while
Quoted 52 lines
Quoted 52 lines
drivers/vhost/net.c new file +52
-0+static void handle_tx (struct vhost_net *net) +{ +struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_TX ]; +unsigned head, out, in; +struct msghdr msg = { + .msg_name =NULL , + .msg_namelen =0 , + .msg_control =NULL , + .msg_controllen =0 , + .msg_iov = (struct iovec *)vq->iov +1 , + .msg_flags =MSG_DONTWAIT , + }; +size_t len; +int err; +struct socket *sock =rcu_dereference (net->sock); +if (!sock || !sock_writeable (sock->sk)) +return ; + +use_mm (net->dev.mm); +mutex_lock (&vq->mutex); +for (;;) { + head =vhost_get_vq_desc (&net->dev, vq, vq->iov, &out, &in); +if (head == vq->num) +break ; +if (out <=1 || in) { +vq_err (vq,"Unexpected descriptor format for TX: " +"out %d, int %d\n" , out, in); +break ; + } +/* Sanity check */ +if (vq->iov->iov_len !=sizeof (struct virtio_net_hdr )) { +vq_err (vq,"Unexpected header len for TX: " +"%ld expected %zd\n" , vq->iov->iov_len, +sizeof (struct virtio_net_hdr )); +break ; + } +/* Skip header. TODO: support TSO. */ + msg.msg_iovlen = out -1 ; + len =iov_length (vq->iov +1 , out -1 ); +/* TODO: Check specific error and bomb out unless ENOBUFS? */ + err = sock->ops->sendmsg (NULL , sock, &msg, len); +if (err <0 ) { +vhost_discard_vq_desc (vq); +break ; + } +if (err != len) +pr_err ("Truncated TX packet: " +" len %d != %zd\n" , err, len); +vhost_add_used_and_trigger (vq, head, + len +sizeof (struct virtio_net_hdr )); + } +
Quoted 8 lines
drivers/vhost/net.c line 328
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+mutex_unlock (&vq->mutex); +unuse_mm (net->dev.mm); +}I guess that this is where one could plug into macvlan directly, using sock_alloc_send_skb/memcpy_fromiovec/dev_queue_xmit directly, instead of filling a msghdr for each, if we want to combine this with the work I did on that.
quite possibly. Or one can just bind a raw socket to macvlan :)
Quoted 1 line
Arnd <><
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 9 lines · On Monday 10 August 2009 20:10:44 Michael S. Tsirkin wrote:
Quoted 2 lines · Arnd Bergmann
what is the difference between
vhost_net_reset_owner(n)andvhost_net_set_socket(n, -1)?set socket to -1 will only stop the device.
reset owner will let another process take over the device. It also needs to reset all parameters to make it safe for that other process, so in particular the device is stopped.
ok
Quoted 2 lines
I tried explaining this in the header
vhost.h- does the comment there help, or do I need to clarify it?
No, I just didn't get there yet.
Quoted 2 lines
I had the impression that if there's no
compat_ioctl,unlocked_ioctlwill get called automatically. No?
It will issue a kernel warning but not call unlocked_ioctl, so you need either a compat_ioctl method or list the numbers in fs/compat_ioctl.c, which I try to avoid.
Quoted 10 lines
Quoted 3 lines
Why do you need a kernel thread here? If the data transfer functions all get called from a guest intercept, shouldn't you already be in the right mm?
several reasons :)
- I get called under lock, so can't block
- eventfd can be passed to another process, and I won't be in guest context at all
- this also gets called outside guest context from socket poll
- vcpu is blocked while it's doing i/o. it is better to free it up
as all the packet copying might take a while
Ok.
Quoted 6 lines
Quoted 4 lines
I guess that this is where one could plug into macvlan directly, using sock_alloc_send_skb/memcpy_fromiovec/dev_queue_xmit directly, instead of filling a msghdr for each, if we want to combine this with the work I did on that.
quite possibly. Or one can just bind a raw socket to macvlan :)
Right, that works as well, but may get more complicated once we try to add zero-copy or other optimizations.
Arnd <><
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 9 lines · On Monday 10 August 2009 20:10:44 Michael S. Tsirkin wrote:
Quoted 2 lines · Arnd Bergmann
what is the difference between
vhost_net_reset_owner(n)andvhost_net_set_socket(n, -1)?set socket to -1 will only stop the device.
reset owner will let another process take over the device. It also needs to reset all parameters to make it safe for that other process, so in particular the device is stopped.
ok
Quoted 2 lines
I tried explaining this in the header
vhost.h- does the comment there help, or do I need to clarify it?
No, I just didn't get there yet.
Quoted 2 lines
I had the impression that if there's no
compat_ioctl,unlocked_ioctlwill get called automatically. No?
It will issue a kernel warning but not call unlocked_ioctl, so you need either a compat_ioctl method or list the numbers in fs/compat_ioctl.c, which I try to avoid.
Quoted 10 lines
Quoted 3 lines
Why do you need a kernel thread here? If the data transfer functions all get called from a guest intercept, shouldn't you already be in the right mm?
several reasons :)
- I get called under lock, so can't block
- eventfd can be passed to another process, and I won't be in guest context at all
- this also gets called outside guest context from socket poll
- vcpu is blocked while it's doing i/o. it is better to free it up
as all the packet copying might take a while
Ok.
Quoted 6 lines
Quoted 4 lines
I guess that this is where one could plug into macvlan directly, using sock_alloc_send_skb/memcpy_fromiovec/dev_queue_xmit directly, instead of filling a msghdr for each, if we want to combine this with the work I did on that.
quite possibly. Or one can just bind a raw socket to macvlan :)
Right, that works as well, but may get more complicated once we try to add zero-copy or other optimizations.
Arnd <><
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 21 lines · Arnd Bergmann
Quoted 3 lines · Michael S. Tsirkin
What it is: vhost net is a character device that can be used to reduce the number of system calls involved in virtio networking. Existing virtio net code is used in the guest without modification.
Very nice, I loved reading it. It's getting rather late in my time zone, so this comments only on the network driver. I'll go through the rest tomorrow.
Quoted 12 lines
drivers/block/virtio_blk.c +1
-0@@ -293,6 +293,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) err =PTR_ERR (vblk->vq); goto out_free_vblk; } +printk (KERN_ERR "vblk->vq = %p\n" , vblk->vq); vblk->pool =mempool_create_kmalloc_pool (1 ,sizeof (struct virtblk_req ));if (!vblk->pool) {@@ -383,6 +384,8 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)if (!err)blk_queue_logical_block_size (vblk->disk->queue, blk_size);
Quoted 6 lines
drivers/block/virtio_blk.c static int __devinit virtblk_probe(struct virtio_device *vdev) · line 390
Quoted 4 lines
drivers/block/virtio_blk.c +2
-0+printk (KERN_ERR "virtio_config_val returned %d\n" , err); +add_disk (vblk->disk);return 0 ;I guess you meant to remove these before submitting.
Good catch, thanks!
Quoted 7 lines
drivers/vhost/net.c line 52
Quoted 4 lines
drivers/vhost/net.c new file +4
-0+static void handle_tx_kick (struct work_struct *work); +static void handle_rx_kick (struct work_struct *work); +static void handle_tx_net (struct work_struct *work); +static void handle_rx_net (struct work_struct *work);[style] I think the code gets more readable if you reorder it so that you don't need forward declarations for static functions.
Right.
Quoted 11 lines
Quoted 11 lines
drivers/vhost/net.c new file +11
-0+static long vhost_net_reset_owner (struct vhost_net *n) +{ +struct socket *sock =NULL ; +long r; +mutex_lock (&n->dev.mutex); + r =vhost_dev_check_owner (&n->dev); +if (r) + goto done; + sock =vhost_net_stop (n); + r =vhost_dev_reset_owner (&n->dev); +done:
Quoted 8 lines
drivers/vhost/net.c line 179
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+mutex_unlock (&n->dev.mutex); +if (sock) +fput (sock->file); +return r; +}what is the difference between
vhost_net_reset_owner(n)andvhost_net_set_socket(n, -1)?
set socket to -1 will only stop the device.
reset owner will let another process take over the device. It also needs to reset all parameters to make it safe for that other process, so in particular the device is stopped.
I tried explaining this in the header vhost.h - does the comment there help, or do I need to clarify it?
Quoted 10 lines
drivers/vhost/net.c line 220
Quoted 7 lines
drivers/vhost/net.c new file +7
-0+ +static struct file_operations vhost_net_fops = { + .owner =THIS_MODULE , + .release = vhost_net_release, + .unlocked_ioctl = vhost_net_ioctl, + .open = vhost_net_open, +};This is missing a
compat_ioctlpointer. It should simply be
Quoted 5 lines
static long vhost_net_compat_ioctl(struct file *f,
unsigned int ioctl, unsigned long arg){ return f, ioctl, (unsigned long)compat_ptr(arg); }
I had the impression that if there's no compat_ioctl, unlocked_ioctl will get called automatically. No?
Quoted 22 lines
Quoted 22 lines
drivers/vhost/net.c new file +22
-0+/* Bits from fs/aio.c. TODO: export and use from there? */ +/* +* use_mm +* Makes the calling kernel thread take on the specified +* mm context. +* Called by the retry thread execute retries within the +* iocb issuer's mm context, so that copy_from/to_user +* operations work seamlessly for aio. +* (Note: this routine is intended to be called only +* from a kernel thread context) +*/ +static void use_mm (struct mm_struct *mm) +{ +struct mm_struct *active_mm; +struct task_struct *tsk = current; + +task_lock (tsk); + active_mm = tsk->active_mm; +atomic_inc (&mm->mm_count); + tsk->mm = mm; + tsk->active_mm = mm; +switch_mm (active_mm, mm, tsk);
Quoted 8 lines
drivers/vhost/net.c line 253
Quoted 4 lines
drivers/vhost/net.c new file +4
-0+task_unlock (tsk); + +mmdrop (active_mm); +}Why do you need a kernel thread here? If the data transfer functions all get called from a guest intercept, shouldn't you already be in the right mm?
several reasons :)
- I get called under lock, so can't block
- eventfd can be passed to another process, and I won't be in guest context at all
- this also gets called outside guest context from socket poll
- vcpu is blocked while it's doing i/o. it is better to free it up
as all the packet copying might take a while
Quoted 52 lines
Quoted 52 lines
drivers/vhost/net.c new file +52
-0+static void handle_tx (struct vhost_net *net) +{ +struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_TX ]; +unsigned head, out, in; +struct msghdr msg = { + .msg_name =NULL , + .msg_namelen =0 , + .msg_control =NULL , + .msg_controllen =0 , + .msg_iov = (struct iovec *)vq->iov +1 , + .msg_flags =MSG_DONTWAIT , + }; +size_t len; +int err; +struct socket *sock =rcu_dereference (net->sock); +if (!sock || !sock_writeable (sock->sk)) +return ; + +use_mm (net->dev.mm); +mutex_lock (&vq->mutex); +for (;;) { + head =vhost_get_vq_desc (&net->dev, vq, vq->iov, &out, &in); +if (head == vq->num) +break ; +if (out <=1 || in) { +vq_err (vq,"Unexpected descriptor format for TX: " +"out %d, int %d\n" , out, in); +break ; + } +/* Sanity check */ +if (vq->iov->iov_len !=sizeof (struct virtio_net_hdr )) { +vq_err (vq,"Unexpected header len for TX: " +"%ld expected %zd\n" , vq->iov->iov_len, +sizeof (struct virtio_net_hdr )); +break ; + } +/* Skip header. TODO: support TSO. */ + msg.msg_iovlen = out -1 ; + len =iov_length (vq->iov +1 , out -1 ); +/* TODO: Check specific error and bomb out unless ENOBUFS? */ + err = sock->ops->sendmsg (NULL , sock, &msg, len); +if (err <0 ) { +vhost_discard_vq_desc (vq); +break ; + } +if (err != len) +pr_err ("Truncated TX packet: " +" len %d != %zd\n" , err, len); +vhost_add_used_and_trigger (vq, head, + len +sizeof (struct virtio_net_hdr )); + } +
Quoted 8 lines
drivers/vhost/net.c line 328
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+mutex_unlock (&vq->mutex); +unuse_mm (net->dev.mm); +}I guess that this is where one could plug into macvlan directly, using sock_alloc_send_skb/memcpy_fromiovec/dev_queue_xmit directly, instead of filling a msghdr for each, if we want to combine this with the work I did on that.
quite possibly. Or one can just bind a raw socket to macvlan :)
Quoted 1 line
Arnd <><
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 3 lines · Michael S. Tsirkin
What it is: vhost net is a character device that can be used to reduce the number of system calls involved in virtio networking. Existing virtio net code is used in the guest without modification.
Very nice, I loved reading it. It's getting rather late in my time zone, so this comments only on the network driver. I'll go through the rest tomorrow.
Quoted 9 lines
drivers/block/virtio_blk.c +1
-0@@ -293,6 +293,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) err =PTR_ERR (vblk->vq); goto out_free_vblk; } +printk (KERN_ERR "vblk->vq = %p\n" , vblk->vq); vblk->pool =mempool_create_kmalloc_pool (1 ,sizeof (struct virtblk_req ));if (!vblk->pool) {
drivers/block/virtio_blk.c static int __devinit virtblk_probe(struct virtio_device *vdev) · line 390
Quoted 7 lines
drivers/block/virtio_blk.c +2
-0@@ -383,6 +384,8 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)if (!err)blk_queue_logical_block_size (vblk->disk->queue, blk_size); +printk (KERN_ERR "virtio_config_val returned %d\n" , err); +add_disk (vblk->disk);return 0 ;
I guess you meant to remove these before submitting.
drivers/vhost/net.c line 52
Quoted 4 lines
drivers/vhost/net.c new file +4
-0+static void handle_tx_kick (struct work_struct *work); +static void handle_rx_kick (struct work_struct *work); +static void handle_tx_net (struct work_struct *work); +static void handle_rx_net (struct work_struct *work);
[style] I think the code gets more readable if you reorder it so that you don't need forward declarations for static functions.
Quoted 6 lines
drivers/vhost/net.c new file +6
-0+static long vhost_net_reset_owner (struct vhost_net *n) +{ +struct socket *sock =NULL ; +long r; +mutex_lock (&n->dev.mutex); + r =vhost_dev_check_owner (&n->dev);
drivers/vhost/net.c line 179
Quoted 10 lines
drivers/vhost/net.c new file +10
-0+if (r) + goto done; + sock =vhost_net_stop (n); + r =vhost_dev_reset_owner (&n->dev); +done: +mutex_unlock (&n->dev.mutex); +if (sock) +fput (sock->file); +return r; +}
what is the difference between vhost_net_reset_owner(n) and vhost_net_set_socket(n, -1)?
drivers/vhost/net.c line 220
Quoted 7 lines
drivers/vhost/net.c new file +7
-0+ +static struct file_operations vhost_net_fops = { + .owner =THIS_MODULE , + .release = vhost_net_release, + .unlocked_ioctl = vhost_net_ioctl, + .open = vhost_net_open, +};
This is missing a compat_ioctl pointer. It should simply be
static long vhost_net_compat_ioctl(struct file *f,
unsigned int ioctl, unsigned long arg)
{
return f, ioctl, (unsigned long)compat_ptr(arg);
}Quoted 16 lines
drivers/vhost/net.c new file +16
-0+/* Bits from fs/aio.c. TODO: export and use from there? */ +/* +* use_mm +* Makes the calling kernel thread take on the specified +* mm context. +* Called by the retry thread execute retries within the +* iocb issuer's mm context, so that copy_from/to_user +* operations work seamlessly for aio. +* (Note: this routine is intended to be called only +* from a kernel thread context) +*/ +static void use_mm (struct mm_struct *mm) +{ +struct mm_struct *active_mm; +struct task_struct *tsk = current; +
drivers/vhost/net.c line 253
Quoted 10 lines
drivers/vhost/net.c new file +10
-0+task_lock (tsk); + active_mm = tsk->active_mm; +atomic_inc (&mm->mm_count); + tsk->mm = mm; + tsk->active_mm = mm; +switch_mm (active_mm, mm, tsk); +task_unlock (tsk); + +mmdrop (active_mm); +}
Why do you need a kernel thread here? If the data transfer functions all get called from a guest intercept, shouldn't you already be in the right mm?
Quoted 45 lines
drivers/vhost/net.c new file +45
-0+static void handle_tx (struct vhost_net *net) +{ +struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_TX ]; +unsigned head, out, in; +struct msghdr msg = { + .msg_name =NULL , + .msg_namelen =0 , + .msg_control =NULL , + .msg_controllen =0 , + .msg_iov = (struct iovec *)vq->iov +1 , + .msg_flags =MSG_DONTWAIT , + }; +size_t len; +int err; +struct socket *sock =rcu_dereference (net->sock); +if (!sock || !sock_writeable (sock->sk)) +return ; + +use_mm (net->dev.mm); +mutex_lock (&vq->mutex); +for (;;) { + head =vhost_get_vq_desc (&net->dev, vq, vq->iov, &out, &in); +if (head == vq->num) +break ; +if (out <=1 || in) { +vq_err (vq,"Unexpected descriptor format for TX: " +"out %d, int %d\n" , out, in); +break ; + } +/* Sanity check */ +if (vq->iov->iov_len !=sizeof (struct virtio_net_hdr )) { +vq_err (vq,"Unexpected header len for TX: " +"%ld expected %zd\n" , vq->iov->iov_len, +sizeof (struct virtio_net_hdr )); +break ; + } +/* Skip header. TODO: support TSO. */ + msg.msg_iovlen = out -1 ; + len =iov_length (vq->iov +1 , out -1 ); +/* TODO: Check specific error and bomb out unless ENOBUFS? */ + err = sock->ops->sendmsg (NULL , sock, &msg, len); +if (err <0 ) { +vhost_discard_vq_desc (vq); +break ; + }
drivers/vhost/net.c line 328
Quoted 10 lines
drivers/vhost/net.c new file +10
-0+if (err != len) +pr_err ("Truncated TX packet: " +" len %d != %zd\n" , err, len); +vhost_add_used_and_trigger (vq, head, + len +sizeof (struct virtio_net_hdr )); + } + +mutex_unlock (&vq->mutex); +unuse_mm (net->dev.mm); +}
I guess that this is where one could plug into macvlan directly, using sock_alloc_send_skb/memcpy_fromiovec/dev_queue_xmit directly, instead of filling a msghdr for each, if we want to combine this with the work I did on that.
Arnd <><
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 1 line · Michael S. Tsirkin
+struct
workqueue_struct*vhost_workqueue;
[nitpicking] This could be static.
Quoted 26 lines
drivers/vhost/vhost.h new file +26
-0+/* The virtqueue structure describes a queue attached to a device. */ +struct vhost_virtqueue { +struct vhost_dev *dev; + +/* The actual ring of buffers. */ +struct mutex mutex; +unsigned int num; +struct vring_desc __user *desc; +struct vring_avail __user *avail; +struct vring_used __user *used; +struct file *kick; +struct file *call; +struct file *error; +struct eventfd_ctx *call_ctx; +struct eventfd_ctx *error_ctx; + +struct vhost_poll poll; + +/* The routine to call when the Guest pings us, or timeout. */ +work_func_t handle_kick; + +/* Last available index we saw. */ +u16 last_avail_idx; + +/* Last index we used. */ +u16 last_used_idx;
drivers/vhost/vhost.h line 71
Quoted 10 lines
drivers/vhost/vhost.h new file +10
-0+ +/* Outstanding buffers */ +unsigned int inflight; + +/* Is this blocked? */ +bool blocked; + +struct iovec iov[VHOST_NET_MAX_SG ]; + +}____cacheline_aligned ;
We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that vring_virtqueue is derived from struct virtqueue. That would make it possible for simple device drivers to use the same driver in both host and guest, similar to how Ira Snyder used virtqueues to make virtio_net run between two hosts running the same code [1].
Ideally, I guess you should be able to even make virtio_net work in the host if you do that, but that could bring other complexities.
Arnd <><
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 50 lines · Arnd Bergmann
Quoted 1 line · Michael S. Tsirkin
+struct
workqueue_struct*vhost_workqueue;[nitpicking] This could be static.
Quoted 26 lines
drivers/vhost/vhost.h new file +26
-0+/* The virtqueue structure describes a queue attached to a device. */ +struct vhost_virtqueue { +struct vhost_dev *dev; + +/* The actual ring of buffers. */ +struct mutex mutex; +unsigned int num; +struct vring_desc __user *desc; +struct vring_avail __user *avail; +struct vring_used __user *used; +struct file *kick; +struct file *call; +struct file *error; +struct eventfd_ctx *call_ctx; +struct eventfd_ctx *error_ctx; + +struct vhost_poll poll; + +/* The routine to call when the Guest pings us, or timeout. */ +work_func_t handle_kick; + +/* Last available index we saw. */ +u16 last_avail_idx; + +/* Last index we used. */ +u16 last_used_idx;drivers/vhost/vhost.h line 71
Quoted 10 lines
drivers/vhost/vhost.h new file +10
-0+ +/* Outstanding buffers */ +unsigned int inflight; + +/* Is this blocked? */ +bool blocked; + +struct iovec iov[VHOST_NET_MAX_SG ]; + +}____cacheline_aligned ;We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue. That would make it possible for simple device drivers to use the same driver in both host and guest, similar to how Ira Snyder used virtqueues to makevirtio_netrun between two hosts running the same code [1].
Quoted 2 lines
Ideally, I guess you should be able to even make
virtio_network in the host if you do that, but that could bring other complexities.
I have no comments about the vhost code itself, I haven't reviewed it.
It might be interesting to try using a virtio-net in the host kernel to communicate with the virtio-net running in the guest kernel. The lack of a management interface is the biggest problem you will face (setting MAC addresses, negotiating features, etc. doesn't work intuitively). Getting the network interfaces talking is relatively easy.
Ira
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 54 lines · Ira W. Snyder
Quoted 50 lines · Arnd Bergmann
Quoted 1 line · Michael S. Tsirkin
+struct
workqueue_struct*vhost_workqueue;[nitpicking] This could be static.
Quoted 26 lines
drivers/vhost/vhost.h new file +26
-0+/* The virtqueue structure describes a queue attached to a device. */ +struct vhost_virtqueue { +struct vhost_dev *dev; + +/* The actual ring of buffers. */ +struct mutex mutex; +unsigned int num; +struct vring_desc __user *desc; +struct vring_avail __user *avail; +struct vring_used __user *used; +struct file *kick; +struct file *call; +struct file *error; +struct eventfd_ctx *call_ctx; +struct eventfd_ctx *error_ctx; + +struct vhost_poll poll; + +/* The routine to call when the Guest pings us, or timeout. */ +work_func_t handle_kick; + +/* Last available index we saw. */ +u16 last_avail_idx; + +/* Last index we used. */ +u16 last_used_idx;drivers/vhost/vhost.h line 71
Quoted 10 lines
drivers/vhost/vhost.h new file +10
-0+ +/* Outstanding buffers */ +unsigned int inflight; + +/* Is this blocked? */ +bool blocked; + +struct iovec iov[VHOST_NET_MAX_SG ]; + +}____cacheline_aligned ;We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue. That would make it possible for simple device drivers to use the same driver in both host and guest, similar to how Ira Snyder used virtqueues to makevirtio_netrun between two hosts running the same code [1].Quoted 2 lines
Ideally, I guess you should be able to even make
virtio_network in the host if you do that, but that could bring other complexities.
Quoted 6 lines
I have no comments about the vhost code itself, I haven't reviewed it.
It might be interesting to try using a virtio-net in the host kernel to communicate with the virtio-net running in the guest kernel. The lack of a management interface is the biggest problem you will face (setting MAC addresses, negotiating features, etc. doesn't work intuitively).
That was one of the reasons I decided to move most of code out to userspace. My kernel driver only handles datapath,
it's much smaller than virtio net.
Quoted 4 lines
Getting
the network interfaces talking is relatively easy.Ira
Tried this, but
- guest memory isn't pinned, so copy_to_user to access it, errors need to be handled in a sane way
- used/available roles are reversed
- kick/interrupt roles are reversed
So most of the code then looks like
if (host) {
} else {
}
returnThe only common part is walking the descriptor list, but that's like 10 lines of code.
At which point it's better to keep host/guest code separate, IMO.
Signature · 1 line
MST
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 1 line · Michael S. Tsirkin
On Wed, Aug 12, 2009 at 10:19:22AM -0700, Ira W. Snyder wrote:
[ snip out code ]
Quoted 41 lines
Quoted 11 lines
Quoted 10 lines
We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue. That would make it possible for simple device drivers to use the same driver in both host and guest, similar to how Ira Snyder used virtqueues to makevirtio_netrun between two hosts running the same code [1].Ideally, I guess you should be able to even make
virtio_network in the host if you do that, but that could bring other complexities.Quoted 6 lines
I have no comments about the vhost code itself, I haven't reviewed it.
It might be interesting to try using a virtio-net in the host kernel to communicate with the virtio-net running in the guest kernel. The lack of a management interface is the biggest problem you will face (setting MAC addresses, negotiating features, etc. doesn't work intuitively).
That was one of the reasons I decided to move most of code out to userspace. My kernel driver only handles datapath,
it's much smaller than virtio net.Quoted 4 lines
Getting
the network interfaces talking is relatively easy.Ira
Tried this, but
- guest memory isn't pinned, socopy_to_userto access it, errors need to be handled in a sane way
- used/available roles are reversed
- kick/interrupt roles are reversedSo most of the code then looks like
if (host) { } else { } return
Quoted 5 lines
The only common part is walking the descriptor list, but that's like 10 lines of code.
At which point it's better to keep host/guest code separate, IMO.
Ok, that makes sense. Let me see if I understand the concept of the driver. Here's a picture of what makes sense to me:
guest system
---------------------------------
| userspace applications |
---------------------------------
| kernel network stack |
---------------------------------
| virtio-net |
---------------------------------
| transport (virtio-ring, etc.) |
---------------------------------
|
|---------------------------------
| transport (virtio-ring, etc.) |
---------------------------------
| some driver (maybe vhost?) | <-- [1]
---------------------------------
| kernel network stack |
---------------------------------
host system
Quoted 1 line
From the host's network stack, packets can be forwarded out to the
physical network, or be consumed by a normal userspace application on the host. Just as if this were any other network interface.
In my patch, [1] was the virtio-net driver, completely unmodified.
So, does this patch accomplish the above diagram? If so, why the copy_to_user(), etc? Maybe I'm confusing this with my system, where the "guest" is another physical system, separated by the PCI bus.
Ira
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 76 lines · Ira W. Snyder
Quoted 1 line · Michael S. Tsirkin
On Wed, Aug 12, 2009 at 10:19:22AM -0700, Ira W. Snyder wrote:
[ snip out code ]
Quoted 41 lines
Quoted 11 lines
Quoted 10 lines
We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue. That would make it possible for simple device drivers to use the same driver in both host and guest, similar to how Ira Snyder used virtqueues to makevirtio_netrun between two hosts running the same code [1].Ideally, I guess you should be able to even make
virtio_network in the host if you do that, but that could bring other complexities.Quoted 6 lines
I have no comments about the vhost code itself, I haven't reviewed it.
It might be interesting to try using a virtio-net in the host kernel to communicate with the virtio-net running in the guest kernel. The lack of a management interface is the biggest problem you will face (setting MAC addresses, negotiating features, etc. doesn't work intuitively).
That was one of the reasons I decided to move most of code out to userspace. My kernel driver only handles datapath,
it's much smaller than virtio net.Quoted 4 lines
Getting
the network interfaces talking is relatively easy.Ira
Tried this, but
- guest memory isn't pinned, socopy_to_userto access it, errors need to be handled in a sane way
- used/available roles are reversed
- kick/interrupt roles are reversedSo most of the code then looks like
if (host) { } else { } returnQuoted 5 lines
The only common part is walking the descriptor list, but that's like 10 lines of code.
At which point it's better to keep host/guest code separate, IMO.
Ok, that makes sense. Let me see if I understand the concept of the driver. Here's a picture of what makes sense to me:
guest system
---------------------------------
| userspace applications |
---------------------------------
| kernel network stack |
---------------------------------
| virtio-net |
---------------------------------
| transport (virtio-ring, etc.) |
---------------------------------| |---------------------------------
| transport (virtio-ring, etc.) |
---------------------------------
| some driver (maybe vhost?) | <-- [1]
---------------------------------
| kernel network stack |
---------------------------------
host system
Quoted 7 lines
Quoted 1 line
From the host's network stack, packets can be forwarded out to the
physical network, or be consumed by a normal userspace application on the host. Just as if this were any other network interface.
In my patch, [1] was the virtio-net driver, completely unmodified.
So, does this patch accomplish the above diagram?
Not exactly. vhost passes packets to a physical device, through a raw socket, not into host network stack.
Quoted 1 line
If so, why the
copy_to_user(), etc?
Guest memory is not pinned. Memory access needs to go through translation process, could cause page faults, etc.
Quoted 4 lines
Maybe I'm confusing this with my system, where the "guest" is another physical system, separated by the PCI bus.
Ira
Yes, that's different.
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Ira W. SnyderQuoted 76 lines · Ira W. Snyder
Quoted 1 line · Michael S. Tsirkin
On Wed, Aug 12, 2009 at 10:19:22AM -0700, Ira W. Snyder wrote:
[ snip out code ]
Quoted 41 lines
Quoted 11 lines
Quoted 10 lines
We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue. That would make it possible for simple device drivers to use the same driver in both host and guest, similar to how Ira Snyder used virtqueues to makevirtio_netrun between two hosts running the same code [1].Ideally, I guess you should be able to even make
virtio_network in the host if you do that, but that could bring other complexities.Quoted 6 lines
I have no comments about the vhost code itself, I haven't reviewed it.
It might be interesting to try using a virtio-net in the host kernel to communicate with the virtio-net running in the guest kernel. The lack of a management interface is the biggest problem you will face (setting MAC addresses, negotiating features, etc. doesn't work intuitively).
That was one of the reasons I decided to move most of code out to userspace. My kernel driver only handles datapath,
it's much smaller than virtio net.Quoted 4 lines
Getting
the network interfaces talking is relatively easy.Ira
Tried this, but
- guest memory isn't pinned, socopy_to_userto access it, errors need to be handled in a sane way
- used/available roles are reversed
- kick/interrupt roles are reversedSo most of the code then looks like
if (host) { } else { } returnQuoted 5 lines
The only common part is walking the descriptor list, but that's like 10 lines of code.
At which point it's better to keep host/guest code separate, IMO.
Ok, that makes sense. Let me see if I understand the concept of the driver. Here's a picture of what makes sense to me:
guest system
---------------------------------
| userspace applications |
---------------------------------
| kernel network stack |
---------------------------------
| virtio-net |
---------------------------------
| transport (virtio-ring, etc.) |
---------------------------------| |---------------------------------
| transport (virtio-ring, etc.) |
---------------------------------
| some driver (maybe vhost?) | <-- [1]
---------------------------------
| kernel network stack |
---------------------------------
host system
Quoted 7 lines
Quoted 1 line
From the host's network stack, packets can be forwarded out to the
physical network, or be consumed by a normal userspace application on the host. Just as if this were any other network interface.
In my patch, [1] was the virtio-net driver, completely unmodified.
So, does this patch accomplish the above diagram?
Not exactly. vhost passes packets to a physical device, through a raw socket, not into host network stack.
Quoted 1 line
If so, why the
copy_to_user(), etc?
Guest memory is not pinned. Memory access needs to go through translation process, could cause page faults, etc.
Quoted 4 lines
Maybe I'm confusing this with my system, where the "guest" is another physical system, separated by the PCI bus.
Ira
Yes, that's different.
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 1 line · Michael S. Tsirkin
On Wed, Aug 12, 2009 at 10:19:22AM -0700, Ira W. Snyder wrote:
[ snip out code ]
Quoted 41 lines
Quoted 11 lines
Quoted 10 lines
We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue. That would make it possible for simple device drivers to use the same driver in both host and guest, similar to how Ira Snyder used virtqueues to makevirtio_netrun between two hosts running the same code [1].Ideally, I guess you should be able to even make
virtio_network in the host if you do that, but that could bring other complexities.Quoted 6 lines
I have no comments about the vhost code itself, I haven't reviewed it.
It might be interesting to try using a virtio-net in the host kernel to communicate with the virtio-net running in the guest kernel. The lack of a management interface is the biggest problem you will face (setting MAC addresses, negotiating features, etc. doesn't work intuitively).
That was one of the reasons I decided to move most of code out to userspace. My kernel driver only handles datapath,
it's much smaller than virtio net.Quoted 4 lines
Getting
the network interfaces talking is relatively easy.Ira
Tried this, but
- guest memory isn't pinned, socopy_to_userto access it, errors need to be handled in a sane way
- used/available roles are reversed
- kick/interrupt roles are reversedSo most of the code then looks like
if (host) { } else { } return
Quoted 5 lines
The only common part is walking the descriptor list, but that's like 10 lines of code.
At which point it's better to keep host/guest code separate, IMO.
Ok, that makes sense. Let me see if I understand the concept of the driver. Here's a picture of what makes sense to me:
guest system
---------------------------------
| userspace applications |
---------------------------------
| kernel network stack |
---------------------------------
| virtio-net |
---------------------------------
| transport (virtio-ring, etc.) |
---------------------------------
|
|---------------------------------
| transport (virtio-ring, etc.) |
---------------------------------
| some driver (maybe vhost?) | <-- [1]
---------------------------------
| kernel network stack |
---------------------------------
host system
From the host's network stack, packets can be forwarded out to the physical network, or be consumed by a normal userspace application on the host. Just as if this were any other network interface.
In my patch, [1] was the virtio-net driver, completely unmodified.
So, does this patch accomplish the above diagram? If so, why the copy_to_user(), etc? Maybe I'm confusing this with my system, where the "guest" is another physical system, separated by the PCI bus.
Ira
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Ira W. SnyderQuoted 54 lines · Ira W. Snyder
Quoted 50 lines · Arnd Bergmann
Quoted 1 line · Michael S. Tsirkin
+struct
workqueue_struct*vhost_workqueue;[nitpicking] This could be static.
Quoted 26 lines
drivers/vhost/vhost.h new file +26
-0+/* The virtqueue structure describes a queue attached to a device. */ +struct vhost_virtqueue { +struct vhost_dev *dev; + +/* The actual ring of buffers. */ +struct mutex mutex; +unsigned int num; +struct vring_desc __user *desc; +struct vring_avail __user *avail; +struct vring_used __user *used; +struct file *kick; +struct file *call; +struct file *error; +struct eventfd_ctx *call_ctx; +struct eventfd_ctx *error_ctx; + +struct vhost_poll poll; + +/* The routine to call when the Guest pings us, or timeout. */ +work_func_t handle_kick; + +/* Last available index we saw. */ +u16 last_avail_idx; + +/* Last index we used. */ +u16 last_used_idx;drivers/vhost/vhost.h line 71
Quoted 10 lines
drivers/vhost/vhost.h new file +10
-0+ +/* Outstanding buffers */ +unsigned int inflight; + +/* Is this blocked? */ +bool blocked; + +struct iovec iov[VHOST_NET_MAX_SG ]; + +}____cacheline_aligned ;We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue. That would make it possible for simple device drivers to use the same driver in both host and guest, similar to how Ira Snyder used virtqueues to makevirtio_netrun between two hosts running the same code [1].Quoted 2 lines
Ideally, I guess you should be able to even make
virtio_network in the host if you do that, but that could bring other complexities.
Quoted 6 lines
I have no comments about the vhost code itself, I haven't reviewed it.
It might be interesting to try using a virtio-net in the host kernel to communicate with the virtio-net running in the guest kernel. The lack of a management interface is the biggest problem you will face (setting MAC addresses, negotiating features, etc. doesn't work intuitively).
That was one of the reasons I decided to move most of code out to userspace. My kernel driver only handles datapath,
it's much smaller than virtio net.
Quoted 4 lines
Getting
the network interfaces talking is relatively easy.Ira
Tried this, but
- guest memory isn't pinned, so copy_to_user to access it, errors need to be handled in a sane way
- used/available roles are reversed
- kick/interrupt roles are reversed
So most of the code then looks like
if (host) {
} else {
}
returnThe only common part is walking the descriptor list, but that's like 10 lines of code.
At which point it's better to keep host/guest code separate, IMO.
Signature · 1 line
MST
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 50 lines · Arnd Bergmann
Quoted 1 line · Michael S. Tsirkin
+struct
workqueue_struct*vhost_workqueue;[nitpicking] This could be static.
Quoted 26 lines
drivers/vhost/vhost.h new file +26
-0+/* The virtqueue structure describes a queue attached to a device. */ +struct vhost_virtqueue { +struct vhost_dev *dev; + +/* The actual ring of buffers. */ +struct mutex mutex; +unsigned int num; +struct vring_desc __user *desc; +struct vring_avail __user *avail; +struct vring_used __user *used; +struct file *kick; +struct file *call; +struct file *error; +struct eventfd_ctx *call_ctx; +struct eventfd_ctx *error_ctx; + +struct vhost_poll poll; + +/* The routine to call when the Guest pings us, or timeout. */ +work_func_t handle_kick; + +/* Last available index we saw. */ +u16 last_avail_idx; + +/* Last index we used. */ +u16 last_used_idx;drivers/vhost/vhost.h line 71
Quoted 10 lines
drivers/vhost/vhost.h new file +10
-0+ +/* Outstanding buffers */ +unsigned int inflight; + +/* Is this blocked? */ +bool blocked; + +struct iovec iov[VHOST_NET_MAX_SG ]; + +}____cacheline_aligned ;We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue. That would make it possible for simple device drivers to use the same driver in both host and guest, similar to how Ira Snyder used virtqueues to makevirtio_netrun between two hosts running the same code [1].
Quoted 2 lines
Ideally, I guess you should be able to even make
virtio_network in the host if you do that, but that could bring other complexities.
I have no comments about the vhost code itself, I haven't reviewed it.
It might be interesting to try using a virtio-net in the host kernel to communicate with the virtio-net running in the guest kernel. The lack of a management interface is the biggest problem you will face (setting MAC addresses, negotiating features, etc. doesn't work intuitively). Getting the network interfaces talking is relatively easy.
Ira
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 5 lines · Arnd Bergmann
Quoted 1 line · Michael S. Tsirkin
+struct
workqueue_struct*vhost_workqueue;[nitpicking] This could be static.
Good catch. Thanks!
Quoted 32 lines
Quoted 32 lines
drivers/vhost/vhost.h new file +32
-0+/* The virtqueue structure describes a queue attached to a device. */ +struct vhost_virtqueue { +struct vhost_dev *dev; + +/* The actual ring of buffers. */ +struct mutex mutex; +unsigned int num; +struct vring_desc __user *desc; +struct vring_avail __user *avail; +struct vring_used __user *used; +struct file *kick; +struct file *call; +struct file *error; +struct eventfd_ctx *call_ctx; +struct eventfd_ctx *error_ctx; + +struct vhost_poll poll; + +/* The routine to call when the Guest pings us, or timeout. */ +work_func_t handle_kick; + +/* Last available index we saw. */ +u16 last_avail_idx; + +/* Last index we used. */ +u16 last_used_idx; + +/* Outstanding buffers */ +unsigned int inflight; + +/* Is this blocked? */ +bool blocked;
Quoted 8 lines
drivers/vhost/vhost.h line 71
Quoted 4 lines
drivers/vhost/vhost.h new file +4
-0+ +struct iovec iov[VHOST_NET_MAX_SG ]; + +}____cacheline_aligned ;We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue.
I prefer keeping it simple. Much of abstraction in virtio is due to the fact that it needs to work on top of different hardware emulations:
lguest,kvm, possibly others in the future. vhost is always working on real hardware, using eventfd as the interface, so it does not need that.
Quoted 2 lines
That would make it possible for simple device drivers to use the same driver in both host and guest,
I don't think so. For example, there's a callback field that gets invoked in guest when buffers are consumed. It could be overloaded to mean "buffers are available" in host but you never handle both situations in the same way, so what's the point?
Quoted 8 lines
similar to how Ira Snyder used virtqueues to make
virtio_netrun between two hosts running the same code [1].
Ideally, I guess you should be able to even makevirtio_network in the host if you do that, but that could bring other complexities.Arnd <><
As I pointed out earlier, most code in virtio net is asymmetrical: guest provides buffers, host consumes them. Possibly, one could use virtio rings in a symmetrical way, but support of existing guest virtio net means there's almost no shared code.
Signature · 1 line
MST
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 9 lines · Michael S. Tsirkin
Quoted 3 lines · Arnd Bergmann
We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue.I prefer keeping it simple. Much of abstraction in virtio is due to the fact that it needs to work on top of different hardware emulations:
lguest,kvm, possibly others in the future. vhost is always working on real hardware, using eventfd as the interface, so it does not need that.
Well, that was my point: virtio can already work on a number of abstractions, so adding one more for vhost should not be too hard.
Quoted 8 lines
Quoted 2 lines
That would make it possible for simple device drivers to use the same driver in both host and guest,
I don't think so. For example, there's a callback field that gets invoked in guest when buffers are consumed. It could be overloaded to mean "buffers are available" in host but you never handle both situations in the same way, so what's the point?
...
Quoted 5 lines
As I pointed out earlier, most code in virtio net is asymmetrical: guest provides buffers, host consumes them. Possibly, one could use virtio rings in a symmetrical way, but support of existing guest virtio net means there's almost no shared code.
The trick is to swap the virtqueues instead. virtio-net is actually mostly symmetric in just the same way that the physical wires on a twisted pair ethernet are symmetric (I like how that analogy fits).
virtio_net kicks the transmit virtqueue when it has data and it kicks the receive queue when it has empty buffers to fill, and it has callbacks when the two are done. You can do the same in both the guest and the host, but then the guests input virtqueue is the hosts output virtqueue and vice versa.
Once a virtqueue got kicked from both sides, the vhost_virtqueue implementation between the two only needs to do a copy_from_user or copy_to_user (possibly from a thread if it is in atomic context) and then call the two callback functions. This is basically the same thing you do already, except that you use slightly different names for the components.
Arnd <><
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 10 lines · Arnd Bergmann
Quoted 5 lines
As I pointed out earlier, most code in virtio net is asymmetrical: guest provides buffers, host consumes them. Possibly, one could use virtio rings in a symmetrical way, but support of existing guest virtio net means there's almost no shared code.
The trick is to swap the virtqueues instead. virtio-net is actually mostly symmetric in just the same way that the physical wires on a twisted pair ethernet are symmetric (I like how that analogy fits).
It's already been done between two guests. See http://article.gmane.org/gmane.linux.kernel.virtualization/5423
Regards,
Anthony Liguori
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 12 lines · Anthony Liguori
Quoted 10 lines · Arnd Bergmann
Quoted 5 lines
As I pointed out earlier, most code in virtio net is asymmetrical: guest provides buffers, host consumes them. Possibly, one could use virtio rings in a symmetrical way, but support of existing guest virtio net means there's almost no shared code.
The trick is to swap the virtqueues instead. virtio-net is actually mostly symmetric in just the same way that the physical wires on a twisted pair ethernet are symmetric (I like how that analogy fits).
Quoted 6 lines
It's already been done between two guests. See
http://article.gmane.org/gmane.linux.kernel.virtualization/5423Regards,
Anthony Liguori
Yes, this works by copying data (see PATCH 5/5). Another possibility is page flipping. Either will kill performance.
Signature · 1 line
MST
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Anthony LiguoriQuoted 12 lines · Anthony Liguori
Quoted 10 lines · Arnd Bergmann
Quoted 5 lines
As I pointed out earlier, most code in virtio net is asymmetrical: guest provides buffers, host consumes them. Possibly, one could use virtio rings in a symmetrical way, but support of existing guest virtio net means there's almost no shared code.
The trick is to swap the virtqueues instead. virtio-net is actually mostly symmetric in just the same way that the physical wires on a twisted pair ethernet are symmetric (I like how that analogy fits).
Quoted 6 lines
It's already been done between two guests. See
http://article.gmane.org/gmane.linux.kernel.virtualization/5423Regards,
Anthony Liguori
Yes, this works by copying data (see PATCH 5/5). Another possibility is page flipping. Either will kill performance.
Signature · 1 line
MST
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 10 lines · Arnd Bergmann
Quoted 5 lines
As I pointed out earlier, most code in virtio net is asymmetrical: guest provides buffers, host consumes them. Possibly, one could use virtio rings in a symmetrical way, but support of existing guest virtio net means there's almost no shared code.
The trick is to swap the virtqueues instead. virtio-net is actually mostly symmetric in just the same way that the physical wires on a twisted pair ethernet are symmetric (I like how that analogy fits).
It's already been done between two guests. See
http://article.gmane.org/gmane.linux.kernel.virtualization/5423
Regards,
Anthony Liguori
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 3 lines · Arnd Bergmann
The trick is to swap the virtqueues instead. virtio-net is actually mostly symmetric in just the same way that the physical wires on a twisted pair ethernet are symmetric (I like how that analogy fits).
You need to really squint hard for it to look symmetric.
For example, for RX, virtio allocates an skb, puts a descriptor on a ring and waits for host to fill it in. Host system can not do the same:
guest does not have access to host memory.
You can do a copy in transport to hide this fact, but it will kill performance.
Signature · 1 line
MST
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 5 lines · Michael S. Tsirkin
Quoted 3 lines · Arnd Bergmann
The trick is to swap the virtqueues instead. virtio-net is actually mostly symmetric in just the same way that the physical wires on a twisted pair ethernet are symmetric (I like how that analogy fits).
Quoted 8 lines
You need to really squint hard for it to look symmetric.
For example, for RX, virtio allocates an skb, puts a descriptor on a ring and waits for host to fill it in. Host system can not do the same:
guest does not have access to host memory.You can do a copy in transport to hide this fact, but it will kill performance.
Yes, that is what I was suggesting all along. The actual copy operation has to be done by the host transport, which is obviously different from the guest transport that just calls the host using vring_kick().
Right now, the number of copy operations in your code is the same. You are doing the copy a little bit later in skb_copy_datagram_iovec(), which is indeed a very nice hack. Changing to a virtqueue based method would imply that the host needs to add each skb_frag_t to its outbound virtqueue, which then gets copied into the guests inbound virtqueue.
Unfortunately, this also implies that you could no longer simply use the packet socket interface as you do currently, as I realized only now. This obviously has a significant impact on your user space interface.
Arnd <><
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 3 lines · Arnd Bergmann
Unfortunately, this also implies that you could no longer simply use the packet socket interface as you do currently, as I realized only now. This obviously has a significant impact on your user space interface.
Also, if we do the copy in the transport, it definitely means that we can't get to zero-copy RX/TX from guest space any more. The current vhost_net driver doesn't do that yet, but could be extended in the same way that I'm hoping to do it for macvtap.
Arnd <><
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 11 lines · Arnd Bergmann
Quoted 3 lines · Arnd Bergmann
Unfortunately, this also implies that you could no longer simply use the packet socket interface as you do currently, as I realized only now. This obviously has a significant impact on your user space interface.
Also, if we do the copy in the transport, it definitely means that we can't get to zero-copy RX/TX from guest space any more. The current
vhost_netdriver doesn't do that yet, but could be extended in the same way that I'm hoping to do it for macvtap.Arnd <><
The best way to do this IMO would be to add zero copy support to raw sockets, vhost will then get it basically for free.
Signature · 1 line
MST
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 2 lines · Michael S. Tsirkin
The best way to do this IMO would be to add zero copy support to raw sockets, vhost will then get it basically for free.
Yes, that would be nice. I wonder if that could lead to security problems on TX though. I guess It will only bring significant performance improvements if we leave the data writable in the user space or guest during the operation. If the user finds the right timing, it could modify the frame headers after they have been checked using netfilter, or while the frames are being consumed in the kernel (e.g. an NFS server running in a guest).
Ardn <><
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 6 lines · Arnd Bergmann
Quoted 3 lines · Michael S. Tsirkin
The best way to do this IMO would be to add zero copy support to raw sockets, vhost will then get it basically for free.
Quoted 8 lines
Yes, that would be nice. I wonder if that could lead to security problems on TX though. I guess It will only bring significant performance improvements if we leave the data writable in the user space or guest during the operation. If the user finds the right timing, it could modify the frame headers after they have been checked using netfilter, or while the frames are being consumed in the kernel (e.g. an NFS server running in a guest).
IIRC when the kernel consumes data it linearizes the skb. We just need to make sure all the zerocopy data is in the nonlinear part, and the kernel will copy if/when it needs to access packet data.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 6 lines · Arnd Bergmann
Quoted 3 lines · Michael S. Tsirkin
The best way to do this IMO would be to add zero copy support to raw sockets, vhost will then get it basically for free.
Quoted 8 lines
Yes, that would be nice. I wonder if that could lead to security problems on TX though. I guess It will only bring significant performance improvements if we leave the data writable in the user space or guest during the operation. If the user finds the right timing, it could modify the frame headers after they have been checked using netfilter, or while the frames are being consumed in the kernel (e.g. an NFS server running in a guest).
IIRC when the kernel consumes data it linearizes the skb. We just need to make sure all the zerocopy data is in the nonlinear part, and the kernel will copy if/when it needs to access packet data.
Signature · 1 line
error compiling committee.c: too many arguments to function
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 11 lines · Arnd Bergmann
Quoted 2 lines · Michael S. Tsirkin
The best way to do this IMO would be to add zero copy support to raw sockets, vhost will then get it basically for free.
Yes, that would be nice. I wonder if that could lead to security problems on TX though. I guess It will only bring significant performance improvements if we leave the data writable in the user space or guest during the operation. If the user finds the right timing, it could modify the frame headers after they have been checked using netfilter, or while the frames are being consumed in the kernel (e.g. an NFS server running in a guest).
For this reason, we always linearize parts of packets we're filtering. ie. copy.
Cheers,
Rusty.
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 11 lines · Arnd Bergmann
Quoted 2 lines · Michael S. Tsirkin
The best way to do this IMO would be to add zero copy support to raw sockets, vhost will then get it basically for free.
Yes, that would be nice. I wonder if that could lead to security problems on TX though. I guess It will only bring significant performance improvements if we leave the data writable in the user space or guest during the operation. If the user finds the right timing, it could modify the frame headers after they have been checked using netfilter, or while the frames are being consumed in the kernel (e.g. an NFS server running in a guest).
For this reason, we always linearize parts of packets we're filtering. ie. copy.
Cheers,
Rusty.
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 2 lines · Michael S. Tsirkin
The best way to do this IMO would be to add zero copy support to raw sockets, vhost will then get it basically for free.
Yes, that would be nice. I wonder if that could lead to security problems on TX though. I guess It will only bring significant performance improvements if we leave the data writable in the user space or guest during the operation. If the user finds the right timing, it could modify the frame headers after they have been checked using netfilter, or while the frames are being consumed in the kernel (e.g. an NFS server running in a guest).
Ardn <><
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 11 lines · Arnd Bergmann
Quoted 3 lines · Arnd Bergmann
Unfortunately, this also implies that you could no longer simply use the packet socket interface as you do currently, as I realized only now. This obviously has a significant impact on your user space interface.
Also, if we do the copy in the transport, it definitely means that we can't get to zero-copy RX/TX from guest space any more. The current
vhost_netdriver doesn't do that yet, but could be extended in the same way that I'm hoping to do it for macvtap.Arnd <><
The best way to do this IMO would be to add zero copy support to raw sockets, vhost will then get it basically for free.
Signature · 1 line
MST
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 3 lines · Arnd Bergmann
Unfortunately, this also implies that you could no longer simply use the packet socket interface as you do currently, as I realized only now. This obviously has a significant impact on your user space interface.
Also, if we do the copy in the transport, it definitely means that we can't get to zero-copy RX/TX from guest space any more. The current vhost_net driver doesn't do that yet, but could be extended in the same way that I'm hoping to do it for macvtap.
Arnd <><
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 19 lines · Arnd Bergmann
Quoted 5 lines · Michael S. Tsirkin
Quoted 3 lines · Arnd Bergmann
The trick is to swap the virtqueues instead. virtio-net is actually mostly symmetric in just the same way that the physical wires on a twisted pair ethernet are symmetric (I like how that analogy fits).
Quoted 8 lines
You need to really squint hard for it to look symmetric.
For example, for RX, virtio allocates an skb, puts a descriptor on a ring and waits for host to fill it in. Host system can not do the same:
guest does not have access to host memory.You can do a copy in transport to hide this fact, but it will kill performance.
Yes, that is what I was suggesting all along. The actual copy operation has to be done by the host transport, which is obviously different from the guest transport that just calls the host using
vring_kick().
Quoted 5 lines
Right now, the number of copy operations in your code is the same. You are doing the copy a little bit later in
skb_copy_datagram_iovec(), which is indeed a very nice hack. Changing to a virtqueue based method would imply that the host needs to add eachskb_frag_tto its outbound virtqueue, which then gets copied into the guests inbound virtqueue.
Which is a lot more code than just calling skb_copy_datagram_iovec.
Quoted 5 lines
Unfortunately, this also implies that you could no longer simply use the packet socket interface as you do currently, as I realized only now. This obviously has a significant impact on your user space interface.
Arnd <><
And, it will remove our ability to implement zero copy down the road (when raw sockets support it).
Signature · 1 line
MST
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 7 lines · Michael S. Tsirkin
Quoted 5 lines
Right now, the number of copy operations in your code is the same. You are doing the copy a little bit later in
skb_copy_datagram_iovec(), which is indeed a very nice hack. Changing to a virtqueue based method would imply that the host needs to add eachskb_frag_tto its outbound virtqueue, which then gets copied into the guests inbound virtqueue.Which is a lot more code than just calling
skb_copy_datagram_iovec.
Well, I don't see this part as much of a problem, because the code already exists in virtio_net. If we really wanted to go down that road, just using virtio_net would solve the problem of frame handling entirely, but create new problems elsewhere, as we have mentioned.
Arnd <><
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 11 lines · Arnd Bergmann
Quoted 7 lines · Michael S. Tsirkin
Quoted 5 lines
Right now, the number of copy operations in your code is the same. You are doing the copy a little bit later in
skb_copy_datagram_iovec(), which is indeed a very nice hack. Changing to a virtqueue based method would imply that the host needs to add eachskb_frag_tto its outbound virtqueue, which then gets copied into the guests inbound virtqueue.Which is a lot more code than just calling
skb_copy_datagram_iovec.Well, I don't see this part as much of a problem, because the code already exists in
virtio_net.
I am talking about the copying done in low level transport, here.
Quoted 5 lines
If we really wanted to go down that road,
just usingvirtio_netwould solve the problem of frame handling entirely, but create new problems elsewhere, as we have mentioned.Arnd <><
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 11 lines · Arnd Bergmann
Quoted 7 lines · Michael S. Tsirkin
Quoted 5 lines
Right now, the number of copy operations in your code is the same. You are doing the copy a little bit later in
skb_copy_datagram_iovec(), which is indeed a very nice hack. Changing to a virtqueue based method would imply that the host needs to add eachskb_frag_tto its outbound virtqueue, which then gets copied into the guests inbound virtqueue.Which is a lot more code than just calling
skb_copy_datagram_iovec.Well, I don't see this part as much of a problem, because the code already exists in
virtio_net.
I am talking about the copying done in low level transport, here.
Quoted 5 lines
If we really wanted to go down that road,
just usingvirtio_netwould solve the problem of frame handling entirely, but create new problems elsewhere, as we have mentioned.Arnd <><
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 7 lines · Michael S. Tsirkin
Quoted 5 lines
Right now, the number of copy operations in your code is the same. You are doing the copy a little bit later in
skb_copy_datagram_iovec(), which is indeed a very nice hack. Changing to a virtqueue based method would imply that the host needs to add eachskb_frag_tto its outbound virtqueue, which then gets copied into the guests inbound virtqueue.Which is a lot more code than just calling
skb_copy_datagram_iovec.
Well, I don't see this part as much of a problem, because the code already exists in virtio_net. If we really wanted to go down that road, just using virtio_net would solve the problem of frame handling entirely, but create new problems elsewhere, as we have mentioned.
Arnd <><
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 19 lines · Arnd Bergmann
Quoted 5 lines · Michael S. Tsirkin
Quoted 3 lines · Arnd Bergmann
The trick is to swap the virtqueues instead. virtio-net is actually mostly symmetric in just the same way that the physical wires on a twisted pair ethernet are symmetric (I like how that analogy fits).
Quoted 8 lines
You need to really squint hard for it to look symmetric.
For example, for RX, virtio allocates an skb, puts a descriptor on a ring and waits for host to fill it in. Host system can not do the same:
guest does not have access to host memory.You can do a copy in transport to hide this fact, but it will kill performance.
Yes, that is what I was suggesting all along. The actual copy operation has to be done by the host transport, which is obviously different from the guest transport that just calls the host using
vring_kick().
Quoted 5 lines
Right now, the number of copy operations in your code is the same. You are doing the copy a little bit later in
skb_copy_datagram_iovec(), which is indeed a very nice hack. Changing to a virtqueue based method would imply that the host needs to add eachskb_frag_tto its outbound virtqueue, which then gets copied into the guests inbound virtqueue.
Which is a lot more code than just calling skb_copy_datagram_iovec.
Quoted 5 lines
Unfortunately, this also implies that you could no longer simply use the packet socket interface as you do currently, as I realized only now. This obviously has a significant impact on your user space interface.
Arnd <><
And, it will remove our ability to implement zero copy down the road (when raw sockets support it).
Signature · 1 line
MST
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 5 lines · Michael S. Tsirkin
Quoted 3 lines · Arnd Bergmann
The trick is to swap the virtqueues instead. virtio-net is actually mostly symmetric in just the same way that the physical wires on a twisted pair ethernet are symmetric (I like how that analogy fits).
Quoted 8 lines
You need to really squint hard for it to look symmetric.
For example, for RX, virtio allocates an skb, puts a descriptor on a ring and waits for host to fill it in. Host system can not do the same:
guest does not have access to host memory.You can do a copy in transport to hide this fact, but it will kill performance.
Yes, that is what I was suggesting all along. The actual copy operation has to be done by the host transport, which is obviously different from the guest transport that just calls the host using vring_kick().
Right now, the number of copy operations in your code is the same. You are doing the copy a little bit later in skb_copy_datagram_iovec(), which is indeed a very nice hack. Changing to a virtqueue based method would imply that the host needs to add each skb_frag_t to its outbound virtqueue, which then gets copied into the guests inbound virtqueue.
Unfortunately, this also implies that you could no longer simply use the packet socket interface as you do currently, as I realized only now. This obviously has a significant impact on your user space interface.
Arnd <><
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 3 lines · Arnd Bergmann
The trick is to swap the virtqueues instead. virtio-net is actually mostly symmetric in just the same way that the physical wires on a twisted pair ethernet are symmetric (I like how that analogy fits).
You need to really squint hard for it to look symmetric.
For example, for RX, virtio allocates an skb, puts a descriptor on a ring and waits for host to fill it in. Host system can not do the same:
guest does not have access to host memory.
You can do a copy in transport to hide this fact, but it will kill performance.
Signature · 1 line
MST
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 9 lines · Michael S. Tsirkin
Quoted 3 lines · Arnd Bergmann
We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue.I prefer keeping it simple. Much of abstraction in virtio is due to the fact that it needs to work on top of different hardware emulations:
lguest,kvm, possibly others in the future. vhost is always working on real hardware, using eventfd as the interface, so it does not need that.
Well, that was my point: virtio can already work on a number of abstractions, so adding one more for vhost should not be too hard.
Quoted 8 lines
Quoted 2 lines
That would make it possible for simple device drivers to use the same driver in both host and guest,
I don't think so. For example, there's a callback field that gets invoked in guest when buffers are consumed. It could be overloaded to mean "buffers are available" in host but you never handle both situations in the same way, so what's the point?
...
Quoted 5 lines
As I pointed out earlier, most code in virtio net is asymmetrical: guest provides buffers, host consumes them. Possibly, one could use virtio rings in a symmetrical way, but support of existing guest virtio net means there's almost no shared code.
The trick is to swap the virtqueues instead. virtio-net is actually mostly symmetric in just the same way that the physical wires on a twisted pair ethernet are symmetric (I like how that analogy fits).
virtio_net kicks the transmit virtqueue when it has data and it kicks the receive queue when it has empty buffers to fill, and it has callbacks when the two are done. You can do the same in both the guest and the host, but then the guests input virtqueue is the hosts output virtqueue and vice versa.
Once a virtqueue got kicked from both sides, the vhost_virtqueue implementation between the two only needs to do a copy_from_user or copy_to_user (possibly from a thread if it is in atomic context) and then call the two callback functions. This is basically the same thing you do already, except that you use slightly different names for the components.
Arnd <><
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 11 lines · Michael S. Tsirkin
Quoted 5 lines
We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue.I prefer keeping it simple. Much of abstraction in virtio is due to the fact that it needs to work on top of different hardware emulations:
lguest,kvm, possibly others in the future. vhost is always working on real hardware, using eventfd as the interface, so it does not need that.
Actually, vhost may not always be limited to real hardware.
We may on day use vhost as the basis of a driver domain. There's quite a lot of interest in this for networking.
At any rate, I'd like to see performance results before we consider trying to reuse virtio code.
Regards,
Anthony Liguori
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
Quoted 7 lines · Anthony Liguori
Quoted 6 lines · Michael S. Tsirkin
Quoted 5 lines
We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue.
Quoted 7 lines
Quoted 5 lines
I prefer keeping it simple. Much of abstraction in virtio is due to the fact that it needs to work on top of different hardware emulations:
lguest,kvm, possibly others in the future. vhost is always working on real hardware, using eventfd as the interface, so it does not need that.Actually, vhost may not always be limited to real hardware.
Yes, any ethernet device will do. What I mean is that vhost does not deal with emulation at all. All setup is done in userspace.
Quoted 3 lines
We may on day use vhost as the basis of a driver domain. There's quite a lot of interest in this for networking.
You can use veth for this. This works today.
Quoted 6 lines
At any rate, I'd like to see performance results before we consider trying to reuse virtio code.
Regards,
Anthony Liguori
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Anthony LiguoriQuoted 7 lines · Anthony Liguori
Quoted 6 lines · Michael S. Tsirkin
Quoted 5 lines
We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue.
Quoted 7 lines
Quoted 5 lines
I prefer keeping it simple. Much of abstraction in virtio is due to the fact that it needs to work on top of different hardware emulations:
lguest,kvm, possibly others in the future. vhost is always working on real hardware, using eventfd as the interface, so it does not need that.Actually, vhost may not always be limited to real hardware.
Yes, any ethernet device will do. What I mean is that vhost does not deal with emulation at all. All setup is done in userspace.
Quoted 3 lines
We may on day use vhost as the basis of a driver domain. There's quite a lot of interest in this for networking.
You can use veth for this. This works today.
Quoted 6 lines
At any rate, I'd like to see performance results before we consider trying to reuse virtio code.
Regards,
Anthony Liguori
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Anthony LiguoriQuoted 2 lines · Anthony Liguori
At any rate, I'd like to see performance results before we consider trying to reuse virtio code.
Yes, I agree. I'd also like to do more work on the macvlan extensions to see if it works out without involving a socket. Passing the socket into the vhost_net device is a nice feature of the current implementation that we'd have to give up for something else (e.g. making the vhost a real network interface that you can hook up to a bridge) if it were to use virtio.
Unless I can come up with a solution that is clearly superior, I'm taking back my objections on that part for now.
Arnd <><
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Anthony LiguoriQuoted 2 lines · Anthony Liguori
At any rate, I'd like to see performance results before we consider trying to reuse virtio code.
Yes, I agree. I'd also like to do more work on the macvlan extensions to see if it works out without involving a socket. Passing the socket into the vhost_net device is a nice feature of the current implementation that we'd have to give up for something else (e.g. making the vhost a real network interface that you can hook up to a bridge) if it were to use virtio.
Unless I can come up with a solution that is clearly superior, I'm taking back my objections on that part for now.
Arnd <><
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 11 lines · Michael S. Tsirkin
Quoted 5 lines
We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue.I prefer keeping it simple. Much of abstraction in virtio is due to the fact that it needs to work on top of different hardware emulations:
lguest,kvm, possibly others in the future. vhost is always working on real hardware, using eventfd as the interface, so it does not need that.
Actually, vhost may not always be limited to real hardware.
We may on day use vhost as the basis of a driver domain. There's quite a lot of interest in this for networking.
At any rate, I'd like to see performance results before we consider trying to reuse virtio code.
Regards,
Anthony Liguori
Michael S. Tsirkin
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Arnd BergmannQuoted 5 lines · Arnd Bergmann
Quoted 1 line · Michael S. Tsirkin
+struct
workqueue_struct*vhost_workqueue;[nitpicking] This could be static.
Good catch. Thanks!
Quoted 32 lines
Quoted 32 lines
drivers/vhost/vhost.h new file +32
-0+/* The virtqueue structure describes a queue attached to a device. */ +struct vhost_virtqueue { +struct vhost_dev *dev; + +/* The actual ring of buffers. */ +struct mutex mutex; +unsigned int num; +struct vring_desc __user *desc; +struct vring_avail __user *avail; +struct vring_used __user *used; +struct file *kick; +struct file *call; +struct file *error; +struct eventfd_ctx *call_ctx; +struct eventfd_ctx *error_ctx; + +struct vhost_poll poll; + +/* The routine to call when the Guest pings us, or timeout. */ +work_func_t handle_kick; + +/* Last available index we saw. */ +u16 last_avail_idx; + +/* Last index we used. */ +u16 last_used_idx; + +/* Outstanding buffers */ +unsigned int inflight; + +/* Is this blocked? */ +bool blocked;
Quoted 8 lines
drivers/vhost/vhost.h line 71
Quoted 4 lines
drivers/vhost/vhost.h new file +4
-0+ +struct iovec iov[VHOST_NET_MAX_SG ]; + +}____cacheline_aligned ;We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that
vring_virtqueueis derived from struct virtqueue.
I prefer keeping it simple. Much of abstraction in virtio is due to the fact that it needs to work on top of different hardware emulations:
lguest,kvm, possibly others in the future. vhost is always working on real hardware, using eventfd as the interface, so it does not need that.
Quoted 2 lines
That would make it possible for simple device drivers to use the same driver in both host and guest,
I don't think so. For example, there's a callback field that gets invoked in guest when buffers are consumed. It could be overloaded to mean "buffers are available" in host but you never handle both situations in the same way, so what's the point?
Quoted 8 lines
similar to how Ira Snyder used virtqueues to make
virtio_netrun between two hosts running the same code [1].
Ideally, I guess you should be able to even makevirtio_network in the host if you do that, but that could bring other complexities.Arnd <><
As I pointed out earlier, most code in virtio net is asymmetrical: guest provides buffers, host consumes them. Possibly, one could use virtio rings in a symmetrical way, but support of existing guest virtio net means there's almost no shared code.
Signature · 1 line
MST
Arnd Bergmann
maintainerRe: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 1 line · Michael S. Tsirkin
+struct
workqueue_struct*vhost_workqueue;
[nitpicking] This could be static.
Quoted 26 lines
drivers/vhost/vhost.h new file +26
-0+/* The virtqueue structure describes a queue attached to a device. */ +struct vhost_virtqueue { +struct vhost_dev *dev; + +/* The actual ring of buffers. */ +struct mutex mutex; +unsigned int num; +struct vring_desc __user *desc; +struct vring_avail __user *avail; +struct vring_used __user *used; +struct file *kick; +struct file *call; +struct file *error; +struct eventfd_ctx *call_ctx; +struct eventfd_ctx *error_ctx; + +struct vhost_poll poll; + +/* The routine to call when the Guest pings us, or timeout. */ +work_func_t handle_kick; + +/* Last available index we saw. */ +u16 last_avail_idx; + +/* Last index we used. */ +u16 last_used_idx;
drivers/vhost/vhost.h line 71
Quoted 10 lines
drivers/vhost/vhost.h new file +10
-0+ +/* Outstanding buffers */ +unsigned int inflight; + +/* Is this blocked? */ +bool blocked; + +struct iovec iov[VHOST_NET_MAX_SG ]; + +}____cacheline_aligned ;
We discussed this before, and I still think this could be directly derived from struct virtqueue, in the same way that vring_virtqueue is derived from struct virtqueue. That would make it possible for simple device drivers to use the same driver in both host and guest, similar to how Ira Snyder used virtqueues to make virtio_net run between two hosts running the same code [1].
Ideally, I guess you should be able to even make virtio_net work in the host if you do that, but that could bring other complexities.
Arnd <><
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 30 lines · Michael S. Tsirkin
What it is: vhost net is a character device that can be used to reduce the number of system calls involved in virtio networking. Existing virtio net code is used in the guest without modification.
There's similarity with vringfd, with some differences and reduced scope
- uses eventfd for signalling
- structures can be moved around in memory at any time (good for migration)
- support memory table and not just an offset (needed for kvm)common virtio related code has been put in a separate file
vhost.cand can be made into a separate module if/when more backend appear. I used Rusty'slguest.cas the source for developing this part : this supplied me with witty comments I wouldn't be able to write myself.What it is not: vhost net is not a bus, and not a generic new system call. No assumptions are made on how guest performs hypercalls. Userspace hypervisors are supported as well as kvm.
How it works: Basically, we connect virtio frontend (configured by userspace) to a backend. The backend could be a network device, or a tun-like device. In this version I only support raw socket as a backend, which can be bound to e.g. SR IOV, or to macvlan device. Backend is also configured by userspace, including vlan/mac etc.
Status:
This works for me, and I haven't see any crashes.
I have not run any benchmarks yet, compared to userspace, I expect to see improved latency (as I save up to 4 system calls per packet) but not yet bandwidth/CPU (as TSO and interrupt mitigation are not yet supported).
Quoted 4 lines
Features that I plan to look at in the future:
- TSO
- interrupt mitigation
- zero copy
Much better -- a couple of documentation nits below.
Thanx, Paul
Quoted 153 lines
Signed-off-by Michael S. Tsirkin <[email protected]>--- MAINTAINERS | 10 + arch/x86/kvm/Kconfig | 1 + drivers/Makefile | 1 + drivers/block/virtio_blk.c | 3 + drivers/vhost/Kconfig | 11 + drivers/vhost/Makefile | 2 + drivers/vhost/net.c | 462 ++++++++++++++++++++++++++++++ drivers/vhost/vhost.c | 663 ++++++++++++++++++++++++++++++++++++++++++++ drivers/vhost/vhost.h | 108 +++++++ include/linux/Kbuild | 1 + include/linux/miscdevice.h | 1 + include/linux/vhost.h | 100 +++++++ 12 files changed, 1363 insertions(+), 0 deletions(-) create mode 100644 drivers/vhost/Kconfig create mode 100644 drivers/vhost/Makefile create mode 100644 drivers/vhost/net.c create mode 100644 drivers/vhost/vhost.c create mode 100644 drivers/vhost/vhost.h create mode 100644 include/linux/vhost.hMAINTAINERS +10
-0@@ -6312,6 +6312,16 @@ S: Maintained F: Documentation/filesystems/vfat.txt F: fs/fat/ +VIRTIO HOST (VHOST) +P: Michael S. Tsirkin +M: [email protected] +L: [email protected] +L: [email protected] +L: [email protected] +S: Maintained +F: drivers/vhost/ +F: include/linux/vhost.h + VIA RHINE NETWORK DRIVER P: Roger Luethi M: [email protected]arch/x86/kvm/Kconfig +1
-0@@ -64,6 +64,7 @@ config KVM_AMD # OK, it's a little counter-intuitive to do this, but it puts it neatly under # the virtualization menu. +source drivers/vhost/Kconfig source drivers/lguest/Kconfig source drivers/virtio/Kconfigdrivers/Makefile +1
-0@@ -105,6 +105,7 @@ obj-$(CONFIG_HID) += hid/ obj-$(CONFIG_PPC_PS3) += ps3/ obj-$(CONFIG_OF) += of/ obj-$(CONFIG_SSB) += ssb/ +obj-$(CONFIG_VHOST_NET) += vhost/ obj-$(CONFIG_VIRTIO) += virtio/ obj-$(CONFIG_VLYNQ) += vlynq/ obj-$(CONFIG_STAGING) += staging/drivers/block/virtio_blk.c +3
-0@@ -293,6 +293,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) err =PTR_ERR (vblk->vq); goto out_free_vblk; } +printk (KERN_ERR "vblk->vq = %p\n" , vblk->vq); vblk->pool =mempool_create_kmalloc_pool (1 ,sizeof (struct virtblk_req ));if (!vblk->pool) {@@ -383,6 +384,8 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)if (!err)blk_queue_logical_block_size (vblk->disk->queue, blk_size); +printk (KERN_ERR "virtio_config_val returned %d\n" , err); +add_disk (vblk->disk);return 0 ;drivers/vhost/Kconfig new file +11
-0@@ -0,0 +1,11 @@ +config VHOST_NET + tristate "Host kernel accelerator for virtio net" + depends on NET && EVENTFD + ---help--- + This kernel module can be loaded in host kernel to accelerate + guest networking with virtio_net. Not to be confused with virtio_net + module itself which needs to be loaded in guest kernel. + + To compile this driver as a module, choose M here: the module will + be called vhost_net. +drivers/vhost/Makefile new file +2
-0@@ -0,0 +1,2 @@ +obj-$(CONFIG_VHOST_NET) += vhost_net.o +vhost_net-y := vhost.o net.odrivers/vhost/net.c new file +33
-0@@ -0,0 +1,462 @@ +/* Copyright (C) 2009 Red Hat, Inc. +* Author: Michael S. Tsirkin <[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. +* +* virtio-net server in host kernel. +*/ + +#include <linux/eventfd.h> +#include <linux/vhost.h> +#include <linux/virtio_net.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/rcupdate.h> +#include <linux/file.h> + +#include <linux/net.h> +#include <linux/if_packet.h> +#include <linux/if_arp.h> + +#include <net/sock.h> + +#include <asm/mmu_context.h> + +#include "vhost.h" + +enum { +VHOST_NET_VQ_RX =0 , +VHOST_NET_VQ_TX =1 , +VHOST_NET_VQ_MAX =2 ,
drivers/vhost/net.c line 43
Quoted 10 lines
drivers/vhost/net.c new file +10
-0+}; + +struct vhost_net { +struct vhost_dev dev; +struct vhost_virtqueue vqs[VHOST_NET_VQ_MAX ]; +/* We use a kind of RCU to access sock pointer. +* All readers access it from workqueue, +* which makes it possible to flush the workqueue +* instead of synchronize_rcu. Therefore readers +* do not need rcu_read_lock/rcu_read_unlock.
How about something like "Therefore the beginning of workqueue execution acts as rcu_read_lock() and the end of workqueue execution acts as rcu_read_lock()"?
It would also be good to add comments to the workqueue functions themselves saying that they act as read-side critical sections for your kind of RCU.
Thanx, Paul
Quoted 1,339 lines
drivers/vhost/net.c new file +419
-0+* Writers use device mutex. */ +struct socket *sock; +struct vhost_poll poll[VHOST_NET_VQ_MAX ]; +}; + +static void handle_tx_kick (struct work_struct *work); +static void handle_rx_kick (struct work_struct *work); +static void handle_tx_net (struct work_struct *work); +static void handle_rx_net (struct work_struct *work); + +static int vhost_net_open (struct inode *inode,struct file *f) +{ +struct vhost_net *n =kzalloc (sizeof *n,GFP_KERNEL ); +int r; +if (!n) +return -ENOMEM ; + f->private_data = n; + n->vqs[VHOST_NET_VQ_TX ].handle_kick = handle_tx_kick; + n->vqs[VHOST_NET_VQ_RX ].handle_kick = handle_rx_kick; + r =vhost_dev_init (&n->dev, n->vqs,VHOST_NET_VQ_MAX ); +if (r <0 ) { +kfree (n); +return r; + } + +vhost_poll_init (n->poll +VHOST_NET_VQ_TX , handle_tx_net,POLLOUT ); +vhost_poll_init (n->poll +VHOST_NET_VQ_RX , handle_rx_net,POLLIN ); +return 0 ; +} + +static struct socket *vhost_net_stop (struct vhost_net *n) +{ +struct socket *sock = n->sock; +rcu_assign_pointer (n->sock,NULL ); +if (sock) { +vhost_poll_flush (n->poll +VHOST_NET_VQ_TX ); +vhost_poll_flush (n->poll +VHOST_NET_VQ_RX ); + } +return sock; +} + +static int vhost_net_release (struct inode *inode,struct file *f) +{ +struct vhost_net *n = f->private_data; +struct socket *sock; + + sock =vhost_net_stop (n); +vhost_dev_cleanup (&n->dev); +if (sock) +fput (sock->file); +kfree (n); +return 0 ; +} + +static long vhost_net_set_socket (struct vhost_net *n,int fd) +{ +struct { +struct sockaddr_ll sa; +char buf[MAX_ADDR_LEN ]; + } uaddr; +struct socket *sock, *oldsock =NULL ; +int uaddr_len =sizeof uaddr, r; + +mutex_lock (&n->dev.mutex); + r =vhost_dev_check_owner (&n->dev); +if (r) + goto done; + +if (fd ==-1 ) { +/* Disconnect from socket and device. */ + oldsock =vhost_net_stop (n); + goto done; + } + + sock =sockfd_lookup (fd, &r); +if (!sock) { + r = -ENOTSOCK ; + goto done; + } + +/* Parameter checking */ +if (sock->sk->sk_type !=SOCK_RAW ) { + r = -ESOCKTNOSUPPORT ; + goto done; + } + + r = sock->ops->getname (sock, (struct sockaddr *)&uaddr.sa, + &uaddr_len,0 ); +if (r) + goto done; + +if (uaddr.sa.sll_family !=AF_PACKET ) { + r = -EPFNOSUPPORT ; + goto done; + } + +/* start polling new socket */ +if (sock == oldsock) + goto done; + +if (oldsock) { +vhost_poll_stop (n->poll +VHOST_NET_VQ_TX ); +vhost_poll_stop (n->poll +VHOST_NET_VQ_RX ); + } + oldsock = n->sock; +rcu_assign_pointer (n->sock, sock); +vhost_poll_start (n->poll +VHOST_NET_VQ_TX , sock->file); +vhost_poll_start (n->poll +VHOST_NET_VQ_RX , sock->file); +done: +mutex_unlock (&n->dev.mutex); +if (oldsock) { +vhost_poll_flush (n->poll +VHOST_NET_VQ_TX ); +vhost_poll_flush (n->poll +VHOST_NET_VQ_RX ); +vhost_poll_flush (&n->dev.vqs[VHOST_NET_VQ_TX ].poll); +vhost_poll_flush (&n->dev.vqs[VHOST_NET_VQ_RX ].poll); +fput (oldsock->file); + } +return r; +} + +static long vhost_net_reset_owner (struct vhost_net *n) +{ +struct socket *sock =NULL ; +long r; +mutex_lock (&n->dev.mutex); + r =vhost_dev_check_owner (&n->dev); +if (r) + goto done; + sock =vhost_net_stop (n); + r =vhost_dev_reset_owner (&n->dev); +done: +mutex_unlock (&n->dev.mutex); +if (sock) +fput (sock->file); +return r; +} + +static long vhost_net_ioctl (struct file *f,unsigned int ioctl, +unsigned long arg) +{ +struct vhost_net *n = f->private_data; +void __user *argp = (void __user *)arg; +u32 __user *featurep = argp; +int __user *fdp = argp; +u32 features; +int fd, r; +switch (ioctl) { +case VHOST_NET_SET_SOCKET : + r =get_user (fd, fdp); +if (r <0 ) +return r; +return vhost_net_set_socket (n, fd); +case VHOST_GET_FEATURES : +/* No features for now */ + features =0 ; +return put_user (features, featurep); +case VHOST_ACK_FEATURES : + r =get_user (features, featurep); +/* No features for now */ +if (r <0 ) +return r; +if (features) +return -EOPNOTSUPP ; +return 0 ; +case VHOST_RESET_OWNER : +return vhost_net_reset_owner (n); +default : +return vhost_dev_ioctl (&n->dev, ioctl, arg); + } +} + +static struct file_operations vhost_net_fops = { + .owner =THIS_MODULE , + .release = vhost_net_release, + .unlocked_ioctl = vhost_net_ioctl, + .open = vhost_net_open, +}; + +static struct miscdevice vhost_net_misc = { +VHOST_NET_MINOR , +"vhost-net" , + &vhost_net_fops, +}; + +/* Bits from fs/aio.c. TODO: export and use from there? */ +/* +* use_mm +* Makes the calling kernel thread take on the specified +* mm context. +* Called by the retry thread execute retries within the +* iocb issuer's mm context, so that copy_from/to_user +* operations work seamlessly for aio. +* (Note: this routine is intended to be called only +* from a kernel thread context) +*/ +static void use_mm (struct mm_struct *mm) +{ +struct mm_struct *active_mm; +struct task_struct *tsk = current; + +task_lock (tsk); + active_mm = tsk->active_mm; +atomic_inc (&mm->mm_count); + tsk->mm = mm; + tsk->active_mm = mm; +switch_mm (active_mm, mm, tsk); +task_unlock (tsk); + +mmdrop (active_mm); +} + +/* +* unuse_mm +* Reverses the effect of use_mm, i.e. releases the +* specified mm context which was earlier taken on +* by the calling kernel thread +* (Note: this routine is intended to be called only +* from a kernel thread context) +*/ +static void unuse_mm (struct mm_struct *mm) +{ +struct task_struct *tsk = current; + +task_lock (tsk); + tsk->mm =NULL ; +/* active_mm is still 'mm' */ +enter_lazy_tlb (mm, tsk); +task_unlock (tsk); +} + +static void handle_tx (struct vhost_net *net) +{ +struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_TX ]; +unsigned head, out, in; +struct msghdr msg = { + .msg_name =NULL , + .msg_namelen =0 , + .msg_control =NULL , + .msg_controllen =0 , + .msg_iov = (struct iovec *)vq->iov +1 , + .msg_flags =MSG_DONTWAIT , + }; +size_t len; +int err; +struct socket *sock =rcu_dereference (net->sock); +if (!sock || !sock_writeable (sock->sk)) +return ; + +use_mm (net->dev.mm); +mutex_lock (&vq->mutex); +for (;;) { + head =vhost_get_vq_desc (&net->dev, vq, vq->iov, &out, &in); +if (head == vq->num) +break ; +if (out <=1 || in) { +vq_err (vq,"Unexpected descriptor format for TX: " +"out %d, int %d\n" , out, in); +break ; + } +/* Sanity check */ +if (vq->iov->iov_len !=sizeof (struct virtio_net_hdr )) { +vq_err (vq,"Unexpected header len for TX: " +"%ld expected %zd\n" , vq->iov->iov_len, +sizeof (struct virtio_net_hdr )); +break ; + } +/* Skip header. TODO: support TSO. */ + msg.msg_iovlen = out -1 ; + len =iov_length (vq->iov +1 , out -1 ); +/* TODO: Check specific error and bomb out unless ENOBUFS? */ + err = sock->ops->sendmsg (NULL , sock, &msg, len); +if (err <0 ) { +vhost_discard_vq_desc (vq); +break ; + } +if (err != len) +pr_err ("Truncated TX packet: " +" len %d != %zd\n" , err, len); +vhost_add_used_and_trigger (vq, head, + len +sizeof (struct virtio_net_hdr )); + } + +mutex_unlock (&vq->mutex); +unuse_mm (net->dev.mm); +} + +static void handle_rx (struct vhost_net *net) +{ +struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX ]; +unsigned head, out, in; +struct msghdr msg = { + .msg_name =NULL , + .msg_namelen =0 , + .msg_control =NULL ,/* FIXME: get and handle RX aux data. */ + .msg_controllen =0 , + .msg_iov = vq->iov +1 , + .msg_flags =MSG_DONTWAIT , + }; + +struct virtio_net_hdr hdr = { + .flags =0 , + .gso_type =VIRTIO_NET_HDR_GSO_NONE + }; + +size_t len; +int err; +struct socket *sock =rcu_dereference (net->sock); +if (!sock ||skb_queue_empty (&sock->sk->sk_receive_queue)) +return ; + +use_mm (net->dev.mm); +mutex_lock (&vq->mutex); + +for (;;) { + head =vhost_get_vq_desc (&net->dev, vq, vq->iov, &out, &in); +if (head == vq->num) +break ; +if (in <=1 || out) { +vq_err (vq,"Unexpected descriptor format for RX: out %d, int %d\n" , + out, in); +break ; + } +/* Sanity check */ +if (vq->iov->iov_len !=sizeof (struct virtio_net_hdr )) { +vq_err (vq,"Unexpected header len for RX: %ld expected %zd\n" , + vq->iov->iov_len,sizeof (struct virtio_net_hdr )); +break ; + } +/* Skip header. TODO: support TSO/mergeable rx buffers. */ + msg.msg_iovlen = in -1 ; + len =iov_length (vq->iov +1 , in -1 ); + err = sock->ops->recvmsg (NULL , sock, &msg, + len,MSG_DONTWAIT |MSG_TRUNC ); +/* TODO: Check specific error and bomb out unless EAGAIN? */ +if (err <0 ) { +vhost_discard_vq_desc (vq); +break ; + } +/* TODO: Should check and handle checksum. */ +if (err > len) { +pr_err ("Discarded truncated rx packet: " +" len %d > %zd\n" , err, len); +vhost_discard_vq_desc (vq); +continue ; + } + len = err; + err =copy_to_user (vq->iov->iov_base, &hdr,sizeof hdr); +if (err) { +vq_err (vq,"Unable to write vnet_hdr at addr %p: %d\n" , + vq->iov->iov_base, err); +break ; + } +vhost_add_used_and_trigger (vq, head, len +sizeof hdr); + } + +mutex_unlock (&vq->mutex); +unuse_mm (net->dev.mm); +} + +static void handle_tx_kick (struct work_struct *work) +{ +struct vhost_virtqueue *vq; +struct vhost_net *net; + vq = container_of(work, struct vhost_virtqueue, poll.work); + net = container_of(vq->dev, struct vhost_net, dev); +handle_tx (net); +} + +static void handle_rx_kick (struct work_struct *work) +{ +struct vhost_virtqueue *vq; +struct vhost_net *net; + vq = container_of(work, struct vhost_virtqueue, poll.work); + net = container_of(vq->dev, struct vhost_net, dev); +handle_rx (net); +} + +static void handle_tx_net (struct work_struct *work) +{ +struct vhost_net *net; + net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_TX].work); +handle_tx (net); +} + +static void handle_rx_net (struct work_struct *work) +{ +struct vhost_net *net; + net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_RX].work); +handle_rx (net); +} + +int vhost_net_init (void ) +{ +int r =vhost_init (); +if (r) + goto err_init; + r =misc_register (&vhost_net_misc); +if (r) + goto err_reg; +return 0 ; +err_reg: +vhost_cleanup (); +err_init: +return r; + +} +module_init (vhost_net_init); + +void vhost_net_exit (void ) +{ +misc_deregister (&vhost_net_misc); +vhost_cleanup (); +} +module_exit (vhost_net_exit); + +MODULE_VERSION ("0.0.1" ); +MODULE_LICENSE ("GPL v2" ); +MODULE_AUTHOR ("Michael S. Tsirkin" ); +MODULE_DESCRIPTION ("Host kernel accelerator for virtio net" );drivers/vhost/vhost.c new file +663
-0@@ -0,0 +1,663 @@ +/* Copyright (C) 2009 Red Hat, Inc. +* Copyright (C) 2006 Rusty Russell IBM Corporation +* +* Author: Michael S. Tsirkin <[email protected]> +* +* Inspiration, some code, and most witty comments come from +* Documentation/lguest/lguest.c, by Rusty Russell +* +* This work is licensed under the terms of the GNU GPL, version 2. +* +* Generic code for virtio server in host kernel. +*/ + +#include <linux/eventfd.h> +#include <linux/vhost.h> +#include <linux/virtio_net.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/rcupdate.h> +#include <linux/poll.h> +#include <linux/file.h> + +#include <linux/net.h> +#include <linux/if_packet.h> +#include <linux/if_arp.h> + +#include <net/sock.h> + +#include <asm/mmu_context.h> + +#include "vhost.h" + +enum { +VHOST_MEMORY_MAX_NREGIONS =64 , +}; + +struct workqueue_struct *vhost_workqueue; + +static void vhost_poll_func (struct file *file,wait_queue_head_t *wqh, +poll_table *pt) +{ +struct vhost_poll *poll; + poll = container_of(pt, struct vhost_poll, table); + + poll->wqh = wqh; +add_wait_queue (wqh, &poll->wait); +} + +static int vhost_poll_wakeup (wait_queue_t *wait,unsigned mode,int sync,void *key) +{ +struct vhost_poll *poll; + poll = container_of(wait, struct vhost_poll, wait); +if (!((unsigned long )key & poll->mask)) +return 0 ; + +queue_work (vhost_workqueue, &poll->work); +return 0 ; +} + +/* Init poll structure */ +void vhost_poll_init (struct vhost_poll *poll,work_func_t func, +unsigned long mask) +{ +INIT_WORK (&poll->work, func); +init_waitqueue_func_entry (&poll->wait, vhost_poll_wakeup); +init_poll_funcptr (&poll->table, vhost_poll_func); + poll->mask = mask; +} + +/* Start polling a file. We add ourselves to file's wait queue. The user must +* keep a reference to a file until after vhost_poll_stop is called. */ +void vhost_poll_start (struct vhost_poll *poll,struct file *file) +{ +unsigned long mask; + mask = file->f_op->poll (file, &poll->table); +if (mask) +vhost_poll_wakeup (&poll->wait,0 ,0 , (void *)mask); +} + +/* Stop polling a file. After this function returns, it becomes safe to drop the +* file reference. You must also flush afterwards. */ +void vhost_poll_stop (struct vhost_poll *poll) +{ +remove_wait_queue (poll->wqh, &poll->wait); +} + +/* Flush any work that has been scheduled. When calling this, don't hold any +* locks that are also used by the callback. */ +void vhost_poll_flush (struct vhost_poll *poll) +{ +flush_work (&poll->work); +} + +long vhost_dev_init (struct vhost_dev *dev,struct vhost_virtqueue *vqs,int nvqs) +{ +int i; + dev->vqs = vqs; + dev->nvqs = nvqs; +mutex_init (&dev->mutex); + +for (i =0 ; i < dev->nvqs; ++i) { + dev->vqs[i].dev = dev; +mutex_init (&dev->vqs[i].mutex); +if (dev->vqs[i].handle_kick) +vhost_poll_init (&dev->vqs[i].poll, + dev->vqs[i].handle_kick, +POLLIN ); + } +return 0 ; +} + +/* User should have device mutex */ +long vhost_dev_check_owner (struct vhost_dev *dev) +{ +return dev->mm == current->mm ?0 : -EPERM ; +} + +/* User should have device mutex */ +static long vhost_dev_set_owner (struct vhost_dev *dev) +{ +if (dev->mm) +return -EBUSY ; + dev->mm =get_task_mm (current); +return 0 ; +} + +/* User should have device mutex */ +long vhost_dev_reset_owner (struct vhost_dev *dev) +{ +struct vhost_memory *memory; + +/* Restore memory to default 1:1 mapping. */ + memory =kmalloc (offsetof(struct vhost_memory , regions) + +2 *sizeof *memory->regions,GFP_KERNEL ); +if (!memory) +return -ENOMEM ; + +vhost_dev_cleanup (dev); + + memory->nregions =2 ; + memory->regions[0 ].guest_phys_addr =1 ; + memory->regions[0 ].userspace_addr =1 ; + memory->regions[0 ].memory_size = ~0ULL ; + memory->regions[1 ].guest_phys_addr =0 ; + memory->regions[1 ].userspace_addr =0 ; + memory->regions[1 ].memory_size =1 ; + dev->memory = memory; +return 0 ; +} + +/* User should have device mutex */ +void vhost_dev_cleanup (struct vhost_dev *dev) +{ +int i; +for (i =0 ; i < dev->nvqs; ++i) { +if (dev->vqs[i].kick && dev->vqs[i].handle_kick) { +vhost_poll_stop (&dev->vqs[i].poll); +vhost_poll_flush (&dev->vqs[i].poll); + } +if (dev->vqs[i].error_ctx) +eventfd_ctx_put (dev->vqs[i].error_ctx); +if (dev->vqs[i].error) +fput (dev->vqs[i].error); +if (dev->vqs[i].kick) +fput (dev->vqs[i].kick); +if (dev->vqs[i].call_ctx) +eventfd_ctx_put (dev->vqs[i].call_ctx); +if (dev->vqs[i].call) +fput (dev->vqs[i].call); + dev->vqs[i].error_ctx =NULL ; + dev->vqs[i].error =NULL ; + dev->vqs[i].kick =NULL ; + dev->vqs[i].call_ctx =NULL ; + dev->vqs[i].call =NULL ; + } +/* No one will access memory at this point */ +kfree (dev->memory); + dev->memory =NULL ; +if (dev->mm) +mmput (dev->mm); + dev->mm =NULL ; +} + +static long vhost_set_memory (struct vhost_dev *d,struct vhost_memory __user *m) +{ +struct vhost_memory mem, *newmem, *oldmem; +unsigned long size = offsetof(struct vhost_memory , regions); +long r; + r =copy_from_user (&mem, m, size); +if (r) +return r; +if (mem.padding) +return -EOPNOTSUPP ; +if (mem.nregions >VHOST_MEMORY_MAX_NREGIONS ) +return -E2BIG ; + newmem =kmalloc (size + mem.nregions *sizeof *m->regions,GFP_KERNEL ); +if (!newmem) +return -ENOMEM ; + +memcpy (newmem, &mem, size); + r =copy_from_user (newmem->regions, m->regions, + mem.nregions *sizeof *m->regions); +if (r) { +kfree (newmem); +return r; + } + oldmem = d->memory; +rcu_assign_pointer (d->memory, newmem); +synchronize_rcu (); +kfree (oldmem); +return 0 ; +} + +static int init_used (struct vhost_virtqueue *vq) +{ +u16 flags =0 ; +int r =put_user (flags, &vq->used->flags); +if (r) +return r; +return get_user (vq->last_used_idx, &vq->used->idx); +} + +static long vhost_set_vring (struct vhost_dev *d,int ioctl,void __user *argp) +{ +struct file *eventfp, *filep =NULL , *pollstart =NULL , *pollstop =NULL ; +struct eventfd_ctx *ctx =NULL ; +u32 __user *idxp = argp; +struct vhost_virtqueue *vq; +struct vhost_vring_state s; +struct vhost_vring_file f; +struct vhost_vring_addr a; +u32 idx; +long r; + + r =get_user (idx, idxp); +if (r <0 ) +return r; +if (idx > d->nvqs) +return -ENOBUFS ; + + vq = d->vqs + idx; + +mutex_lock (&vq->mutex); + +switch (ioctl) { +case VHOST_SET_VRING_NUM : + r =copy_from_user (&s, argp,sizeof s); +if (r <0 ) +break ; +if (s.num >0xffff ) { + r = -EINVAL ; +break ; + } + vq->num = s.num; +break ; +case VHOST_SET_VRING_BASE : + r =copy_from_user (&s, argp,sizeof s); +if (r <0 ) +break ; +if (s.num >0xffff ) { + r = -EINVAL ; +break ; + } + vq->last_avail_idx = s.num; +break ; +case VHOST_GET_VRING_BASE : + s.index = idx; + s.num = vq->last_avail_idx; + r =copy_to_user (argp, &s,sizeof s); +break ; +case VHOST_SET_VRING_DESC : + r =copy_from_user (&a, argp,sizeof a); +if (r <0 ) +break ; +if (a.padding) { + r = -EOPNOTSUPP ; +break ; + } +if ((u64 )(long )a.user_addr != a.user_addr) { + r = -EFAULT ; +break ; + } + vq->desc = (void __user *)(long )a.user_addr; +break ; +case VHOST_SET_VRING_AVAIL : + r =copy_from_user (&a, argp,sizeof a); +if (r <0 ) +break ; +if (a.padding) { + r = -EOPNOTSUPP ; +break ; + } +if ((u64 )(long )a.user_addr != a.user_addr) { + r = -EFAULT ; +break ; + } + vq->avail = (void __user *)(long )a.user_addr; +break ; +case VHOST_SET_VRING_USED : + r =copy_from_user (&a, argp,sizeof a); +if (r <0 ) +break ; +if (a.padding) { + r = -EOPNOTSUPP ; +break ; + } +if ((u64 )(long )a.user_addr != a.user_addr) { + r = -EFAULT ; +break ; + } + vq->used = (void __user *)(long )a.user_addr; + r =init_used (vq); +if (r) +break ; +break ; +case VHOST_SET_VRING_KICK : + r =copy_from_user (&f, argp,sizeof f); +if (r <0 ) +break ; + eventfp = f.fd ==-1 ?NULL :eventfd_fget (f.fd); +if (IS_ERR (eventfp)) +return PTR_ERR (eventfp); +if (eventfp != vq->kick) { + pollstop = filep = vq->kick; + pollstart = vq->kick = eventfp; + }else + filep = eventfp; +break ; +case VHOST_SET_VRING_CALL : + r =copy_from_user (&f, argp,sizeof f); +if (r <0 ) +break ; + eventfp = f.fd ==-1 ?NULL :eventfd_fget (f.fd); +if (IS_ERR (eventfp)) +return PTR_ERR (eventfp); +if (eventfp != vq->call) { + filep = vq->call; + ctx = vq->call_ctx; + vq->call = eventfp; + vq->call_ctx = eventfp ? +eventfd_ctx_fileget (eventfp) :NULL ; + }else + filep = eventfp; +break ; +case VHOST_SET_VRING_ERR : + r =copy_from_user (&f, argp,sizeof f); +if (r <0 ) +break ; + eventfp = f.fd ==-1 ?NULL :eventfd_fget (f.fd); +if (IS_ERR (eventfp)) +return PTR_ERR (eventfp); +if (eventfp != vq->error) { + filep = vq->error; + vq->error = eventfp; + ctx = vq->error_ctx; + vq->error_ctx = eventfp ? +eventfd_ctx_fileget (eventfp) :NULL ; + }else + filep = eventfp; +break ; +default : + r = -ENOTTY ; + } + +if (pollstop && vq->handle_kick) +vhost_poll_stop (&vq->poll); + +if (ctx) +eventfd_ctx_put (ctx); +if (filep) +fput (filep); + +if (pollstart && vq->handle_kick) +vhost_poll_start (&vq->poll, vq->kick); + +mutex_unlock (&vq->mutex); + +if (pollstop && vq->handle_kick) +vhost_poll_flush (&vq->poll); +return 0 ; +} + +long vhost_dev_ioctl (struct vhost_dev *d,unsigned int ioctl,unsigned long arg) +{ +void __user *argp = (void __user *)arg; +long r; + +mutex_lock (&d->mutex); +if (ioctl ==VHOST_SET_OWNER ) { + r =vhost_dev_set_owner (d); + goto done; + } + + r =vhost_dev_check_owner (d); +if (r) + goto done; + +switch (ioctl) { +case VHOST_SET_MEM_TABLE : + r =vhost_set_memory (d, argp); +break ; +default : + r =vhost_set_vring (d, ioctl, argp); +break ; + } +done: +mutex_unlock (&d->mutex); +return r; +} + +static const struct vhost_memory_region *find_region (struct vhost_memory *mem, +__u64 addr,__u32 len) +{ +struct vhost_memory_region *reg; +int i; +/* linear search is not brilliant, but we really have on the order of 6 +* regions in practice */ +for (i =0 ; i < mem->nregions; ++i) { + reg = mem->regions + i; +if (reg->guest_phys_addr <= addr && + reg->guest_phys_addr + reg->memory_size -1 >= addr) +return reg; + } +return NULL ; +} + +/* FIXME: this does not handle a region that spans multiple +* address/len pairs */ +int translate_desc (struct vhost_dev *dev,u64 addr,u32 len, +struct iovec iov[],int iov_count,int iov_size, +unsigned *num) +{ +const struct vhost_memory_region *reg; +struct vhost_memory *mem; +struct iovec *_iov; +u64 s =0 ; +int ret =0 ; + +rcu_read_lock (); + + mem =rcu_dereference (dev->memory); +while ((u64 )len > s) { +u64 size; +if (*num + iov_count >= iov_size) { + ret = -ENOBUFS ; +break ; + } + reg =find_region (mem, addr, len); +if (!reg) { + ret = -EFAULT ; +break ; + } + _iov = iov + iov_count + *num; + size = reg->memory_size - addr + reg->guest_phys_addr; + _iov->iov_len =min ((u64 )len, size); + _iov->iov_base = (void *) + (reg->userspace_addr + addr - reg->guest_phys_addr); + s += size; + addr += size; + ++*num; + } + +rcu_read_unlock (); +return ret; +} + +/* Each buffer in the virtqueues is actually a chain of descriptors. This +* function returns the next descriptor in the chain, or vq->vring.num if we're +* at the end. */ +static unsigned next_desc (struct vhost_virtqueue *vq,struct vring_desc *desc) +{ +unsigned int next; + +/* If this descriptor says it doesn't chain, we're done. */ +if (!(desc->flags &VRING_DESC_F_NEXT )) +return vq->num; + +/* Check they're not leading us off end of descriptors. */ + next = desc->next; +/* Make sure compiler knows to grab that: we don't want it changing! */ +/* We will use the result as an index in an array, so most +* architectures only need a compiler barrier here. */ +read_barrier_depends (); + +if (next >= vq->num) { +vq_err (vq,"Desc next is %u > %u" , next, vq->num); +return vq->num; + } + +return next; +} + +/* This looks in the virtqueue and for the first available buffer, and converts +* it to an iovec for convenient access. Since descriptors consist of some +* number of output then some number of input descriptors, it's actually two +* iovecs, but we pack them into one and note how many of each there were. +* +* This function returns the descriptor number found, or vq->num (which +* is never a valid descriptor number) if none was found. */ +unsigned vhost_get_vq_desc (struct vhost_dev *dev,struct vhost_virtqueue *vq, +struct iovec iov[], +unsigned int *out_num,unsigned int *in_num) +{ +struct vring_desc desc; +unsigned int i, head; +u16 last_avail_idx, idx; + +/* Check it isn't doing very strange things with descriptor numbers. */ + last_avail_idx = vq->last_avail_idx; +if (get_user (idx, &vq->avail->idx)) { +vq_err (vq,"Failed to access avail idx at %p\n" , + &vq->avail->idx); +return vq->num; + } + +if ((u16)(idx - last_avail_idx) > vq->num) { +vq_err (vq,"Guest moved used index from %u to %u" , + last_avail_idx, idx); +return vq->num; + } + +/* If there's nothing new since last we looked, return invalid. */ +if (idx == last_avail_idx) +return vq->num; + +/* Grab the next descriptor number they're advertising, and increment +* the index we've seen. */ +if (get_user (head, &vq->avail->ring[last_avail_idx % vq->num])) { +vq_err (vq,"Failed to read head: idx %d address %p\n" , + idx, &vq->avail->ring[last_avail_idx % vq->num]); +return vq->num; + } + +/* If their number is silly, that's a fatal mistake. */ +if (head >= vq->num) { +vq_err (vq,"Guest says index %u > %u is available" , + head, vq->num); +return vq->num; + } + + vq->last_avail_idx++; + +/* When we start there are none of either input nor output. */ + *out_num = *in_num =0 ; + + i = head; +do { +unsigned *num; +unsigned iov_count; +if (copy_from_user (&desc, vq->desc + i,sizeof desc)) { +vq_err (vq,"Failed to get descriptor: idx %d addr %p\n" , + i, vq->desc + i); +return vq->num; + } +/* If this is an input descriptor, increment that count. */ +if (desc.flags &VRING_DESC_F_WRITE ) { + num = in_num; + iov_count = *out_num; + }else { +/* If it's an output descriptor, they're all supposed +* to come before any input descriptors. */ +if (*in_num) { +vq_err (vq,"Descriptor has out after in: " +"idx %d\n" , i); +return vq->num; + } + num = out_num; + iov_count = *in_num; + } +if (translate_desc (dev, desc.addr, desc.len, iov, iov_count, +VHOST_NET_MAX_SG , num)) { +vq_err (vq,"Failed to translate descriptor: idx %d\n" , + i); +return vq->num; + } + +/* If we've got too many, that implies a descriptor loop. */ +if (*out_num + *in_num > vq->num) { +vq_err (vq,"Looped descriptor: idx %d\n" , i); +return vq->num; + } + }while ((i =next_desc (vq, &desc)) != vq->num); + + vq->inflight++; +return head; +} + +/* Reverse the effect of vhost_get_vq_desc. Useful for error handling. */ +void vhost_discard_vq_desc (struct vhost_virtqueue *vq) +{ + vq->last_avail_idx--; + vq->inflight--; +} + +/* After we've used one of their buffers, we tell them about it. We'll then +* want to send them an interrupt, using vq->call. */ +int vhost_add_used (struct vhost_virtqueue *vq, +unsigned int head,int len) +{ +struct vring_used_elem *used; + +/* The virtqueue contains a ring of used buffers. Get a pointer to the +* next entry in that used ring. */ + used = &vq->used->ring[vq->last_used_idx % vq->num]; +if (put_user (head, &used->id)) { +vq_err (vq,"Failed to write used id" ); +return -EFAULT ; + } +if (put_user (len, &used->len)) { +vq_err (vq,"Failed to write used len" ); +return -EFAULT ; + } +/* Make sure buffer is written before we update index. */ +wmb (); +if (put_user (vq->last_used_idx +1 , &vq->used->idx)) { +vq_err (vq,"Failed to increment used idx" ); +return -EFAULT ; + } + vq->last_used_idx++; + vq->inflight--; +return 0 ; +} + +/* This actually sends the interrupt for this virtqueue */ +void vhost_trigger_irq (struct vhost_virtqueue *vq) +{ +__u16 flags =0 ; +if (get_user (flags, &vq->avail->flags)) { +vq_err (vq,"Failed to get flags" ); +return ; + } + +/* If they don't want an interrupt, don't send one, unless empty. */ +if ((flags &VRING_AVAIL_F_NO_INTERRUPT ) && vq->inflight) +return ; + +/* Send the Guest an interrupt tell them we used something up. */ +if (vq->call_ctx) +eventfd_signal (vq->call_ctx,1 ); +} + +/* And here's the combo meal deal. Supersize me! */ +void vhost_add_used_and_trigger (struct vhost_virtqueue *vq, +unsigned int head,int len) +{ +vhost_add_used (vq, head, len); +vhost_trigger_irq (vq); +} + +int vhost_init (void ) +{ + vhost_workqueue =create_workqueue ("vhost" ); +if (!vhost_workqueue) +return -ENOMEM ; +return 0 ; +} + +void vhost_cleanup (void ) +{ +destroy_workqueue (vhost_workqueue); +}drivers/vhost/vhost.h new file +108
-0@@ -0,0 +1,108 @@ +#ifndef _VHOST_H +#define _VHOST_H + +#include <linux/eventfd.h> +#include <linux/vhost.h> +#include <linux/mm.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/poll.h> +#include <linux/file.h> +#include <linux/skbuff.h> + +struct vhost_device ; + +enum { +VHOST_NET_MAX_SG =MAX_SKB_FRAGS +2 , +}; + +/* Poll a file (eventfd or socket) */ +/* Note: there's nothing vhost specific about this structure. */ +struct vhost_poll { +poll_table table; +wait_queue_head_t *wqh; +wait_queue_t wait; +/* struct which will handle all actual work. */ +struct work_struct work; +unsigned long mask; +}; + +void vhost_poll_init (struct vhost_poll *poll,work_func_t func, +unsigned long mask); +void vhost_poll_start (struct vhost_poll *poll,struct file *file); +void vhost_poll_stop (struct vhost_poll *poll); +void vhost_poll_flush (struct vhost_poll *poll); + +/* The virtqueue structure describes a queue attached to a device. */ +struct vhost_virtqueue { +struct vhost_dev *dev; + +/* The actual ring of buffers. */ +struct mutex mutex; +unsigned int num; +struct vring_desc __user *desc; +struct vring_avail __user *avail; +struct vring_used __user *used; +struct file *kick; +struct file *call; +struct file *error; +struct eventfd_ctx *call_ctx; +struct eventfd_ctx *error_ctx; + +struct vhost_poll poll; + +/* The routine to call when the Guest pings us, or timeout. */ +work_func_t handle_kick; + +/* Last available index we saw. */ +u16 last_avail_idx; + +/* Last index we used. */ +u16 last_used_idx; + +/* Outstanding buffers */ +unsigned int inflight; + +/* Is this blocked? */ +bool blocked; + +struct iovec iov[VHOST_NET_MAX_SG ]; + +}____cacheline_aligned ; + +struct vhost_dev { +/* Readers use RCU to access memory table pointer. +* Writers use mutex below.*/ +struct vhost_memory *memory; +struct mm_struct *mm; +struct vhost_virtqueue *vqs; +int nvqs; +struct mutex mutex; +}; + +long vhost_dev_init (struct vhost_dev *,struct vhost_virtqueue *vqs,int nvqs); +long vhost_dev_check_owner (struct vhost_dev *); +long vhost_dev_reset_owner (struct vhost_dev *); +void vhost_dev_cleanup (struct vhost_dev *); +long vhost_dev_ioctl (struct vhost_dev *,unsigned int ioctl,unsigned long arg); + +unsigned vhost_get_vq_desc (struct vhost_dev *,struct vhost_virtqueue *, +struct iovec iov[], +unsigned int *out_num,unsigned int *in_num); +void vhost_discard_vq_desc (struct vhost_virtqueue *); + +int vhost_add_used (struct vhost_virtqueue *,unsigned int head,int len); +void vhost_trigger_irq (struct vhost_virtqueue *); +void vhost_add_used_and_trigger (struct vhost_virtqueue *, +unsigned int head,int len); + +int vhost_init (void ); +void vhost_cleanup (void ); + +#define vq_err (vq, fmt, ...) do { \ + printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__); \ + if ((vq)->error_ctx) \ + eventfd_signal((vq)->error_ctx, 1);\ + } while (0) + +#endif include/linux/Kbuild +1
-0@@ -360,6 +360,7 @@ unifdef-y += uio.h unifdef-y += unistd.h unifdef-y += usbdevice_fs.h unifdef-y += utsname.h +unifdef-y += vhost.h unifdef-y += videodev2.h unifdef-y += videodev.h unifdef-y += virtio_config.hinclude/linux/miscdevice.h +1
-0@@ -30,6 +30,7 @@#define HPET_MINOR 228#define FUSE_MINOR 229#define KVM_MINOR 232 +#define VHOST_NET_MINOR 233#define MISC_DYNAMIC_MINOR 255struct device ;include/linux/vhost.h new file +100
-0@@ -0,0 +1,100 @@ +#ifndef _LINUX_VHOST_H +#define _LINUX_VHOST_H +/* Userspace interface for in-kernel virtio accelerators. */ + +/* vhost is used to reduce the number of system calls involved in virtio. +* +* Existing virtio net code is used in the guest without modification. +* +* This header includes interface used by userspace hypervisor for +* device configuration. +*/ + +#include <linux/types.h> +#include <linux/compiler.h> +#include <linux/ioctl.h> +#include <linux/virtio_config.h> +#include <linux/virtio_ring.h> + +struct vhost_vring_state { +unsigned int index; +unsigned int num; +}; + +struct vhost_vring_file { +unsigned int index; +int fd; +}; + +struct vhost_vring_addr { +unsigned int index; +unsigned int padding; +__u64 user_addr; +}; + +struct vhost_memory_region { +__u64 guest_phys_addr; +__u64 memory_size;/* bytes */ +__u64 userspace_addr; +__u64 padding;/* read/write protection? */ +}; + +struct vhost_memory { +__u32 nregions; +__u32 padding; +struct vhost_memory_region regions[0 ]; +}; + +/* ioctls */ + +#define VHOST_VIRTIO 0xAF + +/* Features bitmask for forward compatibility. Transport bits must be zero. */ +#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u32) +#define VHOST_ACK_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u32) + +/* Set current process as the (exclusive) owner of this file descriptor. This +* must be called before any other vhost command. Further calls to +* VHOST_OWNER_SET fail until VHOST_OWNER_RESET is called. */ +#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01) +/* Give up ownership, and reset the device to default values. +* Allows subsequent call to VHOST_OWNER_SET to succeed. */ +#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02) + +/* Set up/modify memory layout */ +#define VHOST_SET_MEM_TABLE _IOW(VHOST_VIRTIO, 0x03, struct vhost_memory) + +/* Ring setup. These parameters can not be modified while ring is running +* (bound to a device). */ +/* Set number of descriptors in ring */ +#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state) +/* Start of array of descriptors (virtually contiguous) */ +#define VHOST_SET_VRING_DESC _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr) +/* Used structure address */ +#define VHOST_SET_VRING_USED _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_addr) +/* Available structure address */ +#define VHOST_SET_VRING_AVAIL _IOW(VHOST_VIRTIO, 0x13, struct vhost_vring_addr) +/* Base value where queue looks for available descriptors */ +#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x14, struct vhost_vring_state) +/* Get accessor: reads index, writes value in num */ +#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x14, struct vhost_vring_state) + +/* The following ioctls use eventfd file descriptors to signal and poll +* for events. */ + +/* Set eventfd to poll for added buffers */ +#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file) +/* Set eventfd to signal when buffers have beed used */ +#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file) +/* Set eventfd to signal an error */ +#define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file) + +/* VHOST_NET specific defines */ + +/* Attach virtio net device to a raw socket. The socket must be already +* bound to an ethernet device, this device will be used for transmit. +* Pass -1 to unbind from the socket and the transmit device. +* This can be used to stop the device (e.g. for migration). */ +#define VHOST_NET_SET_SOCKET _IOW(VHOST_VIRTIO, 0x30, int) + +#endif Signature · 6 lines
1.6.2.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH 2/2] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 30 lines · Michael S. Tsirkin
What it is: vhost net is a character device that can be used to reduce the number of system calls involved in virtio networking. Existing virtio net code is used in the guest without modification.
There's similarity with vringfd, with some differences and reduced scope
- uses eventfd for signalling
- structures can be moved around in memory at any time (good for migration)
- support memory table and not just an offset (needed for kvm)common virtio related code has been put in a separate file
vhost.cand can be made into a separate module if/when more backend appear. I used Rusty'slguest.cas the source for developing this part : this supplied me with witty comments I wouldn't be able to write myself.What it is not: vhost net is not a bus, and not a generic new system call. No assumptions are made on how guest performs hypercalls. Userspace hypervisors are supported as well as kvm.
How it works: Basically, we connect virtio frontend (configured by userspace) to a backend. The backend could be a network device, or a tun-like device. In this version I only support raw socket as a backend, which can be bound to e.g. SR IOV, or to macvlan device. Backend is also configured by userspace, including vlan/mac etc.
Status:
This works for me, and I haven't see any crashes.
I have not run any benchmarks yet, compared to userspace, I expect to see improved latency (as I save up to 4 system calls per packet) but not yet bandwidth/CPU (as TSO and interrupt mitigation are not yet supported).
Quoted 4 lines
Features that I plan to look at in the future:
- TSO
- interrupt mitigation
- zero copy
Much better -- a couple of documentation nits below.
Thanx, Paul
Quoted 153 lines
Signed-off-by Michael S. Tsirkin <[email protected]>--- MAINTAINERS | 10 + arch/x86/kvm/Kconfig | 1 + drivers/Makefile | 1 + drivers/block/virtio_blk.c | 3 + drivers/vhost/Kconfig | 11 + drivers/vhost/Makefile | 2 + drivers/vhost/net.c | 462 ++++++++++++++++++++++++++++++ drivers/vhost/vhost.c | 663 ++++++++++++++++++++++++++++++++++++++++++++ drivers/vhost/vhost.h | 108 +++++++ include/linux/Kbuild | 1 + include/linux/miscdevice.h | 1 + include/linux/vhost.h | 100 +++++++ 12 files changed, 1363 insertions(+), 0 deletions(-) create mode 100644 drivers/vhost/Kconfig create mode 100644 drivers/vhost/Makefile create mode 100644 drivers/vhost/net.c create mode 100644 drivers/vhost/vhost.c create mode 100644 drivers/vhost/vhost.h create mode 100644 include/linux/vhost.hMAINTAINERS +10
-0@@ -6312,6 +6312,16 @@ S: Maintained F: Documentation/filesystems/vfat.txt F: fs/fat/ +VIRTIO HOST (VHOST) +P: Michael S. Tsirkin +M: [email protected] +L: [email protected] +L: [email protected] +L: [email protected] +S: Maintained +F: drivers/vhost/ +F: include/linux/vhost.h + VIA RHINE NETWORK DRIVER P: Roger Luethi M: [email protected]arch/x86/kvm/Kconfig +1
-0@@ -64,6 +64,7 @@ config KVM_AMD # OK, it's a little counter-intuitive to do this, but it puts it neatly under # the virtualization menu. +source drivers/vhost/Kconfig source drivers/lguest/Kconfig source drivers/virtio/Kconfigdrivers/Makefile +1
-0@@ -105,6 +105,7 @@ obj-$(CONFIG_HID) += hid/ obj-$(CONFIG_PPC_PS3) += ps3/ obj-$(CONFIG_OF) += of/ obj-$(CONFIG_SSB) += ssb/ +obj-$(CONFIG_VHOST_NET) += vhost/ obj-$(CONFIG_VIRTIO) += virtio/ obj-$(CONFIG_VLYNQ) += vlynq/ obj-$(CONFIG_STAGING) += staging/drivers/block/virtio_blk.c +3
-0@@ -293,6 +293,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) err =PTR_ERR (vblk->vq); goto out_free_vblk; } +printk (KERN_ERR "vblk->vq = %p\n" , vblk->vq); vblk->pool =mempool_create_kmalloc_pool (1 ,sizeof (struct virtblk_req ));if (!vblk->pool) {@@ -383,6 +384,8 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)if (!err)blk_queue_logical_block_size (vblk->disk->queue, blk_size); +printk (KERN_ERR "virtio_config_val returned %d\n" , err); +add_disk (vblk->disk);return 0 ;drivers/vhost/Kconfig new file +11
-0@@ -0,0 +1,11 @@ +config VHOST_NET + tristate "Host kernel accelerator for virtio net" + depends on NET && EVENTFD + ---help--- + This kernel module can be loaded in host kernel to accelerate + guest networking with virtio_net. Not to be confused with virtio_net + module itself which needs to be loaded in guest kernel. + + To compile this driver as a module, choose M here: the module will + be called vhost_net. +drivers/vhost/Makefile new file +2
-0@@ -0,0 +1,2 @@ +obj-$(CONFIG_VHOST_NET) += vhost_net.o +vhost_net-y := vhost.o net.odrivers/vhost/net.c new file +33
-0@@ -0,0 +1,462 @@ +/* Copyright (C) 2009 Red Hat, Inc. +* Author: Michael S. Tsirkin <[email protected]> +* +* This work is licensed under the terms of the GNU GPL, version 2. +* +* virtio-net server in host kernel. +*/ + +#include <linux/eventfd.h> +#include <linux/vhost.h> +#include <linux/virtio_net.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/rcupdate.h> +#include <linux/file.h> + +#include <linux/net.h> +#include <linux/if_packet.h> +#include <linux/if_arp.h> + +#include <net/sock.h> + +#include <asm/mmu_context.h> + +#include "vhost.h" + +enum { +VHOST_NET_VQ_RX =0 , +VHOST_NET_VQ_TX =1 , +VHOST_NET_VQ_MAX =2 ,
drivers/vhost/net.c line 43
Quoted 10 lines
drivers/vhost/net.c new file +10
-0+}; + +struct vhost_net { +struct vhost_dev dev; +struct vhost_virtqueue vqs[VHOST_NET_VQ_MAX ]; +/* We use a kind of RCU to access sock pointer. +* All readers access it from workqueue, +* which makes it possible to flush the workqueue +* instead of synchronize_rcu. Therefore readers +* do not need rcu_read_lock/rcu_read_unlock.
How about something like "Therefore the beginning of workqueue execution acts as rcu_read_lock() and the end of workqueue execution acts as rcu_read_lock()"?
It would also be good to add comments to the workqueue functions themselves saying that they act as read-side critical sections for your kind of RCU.
Thanx, Paul
Quoted 1,339 lines
drivers/vhost/net.c new file +419
-0+* Writers use device mutex. */ +struct socket *sock; +struct vhost_poll poll[VHOST_NET_VQ_MAX ]; +}; + +static void handle_tx_kick (struct work_struct *work); +static void handle_rx_kick (struct work_struct *work); +static void handle_tx_net (struct work_struct *work); +static void handle_rx_net (struct work_struct *work); + +static int vhost_net_open (struct inode *inode,struct file *f) +{ +struct vhost_net *n =kzalloc (sizeof *n,GFP_KERNEL ); +int r; +if (!n) +return -ENOMEM ; + f->private_data = n; + n->vqs[VHOST_NET_VQ_TX ].handle_kick = handle_tx_kick; + n->vqs[VHOST_NET_VQ_RX ].handle_kick = handle_rx_kick; + r =vhost_dev_init (&n->dev, n->vqs,VHOST_NET_VQ_MAX ); +if (r <0 ) { +kfree (n); +return r; + } + +vhost_poll_init (n->poll +VHOST_NET_VQ_TX , handle_tx_net,POLLOUT ); +vhost_poll_init (n->poll +VHOST_NET_VQ_RX , handle_rx_net,POLLIN ); +return 0 ; +} + +static struct socket *vhost_net_stop (struct vhost_net *n) +{ +struct socket *sock = n->sock; +rcu_assign_pointer (n->sock,NULL ); +if (sock) { +vhost_poll_flush (n->poll +VHOST_NET_VQ_TX ); +vhost_poll_flush (n->poll +VHOST_NET_VQ_RX ); + } +return sock; +} + +static int vhost_net_release (struct inode *inode,struct file *f) +{ +struct vhost_net *n = f->private_data; +struct socket *sock; + + sock =vhost_net_stop (n); +vhost_dev_cleanup (&n->dev); +if (sock) +fput (sock->file); +kfree (n); +return 0 ; +} + +static long vhost_net_set_socket (struct vhost_net *n,int fd) +{ +struct { +struct sockaddr_ll sa; +char buf[MAX_ADDR_LEN ]; + } uaddr; +struct socket *sock, *oldsock =NULL ; +int uaddr_len =sizeof uaddr, r; + +mutex_lock (&n->dev.mutex); + r =vhost_dev_check_owner (&n->dev); +if (r) + goto done; + +if (fd ==-1 ) { +/* Disconnect from socket and device. */ + oldsock =vhost_net_stop (n); + goto done; + } + + sock =sockfd_lookup (fd, &r); +if (!sock) { + r = -ENOTSOCK ; + goto done; + } + +/* Parameter checking */ +if (sock->sk->sk_type !=SOCK_RAW ) { + r = -ESOCKTNOSUPPORT ; + goto done; + } + + r = sock->ops->getname (sock, (struct sockaddr *)&uaddr.sa, + &uaddr_len,0 ); +if (r) + goto done; + +if (uaddr.sa.sll_family !=AF_PACKET ) { + r = -EPFNOSUPPORT ; + goto done; + } + +/* start polling new socket */ +if (sock == oldsock) + goto done; + +if (oldsock) { +vhost_poll_stop (n->poll +VHOST_NET_VQ_TX ); +vhost_poll_stop (n->poll +VHOST_NET_VQ_RX ); + } + oldsock = n->sock; +rcu_assign_pointer (n->sock, sock); +vhost_poll_start (n->poll +VHOST_NET_VQ_TX , sock->file); +vhost_poll_start (n->poll +VHOST_NET_VQ_RX , sock->file); +done: +mutex_unlock (&n->dev.mutex); +if (oldsock) { +vhost_poll_flush (n->poll +VHOST_NET_VQ_TX ); +vhost_poll_flush (n->poll +VHOST_NET_VQ_RX ); +vhost_poll_flush (&n->dev.vqs[VHOST_NET_VQ_TX ].poll); +vhost_poll_flush (&n->dev.vqs[VHOST_NET_VQ_RX ].poll); +fput (oldsock->file); + } +return r; +} + +static long vhost_net_reset_owner (struct vhost_net *n) +{ +struct socket *sock =NULL ; +long r; +mutex_lock (&n->dev.mutex); + r =vhost_dev_check_owner (&n->dev); +if (r) + goto done; + sock =vhost_net_stop (n); + r =vhost_dev_reset_owner (&n->dev); +done: +mutex_unlock (&n->dev.mutex); +if (sock) +fput (sock->file); +return r; +} + +static long vhost_net_ioctl (struct file *f,unsigned int ioctl, +unsigned long arg) +{ +struct vhost_net *n = f->private_data; +void __user *argp = (void __user *)arg; +u32 __user *featurep = argp; +int __user *fdp = argp; +u32 features; +int fd, r; +switch (ioctl) { +case VHOST_NET_SET_SOCKET : + r =get_user (fd, fdp); +if (r <0 ) +return r; +return vhost_net_set_socket (n, fd); +case VHOST_GET_FEATURES : +/* No features for now */ + features =0 ; +return put_user (features, featurep); +case VHOST_ACK_FEATURES : + r =get_user (features, featurep); +/* No features for now */ +if (r <0 ) +return r; +if (features) +return -EOPNOTSUPP ; +return 0 ; +case VHOST_RESET_OWNER : +return vhost_net_reset_owner (n); +default : +return vhost_dev_ioctl (&n->dev, ioctl, arg); + } +} + +static struct file_operations vhost_net_fops = { + .owner =THIS_MODULE , + .release = vhost_net_release, + .unlocked_ioctl = vhost_net_ioctl, + .open = vhost_net_open, +}; + +static struct miscdevice vhost_net_misc = { +VHOST_NET_MINOR , +"vhost-net" , + &vhost_net_fops, +}; + +/* Bits from fs/aio.c. TODO: export and use from there? */ +/* +* use_mm +* Makes the calling kernel thread take on the specified +* mm context. +* Called by the retry thread execute retries within the +* iocb issuer's mm context, so that copy_from/to_user +* operations work seamlessly for aio. +* (Note: this routine is intended to be called only +* from a kernel thread context) +*/ +static void use_mm (struct mm_struct *mm) +{ +struct mm_struct *active_mm; +struct task_struct *tsk = current; + +task_lock (tsk); + active_mm = tsk->active_mm; +atomic_inc (&mm->mm_count); + tsk->mm = mm; + tsk->active_mm = mm; +switch_mm (active_mm, mm, tsk); +task_unlock (tsk); + +mmdrop (active_mm); +} + +/* +* unuse_mm +* Reverses the effect of use_mm, i.e. releases the +* specified mm context which was earlier taken on +* by the calling kernel thread +* (Note: this routine is intended to be called only +* from a kernel thread context) +*/ +static void unuse_mm (struct mm_struct *mm) +{ +struct task_struct *tsk = current; + +task_lock (tsk); + tsk->mm =NULL ; +/* active_mm is still 'mm' */ +enter_lazy_tlb (mm, tsk); +task_unlock (tsk); +} + +static void handle_tx (struct vhost_net *net) +{ +struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_TX ]; +unsigned head, out, in; +struct msghdr msg = { + .msg_name =NULL , + .msg_namelen =0 , + .msg_control =NULL , + .msg_controllen =0 , + .msg_iov = (struct iovec *)vq->iov +1 , + .msg_flags =MSG_DONTWAIT , + }; +size_t len; +int err; +struct socket *sock =rcu_dereference (net->sock); +if (!sock || !sock_writeable (sock->sk)) +return ; + +use_mm (net->dev.mm); +mutex_lock (&vq->mutex); +for (;;) { + head =vhost_get_vq_desc (&net->dev, vq, vq->iov, &out, &in); +if (head == vq->num) +break ; +if (out <=1 || in) { +vq_err (vq,"Unexpected descriptor format for TX: " +"out %d, int %d\n" , out, in); +break ; + } +/* Sanity check */ +if (vq->iov->iov_len !=sizeof (struct virtio_net_hdr )) { +vq_err (vq,"Unexpected header len for TX: " +"%ld expected %zd\n" , vq->iov->iov_len, +sizeof (struct virtio_net_hdr )); +break ; + } +/* Skip header. TODO: support TSO. */ + msg.msg_iovlen = out -1 ; + len =iov_length (vq->iov +1 , out -1 ); +/* TODO: Check specific error and bomb out unless ENOBUFS? */ + err = sock->ops->sendmsg (NULL , sock, &msg, len); +if (err <0 ) { +vhost_discard_vq_desc (vq); +break ; + } +if (err != len) +pr_err ("Truncated TX packet: " +" len %d != %zd\n" , err, len); +vhost_add_used_and_trigger (vq, head, + len +sizeof (struct virtio_net_hdr )); + } + +mutex_unlock (&vq->mutex); +unuse_mm (net->dev.mm); +} + +static void handle_rx (struct vhost_net *net) +{ +struct vhost_virtqueue *vq = &net->dev.vqs[VHOST_NET_VQ_RX ]; +unsigned head, out, in; +struct msghdr msg = { + .msg_name =NULL , + .msg_namelen =0 , + .msg_control =NULL ,/* FIXME: get and handle RX aux data. */ + .msg_controllen =0 , + .msg_iov = vq->iov +1 , + .msg_flags =MSG_DONTWAIT , + }; + +struct virtio_net_hdr hdr = { + .flags =0 , + .gso_type =VIRTIO_NET_HDR_GSO_NONE + }; + +size_t len; +int err; +struct socket *sock =rcu_dereference (net->sock); +if (!sock ||skb_queue_empty (&sock->sk->sk_receive_queue)) +return ; + +use_mm (net->dev.mm); +mutex_lock (&vq->mutex); + +for (;;) { + head =vhost_get_vq_desc (&net->dev, vq, vq->iov, &out, &in); +if (head == vq->num) +break ; +if (in <=1 || out) { +vq_err (vq,"Unexpected descriptor format for RX: out %d, int %d\n" , + out, in); +break ; + } +/* Sanity check */ +if (vq->iov->iov_len !=sizeof (struct virtio_net_hdr )) { +vq_err (vq,"Unexpected header len for RX: %ld expected %zd\n" , + vq->iov->iov_len,sizeof (struct virtio_net_hdr )); +break ; + } +/* Skip header. TODO: support TSO/mergeable rx buffers. */ + msg.msg_iovlen = in -1 ; + len =iov_length (vq->iov +1 , in -1 ); + err = sock->ops->recvmsg (NULL , sock, &msg, + len,MSG_DONTWAIT |MSG_TRUNC ); +/* TODO: Check specific error and bomb out unless EAGAIN? */ +if (err <0 ) { +vhost_discard_vq_desc (vq); +break ; + } +/* TODO: Should check and handle checksum. */ +if (err > len) { +pr_err ("Discarded truncated rx packet: " +" len %d > %zd\n" , err, len); +vhost_discard_vq_desc (vq); +continue ; + } + len = err; + err =copy_to_user (vq->iov->iov_base, &hdr,sizeof hdr); +if (err) { +vq_err (vq,"Unable to write vnet_hdr at addr %p: %d\n" , + vq->iov->iov_base, err); +break ; + } +vhost_add_used_and_trigger (vq, head, len +sizeof hdr); + } + +mutex_unlock (&vq->mutex); +unuse_mm (net->dev.mm); +} + +static void handle_tx_kick (struct work_struct *work) +{ +struct vhost_virtqueue *vq; +struct vhost_net *net; + vq = container_of(work, struct vhost_virtqueue, poll.work); + net = container_of(vq->dev, struct vhost_net, dev); +handle_tx (net); +} + +static void handle_rx_kick (struct work_struct *work) +{ +struct vhost_virtqueue *vq; +struct vhost_net *net; + vq = container_of(work, struct vhost_virtqueue, poll.work); + net = container_of(vq->dev, struct vhost_net, dev); +handle_rx (net); +} + +static void handle_tx_net (struct work_struct *work) +{ +struct vhost_net *net; + net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_TX].work); +handle_tx (net); +} + +static void handle_rx_net (struct work_struct *work) +{ +struct vhost_net *net; + net = container_of(work, struct vhost_net, poll[VHOST_NET_VQ_RX].work); +handle_rx (net); +} + +int vhost_net_init (void ) +{ +int r =vhost_init (); +if (r) + goto err_init; + r =misc_register (&vhost_net_misc); +if (r) + goto err_reg; +return 0 ; +err_reg: +vhost_cleanup (); +err_init: +return r; + +} +module_init (vhost_net_init); + +void vhost_net_exit (void ) +{ +misc_deregister (&vhost_net_misc); +vhost_cleanup (); +} +module_exit (vhost_net_exit); + +MODULE_VERSION ("0.0.1" ); +MODULE_LICENSE ("GPL v2" ); +MODULE_AUTHOR ("Michael S. Tsirkin" ); +MODULE_DESCRIPTION ("Host kernel accelerator for virtio net" );drivers/vhost/vhost.c new file +663
-0@@ -0,0 +1,663 @@ +/* Copyright (C) 2009 Red Hat, Inc. +* Copyright (C) 2006 Rusty Russell IBM Corporation +* +* Author: Michael S. Tsirkin <[email protected]> +* +* Inspiration, some code, and most witty comments come from +* Documentation/lguest/lguest.c, by Rusty Russell +* +* This work is licensed under the terms of the GNU GPL, version 2. +* +* Generic code for virtio server in host kernel. +*/ + +#include <linux/eventfd.h> +#include <linux/vhost.h> +#include <linux/virtio_net.h> +#include <linux/mm.h> +#include <linux/miscdevice.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/rcupdate.h> +#include <linux/poll.h> +#include <linux/file.h> + +#include <linux/net.h> +#include <linux/if_packet.h> +#include <linux/if_arp.h> + +#include <net/sock.h> + +#include <asm/mmu_context.h> + +#include "vhost.h" + +enum { +VHOST_MEMORY_MAX_NREGIONS =64 , +}; + +struct workqueue_struct *vhost_workqueue; + +static void vhost_poll_func (struct file *file,wait_queue_head_t *wqh, +poll_table *pt) +{ +struct vhost_poll *poll; + poll = container_of(pt, struct vhost_poll, table); + + poll->wqh = wqh; +add_wait_queue (wqh, &poll->wait); +} + +static int vhost_poll_wakeup (wait_queue_t *wait,unsigned mode,int sync,void *key) +{ +struct vhost_poll *poll; + poll = container_of(wait, struct vhost_poll, wait); +if (!((unsigned long )key & poll->mask)) +return 0 ; + +queue_work (vhost_workqueue, &poll->work); +return 0 ; +} + +/* Init poll structure */ +void vhost_poll_init (struct vhost_poll *poll,work_func_t func, +unsigned long mask) +{ +INIT_WORK (&poll->work, func); +init_waitqueue_func_entry (&poll->wait, vhost_poll_wakeup); +init_poll_funcptr (&poll->table, vhost_poll_func); + poll->mask = mask; +} + +/* Start polling a file. We add ourselves to file's wait queue. The user must +* keep a reference to a file until after vhost_poll_stop is called. */ +void vhost_poll_start (struct vhost_poll *poll,struct file *file) +{ +unsigned long mask; + mask = file->f_op->poll (file, &poll->table); +if (mask) +vhost_poll_wakeup (&poll->wait,0 ,0 , (void *)mask); +} + +/* Stop polling a file. After this function returns, it becomes safe to drop the +* file reference. You must also flush afterwards. */ +void vhost_poll_stop (struct vhost_poll *poll) +{ +remove_wait_queue (poll->wqh, &poll->wait); +} + +/* Flush any work that has been scheduled. When calling this, don't hold any +* locks that are also used by the callback. */ +void vhost_poll_flush (struct vhost_poll *poll) +{ +flush_work (&poll->work); +} + +long vhost_dev_init (struct vhost_dev *dev,struct vhost_virtqueue *vqs,int nvqs) +{ +int i; + dev->vqs = vqs; + dev->nvqs = nvqs; +mutex_init (&dev->mutex); + +for (i =0 ; i < dev->nvqs; ++i) { + dev->vqs[i].dev = dev; +mutex_init (&dev->vqs[i].mutex); +if (dev->vqs[i].handle_kick) +vhost_poll_init (&dev->vqs[i].poll, + dev->vqs[i].handle_kick, +POLLIN ); + } +return 0 ; +} + +/* User should have device mutex */ +long vhost_dev_check_owner (struct vhost_dev *dev) +{ +return dev->mm == current->mm ?0 : -EPERM ; +} + +/* User should have device mutex */ +static long vhost_dev_set_owner (struct vhost_dev *dev) +{ +if (dev->mm) +return -EBUSY ; + dev->mm =get_task_mm (current); +return 0 ; +} + +/* User should have device mutex */ +long vhost_dev_reset_owner (struct vhost_dev *dev) +{ +struct vhost_memory *memory; + +/* Restore memory to default 1:1 mapping. */ + memory =kmalloc (offsetof(struct vhost_memory , regions) + +2 *sizeof *memory->regions,GFP_KERNEL ); +if (!memory) +return -ENOMEM ; + +vhost_dev_cleanup (dev); + + memory->nregions =2 ; + memory->regions[0 ].guest_phys_addr =1 ; + memory->regions[0 ].userspace_addr =1 ; + memory->regions[0 ].memory_size = ~0ULL ; + memory->regions[1 ].guest_phys_addr =0 ; + memory->regions[1 ].userspace_addr =0 ; + memory->regions[1 ].memory_size =1 ; + dev->memory = memory; +return 0 ; +} + +/* User should have device mutex */ +void vhost_dev_cleanup (struct vhost_dev *dev) +{ +int i; +for (i =0 ; i < dev->nvqs; ++i) { +if (dev->vqs[i].kick && dev->vqs[i].handle_kick) { +vhost_poll_stop (&dev->vqs[i].poll); +vhost_poll_flush (&dev->vqs[i].poll); + } +if (dev->vqs[i].error_ctx) +eventfd_ctx_put (dev->vqs[i].error_ctx); +if (dev->vqs[i].error) +fput (dev->vqs[i].error); +if (dev->vqs[i].kick) +fput (dev->vqs[i].kick); +if (dev->vqs[i].call_ctx) +eventfd_ctx_put (dev->vqs[i].call_ctx); +if (dev->vqs[i].call) +fput (dev->vqs[i].call); + dev->vqs[i].error_ctx =NULL ; + dev->vqs[i].error =NULL ; + dev->vqs[i].kick =NULL ; + dev->vqs[i].call_ctx =NULL ; + dev->vqs[i].call =NULL ; + } +/* No one will access memory at this point */ +kfree (dev->memory); + dev->memory =NULL ; +if (dev->mm) +mmput (dev->mm); + dev->mm =NULL ; +} + +static long vhost_set_memory (struct vhost_dev *d,struct vhost_memory __user *m) +{ +struct vhost_memory mem, *newmem, *oldmem; +unsigned long size = offsetof(struct vhost_memory , regions); +long r; + r =copy_from_user (&mem, m, size); +if (r) +return r; +if (mem.padding) +return -EOPNOTSUPP ; +if (mem.nregions >VHOST_MEMORY_MAX_NREGIONS ) +return -E2BIG ; + newmem =kmalloc (size + mem.nregions *sizeof *m->regions,GFP_KERNEL ); +if (!newmem) +return -ENOMEM ; + +memcpy (newmem, &mem, size); + r =copy_from_user (newmem->regions, m->regions, + mem.nregions *sizeof *m->regions); +if (r) { +kfree (newmem); +return r; + } + oldmem = d->memory; +rcu_assign_pointer (d->memory, newmem); +synchronize_rcu (); +kfree (oldmem); +return 0 ; +} + +static int init_used (struct vhost_virtqueue *vq) +{ +u16 flags =0 ; +int r =put_user (flags, &vq->used->flags); +if (r) +return r; +return get_user (vq->last_used_idx, &vq->used->idx); +} + +static long vhost_set_vring (struct vhost_dev *d,int ioctl,void __user *argp) +{ +struct file *eventfp, *filep =NULL , *pollstart =NULL , *pollstop =NULL ; +struct eventfd_ctx *ctx =NULL ; +u32 __user *idxp = argp; +struct vhost_virtqueue *vq; +struct vhost_vring_state s; +struct vhost_vring_file f; +struct vhost_vring_addr a; +u32 idx; +long r; + + r =get_user (idx, idxp); +if (r <0 ) +return r; +if (idx > d->nvqs) +return -ENOBUFS ; + + vq = d->vqs + idx; + +mutex_lock (&vq->mutex); + +switch (ioctl) { +case VHOST_SET_VRING_NUM : + r =copy_from_user (&s, argp,sizeof s); +if (r <0 ) +break ; +if (s.num >0xffff ) { + r = -EINVAL ; +break ; + } + vq->num = s.num; +break ; +case VHOST_SET_VRING_BASE : + r =copy_from_user (&s, argp,sizeof s); +if (r <0 ) +break ; +if (s.num >0xffff ) { + r = -EINVAL ; +break ; + } + vq->last_avail_idx = s.num; +break ; +case VHOST_GET_VRING_BASE : + s.index = idx; + s.num = vq->last_avail_idx; + r =copy_to_user (argp, &s,sizeof s); +break ; +case VHOST_SET_VRING_DESC : + r =copy_from_user (&a, argp,sizeof a); +if (r <0 ) +break ; +if (a.padding) { + r = -EOPNOTSUPP ; +break ; + } +if ((u64 )(long )a.user_addr != a.user_addr) { + r = -EFAULT ; +break ; + } + vq->desc = (void __user *)(long )a.user_addr; +break ; +case VHOST_SET_VRING_AVAIL : + r =copy_from_user (&a, argp,sizeof a); +if (r <0 ) +break ; +if (a.padding) { + r = -EOPNOTSUPP ; +break ; + } +if ((u64 )(long )a.user_addr != a.user_addr) { + r = -EFAULT ; +break ; + } + vq->avail = (void __user *)(long )a.user_addr; +break ; +case VHOST_SET_VRING_USED : + r =copy_from_user (&a, argp,sizeof a); +if (r <0 ) +break ; +if (a.padding) { + r = -EOPNOTSUPP ; +break ; + } +if ((u64 )(long )a.user_addr != a.user_addr) { + r = -EFAULT ; +break ; + } + vq->used = (void __user *)(long )a.user_addr; + r =init_used (vq); +if (r) +break ; +break ; +case VHOST_SET_VRING_KICK : + r =copy_from_user (&f, argp,sizeof f); +if (r <0 ) +break ; + eventfp = f.fd ==-1 ?NULL :eventfd_fget (f.fd); +if (IS_ERR (eventfp)) +return PTR_ERR (eventfp); +if (eventfp != vq->kick) { + pollstop = filep = vq->kick; + pollstart = vq->kick = eventfp; + }else + filep = eventfp; +break ; +case VHOST_SET_VRING_CALL : + r =copy_from_user (&f, argp,sizeof f); +if (r <0 ) +break ; + eventfp = f.fd ==-1 ?NULL :eventfd_fget (f.fd); +if (IS_ERR (eventfp)) +return PTR_ERR (eventfp); +if (eventfp != vq->call) { + filep = vq->call; + ctx = vq->call_ctx; + vq->call = eventfp; + vq->call_ctx = eventfp ? +eventfd_ctx_fileget (eventfp) :NULL ; + }else + filep = eventfp; +break ; +case VHOST_SET_VRING_ERR : + r =copy_from_user (&f, argp,sizeof f); +if (r <0 ) +break ; + eventfp = f.fd ==-1 ?NULL :eventfd_fget (f.fd); +if (IS_ERR (eventfp)) +return PTR_ERR (eventfp); +if (eventfp != vq->error) { + filep = vq->error; + vq->error = eventfp; + ctx = vq->error_ctx; + vq->error_ctx = eventfp ? +eventfd_ctx_fileget (eventfp) :NULL ; + }else + filep = eventfp; +break ; +default : + r = -ENOTTY ; + } + +if (pollstop && vq->handle_kick) +vhost_poll_stop (&vq->poll); + +if (ctx) +eventfd_ctx_put (ctx); +if (filep) +fput (filep); + +if (pollstart && vq->handle_kick) +vhost_poll_start (&vq->poll, vq->kick); + +mutex_unlock (&vq->mutex); + +if (pollstop && vq->handle_kick) +vhost_poll_flush (&vq->poll); +return 0 ; +} + +long vhost_dev_ioctl (struct vhost_dev *d,unsigned int ioctl,unsigned long arg) +{ +void __user *argp = (void __user *)arg; +long r; + +mutex_lock (&d->mutex); +if (ioctl ==VHOST_SET_OWNER ) { + r =vhost_dev_set_owner (d); + goto done; + } + + r =vhost_dev_check_owner (d); +if (r) + goto done; + +switch (ioctl) { +case VHOST_SET_MEM_TABLE : + r =vhost_set_memory (d, argp); +break ; +default : + r =vhost_set_vring (d, ioctl, argp); +break ; + } +done: +mutex_unlock (&d->mutex); +return r; +} + +static const struct vhost_memory_region *find_region (struct vhost_memory *mem, +__u64 addr,__u32 len) +{ +struct vhost_memory_region *reg; +int i; +/* linear search is not brilliant, but we really have on the order of 6 +* regions in practice */ +for (i =0 ; i < mem->nregions; ++i) { + reg = mem->regions + i; +if (reg->guest_phys_addr <= addr && + reg->guest_phys_addr + reg->memory_size -1 >= addr) +return reg; + } +return NULL ; +} + +/* FIXME: this does not handle a region that spans multiple +* address/len pairs */ +int translate_desc (struct vhost_dev *dev,u64 addr,u32 len, +struct iovec iov[],int iov_count,int iov_size, +unsigned *num) +{ +const struct vhost_memory_region *reg; +struct vhost_memory *mem; +struct iovec *_iov; +u64 s =0 ; +int ret =0 ; + +rcu_read_lock (); + + mem =rcu_dereference (dev->memory); +while ((u64 )len > s) { +u64 size; +if (*num + iov_count >= iov_size) { + ret = -ENOBUFS ; +break ; + } + reg =find_region (mem, addr, len); +if (!reg) { + ret = -EFAULT ; +break ; + } + _iov = iov + iov_count + *num; + size = reg->memory_size - addr + reg->guest_phys_addr; + _iov->iov_len =min ((u64 )len, size); + _iov->iov_base = (void *) + (reg->userspace_addr + addr - reg->guest_phys_addr); + s += size; + addr += size; + ++*num; + } + +rcu_read_unlock (); +return ret; +} + +/* Each buffer in the virtqueues is actually a chain of descriptors. This +* function returns the next descriptor in the chain, or vq->vring.num if we're +* at the end. */ +static unsigned next_desc (struct vhost_virtqueue *vq,struct vring_desc *desc) +{ +unsigned int next; + +/* If this descriptor says it doesn't chain, we're done. */ +if (!(desc->flags &VRING_DESC_F_NEXT )) +return vq->num; + +/* Check they're not leading us off end of descriptors. */ + next = desc->next; +/* Make sure compiler knows to grab that: we don't want it changing! */ +/* We will use the result as an index in an array, so most +* architectures only need a compiler barrier here. */ +read_barrier_depends (); + +if (next >= vq->num) { +vq_err (vq,"Desc next is %u > %u" , next, vq->num); +return vq->num; + } + +return next; +} + +/* This looks in the virtqueue and for the first available buffer, and converts +* it to an iovec for convenient access. Since descriptors consist of some +* number of output then some number of input descriptors, it's actually two +* iovecs, but we pack them into one and note how many of each there were. +* +* This function returns the descriptor number found, or vq->num (which +* is never a valid descriptor number) if none was found. */ +unsigned vhost_get_vq_desc (struct vhost_dev *dev,struct vhost_virtqueue *vq, +struct iovec iov[], +unsigned int *out_num,unsigned int *in_num) +{ +struct vring_desc desc; +unsigned int i, head; +u16 last_avail_idx, idx; + +/* Check it isn't doing very strange things with descriptor numbers. */ + last_avail_idx = vq->last_avail_idx; +if (get_user (idx, &vq->avail->idx)) { +vq_err (vq,"Failed to access avail idx at %p\n" , + &vq->avail->idx); +return vq->num; + } + +if ((u16)(idx - last_avail_idx) > vq->num) { +vq_err (vq,"Guest moved used index from %u to %u" , + last_avail_idx, idx); +return vq->num; + } + +/* If there's nothing new since last we looked, return invalid. */ +if (idx == last_avail_idx) +return vq->num; + +/* Grab the next descriptor number they're advertising, and increment +* the index we've seen. */ +if (get_user (head, &vq->avail->ring[last_avail_idx % vq->num])) { +vq_err (vq,"Failed to read head: idx %d address %p\n" , + idx, &vq->avail->ring[last_avail_idx % vq->num]); +return vq->num; + } + +/* If their number is silly, that's a fatal mistake. */ +if (head >= vq->num) { +vq_err (vq,"Guest says index %u > %u is available" , + head, vq->num); +return vq->num; + } + + vq->last_avail_idx++; + +/* When we start there are none of either input nor output. */ + *out_num = *in_num =0 ; + + i = head; +do { +unsigned *num; +unsigned iov_count; +if (copy_from_user (&desc, vq->desc + i,sizeof desc)) { +vq_err (vq,"Failed to get descriptor: idx %d addr %p\n" , + i, vq->desc + i); +return vq->num; + } +/* If this is an input descriptor, increment that count. */ +if (desc.flags &VRING_DESC_F_WRITE ) { + num = in_num; + iov_count = *out_num; + }else { +/* If it's an output descriptor, they're all supposed +* to come before any input descriptors. */ +if (*in_num) { +vq_err (vq,"Descriptor has out after in: " +"idx %d\n" , i); +return vq->num; + } + num = out_num; + iov_count = *in_num; + } +if (translate_desc (dev, desc.addr, desc.len, iov, iov_count, +VHOST_NET_MAX_SG , num)) { +vq_err (vq,"Failed to translate descriptor: idx %d\n" , + i); +return vq->num; + } + +/* If we've got too many, that implies a descriptor loop. */ +if (*out_num + *in_num > vq->num) { +vq_err (vq,"Looped descriptor: idx %d\n" , i); +return vq->num; + } + }while ((i =next_desc (vq, &desc)) != vq->num); + + vq->inflight++; +return head; +} + +/* Reverse the effect of vhost_get_vq_desc. Useful for error handling. */ +void vhost_discard_vq_desc (struct vhost_virtqueue *vq) +{ + vq->last_avail_idx--; + vq->inflight--; +} + +/* After we've used one of their buffers, we tell them about it. We'll then +* want to send them an interrupt, using vq->call. */ +int vhost_add_used (struct vhost_virtqueue *vq, +unsigned int head,int len) +{ +struct vring_used_elem *used; + +/* The virtqueue contains a ring of used buffers. Get a pointer to the +* next entry in that used ring. */ + used = &vq->used->ring[vq->last_used_idx % vq->num]; +if (put_user (head, &used->id)) { +vq_err (vq,"Failed to write used id" ); +return -EFAULT ; + } +if (put_user (len, &used->len)) { +vq_err (vq,"Failed to write used len" ); +return -EFAULT ; + } +/* Make sure buffer is written before we update index. */ +wmb (); +if (put_user (vq->last_used_idx +1 , &vq->used->idx)) { +vq_err (vq,"Failed to increment used idx" ); +return -EFAULT ; + } + vq->last_used_idx++; + vq->inflight--; +return 0 ; +} + +/* This actually sends the interrupt for this virtqueue */ +void vhost_trigger_irq (struct vhost_virtqueue *vq) +{ +__u16 flags =0 ; +if (get_user (flags, &vq->avail->flags)) { +vq_err (vq,"Failed to get flags" ); +return ; + } + +/* If they don't want an interrupt, don't send one, unless empty. */ +if ((flags &VRING_AVAIL_F_NO_INTERRUPT ) && vq->inflight) +return ; + +/* Send the Guest an interrupt tell them we used something up. */ +if (vq->call_ctx) +eventfd_signal (vq->call_ctx,1 ); +} + +/* And here's the combo meal deal. Supersize me! */ +void vhost_add_used_and_trigger (struct vhost_virtqueue *vq, +unsigned int head,int len) +{ +vhost_add_used (vq, head, len); +vhost_trigger_irq (vq); +} + +int vhost_init (void ) +{ + vhost_workqueue =create_workqueue ("vhost" ); +if (!vhost_workqueue) +return -ENOMEM ; +return 0 ; +} + +void vhost_cleanup (void ) +{ +destroy_workqueue (vhost_workqueue); +}drivers/vhost/vhost.h new file +108
-0@@ -0,0 +1,108 @@ +#ifndef _VHOST_H +#define _VHOST_H + +#include <linux/eventfd.h> +#include <linux/vhost.h> +#include <linux/mm.h> +#include <linux/mutex.h> +#include <linux/workqueue.h> +#include <linux/poll.h> +#include <linux/file.h> +#include <linux/skbuff.h> + +struct vhost_device ; + +enum { +VHOST_NET_MAX_SG =MAX_SKB_FRAGS +2 , +}; + +/* Poll a file (eventfd or socket) */ +/* Note: there's nothing vhost specific about this structure. */ +struct vhost_poll { +poll_table table; +wait_queue_head_t *wqh; +wait_queue_t wait; +/* struct which will handle all actual work. */ +struct work_struct work; +unsigned long mask; +}; + +void vhost_poll_init (struct vhost_poll *poll,work_func_t func, +unsigned long mask); +void vhost_poll_start (struct vhost_poll *poll,struct file *file); +void vhost_poll_stop (struct vhost_poll *poll); +void vhost_poll_flush (struct vhost_poll *poll); + +/* The virtqueue structure describes a queue attached to a device. */ +struct vhost_virtqueue { +struct vhost_dev *dev; + +/* The actual ring of buffers. */ +struct mutex mutex; +unsigned int num; +struct vring_desc __user *desc; +struct vring_avail __user *avail; +struct vring_used __user *used; +struct file *kick; +struct file *call; +struct file *error; +struct eventfd_ctx *call_ctx; +struct eventfd_ctx *error_ctx; + +struct vhost_poll poll; + +/* The routine to call when the Guest pings us, or timeout. */ +work_func_t handle_kick; + +/* Last available index we saw. */ +u16 last_avail_idx; + +/* Last index we used. */ +u16 last_used_idx; + +/* Outstanding buffers */ +unsigned int inflight; + +/* Is this blocked? */ +bool blocked; + +struct iovec iov[VHOST_NET_MAX_SG ]; + +}____cacheline_aligned ; + +struct vhost_dev { +/* Readers use RCU to access memory table pointer. +* Writers use mutex below.*/ +struct vhost_memory *memory; +struct mm_struct *mm; +struct vhost_virtqueue *vqs; +int nvqs; +struct mutex mutex; +}; + +long vhost_dev_init (struct vhost_dev *,struct vhost_virtqueue *vqs,int nvqs); +long vhost_dev_check_owner (struct vhost_dev *); +long vhost_dev_reset_owner (struct vhost_dev *); +void vhost_dev_cleanup (struct vhost_dev *); +long vhost_dev_ioctl (struct vhost_dev *,unsigned int ioctl,unsigned long arg); + +unsigned vhost_get_vq_desc (struct vhost_dev *,struct vhost_virtqueue *, +struct iovec iov[], +unsigned int *out_num,unsigned int *in_num); +void vhost_discard_vq_desc (struct vhost_virtqueue *); + +int vhost_add_used (struct vhost_virtqueue *,unsigned int head,int len); +void vhost_trigger_irq (struct vhost_virtqueue *); +void vhost_add_used_and_trigger (struct vhost_virtqueue *, +unsigned int head,int len); + +int vhost_init (void ); +void vhost_cleanup (void ); + +#define vq_err (vq, fmt, ...) do { \ + printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__); \ + if ((vq)->error_ctx) \ + eventfd_signal((vq)->error_ctx, 1);\ + } while (0) + +#endif include/linux/Kbuild +1
-0@@ -360,6 +360,7 @@ unifdef-y += uio.h unifdef-y += unistd.h unifdef-y += usbdevice_fs.h unifdef-y += utsname.h +unifdef-y += vhost.h unifdef-y += videodev2.h unifdef-y += videodev.h unifdef-y += virtio_config.hinclude/linux/miscdevice.h +1
-0@@ -30,6 +30,7 @@#define HPET_MINOR 228#define FUSE_MINOR 229#define KVM_MINOR 232 +#define VHOST_NET_MINOR 233#define MISC_DYNAMIC_MINOR 255struct device ;include/linux/vhost.h new file +100
-0@@ -0,0 +1,100 @@ +#ifndef _LINUX_VHOST_H +#define _LINUX_VHOST_H +/* Userspace interface for in-kernel virtio accelerators. */ + +/* vhost is used to reduce the number of system calls involved in virtio. +* +* Existing virtio net code is used in the guest without modification. +* +* This header includes interface used by userspace hypervisor for +* device configuration. +*/ + +#include <linux/types.h> +#include <linux/compiler.h> +#include <linux/ioctl.h> +#include <linux/virtio_config.h> +#include <linux/virtio_ring.h> + +struct vhost_vring_state { +unsigned int index; +unsigned int num; +}; + +struct vhost_vring_file { +unsigned int index; +int fd; +}; + +struct vhost_vring_addr { +unsigned int index; +unsigned int padding; +__u64 user_addr; +}; + +struct vhost_memory_region { +__u64 guest_phys_addr; +__u64 memory_size;/* bytes */ +__u64 userspace_addr; +__u64 padding;/* read/write protection? */ +}; + +struct vhost_memory { +__u32 nregions; +__u32 padding; +struct vhost_memory_region regions[0 ]; +}; + +/* ioctls */ + +#define VHOST_VIRTIO 0xAF + +/* Features bitmask for forward compatibility. Transport bits must be zero. */ +#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u32) +#define VHOST_ACK_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u32) + +/* Set current process as the (exclusive) owner of this file descriptor. This +* must be called before any other vhost command. Further calls to +* VHOST_OWNER_SET fail until VHOST_OWNER_RESET is called. */ +#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01) +/* Give up ownership, and reset the device to default values. +* Allows subsequent call to VHOST_OWNER_SET to succeed. */ +#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02) + +/* Set up/modify memory layout */ +#define VHOST_SET_MEM_TABLE _IOW(VHOST_VIRTIO, 0x03, struct vhost_memory) + +/* Ring setup. These parameters can not be modified while ring is running +* (bound to a device). */ +/* Set number of descriptors in ring */ +#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state) +/* Start of array of descriptors (virtually contiguous) */ +#define VHOST_SET_VRING_DESC _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr) +/* Used structure address */ +#define VHOST_SET_VRING_USED _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_addr) +/* Available structure address */ +#define VHOST_SET_VRING_AVAIL _IOW(VHOST_VIRTIO, 0x13, struct vhost_vring_addr) +/* Base value where queue looks for available descriptors */ +#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x14, struct vhost_vring_state) +/* Get accessor: reads index, writes value in num */ +#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x14, struct vhost_vring_state) + +/* The following ioctls use eventfd file descriptors to signal and poll +* for events. */ + +/* Set eventfd to poll for added buffers */ +#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file) +/* Set eventfd to signal when buffers have beed used */ +#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file) +/* Set eventfd to signal an error */ +#define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file) + +/* VHOST_NET specific defines */ + +/* Attach virtio net device to a raw socket. The socket must be already +* bound to an ethernet device, this device will be used for transmit. +* Pass -1 to unbind from the socket and the transmit device. +* This can be used to stop the device (e.g. for migration). */ +#define VHOST_NET_SET_SOCKET _IOW(VHOST_VIRTIO, 0x30, int) + +#endif Signature · 6 lines
1.6.2.5 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Michael S. Tsirkin
maintainerPATCH 2/2 vhost_net: a kernel-level virtio server
In reply to a message not in the archiveWhat it is: vhost net is a character device that can be used to reduce the number of system calls involved in virtio networking. Existing virtio net code is used in the guest without modification.
There's similarity with vringfd, with some differences and reduced scope
- uses eventfd for signalling
- structures can be moved around in memory at any time (good for migration)
- support memory table and not just an offset (needed for kvm)
common virtio related code has been put in a separate file vhost.c and can be made into a separate module if/when more backend appear. I used Rusty's lguest.c as the source for developing this part : this supplied me with witty comments I wouldn't be able to write myself.
What it is not: vhost net is not a bus, and not a generic new system call. No assumptions are made on how guest performs hypercalls. Userspace hypervisors are supported as well as kvm.
How it works: Basically, we connect virtio frontend (configured by userspace) to a backend. The backend could be a network device, or a tun-like device. In this version I only support raw socket as a backend, which can be bound to e.g. SR IOV, or to macvlan device. Backend is also configured by userspace, including vlan/mac etc.
Status:
This works for me, and I haven't see any crashes.
I have not run any benchmarks yet, compared to userspace, I expect to see improved latency (as I save up to 4 system calls per packet) but not yet bandwidth/CPU (as TSO and interrupt mitigation are not yet supported).
Features that I plan to look at in the future:
- TSO
- interrupt mitigation
- zero copy
--- MAINTAINERS | 10 + arch/x86/kvm/Kconfig | 1 + drivers/Makefile | 1 + drivers/block/virtio_blk.c | 3 + drivers/vhost/Kconfig | 11 + drivers/vhost/Makefile | 2 + drivers/vhost/net.c | 462 ++++++++++++++++++++++++++++++ drivers/vhost/vhost.c | 663 ++++++++++++++++++++++++++++++++++++++++++++ drivers/vhost/vhost.h | 108 +++++++ include/linux/Kbuild | 1 + include/linux/miscdevice.h | 1 + include/linux/vhost.h | 100 +++++++ 12 files changed, 1363 insertions(+), 0 deletions(-) create mode 100644 drivers/vhost/Kconfig create mode 100644 drivers/vhost/Makefile create mode 100644 drivers/vhost/net.c create mode 100644 drivers/vhost/vhost.c create mode 100644 drivers/vhost/vhost.h create mode 100644 include/linux/vhost.h
MAINTAINERS +10 -0
@@ -6312,6 +6312,16 @@ S: Maintained F: Documentation/filesystems/vfat.txt F: fs/fat/ +VIRTIO HOST (VHOST) +P: Michael S. Tsirkin +M: [email protected] +L: [email protected] +L: [email protected] +L: [email protected] +S: Maintained +F: drivers/vhost/ +F: include/linux/vhost.h + VIA RHINE NETWORK DRIVER P: Roger Luethi M: [email protected]
arch/x86/kvm/Kconfig +1 -0
@@ -64,6 +64,7 @@ config KVM_AMD # OK, it's a little counter-intuitive to do this, but it puts it neatly under # the virtualization menu. +source drivers/vhost/Kconfig source drivers/lguest/Kconfig source drivers/virtio/Kconfig
drivers/Makefile +1 -0
@@ -105,6 +105,7 @@ obj-$(CONFIG_HID) += hid/ obj-$(CONFIG_PPC_PS3) += ps3/ obj-$(CONFIG_OF) += of/ obj-$(CONFIG_SSB) += ssb/ +obj-$(CONFIG_VHOST_NET) += vhost/ obj-$(CONFIG_VIRTIO) += virtio/ obj-$(CONFIG_VLYNQ) += vlynq/ obj-$(CONFIG_STAGING) += staging/
drivers/block/virtio_blk.c +3 -0
@@ -293,6 +293,7 @@ static int __devinit virtblk_probe(struct virtio_device *vdev) err =PTR_ERR (vblk->vq); goto out_free_vblk; } +printk (KERN_ERR "vblk->vq = %p\n" , vblk->vq); vblk->pool =mempool_create_kmalloc_pool (1 ,sizeof (struct virtblk_req ));if (!vblk->pool) {
@@ -383,6 +384,8 @@ static int __devinit virtblk_probe(struct virtio_device *vdev)if (!err)blk_queue_logical_block_size (vblk->disk->queue, blk_size); +printk (KERN_ERR "virtio_config_val returned %d\n" , err); +add_disk (vblk->disk);return 0 ;
drivers/vhost/Kconfig new file +11 -0
@@ -0,0 +1,11 @@ +config VHOST_NET + tristate "Host kernel accelerator for virtio net" + depends on NET && EVENTFD + ---help--- + This kernel module can be loaded in host kernel to accelerate + guest networking with virtio_net. Not to be confused with virtio_net + module itself which needs to be loaded in guest kernel. + + To compile this driver as a module, choose M here: the module will + be called vhost_net. +
drivers/vhost/Makefile new file +2 -0
@@ -0,0 +1,2 @@ +obj-$(CONFIG_VHOST_NET) += vhost_net.o +vhost_net-y := vhost.o net.o
drivers/vhost/net.c new file +462 -0
462 lines not on this page · raw message
drivers/vhost/vhost.c new file +663 -0
663 lines not on this page · raw message
drivers/vhost/vhost.h new file +108 -0
108 lines not on this page · raw message
include/linux/Kbuild +1 -0
@@ -360,6 +360,7 @@ unifdef-y += uio.h unifdef-y += unistd.h unifdef-y += usbdevice_fs.h unifdef-y += utsname.h +unifdef-y += vhost.h unifdef-y += videodev2.h unifdef-y += videodev.h unifdef-y += virtio_config.h
include/linux/miscdevice.h +1 -0
@@ -30,6 +30,7 @@#define HPET_MINOR 228#define FUSE_MINOR 229#define KVM_MINOR 232 +#define VHOST_NET_MINOR 233#define MISC_DYNAMIC_MINOR 255struct device ;
include/linux/vhost.h new file +100 -0
100 lines not on this page · raw message
Signature · 1 line
1.6.2.5