tun: export underlying socket
Subsystems (10)
A message that is not in the archive (sent off-list or not yet received): [email protected]
Michael S. Tsirkin
maintainerPATCH v8 1/3 tun: export underlying socket
Tun device looks similar to a packet socket in that both pass complete frames from/to userspace.
This patch fills in enough fields in the socket underlying tun driver to support sendmsg/recvmsg operations, and message flags MSG_TRUNC and MSG_DONTWAIT, and exports access to this socket to modules. Regular read/write behaviour is unchanged.
This way, code using raw sockets to inject packets into a physical device, can support injecting packets into host network stack almost without modification.
First user of this interface will be vhost virtualization accelerator.
---
DaveM agreed that this will go in through Rusty's tree together with vhost patch, because vhost is the first user of the new interface.
drivers/net/tun.c | 101 +++++++++++++++++++++++++++++++++++++++--------- include/linux/if_tun.h | 14 +++++++ 2 files changed, 96 insertions(+), 19 deletions(-)
drivers/net/tun.c +82 -19
@@ -144,6 +144,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file) err =0 ; tfile->tun = tun; tun->tfile = tfile; + tun->socket.file = file;dev_hold (tun->dev);sock_hold (tun->socket.sk);atomic_inc (&tfile->count);
@@ -158,6 +159,7 @@ static void __tun_detach(struct tun_struct *tun)/* Detach from net device */ netif_tx_lock_bh (tun->dev); tun->tfile =NULL ; + tun->socket.file =NULL ;netif_tx_unlock_bh (tun->dev);/* Drop read queue */
@@ -387,7 +389,8 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)/* Notify and wake up reader process */ if (tun->flags &TUN_FASYNC )kill_fasync (&tun->fasync,SIGIO ,POLL_IN ); -wake_up_interruptible (&tun->socket.wait); +wake_up_interruptible_poll (&tun->socket.wait,POLLIN | +POLLRDNORM |POLLRDBAND );return NETDEV_TX_OK ; drop:
@@ -743,7 +746,7 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun, len =min_t (int, skb->len, len);skb_copy_datagram_const_iovec (skb,0 , iv, total, len); - total += len; + total += skb->len; tun->dev->stats.tx_packets++; tun->dev->stats.tx_bytes += len;
@@ -751,34 +754,23 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,return total; } -static ssize_t tun_chr_aio_read (struct kiocb *iocb,const struct iovec *iv, -unsigned long count,loff_t pos) +static ssize_t tun_do_read (struct tun_struct *tun, +struct kiocb *iocb,const struct iovec *iv, +ssize_t len,int noblock) { -struct file *file = iocb->ki_filp; -struct tun_file *tfile = file->private_data; -struct tun_struct *tun =__tun_get (tfile);DECLARE_WAITQUEUE (wait, current);struct sk_buff *skb; -ssize_t len, ret =0 ; - -if (!tun) -return -EBADFD ; +ssize_t ret =0 ;DBG (KERN_INFO "%s: tun_chr_read\n" , tun->dev->name); - len =iov_length (iv, count); -if (len <0 ) { - ret = -EINVAL ; - goto out; - } -add_wait_queue (&tun->socket.wait, &wait);while (len) { current->state =TASK_INTERRUPTIBLE ;/* Read frames from the queue */ if (!(skb=skb_dequeue (&tun->socket.sk->sk_receive_queue))) { -if (file->f_flags &O_NONBLOCK ) { +if (noblock) { ret = -EAGAIN ;break ; }
@@ -805,6 +797,27 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv, current->state =TASK_RUNNING ;remove_wait_queue (&tun->socket.wait, &wait); +return ret; +} + +static ssize_t tun_chr_aio_read (struct kiocb *iocb,const struct iovec *iv, +unsigned long count,loff_t pos) +{ +struct file *file = iocb->ki_filp; +struct tun_file *tfile = file->private_data; +struct tun_struct *tun =__tun_get (tfile); +ssize_t len, ret; + +if (!tun) +return -EBADFD ; + len =iov_length (iv, count); +if (len <0 ) { + ret = -EINVAL ; + goto out; + } + + ret =tun_do_read (tun, iocb, iv, len, file->f_flags &O_NONBLOCK ); + ret =min_t (ssize_t, ret, len); out:tun_put (tun);return ret;
@@ -847,7 +860,8 @@ static void tun_sock_write_space(struct sock *sk)return ;if (sk->sk_sleep &&waitqueue_active (sk->sk_sleep)) -wake_up_interruptible_sync (sk->sk_sleep); +wake_up_interruptible_sync_poll (sk->sk_sleep,POLLOUT | +POLLWRNORM |POLLWRBAND ); tun = container_of(sk, struct tun_sock, sk)->tun;kill_fasync (&tun->fasync,SIGIO ,POLL_OUT );
@@ -858,6 +872,37 @@ static void tun_sock_destruct(struct sock *sk) free_netdev(container_of(sk, struct tun_sock, sk)->tun->dev); } +static int tun_sendmsg (struct kiocb *iocb,struct socket *sock, +struct msghdr *m,size_t total_len) +{ + struct tun_struct *tun = container_of(sock, struct tun_struct, socket); +return tun_get_user (tun, m->msg_iov, total_len, + m->msg_flags &MSG_DONTWAIT ); +} + +static int tun_recvmsg (struct kiocb *iocb,struct socket *sock, +struct msghdr *m,size_t total_len, +int flags) +{ + struct tun_struct *tun = container_of(sock, struct tun_struct, socket); +int ret; +if (flags & ~(MSG_DONTWAIT |MSG_TRUNC )) +return -EINVAL ; + ret =tun_do_read (tun, iocb, m->msg_iov, total_len, + flags &MSG_DONTWAIT ); +if (ret > total_len) { + m->msg_flags |=MSG_TRUNC ; + ret = flags &MSG_TRUNC ? ret : total_len; + } +return ret; +} + +/* Ops structure to mimic raw sockets with tun */ +static const struct proto_ops tun_socket_ops = { + .sendmsg = tun_sendmsg, + .recvmsg = tun_recvmsg, +}; +static struct proto tun_proto = { .name ="tun" , .owner =THIS_MODULE ,
@@ -986,6 +1031,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) goto err_free_dev;init_waitqueue_head (&tun->socket.wait); + tun->socket.ops = &tun_socket_ops;sock_init_data (&tun->socket, sk); sk->sk_write_space = tun_sock_write_space; sk->sk_sndbuf =INT_MAX ;
@@ -1489,6 +1535,23 @@ static void tun_cleanup(void)rtnl_link_unregister (&tun_link_ops); } +/* Get an underlying socket object from tun file. Returns error unless file is +* attached to a device. The returned object works like a packet socket, it +* can be used for sock_sendmsg/sock_recvmsg. The caller is responsible for +* holding a reference to the file for as long as the socket is in use. */ +struct socket *tun_get_socket (struct file *file) +{ +struct tun_struct *tun; +if (file->f_op != &tun_fops) +return ERR_PTR (-EINVAL ); + tun =tun_get (file); +if (!tun) +return ERR_PTR (-EBADFD ); +tun_put (tun); +return &tun->socket; +} +EXPORT_SYMBOL_GPL (tun_get_socket); +module_init (tun_init);module_exit (tun_cleanup);MODULE_DESCRIPTION (DRV_DESCRIPTION );
include/linux/if_tun.h +14 -0
@@ -86,4 +86,18 @@ struct tun_filter { __u8 addr[0][ETH_ALEN]; }; +#ifdef __KERNEL__ +#if defined(CONFIG_TUN ) || defined(CONFIG_TUN_MODULE ) +struct socket *tun_get_socket (struct file *); +#else +#include <linux/err.h> +#include <linux/errno.h> +struct file ; +struct socket ; +static inline struct socket *tun_get_socket (struct file *f) +{ +return ERR_PTR (-EINVAL ); +} +#endif /* CONFIG_TUN */ +#endif /* __KERNEL__ */ #endif /* __IF_TUN_H */
Signature · 7 lines
1.6.5.2.143.g8cc62 -- 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
maintainerPATCH v8 1/3 tun: export underlying socket
In reply to a message not in the archiveTun device looks similar to a packet socket in that both pass complete frames from/to userspace.
This patch fills in enough fields in the socket underlying tun driver to support sendmsg/recvmsg operations, and message flags MSG_TRUNC and MSG_DONTWAIT, and exports access to this socket to modules. Regular read/write behaviour is unchanged.
This way, code using raw sockets to inject packets into a physical device, can support injecting packets into host network stack almost without modification.
First user of this interface will be vhost virtualization accelerator.
---
DaveM agreed that this will go in through Rusty's tree together with vhost patch, because vhost is the first user of the new interface.
drivers/net/tun.c | 101 +++++++++++++++++++++++++++++++++++++++--------- include/linux/if_tun.h | 14 +++++++ 2 files changed, 96 insertions(+), 19 deletions(-)
drivers/net/tun.c +82 -19
@@ -144,6 +144,7 @@ static int tun_attach(struct tun_struct *tun, struct file *file) err =0 ; tfile->tun = tun; tun->tfile = tfile; + tun->socket.file = file;dev_hold (tun->dev);sock_hold (tun->socket.sk);atomic_inc (&tfile->count);
@@ -158,6 +159,7 @@ static void __tun_detach(struct tun_struct *tun)/* Detach from net device */ netif_tx_lock_bh (tun->dev); tun->tfile =NULL ; + tun->socket.file =NULL ;netif_tx_unlock_bh (tun->dev);/* Drop read queue */
@@ -387,7 +389,8 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev)/* Notify and wake up reader process */ if (tun->flags &TUN_FASYNC )kill_fasync (&tun->fasync,SIGIO ,POLL_IN ); -wake_up_interruptible (&tun->socket.wait); +wake_up_interruptible_poll (&tun->socket.wait,POLLIN | +POLLRDNORM |POLLRDBAND );return NETDEV_TX_OK ; drop:
@@ -743,7 +746,7 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun, len =min_t (int, skb->len, len);skb_copy_datagram_const_iovec (skb,0 , iv, total, len); - total += len; + total += skb->len; tun->dev->stats.tx_packets++; tun->dev->stats.tx_bytes += len;
@@ -751,34 +754,23 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,return total; } -static ssize_t tun_chr_aio_read (struct kiocb *iocb,const struct iovec *iv, -unsigned long count,loff_t pos) +static ssize_t tun_do_read (struct tun_struct *tun, +struct kiocb *iocb,const struct iovec *iv, +ssize_t len,int noblock) { -struct file *file = iocb->ki_filp; -struct tun_file *tfile = file->private_data; -struct tun_struct *tun =__tun_get (tfile);DECLARE_WAITQUEUE (wait, current);struct sk_buff *skb; -ssize_t len, ret =0 ; - -if (!tun) -return -EBADFD ; +ssize_t ret =0 ;DBG (KERN_INFO "%s: tun_chr_read\n" , tun->dev->name); - len =iov_length (iv, count); -if (len <0 ) { - ret = -EINVAL ; - goto out; - } -add_wait_queue (&tun->socket.wait, &wait);while (len) { current->state =TASK_INTERRUPTIBLE ;/* Read frames from the queue */ if (!(skb=skb_dequeue (&tun->socket.sk->sk_receive_queue))) { -if (file->f_flags &O_NONBLOCK ) { +if (noblock) { ret = -EAGAIN ;break ; }
@@ -805,6 +797,27 @@ static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv, current->state =TASK_RUNNING ;remove_wait_queue (&tun->socket.wait, &wait); +return ret; +} + +static ssize_t tun_chr_aio_read (struct kiocb *iocb,const struct iovec *iv, +unsigned long count,loff_t pos) +{ +struct file *file = iocb->ki_filp; +struct tun_file *tfile = file->private_data; +struct tun_struct *tun =__tun_get (tfile); +ssize_t len, ret; + +if (!tun) +return -EBADFD ; + len =iov_length (iv, count); +if (len <0 ) { + ret = -EINVAL ; + goto out; + } + + ret =tun_do_read (tun, iocb, iv, len, file->f_flags &O_NONBLOCK ); + ret =min_t (ssize_t, ret, len); out:tun_put (tun);return ret;
@@ -847,7 +860,8 @@ static void tun_sock_write_space(struct sock *sk)return ;if (sk->sk_sleep &&waitqueue_active (sk->sk_sleep)) -wake_up_interruptible_sync (sk->sk_sleep); +wake_up_interruptible_sync_poll (sk->sk_sleep,POLLOUT | +POLLWRNORM |POLLWRBAND ); tun = container_of(sk, struct tun_sock, sk)->tun;kill_fasync (&tun->fasync,SIGIO ,POLL_OUT );
@@ -858,6 +872,37 @@ static void tun_sock_destruct(struct sock *sk) free_netdev(container_of(sk, struct tun_sock, sk)->tun->dev); } +static int tun_sendmsg (struct kiocb *iocb,struct socket *sock, +struct msghdr *m,size_t total_len) +{ + struct tun_struct *tun = container_of(sock, struct tun_struct, socket); +return tun_get_user (tun, m->msg_iov, total_len, + m->msg_flags &MSG_DONTWAIT ); +} + +static int tun_recvmsg (struct kiocb *iocb,struct socket *sock, +struct msghdr *m,size_t total_len, +int flags) +{ + struct tun_struct *tun = container_of(sock, struct tun_struct, socket); +int ret; +if (flags & ~(MSG_DONTWAIT |MSG_TRUNC )) +return -EINVAL ; + ret =tun_do_read (tun, iocb, m->msg_iov, total_len, + flags &MSG_DONTWAIT ); +if (ret > total_len) { + m->msg_flags |=MSG_TRUNC ; + ret = flags &MSG_TRUNC ? ret : total_len; + } +return ret; +} + +/* Ops structure to mimic raw sockets with tun */ +static const struct proto_ops tun_socket_ops = { + .sendmsg = tun_sendmsg, + .recvmsg = tun_recvmsg, +}; +static struct proto tun_proto = { .name ="tun" , .owner =THIS_MODULE ,
@@ -986,6 +1031,7 @@ static int tun_set_iff(struct net *net, struct file *file, struct ifreq *ifr) goto err_free_dev;init_waitqueue_head (&tun->socket.wait); + tun->socket.ops = &tun_socket_ops;sock_init_data (&tun->socket, sk); sk->sk_write_space = tun_sock_write_space; sk->sk_sndbuf =INT_MAX ;
@@ -1489,6 +1535,23 @@ static void tun_cleanup(void)rtnl_link_unregister (&tun_link_ops); } +/* Get an underlying socket object from tun file. Returns error unless file is +* attached to a device. The returned object works like a packet socket, it +* can be used for sock_sendmsg/sock_recvmsg. The caller is responsible for +* holding a reference to the file for as long as the socket is in use. */ +struct socket *tun_get_socket (struct file *file) +{ +struct tun_struct *tun; +if (file->f_op != &tun_fops) +return ERR_PTR (-EINVAL ); + tun =tun_get (file); +if (!tun) +return ERR_PTR (-EBADFD ); +tun_put (tun); +return &tun->socket; +} +EXPORT_SYMBOL_GPL (tun_get_socket); +module_init (tun_init);module_exit (tun_cleanup);MODULE_DESCRIPTION (DRV_DESCRIPTION );
include/linux/if_tun.h +14 -0
@@ -86,4 +86,18 @@ struct tun_filter { __u8 addr[0][ETH_ALEN]; }; +#ifdef __KERNEL__ +#if defined(CONFIG_TUN ) || defined(CONFIG_TUN_MODULE ) +struct socket *tun_get_socket (struct file *); +#else +#include <linux/err.h> +#include <linux/errno.h> +struct file ; +struct socket ; +static inline struct socket *tun_get_socket (struct file *f) +{ +return ERR_PTR (-EINVAL ); +} +#endif /* CONFIG_TUN */ +#endif /* __KERNEL__ */ #endif /* __IF_TUN_H */
Signature · 1 line
1.6.5.2.143.g8cc62
Michael S. Tsirkin
maintainerPATCH v8 2/3 mm: export use_mm/unuse_mm 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 use_mm. Export it to modules.
--- mm/mmu_context.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
mm/mmu_context.c +3 -0
@@ -5,6 +5,7 @@#include <linux/mm.h> #include <linux/mmu_context.h> +#include <linux/module.h> #include <linux/sched.h> #include <asm/mmu_context.h>
@@ -37,6 +38,7 @@ void use_mm(struct mm_struct *mm)if (active_mm != mm)mmdrop (active_mm); } +EXPORT_SYMBOL_GPL (use_mm);/* * unuse_mm
@@ -56,3 +58,4 @@ void unuse_mm(struct mm_struct *mm)enter_lazy_tlb (mm, tsk);task_unlock (tsk); } +EXPORT_SYMBOL_GPL (unuse_mm);
Signature · 7 lines
1.6.5.2.143.g8cc62 -- 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
maintainerPATCH v8 2/3 mm: export use_mm/unuse_mm 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 use_mm. Export it to modules.
--- mm/mmu_context.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
mm/mmu_context.c +3 -0
@@ -5,6 +5,7 @@#include <linux/mm.h> #include <linux/mmu_context.h> +#include <linux/module.h> #include <linux/sched.h> #include <asm/mmu_context.h>
@@ -37,6 +38,7 @@ void use_mm(struct mm_struct *mm)if (active_mm != mm)mmdrop (active_mm); } +EXPORT_SYMBOL_GPL (use_mm);/* * unuse_mm
@@ -56,3 +58,4 @@ void unuse_mm(struct mm_struct *mm)enter_lazy_tlb (mm, tsk);task_unlock (tsk); } +EXPORT_SYMBOL_GPL (unuse_mm);
Signature · 1 line
1.6.5.2.143.g8cc62
Michael S. Tsirkin
maintainerPATCH v8 3/3 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, bug work-arounds in userspace)
- write logging is supported (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 backends 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 tap device. Backend is also configured by userspace, including vlan/mac etc.
Status: This works for me, and I haven't see any crashes. Compared to userspace, people reported improved latency (as I save up to 4 system calls per packet), as well as better bandwidth and CPU utilization.
Features that I plan to look at in the future:
- mergeable buffers
- zero copy
- scalability tuning: figure out the best threading model to use
Note on RCU usage (this is also documented in vhost.h, near private_pointer which is the value protected by this variant of RCU):
what is happening is that the rcu_dereference() is being used in a workqueue item. The role of rcu_read_lock() is taken on by the start of execution of the workqueue item, of rcu_read_unlock() by the end of execution of the workqueue item, and of synchronize_rcu() by flush_workqueue()/flush_work().
---
Paul, I'm Cc you so that you can verify that nothing significant changed in vhost/net.c since you approved it the previous time. Could you please Ack use of rcu_dereference/rcu_assign_pointer there? Eric Dumazet proposed that I open-code these, he suggested that this is an abuse of these macros and not enough like RCU. What is your opinion?
MAINTAINERS | 9 + arch/x86/kvm/Kconfig | 1 + drivers/Makefile | 1 + drivers/vhost/Kconfig | 11 + drivers/vhost/Makefile | 2 + drivers/vhost/net.c | 633 +++++++++++++++++++++++++++++ drivers/vhost/vhost.c | 970 ++++++++++++++++++++++++++++++++++++++++++++ drivers/vhost/vhost.h | 158 +++++++ include/linux/Kbuild | 1 + include/linux/miscdevice.h | 1 + include/linux/vhost.h | 126 ++++++ 11 files changed, 1913 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 +9 -0
@@ -5619,6 +5619,15 @@ S: Maintained F: Documentation/filesystems/vfat.txt F: fs/fat/ +VIRTIO HOST (VHOST) +M: "Michael S. Tsirkin" <[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 M: Roger Luethi <[email protected]> S: Maintained
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
@@ -106,6 +106,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/vhost/Kconfig new file +11 -0
@@ -0,0 +1,11 @@ +config VHOST_NET + tristate "Host kernel accelerator for virtio net (EXPERIMENTAL)" + depends on NET && EVENTFD && EXPERIMENTAL + ---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 +633 -0
633 lines not on this page · raw message
drivers/vhost/vhost.c new file +970 -0
970 lines not on this page · raw message
drivers/vhost/vhost.h new file +158 -0
158 lines not on this page · raw message
include/linux/Kbuild +1 -0
@@ -361,6 +361,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 +126 -0
126 lines not on this page · raw message
Signature · 7 lines
1.6.5.2.143.g8cc62 -- 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>
Re: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
Quoted 2 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.
Hi Michael,
Now everyone else has finally kicked all the tires and it seems to pass,
I've done a fairly complete review. Generally, it's really nice; just one bug and a few minor suggestions for polishing.
drivers/vhost/net.c line 80
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+/* Caller must have TX VQ lock */ +static void tx_poll_stop (struct vhost_net *net) +{ +if (likely (net->tx_poll_state !=VHOST_NET_POLL_STARTED )) +return ;
likely? Really?
drivers/vhost/net.c line 129
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+for (;;) { + head =vhost_get_vq_desc (&net->dev, vq, vq->iov, &out, &in, +NULL ,NULL );
Danger! You need an arg to vhost_get_vq_desc to tell it the max desc size you can handle. Otherwise, it's only limited by ring size, and a malicious guest can overflow you here, and below:
Quoted 2 lines
drivers/vhost/net.c new file +2
-0+/* Skip header. TODO: support TSO. */ + s =move_iovec_hdr (vq->iov, vq->hdr, hdr_size, out);
...
drivers/vhost/net.c line 121
Quoted 4 lines
drivers/vhost/net.c new file +4
-0+ +use_mm (net->dev.mm); +mutex_lock (&vq->mutex); +vhost_no_notify (vq);
I prefer a name like "vhost_disable_notify()".
drivers/vhost/net.c line 228
Quoted 10 lines
drivers/vhost/net.c new file +10
-0+/* OK, now we need to know about added descriptors. */ +if (head == vq->num &&vhost_notify (vq)) +/* They could have slipped one in as we were doing that: +* check again. */ +continue ; +/* Nothing new? Wait for eventfd to tell us they refilled. */ +if (head == vq->num) +break ; +/* We don't need to be notified again. */ +vhost_no_notify (vq);
Similarly, vhost_enable_notify. This one is particularly misleading since it doesn't actually notify anything!
In particular, this code would be neater as:
if (head == vq->num) {
if (vhost_enable_notify(vq)) {
/* Try again, they could have slipped one in. */
continue;
}
/* Nothing more to do. */
break;
}
vhost_disable_notify(vq);Now, AFAICT vhost_notify()/enable_notify() would be better rewritten to return true only when there's more pending. Saves a loop around here most of the time. Also, the vhost_no_notify/vhost_disable_notify() can be moved out of the loop entirely. (It could be under an if (unlikely(enabled)), not sure if it's worth it).
drivers/vhost/net.c line 261
Quoted 2 lines
drivers/vhost/net.c new file +2
-0+ len = err; + err =memcpy_toiovec (vq->hdr, (unsigned char *)&hdr, hdr_size);
That unsigned char * arg to memcpy_toiovec is annoying. A patch might be nice, separate from this effort.
drivers/vhost/net.c line 322
Quoted 9 lines
drivers/vhost/net.c new file +9
-0+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;
I have a personal dislike of calloc for structures. In userspace, it's because valgrind can't spot uninitialized fields. These days a similar argument applies in the kernel, because we have KMEMCHECK now. If someone adds a field to the struct and forgets to initialize it, we can spot it.
drivers/vhost/net.c line 348
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+static void vhost_net_enable_vq (struct vhost_net *n,int index) +{ +struct socket *sock = n->vqs[index].private_data;
OK, I can't help but this that presenting the vqs as an array doesn't buy us very much. Esp. if you change vhost_dev_init to take a NULL-terminated varargs. I think readability would improve. It means passing a vq around rather than an index.
Not completely sure it'll be a win tho.
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+static long vhost_net_set_backend (struct vhost_net *n,unsigned index,int fd) +{ +struct socket *sock, *oldsock =NULL ;
...
Quoted 8 lines
drivers/vhost/net.c new file +8
-0+ sock =get_socket (fd); +if (IS_ERR (sock)) { + r =PTR_ERR (sock); + goto done; + } + +/* start polling new socket */ + oldsock = vq->private_data;
...
drivers/vhost/net.c line 503
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+done: +mutex_unlock (&n->dev.mutex); +if (oldsock) { +vhost_net_flush_vq (n, index); +fput (oldsock->file);
I dislike this style; I prefer multiple different goto points, one for when oldsock is set, and one for when it's not.
That way, gcc warns us about uninitialized variables if we get it wrong.
drivers/vhost/net.c line 512
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+static long vhost_net_reset_owner (struct vhost_net *n) +{ +struct socket *tx_sock =NULL ; +struct socket *rx_sock =NULL ; +long r;
This should be called "err", since that's what it is.
drivers/vhost/net.c line 535
Quoted 7 lines
drivers/vhost/net.c new file +7
-0+static void vhost_net_set_features (struct vhost_net *n,u64 features) +{ +size_t hdr_size = features & (1 <<VHOST_NET_F_VIRTIO_NET_HDR ) ? +sizeof (struct virtio_net_hdr ) :0 ; +int i; +mutex_lock (&n->dev.mutex); + n->dev.acked_features = features;
Why is this called "acked_features"? Not just "features"? I expected to see code which exposed these back to userspace, and didn't.
drivers/vhost/net.c line 571
Quoted 11 lines
drivers/vhost/net.c new file +11
-0+case VHOST_GET_FEATURES : + features =VHOST_FEATURES ; +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 & ~VHOST_FEATURES ) +return -EOPNOTSUPP ; +vhost_net_set_features (n, features);
OK, from the userspace POV it's "get features" then "ack features". But I think "VHOST_SET_FEATURES" is more consistent, despite this usage.
drivers/vhost/vhost.c line 264
Quoted 2 lines
drivers/vhost/vhost.c new file +2
-0+switch (ioctl) { +case VHOST_SET_VRING_NUM :
I haven't looked at your userspace implementation, but does a generic VHOST_SET_VRING_STATE & VHOST_GET_VRING_STATE with a struct make more sense? It'd be simpler here, but not sure if it'd be simpler to use?
(Not the fd-setting ioctls of course)
Quoted 9 lines
drivers/vhost/vhost.c new file +9
-0+case VHOST_SET_VRING_LOG : + r =copy_from_user (&a, argp,sizeof a); +if (r <0 ) +break ; +if (a.padding) { + r = -EOPNOTSUPP ; +break ; + } +if (a.user_addr ==VHOST_VRING_LOG_DISABLE ) {
drivers/vhost/vhost.c line 362
Quoted 10 lines
drivers/vhost/vhost.c new file +10
-0+ vq->log_used = false; +break ; + } +if (a.user_addr & (sizeof *vq->used->ring -1 )) { + r = -EINVAL ; +break ; + } + vq->log_used = true; + vq->log_addr = a.user_addr; +break ;
For future reference, this is *exactly* the kind of thing which would have been nice as a followup patch. Easy to separate, easy to review, not critical to the core.
drivers/vhost/vhost.c line 525
Quoted 6 lines
drivers/vhost/vhost.c new file +6
-0+/* TODO: This is really inefficient. We need something like get_user() +* (instruction directly accesses the data, with an exception table entry +* returning -EFAULT). See Documentation/x86/exception-tables.txt. +*/ +static int set_bit_to_user (int nr,void __user *addr) +{
I guess we won't be dealing with many contiguous pages, otherwise we could get a cheap speedup making this set_bits_to_user(int nr, int num_bits...).
drivers/vhost/vhost.c line 635
Quoted 10 lines
drivers/vhost/vhost.c new file +10
-0+/* Each buffer in the virtqueues is actually a chain of descriptors. This +* function returns the next descriptor in the chain, +* or -1 if we're at the end. */ +static unsigned next_desc (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 -1 ;
Hmm, prefer s/-1/-1U/ in comment, here, and below. Clarifies a bit.
Quoted 2 lines
+/* After we've used one of their buffers, we tell them about it. We'll then
+ * want to send them an interrupt, usingvq->call. */
This comment has too much cut & paste:
... want to notify the guest, using the eventfd */
drivers/vhost/vhost.c line 904
Quoted 3 lines
drivers/vhost/vhost.c new file +3
-0+/* This actually sends the interrupt for this virtqueue */ +void vhost_trigger_irq (struct vhost_dev *dev,struct vhost_virtqueue *vq) +{
Rename vhost_notify_eventfd() or something, and fix comments?
drivers/vhost/vhost.h line 19
Quoted 2 lines
drivers/vhost/vhost.h new file +2
-0+enum { +VHOST_NET_MAX_SG =MAX_SKB_FRAGS +2 ,
+2? Believable, but is it correct?
drivers/vhost/vhost.h line 24
Quoted 3 lines
drivers/vhost/vhost.h new file +3
-0+/* Poll a file (eventfd or socket) */ +/* Note: there's nothing vhost specific about this structure. */ +struct vhost_poll {
This comment really helped while reading the code. Kudos!
Thanks!
Rusty.
--
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
maintainerRe: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
Quoted 9 lines · Rusty Russell
Quoted 2 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.
Hi Michael,
Now everyone else has finally kicked all the tires and it seems to pass,
I've done a fairly complete review. Generally, it's really nice; just one bug and a few minor suggestions for polishing.
Thanks for the review! I'll add more polishing and repost. Answers to some questions below.
Quoted 7 lines
drivers/vhost/net.c line 80
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+/* Caller must have TX VQ lock */ +static void tx_poll_stop (struct vhost_net *net) +{ +if (likely (net->tx_poll_state !=VHOST_NET_POLL_STARTED )) +return ;likely? Really?
Hmm ... yes. tx poll stop is called on each packet (as long as we do not fill up 1/2 backend queue), the first call will stop polling the rest checks state and does nothing.
This is because we normally do not care when the message has left the queue in backend device: we tell backend to send it and forget. We only start polling when backend tx queue fills up.
Makes sense?
Quoted 7 lines
drivers/vhost/net.c line 129
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+for (;;) { + head =vhost_get_vq_desc (&net->dev, vq, vq->iov, &out, &in, +NULL ,NULL );Danger! You need an arg to
vhost_get_vq_descto tell it the max desc size you can handle. Otherwise, it's only limited by ring size, and a malicious guest can overflow you here, and below:
In fact, I think this is not a bug. This happens to work correctly (even with malicious guests) because vhost_get_vq_desc is hard-coded to check VHOST_NET_MAX_SG, so in fact no overflow is possible. I agree that it's mich nicer to pass iovec size to vhost_get_vq_desc.
Quoted 10 lines
Quoted 3 lines
drivers/vhost/net.c new file +2
-0+/* Skip header. TODO: support TSO. */ + s =move_iovec_hdr (vq->iov, vq->hdr, hdr_size, out);...
drivers/vhost/net.c line 121
Quoted 4 lines
drivers/vhost/net.c new file +4
-0+ +use_mm (net->dev.mm); +mutex_lock (&vq->mutex); +vhost_no_notify (vq);I prefer a name like "
vhost_disable_notify()".
Good idea.
Quoted 5 lines
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+/* OK, now we need to know about added descriptors. */ +if (head == vq->num &&vhost_notify (vq)) +/* They could have slipped one in as we were doing that: +* check again. */ +continue ;
Quoted 8 lines
drivers/vhost/net.c line 228
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+/* Nothing new? Wait for eventfd to tell us they refilled. */ +if (head == vq->num) +break ; +/* We don't need to be notified again. */ +vhost_no_notify (vq);Similarly,
vhost_enable_notify. This one is particularly misleading since it doesn't actually notify anything!
Good idea.
Quoted 13 lines
In particular, this code would be neater as:
if (head == vq->num) { if (vhost_enable_notify(vq)) { /* Try again, they could have slipped one in. */ continue; } /* Nothing more to do. */ break; } vhost_disable_notify(vq);
Quoted 3 lines
Now, AFAICT vhost_notify()/enable_notify() would be better rewritten to return true only when there's more pending. Saves a loop around here most of the time.
OKay, I'll look into this. It kind of annoys me that we would do get_user for the same value twice: once in vhost_enable_notify and once in vhost_get_vq_desc. OTOH, all the loop does is call vhost_get_vq_desc again.
Quoted 2 lines
Also, the vhost_no_notify/vhost_disable_notify() can be moved
out of the loop entirely.
I don't think it can, if we enabled notification and then see more descriptors in queue, we want to disable notification again. But it can be
Quoted 2 lines
(It could be under an if (unlikely(enabled)), not
sure if it's worth it).
if (unlikely(vhost_enable_notify(vq))) {
/* Try again, they have slipped one in. */ vhost_disable_notify(vq); continue;
}
Quoted 6 lines
drivers/vhost/net.c line 261
Quoted 3 lines
drivers/vhost/net.c new file +2
-0+ len = err; + err =memcpy_toiovec (vq->hdr, (unsigned char *)&hdr, hdr_size);That unsigned char * arg to
memcpy_toiovecis annoying. A patch might be nice, separate from this effort.
Sounds good.
Quoted 11 lines
drivers/vhost/net.c line 322
Quoted 9 lines
drivers/vhost/net.c new file +9
-0+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;I have a personal dislike of calloc for structures.
You mean zalloc?
Quoted 4 lines
In userspace, it's because valgrind can't spot uninitialized fields. These days a similar argument applies in the kernel, because we have KMEMCHECK now. If someone adds a field to the struct and forgets to initialize it, we can spot it.
OK.
Quoted 10 lines
drivers/vhost/net.c line 348
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+static void vhost_net_enable_vq (struct vhost_net *n,int index) +{ +struct socket *sock = n->vqs[index].private_data;OK, I can't help but this that presenting the vqs as an array doesn't buy us very much. Esp. if you change
vhost_dev_initto take a NULL-terminated varargs. I think readability would improve. It means passing a vq around rather than an index.Not completely sure it'll be a win tho.
Hmm, varargs sounds a bit complex. But I agree readability for vhost_net_enable_vq and friends would benefit from passing a vq around rather than an index. I'll try it out and do it if it's a win.
Quoted 22 lines
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+static long vhost_net_set_backend (struct vhost_net *n,unsigned index,int fd) +{ +struct socket *sock, *oldsock =NULL ;...
Quoted 8 lines
drivers/vhost/net.c new file +8
-0+ sock =get_socket (fd); +if (IS_ERR (sock)) { + r =PTR_ERR (sock); + goto done; + } + +/* start polling new socket */ + oldsock = vq->private_data;...
drivers/vhost/net.c line 503
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+done: +mutex_unlock (&n->dev.mutex); +if (oldsock) { +vhost_net_flush_vq (n, index); +fput (oldsock->file);I dislike this style; I prefer multiple different goto points, one for when oldsock is set, and one for when it's not.
Quoted 1 line
That way, gcc warns us about uninitialized variables if we get it wrong.
OK.
Quoted 7 lines
drivers/vhost/net.c line 512
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+static long vhost_net_reset_owner (struct vhost_net *n) +{ +struct socket *tx_sock =NULL ; +struct socket *rx_sock =NULL ; +long r;This should be called "err", since that's what it is.
OK.
Quoted 10 lines
drivers/vhost/net.c line 535
Quoted 7 lines
drivers/vhost/net.c new file +7
-0+static void vhost_net_set_features (struct vhost_net *n,u64 features) +{ +size_t hdr_size = features & (1 <<VHOST_NET_F_VIRTIO_NET_HDR ) ? +sizeof (struct virtio_net_hdr ) :0 ; +int i; +mutex_lock (&n->dev.mutex); + n->dev.acked_features = features;Why is this called "
acked_features"? Not just "features"? I expected to see code which exposed these back to userspace, and didn't.
Not sure how do you mean. Userspace sets them, why does it want to get them exposed back?
Quoted 6 lines
Quoted 6 lines
drivers/vhost/net.c new file +6
-0+case VHOST_GET_FEATURES : + features =VHOST_FEATURES ; +return put_user (features, featurep); +case VHOST_ACK_FEATURES : + r =get_user (features, featurep); +/* No features for now */
Quoted 8 lines
drivers/vhost/net.c line 571
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+if (r <0 ) +return r; +if (features & ~VHOST_FEATURES ) +return -EOPNOTSUPP ; +vhost_net_set_features (n, features);OK, from the userspace POV it's "get features" then "ack features". But I think "
VHOST_SET_FEATURES" is more consistent, despite this usage.
OK.
Quoted 6 lines
drivers/vhost/vhost.c line 264
Quoted 2 lines
drivers/vhost/vhost.c new file +2
-0+switch (ioctl) { +case VHOST_SET_VRING_NUM :I haven't looked at your userspace implementation, but does a generic
VHOST_SET_VRING_STATE&VHOST_GET_VRING_STATEwith a struct make more sense? It'd be simpler here,
Not by much though, right?
Quoted 1 line
but not sure if it'd be simpler to use?
The problem is with VHOST_SET_VRING_BASE as well. I want it to be separate because I want to make it possible to relocate e.g. used ring to another address while ring is running. This would be a good debugging tool (you look at kernel's used ring, check descriptor, then update guest's used ring) and also possibly an extra way to do migration. And it's nicer to have vring size separate as well, because it is initialized by host and never changed, right?
We could merge DESC, AVAIL, USED, and it will reduce the amount of code in userspace. With both base, size and fds separate, it seemed a bit more symmetrical to have desc/avail/used separate as well. What's your opinion?
Quoted 17 lines
(Not the fd-setting ioctls of course)
Quoted 15 lines
drivers/vhost/vhost.c new file +15
-0+case VHOST_SET_VRING_LOG : + r =copy_from_user (&a, argp,sizeof a); +if (r <0 ) +break ; +if (a.padding) { + r = -EOPNOTSUPP ; +break ; + } +if (a.user_addr ==VHOST_VRING_LOG_DISABLE ) { + vq->log_used = false; +break ; + } +if (a.user_addr & (sizeof *vq->used->ring -1 )) { + r = -EINVAL ; +break ;
Quoted 8 lines
drivers/vhost/vhost.c line 362
Quoted 4 lines
drivers/vhost/vhost.c new file +4
-0+ } + vq->log_used = true; + vq->log_addr = a.user_addr; +break ;For future reference, this is *exactly* the kind of thing which would have been nice as a followup patch. Easy to separate, easy to review, not critical to the core.
Yes. It's not too late to split it out though: should I do it yet?
Quoted 9 lines
drivers/vhost/vhost.c line 525
Quoted 6 lines
drivers/vhost/vhost.c new file +6
-0+/* TODO: This is really inefficient. We need something like get_user() +* (instruction directly accesses the data, with an exception table entry +* returning -EFAULT). See Documentation/x86/exception-tables.txt. +*/ +static int set_bit_to_user (int nr,void __user *addr) +{I guess we won't be dealing with many contiguous pages, otherwise we could get a cheap speedup making this
set_bits_to_user(int nr, int num_bits...).
No idea. Let's keep it as simple as possible for now?
Quoted 4 lines
Quoted 4 lines
drivers/vhost/vhost.c new file +4
-0+/* Each buffer in the virtqueues is actually a chain of descriptors. This +* function returns the next descriptor in the chain, +* or -1 if we're at the end. */ +static unsigned next_desc (struct vring_desc *desc)
Quoted 8 lines
drivers/vhost/vhost.c line 635
Quoted 6 lines
drivers/vhost/vhost.c new file +6
-0+{ +unsigned int next; + +/* If this descriptor says it doesn't chain, we're done. */ +if (!(desc->flags &VRING_DESC_F_NEXT )) +return -1 ;Hmm, prefer s/-1/-1U/ in comment, here, and below. Clarifies a bit.
Good idea.
Quoted 4 lines
Quoted 2 lines
+/* After we've used one of their buffers, we tell them about it. We'll then
+ * want to send them an interrupt, usingvq->call. */This comment has too much cut & paste:
I tried to cut and paste as many comments as possible, this made it easy to audit the code by comparing it with lguest, and made them much more witty. But yes, this is definitely going overboard as we do not have vq->call at all :)
Quoted 7 lines
... want to notify the guest, using the eventfd */
drivers/vhost/vhost.c line 904
Quoted 3 lines
drivers/vhost/vhost.c new file +3
-0+/* This actually sends the interrupt for this virtqueue */ +void vhost_trigger_irq (struct vhost_dev *dev,struct vhost_virtqueue *vq) +{Rename
vhost_notify_eventfd()or something, and fix comments?
Sounds good. Since I'm renaming vhost_notify to vhost_enable_notify, this one can just become vhost_notify.
Quoted 4 lines
drivers/vhost/vhost.h line 19
Quoted 2 lines
drivers/vhost/vhost.h new file +2
-0+enum { +VHOST_NET_MAX_SG =MAX_SKB_FRAGS +2 ,+2? Believable, but is it correct?
+ 1 is for skb head, + 1 is for virtio net header. I'll add a comment.
Quoted 8 lines
drivers/vhost/vhost.h line 24
Quoted 3 lines
drivers/vhost/vhost.h new file +3
-0+/* Poll a file (eventfd or socket) */ +/* Note: there's nothing vhost specific about this structure. */ +struct vhost_poll {This comment really helped while reading the code. Kudos!
Thanks!
Rusty.
--
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>
Re: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
Quoted 9 lines · Michael S. Tsirkin
Quoted 7 lines · Rusty Russell
Review
Quoted 5 lines
(quoted diff) +5
-0+/* Caller must have TX VQ lock */ +static void tx_poll_stop(struct vhost_net *net) +{ + if (likely(net->tx_poll_state != VHOST_NET_POLL_STARTED)) + return;likely? Really?
Quoted 7 lines
Hmm ... yes. tx poll stop is called on each packet (as long as we do not fill up 1/2 backend queue), the first call will stop polling the rest checks state and does nothing.
This is because we normally do not care when the message has left the queue in backend device: we tell backend to send it and forget. We only start polling when backend tx queue fills up.
OK, good.
Quoted 11 lines
Quoted 10 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+static void vhost_net_set_features(struct vhost_net *n, u64 features) +{ + size_t hdr_size = features & (1 << VHOST_NET_F_VIRTIO_NET_HDR) ? + sizeof(struct virtio_net_hdr) : 0; + int i; + mutex_lock(&n->dev.mutex); + n->dev.acked_features = features;Why is this called "
acked_features"? Not just "features"? I expected to see code which exposed these back to userspace, and didn't.
Quoted 2 lines
Not sure how do you mean. Userspace sets them, why does it want to get them exposed back?
There's something about the 'acked' which rubs me the wrong way. "enabled_features" is perhaps a better term than "acked_features"; "acked" seems more a user point-of-view, "enabled" seems more driver POV?
set_features matches your ioctl names, but it sounds like a fn name :(
It's marginal. And 'features' is shorter than both.
Quoted 11 lines
Quoted 6 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ switch (ioctl) { + case VHOST_SET_VRING_NUM:I haven't looked at your userspace implementation, but does a generic
VHOST_SET_VRING_STATE&VHOST_GET_VRING_STATEwith a struct make more sense? It'd be simpler here,Not by much though, right?
Quoted 1 line
but not sure if it'd be simpler to use?
Quoted 7 lines
The problem is with
VHOST_SET_VRING_BASEas well. I want it to be separate because I want to make it possible to relocate e.g. used ring to another address while ring is running. This would be a good debugging tool (you look at kernel's used ring, check descriptor, then update guest's used ring) and also possibly an extra way to do migration. And it's nicer to have vring size separate as well, because it is initialized by host and never changed, right?
Actually, this looks wrong to me:
+ case VHOST_SET_VRING_BASE:
...
+ vq->avail_idx = vq->last_avail_idx = s.num;
The last_avail_idx is part of the state of the driver. It needs to be saved and restored over susp/resume. The only reason it's not in the ring itself is because I figured the other side doesn't need to see it (which is true, but missed debugging opportunities as well as man-in-the-middle issues like this one). I had a patch which put this field at the end of the ring, I might resurrect it to avoid this problem. This is backwards compatible with all implementations. See patch at end.
I would drop avail_idx altogether: get_user is basically free, and simplifies a lot. As most state is in the ring, all you need is an ioctl to save/restore the last_avail_idx.
Quoted 4 lines
We could merge DESC, AVAIL, USED, and it will reduce the amount of code in userspace. With both base, size and fds separate, it seemed a bit more symmetrical to have desc/avail/used separate as well. What's your opinion?
Well, DESC, AVAIL, and USED could easily be turned into SET/GET_LAYOUT.
Quoted 5 lines
Quoted 3 lines
For future reference, this is *exactly* the kind of thing which would have been nice as a followup patch. Easy to separate, easy to review, not critical to the core.
Yes. It's not too late to split it out though: should I do it yet?
Only if you're feeling enthused. It's lightly reviewed now.
Cheers,
Rusty.
virtio: put last_used and last_avail index into ring itself.
Generally, the other end of the virtio ring doesn't need to see where you're up to in consuming the ring. However, to completely understand what's going on from the outside, this information must be exposed. For example, if you want to save and restore a virtio_ring, but you're not the consumer because the kernel is using it directly.
Fortunately, we have room to expand: the ring is always a whole number of pages and there's hundreds of bytes of padding after the avail ring and the used ring, whatever the number of descriptors (which must be a power of 2).
We add a feature bit so the guest can tell the host that it's writing out the current value there, if it wants to use that.
--- drivers/virtio/virtio_ring.c | 23 +++++++++++++++-------- include/linux/virtio_ring.h | 12 +++++++++++- 2 files changed, 26 insertions(+), 9 deletions(-)
drivers/virtio/virtio_ring.c +15 -8
@@ -71,9 +71,6 @@ struct vring_virtqueue/* Number we've added since last sync. */ unsigned int num_added; -/* Last used index we've seen. */ -u16 last_used_idx; -/* How to notify other side. FIXME: commonalize hcalls! */ void (*notify)(struct virtqueue *vq);
@@ -278,12 +275,13 @@ static void detach_buf(struct vring_virtstatic inline bool more_used (const struct vring_virtqueue *vq) { -return vq->last_used_idx != vq->vring.used->idx; +return vring_last_used (&vq->vring) != vq->vring.used->idx; }static void *vring_get_buf (struct virtqueue *_vq,unsigned int *len) {struct vring_virtqueue *vq =to_vvq (_vq); +struct vring_used_elem *u;void *ret;unsigned int i;
@@ -300,8 +298,11 @@ static void *vring_get_buf(struct virtqureturn NULL ; } - i = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].id; - *len = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].len; + u = &vq->vring.used->ring[vring_last_used (&vq->vring) % vq->vring.num]; + i = u->id; + *len = u->len; +/* Make sure we don't reload i after doing checks. */ +rmb ();if (unlikely (i >= vq->vring.num)) {BAD_RING (vq,"id %u out of range\n" , i);
@@ -315,7 +316,8 @@ static void *vring_get_buf(struct virtqu/* detach_buf clears data, so grab it now. */ ret = vq->data[i];detach_buf (vq, i); - vq->last_used_idx++; +vring_last_used (&vq->vring)++; +END_USE (vq);return ret; }
@@ -402,7 +404,6 @@ struct virtqueue *vring_new_virtqueue(un vq->vq.name = name; vq->notify = notify; vq->broken = false; - vq->last_used_idx =0 ; vq->num_added =0 ;list_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG
@@ -413,6 +414,10 @@ struct virtqueue *vring_new_virtqueue(un vq->indirect =virtio_has_feature (vdev,VIRTIO_RING_F_INDIRECT_DESC ); +/* We publish indices whether they offer it or not: if not, it's junk +* space anyway. But calling this acknowledges the feature. */ +virtio_has_feature (vdev,VIRTIO_RING_F_PUBLISH_INDICES ); +/* No callback? Tell other side not to bother us. */ if (!callback) vq->vring.avail->flags |=VRING_AVAIL_F_NO_INTERRUPT ;
@@ -443,6 +448,8 @@ void vring_transport_features(struct virswitch (i) {case VIRTIO_RING_F_INDIRECT_DESC :break ; +case VIRTIO_RING_F_PUBLISH_INDICES : +break ;default :/* We don't understand this bit. */ clear_bit (i, vdev->features);
include/linux/virtio_ring.h +11 -1
@@ -29,6 +29,9 @@/* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28 +/* We publish our last-seen used index at the end of the avail ring. */ +#define VIRTIO_RING_F_PUBLISH_INDICES 29 +/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ struct vring_desc {
@@ -87,6 +90,7 @@ struct vring {* __u16 avail_flags; * __u16 avail_idx; * __u16 available[num]; +* __u16 last_used_idx; * * // Padding to the next align boundary. * char pad[];
@@ -95,6 +99,7 @@ struct vring {* __u16 used_flags; * __u16 used_idx; * struct vring_used_elem used[num]; +* __u16 last_avail_idx; * }; */ static inline void vring_init(struct vring *vr, unsigned int num, void *p,
@@ -111,9 +116,14 @@ static inline unsigned vring_size(unsign {return ((sizeof (struct vring_desc ) * num +sizeof (__u16) * (2 + num) + align -1 ) & ~(align -1 )) - +sizeof (__u16) *2 +sizeof (struct vring_used_elem ) * num; + +sizeof (__u16) *2 +sizeof (struct vring_used_elem ) * num +2 ; } +/* We publish the last-seen used index at the end of the available ring, and +* vice-versa. These are at the end for backwards compatibility. */ +#define vring_last_used (vr) ((vr)->avail->ring[(vr)->num]) +#define vring_last_avail (vr) (*(__u16 *)&(vr)->used->ring[(vr)->num]) +#ifdef __KERNEL__#include <linux/irqreturn.h> struct virtio_device ;
--
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>
Re: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
Quoted 11 lines · Rusty Russell
Quoted 11 lines
Quoted 10 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+static void vhost_net_set_features(struct vhost_net *n, u64 features) +{ + size_t hdr_size = features & (1 << VHOST_NET_F_VIRTIO_NET_HDR) ? + sizeof(struct virtio_net_hdr) : 0; + int i; + mutex_lock(&n->dev.mutex); + n->dev.acked_features = features;Why is this called "
acked_features"? Not just "features"? I expected to see code which exposed these back to userspace, and didn't.
Quoted 7 lines
Quoted 2 lines
Not sure how do you mean. Userspace sets them, why does it want to get them exposed back?
There's something about the 'acked' which rubs me the wrong way. "
enabled_features" is perhaps a better term than "acked_features"; "acked" seems more a user point-of-view, "enabled" seems more driver POV?
Hmm. Are you happy with the ioctl name? If yes I think being consistent with that is important.
Quoted 3 lines
set_featuresmatches your ioctl names, but it sounds like a fn name :(It's marginal. And 'features' is shorter than both.
I started with this but I was always getting confused whether this includes all features or just acked features. I'll go with enabled_features.
Quoted 20 lines
Quoted 12 lines
Quoted 7 lines
Quoted 3 lines
+ switch (ioctl) {
+ caseVHOST_SET_VRING_NUM:I haven't looked at your userspace implementation, but does a generic
VHOST_SET_VRING_STATE&VHOST_GET_VRING_STATEwith a struct make more sense? It'd be simpler here,Not by much though, right?
Quoted 1 line
but not sure if it'd be simpler to use?
Quoted 7 lines
The problem is with
VHOST_SET_VRING_BASEas well. I want it to be separate because I want to make it possible to relocate e.g. used ring to another address while ring is running. This would be a good debugging tool (you look at kernel's used ring, check descriptor, then update guest's used ring) and also possibly an extra way to do migration. And it's nicer to have vring size separate as well, because it is initialized by host and never changed, right?
Quoted 8 lines
Actually, this looks wrong to me:
+ case
VHOST_SET_VRING_BASE:
...
+vq->avail_idx=vq->last_avail_idx= s.num;The
last_avail_idxis part of the state of the driver. It needs to be saved and restored over susp/resume.
Exactly. That's what VHOST_GET/SET_VRING_BASE does. avail_idx is just a cached value for notify on empty, so what this does is clear the value. What exactly do you refer to when you say "this looks wrong"? This could trigger an extra notification if I ever called trigger_irq without get first. As I don't, it in fact has no effect.
Quoted 6 lines
The only reason it's not in the ring itself
is because I figured the other side doesn't need to see it (which is true, but missed debugging opportunities as well as man-in-the-middle issues like this one). I had a patch which put this field at the end of the ring, I might resurrect it to avoid this problem. This is backwards compatible with all implementations. See patch at end.
Yes, I remember that patch. There seems to be little point though, at this stage.
Quoted 4 lines
I would drop
avail_idxaltogether:get_useris basically free, and simplifies a lot. As most state is in the ring, all you need is an ioctl to save/restore thelast_avail_idx.
avail_idx is there for notify on empty: I had this thought that it's better to leave the avail cache line alone when we are triggering interrupt to avoid bouncing it around if guest is updating it meanwhile on another CPU, and I think my testing showed that it helped performance, but could be a mistake. You don't believe this can help?
Quoted 7 lines
Quoted 5 lines
We could merge DESC, AVAIL, USED, and it will reduce the amount of code in userspace. With both base, size and fds separate, it seemed a bit more symmetrical to have desc/avail/used separate as well. What's your opinion?
Well, DESC, AVAIL, and USED could easily be turned into SET/GET_LAYOUT.
Will do.
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
For future reference, this is *exactly* the kind of thing which would have been nice as a followup patch. Easy to separate, easy to review, not critical to the core.
Yes. It's not too late to split it out though: should I do it yet?
Only if you're feeling enthused. It's lightly reviewed now.
Not really :) I'll keep this in mind for the future. Thanks!
Quoted 3 lines
Cheers,
Rusty.
Re: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
Quoted 7 lines · Michael S. Tsirkin
Quoted 3 lines · Rusty Russell
There's something about the 'acked' which rubs me the wrong way. "
enabled_features" is perhaps a better term than "acked_features"; "acked" seems more a user point-of-view, "enabled" seems more driver POV?Hmm. Are you happy with the ioctl name? If yes I think being consistent with that is important.
I think in my original comments I noted that I preferred GET / SET, rather than GET/ACK.
Quoted 6 lines
Quoted 6 lines
Actually, this looks wrong to me:
(quoted diff) +2
-0+ case VHOST_SET_VRING_BASE: + vq->avail_idx = vq->last_avail_idx = s.num;
Quoted 6 lines
Quoted 2 lines
The
last_avail_idxis part of the state of the driver. It needs to be saved and restored over susp/resume.Exactly. That's what VHOST_GET/SET_VRING_BASE does.
avail_idxis just a cached value for notify on empty, so what this does is clear the value.
Ah, you actually refresh it every time anyway. Hmm, could you do my poor brain a favor and either just get_user it in vhost_trigger_irq(), or call it 'cached_avail_idx' or something?
Quoted 9 lines
Quoted 6 lines
The only reason it's not in the ring itself
is because I figured the other side doesn't need to see it (which is true, but missed debugging opportunities as well as man-in-the-middle issues like this one). I had a patch which put this field at the end of the ring, I might resurrect it to avoid this problem. This is backwards compatible with all implementations. See patch at end.Yes, I remember that patch. There seems to be little point though, at this stage.
Well, it avoids this ioctl, by exposing all the state. We may well need it later, to expand the ring in other ways.
Quoted 9 lines
Quoted 3 lines
I would drop
avail_idxaltogether:get_useris basically free, and simplifies a lot. As most state is in the ring, all you need is an ioctl to save/restore thelast_avail_idx.
avail_idxis there for notify on empty: I had this thought that it's better to leave the avail cache line alone when we are triggering interrupt to avoid bouncing it around if guest is updating it meanwhile on another CPU, and I think my testing showed that it helped performance, but could be a mistake. You don't believe this can help?
I believe it could help, but this is YA case where it would have been nice to have a dumb basic patch and this as a patch on top. But I am going to ask you to re-run that measurement, see if it stacks up (because it's an interesting lesson if it does..)
Thanks!
Rusty.
--
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>
Re: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 7 lines · Michael S. Tsirkin
Quoted 3 lines · Rusty Russell
There's something about the 'acked' which rubs me the wrong way. "
enabled_features" is perhaps a better term than "acked_features"; "acked" seems more a user point-of-view, "enabled" seems more driver POV?Hmm. Are you happy with the ioctl name? If yes I think being consistent with that is important.
I think in my original comments I noted that I preferred GET / SET, rather than GET/ACK.
Quoted 6 lines
Quoted 6 lines
Actually, this looks wrong to me:
(quoted diff) +2
-0+ case VHOST_SET_VRING_BASE: + vq->avail_idx = vq->last_avail_idx = s.num;
Quoted 6 lines
Quoted 2 lines
The
last_avail_idxis part of the state of the driver. It needs to be saved and restored over susp/resume.Exactly. That's what VHOST_GET/SET_VRING_BASE does.
avail_idxis just a cached value for notify on empty, so what this does is clear the value.
Ah, you actually refresh it every time anyway. Hmm, could you do my poor brain a favor and either just get_user it in vhost_trigger_irq(), or call it 'cached_avail_idx' or something?
Quoted 9 lines
Quoted 6 lines
The only reason it's not in the ring itself
is because I figured the other side doesn't need to see it (which is true, but missed debugging opportunities as well as man-in-the-middle issues like this one). I had a patch which put this field at the end of the ring, I might resurrect it to avoid this problem. This is backwards compatible with all implementations. See patch at end.Yes, I remember that patch. There seems to be little point though, at this stage.
Well, it avoids this ioctl, by exposing all the state. We may well need it later, to expand the ring in other ways.
Quoted 9 lines
Quoted 3 lines
I would drop
avail_idxaltogether:get_useris basically free, and simplifies a lot. As most state is in the ring, all you need is an ioctl to save/restore thelast_avail_idx.
avail_idxis there for notify on empty: I had this thought that it's better to leave the avail cache line alone when we are triggering interrupt to avoid bouncing it around if guest is updating it meanwhile on another CPU, and I think my testing showed that it helped performance, but could be a mistake. You don't believe this can help?
I believe it could help, but this is YA case where it would have been nice to have a dumb basic patch and this as a patch on top. But I am going to ask you to re-run that measurement, see if it stacks up (because it's an interesting lesson if it does..)
Thanks!
Rusty.
Re: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
In reply to Rusty RussellQuoted 11 lines · Rusty Russell
Quoted 11 lines
Quoted 10 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+static void vhost_net_set_features(struct vhost_net *n, u64 features) +{ + size_t hdr_size = features & (1 << VHOST_NET_F_VIRTIO_NET_HDR) ? + sizeof(struct virtio_net_hdr) : 0; + int i; + mutex_lock(&n->dev.mutex); + n->dev.acked_features = features;Why is this called "
acked_features"? Not just "features"? I expected to see code which exposed these back to userspace, and didn't.
Quoted 7 lines
Quoted 2 lines
Not sure how do you mean. Userspace sets them, why does it want to get them exposed back?
There's something about the 'acked' which rubs me the wrong way. "
enabled_features" is perhaps a better term than "acked_features"; "acked" seems more a user point-of-view, "enabled" seems more driver POV?
Hmm. Are you happy with the ioctl name? If yes I think being consistent with that is important.
Quoted 3 lines
set_featuresmatches your ioctl names, but it sounds like a fn name :(It's marginal. And 'features' is shorter than both.
I started with this but I was always getting confused whether this includes all features or just acked features. I'll go with enabled_features.
Quoted 20 lines
Quoted 12 lines
Quoted 7 lines
Quoted 3 lines
+ switch (ioctl) {
+ caseVHOST_SET_VRING_NUM:I haven't looked at your userspace implementation, but does a generic
VHOST_SET_VRING_STATE&VHOST_GET_VRING_STATEwith a struct make more sense? It'd be simpler here,Not by much though, right?
Quoted 1 line
but not sure if it'd be simpler to use?
Quoted 7 lines
The problem is with
VHOST_SET_VRING_BASEas well. I want it to be separate because I want to make it possible to relocate e.g. used ring to another address while ring is running. This would be a good debugging tool (you look at kernel's used ring, check descriptor, then update guest's used ring) and also possibly an extra way to do migration. And it's nicer to have vring size separate as well, because it is initialized by host and never changed, right?
Quoted 8 lines
Actually, this looks wrong to me:
+ case
VHOST_SET_VRING_BASE:
...
+vq->avail_idx=vq->last_avail_idx= s.num;The
last_avail_idxis part of the state of the driver. It needs to be saved and restored over susp/resume.
Exactly. That's what VHOST_GET/SET_VRING_BASE does. avail_idx is just a cached value for notify on empty, so what this does is clear the value. What exactly do you refer to when you say "this looks wrong"? This could trigger an extra notification if I ever called trigger_irq without get first. As I don't, it in fact has no effect.
Quoted 6 lines
The only reason it's not in the ring itself
is because I figured the other side doesn't need to see it (which is true, but missed debugging opportunities as well as man-in-the-middle issues like this one). I had a patch which put this field at the end of the ring, I might resurrect it to avoid this problem. This is backwards compatible with all implementations. See patch at end.
Yes, I remember that patch. There seems to be little point though, at this stage.
Quoted 4 lines
I would drop
avail_idxaltogether:get_useris basically free, and simplifies a lot. As most state is in the ring, all you need is an ioctl to save/restore thelast_avail_idx.
avail_idx is there for notify on empty: I had this thought that it's better to leave the avail cache line alone when we are triggering interrupt to avoid bouncing it around if guest is updating it meanwhile on another CPU, and I think my testing showed that it helped performance, but could be a mistake. You don't believe this can help?
Quoted 7 lines
Quoted 5 lines
We could merge DESC, AVAIL, USED, and it will reduce the amount of code in userspace. With both base, size and fds separate, it seemed a bit more symmetrical to have desc/avail/used separate as well. What's your opinion?
Well, DESC, AVAIL, and USED could easily be turned into SET/GET_LAYOUT.
Will do.
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
For future reference, this is *exactly* the kind of thing which would have been nice as a followup patch. Easy to separate, easy to review, not critical to the core.
Yes. It's not too late to split it out though: should I do it yet?
Only if you're feeling enthused. It's lightly reviewed now.
Not really :) I'll keep this in mind for the future. Thanks!
Quoted 3 lines
Cheers,
Rusty.
Michael S. Tsirkin
maintainerRe: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
In reply to Rusty RussellQuoted 31 lines · Rusty Russell
Quoted 9 lines · Michael S. Tsirkin
Quoted 7 lines · Rusty Russell
Review
Quoted 5 lines
(quoted diff) +5
-0+/* Caller must have TX VQ lock */ +static void tx_poll_stop(struct vhost_net *net) +{ + if (likely(net->tx_poll_state != VHOST_NET_POLL_STARTED)) + return;likely? Really?
Quoted 7 lines
Hmm ... yes. tx poll stop is called on each packet (as long as we do not fill up 1/2 backend queue), the first call will stop polling the rest checks state and does nothing.
This is because we normally do not care when the message has left the queue in backend device: we tell backend to send it and forget. We only start polling when backend tx queue fills up.
OK, good.
Quoted 11 lines
Quoted 10 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+static void vhost_net_set_features(struct vhost_net *n, u64 features) +{ + size_t hdr_size = features & (1 << VHOST_NET_F_VIRTIO_NET_HDR) ? + sizeof(struct virtio_net_hdr) : 0; + int i; + mutex_lock(&n->dev.mutex); + n->dev.acked_features = features;Why is this called "
acked_features"? Not just "features"? I expected to see code which exposed these back to userspace, and didn't.
Quoted 8 lines
Quoted 2 lines
Not sure how do you mean. Userspace sets them, why does it want to get them exposed back?
There's something about the 'acked' which rubs me the wrong way. "
enabled_features" is perhaps a better term than "acked_features"; "acked" seems more a user point-of-view, "enabled" seems more driver POV?
set_featuresmatches your ioctl names, but it sounds like a fn name :(
Hmm. Are you happy with the ioctl name? If yes I think being consistent with that is important.
Quoted 1 line
It's marginal. And 'features' is shorter than both.
I started with this but I was always getting confused whether this includes all features or just acked features. I'll go with enabled_features.
Quoted 25 lines
Quoted 11 lines
Quoted 6 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ switch (ioctl) { + case VHOST_SET_VRING_NUM:I haven't looked at your userspace implementation, but does a generic
VHOST_SET_VRING_STATE&VHOST_GET_VRING_STATEwith a struct make more sense? It'd be simpler here,Not by much though, right?
Quoted 1 line
but not sure if it'd be simpler to use?
Quoted 7 lines
The problem is with
VHOST_SET_VRING_BASEas well. I want it to be separate because I want to make it possible to relocate e.g. used ring to another address while ring is running. This would be a good debugging tool (you look at kernel's used ring, check descriptor, then update guest's used ring) and also possibly an extra way to do migration. And it's nicer to have vring size separate as well, because it is initialized by host and never changed, right?Actually, this looks wrong to me:
(quoted diff) +2
-0+ case VHOST_SET_VRING_BASE: + vq->avail_idx = vq->last_avail_idx = s.num;
Quoted 2 lines
The
last_avail_idxis part of the state of the driver. It needs to be saved and restored over susp/resume.
Exactly. That's what VHOST_GET/SET_VRING_BASE does. avail_idx is just a cached value for notify on empty, so what this does is clear the value. What exactly do you refer to when you say "this looks wrong"? This could trigger an extra notification if I ever called trigger_irq without get first. As I don't, it in fact has no effect.
Quoted 6 lines
The only reason it's not in the ring itself
is because I figured the other side doesn't need to see it (which is true, but missed debugging opportunities as well as man-in-the-middle issues like this one). I had a patch which put this field at the end of the ring, I might resurrect it to avoid this problem. This is backwards compatible with all implementations. See patch at end.
Yes, I remember that patch. There seems to be little point though, at this stage.
Quoted 4 lines
I would drop
avail_idxaltogether:get_useris basically free, and simplifies a lot. As most state is in the ring, all you need is an ioctl to save/restore thelast_avail_idx.
avail_idx is there for notify on empty: I had this thought that it's better to leave the avail cache line alone when we are triggering interrupt to avoid bouncing it around if guest is updating it meanwhile on another CPU, and I think my testing showed that it helped performance, but could be a mistake. You don't believe this can help?
Quoted 6 lines
Quoted 4 lines
We could merge DESC, AVAIL, USED, and it will reduce the amount of code in userspace. With both base, size and fds separate, it seemed a bit more symmetrical to have desc/avail/used separate as well. What's your opinion?
Well, DESC, AVAIL, and USED could easily be turned into SET/GET_LAYOUT.
OK, I'll do this.
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
For future reference, this is *exactly* the kind of thing which would have been nice as a followup patch. Easy to separate, easy to review, not critical to the core.
Yes. It's not too late to split it out though: should I do it yet?
Only if you're feeling enthused. It's lightly reviewed now.
Not really :) I'll keep this in mind for the future. Thanks!
Quoted 140 lines
Cheers,
Rusty.virtio: put
last_usedandlast_availindex into ring itself.Generally, the other end of the virtio ring doesn't need to see where you're up to in consuming the ring. However, to completely understand what's going on from the outside, this information must be exposed. For example, if you want to save and restore a
virtio_ring, but you're not the consumer because the kernel is using it directly.Fortunately, we have room to expand: the ring is always a whole number of pages and there's hundreds of bytes of padding after the avail ring and the used ring, whatever the number of descriptors (which must be a power of 2).
We add a feature bit so the guest can tell the host that it's writing out the current value there, if it wants to use that.
Signed-off-by Rusty Russell <[email protected]>--- drivers/virtio/virtio_ring.c | 23 +++++++++++++++-------- include/linux/virtio_ring.h | 12 +++++++++++- 2 files changed, 26 insertions(+), 9 deletions(-)drivers/virtio/virtio_ring.c +15
-8@@ -71,9 +71,6 @@ struct vring_virtqueue/* Number we've added since last sync. */ unsigned int num_added; -/* Last used index we've seen. */ -u16 last_used_idx; -/* How to notify other side. FIXME: commonalize hcalls! */ void (*notify)(struct virtqueue *vq);@@ -278,12 +275,13 @@ static void detach_buf(struct vring_virtstatic inline bool more_used (const struct vring_virtqueue *vq) { -return vq->last_used_idx != vq->vring.used->idx; +return vring_last_used (&vq->vring) != vq->vring.used->idx; }static void *vring_get_buf (struct virtqueue *_vq,unsigned int *len) {struct vring_virtqueue *vq =to_vvq (_vq); +struct vring_used_elem *u;void *ret;unsigned int i;@@ -300,8 +298,11 @@ static void *vring_get_buf(struct virtqureturn NULL ; } - i = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].id; - *len = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].len; + u = &vq->vring.used->ring[vring_last_used (&vq->vring) % vq->vring.num]; + i = u->id; + *len = u->len; +/* Make sure we don't reload i after doing checks. */ +rmb ();if (unlikely (i >= vq->vring.num)) {BAD_RING (vq,"id %u out of range\n" , i);@@ -315,7 +316,8 @@ static void *vring_get_buf(struct virtqu/* detach_buf clears data, so grab it now. */ ret = vq->data[i];detach_buf (vq, i); - vq->last_used_idx++; +vring_last_used (&vq->vring)++; +END_USE (vq);return ret; }@@ -402,7 +404,6 @@ struct virtqueue *vring_new_virtqueue(un vq->vq.name = name; vq->notify = notify; vq->broken = false; - vq->last_used_idx =0 ; vq->num_added =0 ;list_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG @@ -413,6 +414,10 @@ struct virtqueue *vring_new_virtqueue(un vq->indirect =virtio_has_feature (vdev,VIRTIO_RING_F_INDIRECT_DESC ); +/* We publish indices whether they offer it or not: if not, it's junk +* space anyway. But calling this acknowledges the feature. */ +virtio_has_feature (vdev,VIRTIO_RING_F_PUBLISH_INDICES ); +/* No callback? Tell other side not to bother us. */ if (!callback) vq->vring.avail->flags |=VRING_AVAIL_F_NO_INTERRUPT ;@@ -443,6 +448,8 @@ void vring_transport_features(struct virswitch (i) {case VIRTIO_RING_F_INDIRECT_DESC :break ; +case VIRTIO_RING_F_PUBLISH_INDICES : +break ;default :/* We don't understand this bit. */ clear_bit (i, vdev->features);include/linux/virtio_ring.h +6
-1@@ -29,6 +29,9 @@/* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28 +/* We publish our last-seen used index at the end of the avail ring. */ +#define VIRTIO_RING_F_PUBLISH_INDICES 29 +/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ struct vring_desc {@@ -87,6 +90,7 @@ struct vring {* __u16 avail_flags; * __u16 avail_idx; * __u16 available[num]; +* __u16 last_used_idx; * * // Padding to the next align boundary. * char pad[]; @@ -95,6 +99,7 @@ struct vring {* __u16 used_flags; * __u16 used_idx; * struct vring_used_elem used[num]; +* __u16 last_avail_idx; * }; */ static inline void vring_init(struct vring *vr, unsigned int num, void *p,@@ -111,9 +116,14 @@ static inline unsigned vring_size(unsign {return ((sizeof (struct vring_desc ) * num +sizeof (__u16) * (2 + num) + align -1 ) & ~(align -1 )) - +sizeof (__u16) *2 +sizeof (struct vring_used_elem ) * num; + +sizeof (__u16) *2 +sizeof (struct vring_used_elem ) * num +2 ;
include/linux/virtio_ring.h static inline unsigned vring_size(unsign · line 129
Quoted 10 lines
include/linux/virtio_ring.h +5
-0} +/* We publish the last-seen used index at the end of the available ring, and +* vice-versa. These are at the end for backwards compatibility. */ +#define vring_last_used (vr) ((vr)->avail->ring[(vr)->num]) +#define vring_last_avail (vr) (*(__u16 *)&(vr)->used->ring[(vr)->num]) +#ifdef __KERNEL__#include <linux/irqreturn.h> struct virtio_device ;
--
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
maintainerRe: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
In reply to Rusty RussellQuoted 31 lines · Rusty Russell
Quoted 9 lines · Michael S. Tsirkin
Quoted 7 lines · Rusty Russell
Review
Quoted 5 lines
(quoted diff) +5
-0+/* Caller must have TX VQ lock */ +static void tx_poll_stop(struct vhost_net *net) +{ + if (likely(net->tx_poll_state != VHOST_NET_POLL_STARTED)) + return;likely? Really?
Quoted 7 lines
Hmm ... yes. tx poll stop is called on each packet (as long as we do not fill up 1/2 backend queue), the first call will stop polling the rest checks state and does nothing.
This is because we normally do not care when the message has left the queue in backend device: we tell backend to send it and forget. We only start polling when backend tx queue fills up.
OK, good.
Quoted 11 lines
Quoted 10 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+static void vhost_net_set_features(struct vhost_net *n, u64 features) +{ + size_t hdr_size = features & (1 << VHOST_NET_F_VIRTIO_NET_HDR) ? + sizeof(struct virtio_net_hdr) : 0; + int i; + mutex_lock(&n->dev.mutex); + n->dev.acked_features = features;Why is this called "
acked_features"? Not just "features"? I expected to see code which exposed these back to userspace, and didn't.
Quoted 8 lines
Quoted 2 lines
Not sure how do you mean. Userspace sets them, why does it want to get them exposed back?
There's something about the 'acked' which rubs me the wrong way. "
enabled_features" is perhaps a better term than "acked_features"; "acked" seems more a user point-of-view, "enabled" seems more driver POV?
set_featuresmatches your ioctl names, but it sounds like a fn name :(
Hmm. Are you happy with the ioctl name? If yes I think being consistent with that is important.
Quoted 1 line
It's marginal. And 'features' is shorter than both.
I started with this but I was always getting confused whether this includes all features or just acked features. I'll go with enabled_features.
Quoted 25 lines
Quoted 11 lines
Quoted 6 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ switch (ioctl) { + case VHOST_SET_VRING_NUM:I haven't looked at your userspace implementation, but does a generic
VHOST_SET_VRING_STATE&VHOST_GET_VRING_STATEwith a struct make more sense? It'd be simpler here,Not by much though, right?
Quoted 1 line
but not sure if it'd be simpler to use?
Quoted 7 lines
The problem is with
VHOST_SET_VRING_BASEas well. I want it to be separate because I want to make it possible to relocate e.g. used ring to another address while ring is running. This would be a good debugging tool (you look at kernel's used ring, check descriptor, then update guest's used ring) and also possibly an extra way to do migration. And it's nicer to have vring size separate as well, because it is initialized by host and never changed, right?Actually, this looks wrong to me:
(quoted diff) +2
-0+ case VHOST_SET_VRING_BASE: + vq->avail_idx = vq->last_avail_idx = s.num;
Quoted 2 lines
The
last_avail_idxis part of the state of the driver. It needs to be saved and restored over susp/resume.
Exactly. That's what VHOST_GET/SET_VRING_BASE does. avail_idx is just a cached value for notify on empty, so what this does is clear the value. What exactly do you refer to when you say "this looks wrong"? This could trigger an extra notification if I ever called trigger_irq without get first. As I don't, it in fact has no effect.
Quoted 6 lines
The only reason it's not in the ring itself
is because I figured the other side doesn't need to see it (which is true, but missed debugging opportunities as well as man-in-the-middle issues like this one). I had a patch which put this field at the end of the ring, I might resurrect it to avoid this problem. This is backwards compatible with all implementations. See patch at end.
Yes, I remember that patch. There seems to be little point though, at this stage.
Quoted 4 lines
I would drop
avail_idxaltogether:get_useris basically free, and simplifies a lot. As most state is in the ring, all you need is an ioctl to save/restore thelast_avail_idx.
avail_idx is there for notify on empty: I had this thought that it's better to leave the avail cache line alone when we are triggering interrupt to avoid bouncing it around if guest is updating it meanwhile on another CPU, and I think my testing showed that it helped performance, but could be a mistake. You don't believe this can help?
Quoted 6 lines
Quoted 4 lines
We could merge DESC, AVAIL, USED, and it will reduce the amount of code in userspace. With both base, size and fds separate, it seemed a bit more symmetrical to have desc/avail/used separate as well. What's your opinion?
Well, DESC, AVAIL, and USED could easily be turned into SET/GET_LAYOUT.
OK, I'll do this.
Quoted 7 lines
Quoted 5 lines
Quoted 3 lines
For future reference, this is *exactly* the kind of thing which would have been nice as a followup patch. Easy to separate, easy to review, not critical to the core.
Yes. It's not too late to split it out though: should I do it yet?
Only if you're feeling enthused. It's lightly reviewed now.
Not really :) I'll keep this in mind for the future. Thanks!
Quoted 150 lines
Cheers,
Rusty.virtio: put
last_usedandlast_availindex into ring itself.Generally, the other end of the virtio ring doesn't need to see where you're up to in consuming the ring. However, to completely understand what's going on from the outside, this information must be exposed. For example, if you want to save and restore a
virtio_ring, but you're not the consumer because the kernel is using it directly.Fortunately, we have room to expand: the ring is always a whole number of pages and there's hundreds of bytes of padding after the avail ring and the used ring, whatever the number of descriptors (which must be a power of 2).
We add a feature bit so the guest can tell the host that it's writing out the current value there, if it wants to use that.
Signed-off-by Rusty Russell <[email protected]>--- drivers/virtio/virtio_ring.c | 23 +++++++++++++++-------- include/linux/virtio_ring.h | 12 +++++++++++- 2 files changed, 26 insertions(+), 9 deletions(-)drivers/virtio/virtio_ring.c +15
-8@@ -71,9 +71,6 @@ struct vring_virtqueue/* Number we've added since last sync. */ unsigned int num_added; -/* Last used index we've seen. */ -u16 last_used_idx; -/* How to notify other side. FIXME: commonalize hcalls! */ void (*notify)(struct virtqueue *vq);@@ -278,12 +275,13 @@ static void detach_buf(struct vring_virtstatic inline bool more_used (const struct vring_virtqueue *vq) { -return vq->last_used_idx != vq->vring.used->idx; +return vring_last_used (&vq->vring) != vq->vring.used->idx; }static void *vring_get_buf (struct virtqueue *_vq,unsigned int *len) {struct vring_virtqueue *vq =to_vvq (_vq); +struct vring_used_elem *u;void *ret;unsigned int i;@@ -300,8 +298,11 @@ static void *vring_get_buf(struct virtqureturn NULL ; } - i = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].id; - *len = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].len; + u = &vq->vring.used->ring[vring_last_used (&vq->vring) % vq->vring.num]; + i = u->id; + *len = u->len; +/* Make sure we don't reload i after doing checks. */ +rmb ();if (unlikely (i >= vq->vring.num)) {BAD_RING (vq,"id %u out of range\n" , i);@@ -315,7 +316,8 @@ static void *vring_get_buf(struct virtqu/* detach_buf clears data, so grab it now. */ ret = vq->data[i];detach_buf (vq, i); - vq->last_used_idx++; +vring_last_used (&vq->vring)++; +END_USE (vq);return ret; }@@ -402,7 +404,6 @@ struct virtqueue *vring_new_virtqueue(un vq->vq.name = name; vq->notify = notify; vq->broken = false; - vq->last_used_idx =0 ; vq->num_added =0 ;list_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG @@ -413,6 +414,10 @@ struct virtqueue *vring_new_virtqueue(un vq->indirect =virtio_has_feature (vdev,VIRTIO_RING_F_INDIRECT_DESC ); +/* We publish indices whether they offer it or not: if not, it's junk +* space anyway. But calling this acknowledges the feature. */ +virtio_has_feature (vdev,VIRTIO_RING_F_PUBLISH_INDICES ); +/* No callback? Tell other side not to bother us. */ if (!callback) vq->vring.avail->flags |=VRING_AVAIL_F_NO_INTERRUPT ;@@ -443,6 +448,8 @@ void vring_transport_features(struct virswitch (i) {case VIRTIO_RING_F_INDIRECT_DESC :break ; +case VIRTIO_RING_F_PUBLISH_INDICES : +break ;default :/* We don't understand this bit. */ clear_bit (i, vdev->features);include/linux/virtio_ring.h +11
-1@@ -29,6 +29,9 @@/* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28 +/* We publish our last-seen used index at the end of the avail ring. */ +#define VIRTIO_RING_F_PUBLISH_INDICES 29 +/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ struct vring_desc {@@ -87,6 +90,7 @@ struct vring {* __u16 avail_flags; * __u16 avail_idx; * __u16 available[num]; +* __u16 last_used_idx; * * // Padding to the next align boundary. * char pad[]; @@ -95,6 +99,7 @@ struct vring {* __u16 used_flags; * __u16 used_idx; * struct vring_used_elem used[num]; +* __u16 last_avail_idx; * }; */ static inline void vring_init(struct vring *vr, unsigned int num, void *p,@@ -111,9 +116,14 @@ static inline unsigned vring_size(unsign {return ((sizeof (struct vring_desc ) * num +sizeof (__u16) * (2 + num) + align -1 ) & ~(align -1 )) - +sizeof (__u16) *2 +sizeof (struct vring_used_elem ) * num; + +sizeof (__u16) *2 +sizeof (struct vring_used_elem ) * num +2 ; } +/* We publish the last-seen used index at the end of the available ring, and +* vice-versa. These are at the end for backwards compatibility. */ +#define vring_last_used (vr) ((vr)->avail->ring[(vr)->num]) +#define vring_last_avail (vr) (*(__u16 *)&(vr)->used->ring[(vr)->num]) +#ifdef __KERNEL__#include <linux/irqreturn.h> struct virtio_device ;
Michael S. Tsirkin
maintainerRe: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
In reply to Rusty RussellQuoted 14 lines · Rusty Russell
Actually, this looks wrong to me:
(quoted diff) +2
-0+ case VHOST_SET_VRING_BASE: + vq->avail_idx = vq->last_avail_idx = s.num;The
last_avail_idxis part of the state of the driver. It needs to be saved and restored over susp/resume. The only reason it's not in the ring itself is because I figured the other side doesn't need to see it (which is true, but missed debugging opportunities as well as man-in-the-middle issues like this one). I had a patch which put this field at the end of the ring, I might resurrect it to avoid this problem. This is backwards compatible with all implementations. See patch at end.
Quoted 3 lines
I would drop
avail_idxaltogether:get_useris basically free, and simplifies a lot. As most state is in the ring, all you need is an ioctl to save/restore thelast_avail_idx.
I remembered another reason for caching head in avail_idx. Basically, avail index could change between when I poll for descriptors and when I want to notify guest.
So we could have:
- poll descriptors until empty
- notify
detects not empty so does not notify
And the way to solve it would be to return flag from notify telling us to restart the polling loop.
But, this will be more code, on data path, than
what happens today where I simply keep state
from descriptor polling and use that to notify.
I also suspect that somehow this race in practice can not create deadlocks ... but I prefer to avoid it, these things are very tricky: if I see an empty ring, and stop processing descriptors, I want to trigger notify on empty.
So if we want to avoid keeping "empty" state, IMO the best way would be to pass a flag to vhost_signal that tells it that ring is empty. Makes sense?
Signature · 7 lines
MST -- 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
maintainerRe: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
In reply to Rusty RussellQuoted 14 lines · Rusty Russell
Actually, this looks wrong to me:
(quoted diff) +2
-0+ case VHOST_SET_VRING_BASE: + vq->avail_idx = vq->last_avail_idx = s.num;The
last_avail_idxis part of the state of the driver. It needs to be saved and restored over susp/resume. The only reason it's not in the ring itself is because I figured the other side doesn't need to see it (which is true, but missed debugging opportunities as well as man-in-the-middle issues like this one). I had a patch which put this field at the end of the ring, I might resurrect it to avoid this problem. This is backwards compatible with all implementations. See patch at end.
Quoted 3 lines
I would drop
avail_idxaltogether:get_useris basically free, and simplifies a lot. As most state is in the ring, all you need is an ioctl to save/restore thelast_avail_idx.
I remembered another reason for caching head in avail_idx. Basically, avail index could change between when I poll for descriptors and when I want to notify guest.
So we could have:
- poll descriptors until empty
- notify
detects not empty so does not notify
And the way to solve it would be to return flag from notify telling us to restart the polling loop.
But, this will be more code, on data path, than
what happens today where I simply keep state
from descriptor polling and use that to notify.
I also suspect that somehow this race in practice can not create deadlocks ... but I prefer to avoid it, these things are very tricky: if I see an empty ring, and stop processing descriptors, I want to trigger notify on empty.
So if we want to avoid keeping "empty" state, IMO the best way would be to pass a flag to vhost_signal that tells it that ring is empty. Makes sense?
Signature · 1 line
MST
Michael S. Tsirkin
maintainerRe: virtio: put last_used and last_avail index into ring itself.
In reply to Rusty RussellQuoted 13 lines
virtio: put
last_usedandlast_availindex into ring itself.Generally, the other end of the virtio ring doesn't need to see where you're up to in consuming the ring. However, to completely understand what's going on from the outside, this information must be exposed. For example, if you want to save and restore a
virtio_ring, but you're not the consumer because the kernel is using it directly.Fortunately, we have room to expand: the ring is always a whole number of pages and there's hundreds of bytes of padding after the avail ring and the used ring, whatever the number of descriptors (which must be a power of 2).
Quoted 4 lines
We add a feature bit so the guest can tell the host that it's writing out the current value there, if it wants to use that.
Signed-off-by Rusty Russell <[email protected]>
I've been looking at this patch some more (more on why later), and I wonder: would it be better to add some alignment to the last used index address, so that if we later add more stuff at the tail, it all fits in a single cache line?
We use a new feature bit anyway, so layout change should not be a problem.
Since I raised the question of caches: for used ring, the ring is not aligned to 64 bit, so on CPUs with 64 bit or larger cache lines, used entries will often cross cache line boundaries. Am I right and might it
have been better to align ring entries to cache line boundaries?
What do you think?
Quoted 120 lines
--- drivers/virtio/virtio_ring.c | 23 +++++++++++++++-------- include/linux/virtio_ring.h | 12 +++++++++++- 2 files changed, 26 insertions(+), 9 deletions(-)drivers/virtio/virtio_ring.c +15
-8@@ -71,9 +71,6 @@ struct vring_virtqueue/* Number we've added since last sync. */ unsigned int num_added; -/* Last used index we've seen. */ -u16 last_used_idx; -/* How to notify other side. FIXME: commonalize hcalls! */ void (*notify)(struct virtqueue *vq);@@ -278,12 +275,13 @@ static void detach_buf(struct vring_virtstatic inline bool more_used (const struct vring_virtqueue *vq) { -return vq->last_used_idx != vq->vring.used->idx; +return vring_last_used (&vq->vring) != vq->vring.used->idx; }static void *vring_get_buf (struct virtqueue *_vq,unsigned int *len) {struct vring_virtqueue *vq =to_vvq (_vq); +struct vring_used_elem *u;void *ret;unsigned int i;@@ -300,8 +298,11 @@ static void *vring_get_buf(struct virtqureturn NULL ; } - i = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].id; - *len = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].len; + u = &vq->vring.used->ring[vring_last_used (&vq->vring) % vq->vring.num]; + i = u->id; + *len = u->len; +/* Make sure we don't reload i after doing checks. */ +rmb ();if (unlikely (i >= vq->vring.num)) {BAD_RING (vq,"id %u out of range\n" , i);@@ -315,7 +316,8 @@ static void *vring_get_buf(struct virtqu/* detach_buf clears data, so grab it now. */ ret = vq->data[i];detach_buf (vq, i); - vq->last_used_idx++; +vring_last_used (&vq->vring)++; +END_USE (vq);return ret; }@@ -402,7 +404,6 @@ struct virtqueue *vring_new_virtqueue(un vq->vq.name = name; vq->notify = notify; vq->broken = false; - vq->last_used_idx =0 ; vq->num_added =0 ;list_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG @@ -413,6 +414,10 @@ struct virtqueue *vring_new_virtqueue(un vq->indirect =virtio_has_feature (vdev,VIRTIO_RING_F_INDIRECT_DESC ); +/* We publish indices whether they offer it or not: if not, it's junk +* space anyway. But calling this acknowledges the feature. */ +virtio_has_feature (vdev,VIRTIO_RING_F_PUBLISH_INDICES ); +/* No callback? Tell other side not to bother us. */ if (!callback) vq->vring.avail->flags |=VRING_AVAIL_F_NO_INTERRUPT ;@@ -443,6 +448,8 @@ void vring_transport_features(struct virswitch (i) {case VIRTIO_RING_F_INDIRECT_DESC :break ; +case VIRTIO_RING_F_PUBLISH_INDICES : +break ;default :/* We don't understand this bit. */ clear_bit (i, vdev->features);include/linux/virtio_ring.h +6
-1@@ -29,6 +29,9 @@/* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28 +/* We publish our last-seen used index at the end of the avail ring. */ +#define VIRTIO_RING_F_PUBLISH_INDICES 29 +/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ struct vring_desc {@@ -87,6 +90,7 @@ struct vring {* __u16 avail_flags; * __u16 avail_idx; * __u16 available[num]; +* __u16 last_used_idx; * * // Padding to the next align boundary. * char pad[]; @@ -95,6 +99,7 @@ struct vring {* __u16 used_flags; * __u16 used_idx; * struct vring_used_elem used[num]; +* __u16 last_avail_idx; * }; */ static inline void vring_init(struct vring *vr, unsigned int num, void *p,@@ -111,9 +116,14 @@ static inline unsigned vring_size(unsign {return ((sizeof (struct vring_desc ) * num +sizeof (__u16) * (2 + num) + align -1 ) & ~(align -1 )) - +sizeof (__u16) *2 +sizeof (struct vring_used_elem ) * num; + +sizeof (__u16) *2 +sizeof (struct vring_used_elem ) * num +2 ;
include/linux/virtio_ring.h static inline unsigned vring_size(unsign · line 129
Quoted 10 lines
include/linux/virtio_ring.h +5
-0} +/* We publish the last-seen used index at the end of the available ring, and +* vice-versa. These are at the end for backwards compatibility. */ +#define vring_last_used (vr) ((vr)->avail->ring[(vr)->num]) +#define vring_last_avail (vr) (*(__u16 *)&(vr)->used->ring[(vr)->num]) +#ifdef __KERNEL__#include <linux/irqreturn.h> struct virtio_device ;
--
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>
Re: virtio: put last_used and last_avail index into ring itself.
Quoted 16 lines · Michael S. Tsirkin
Quoted 16 lines
virtio: put
last_usedandlast_availindex into ring itself.Generally, the other end of the virtio ring doesn't need to see where you're up to in consuming the ring. However, to completely understand what's going on from the outside, this information must be exposed. For example, if you want to save and restore a
virtio_ring, but you're not the consumer because the kernel is using it directly.Fortunately, we have room to expand: the ring is always a whole number of pages and there's hundreds of bytes of padding after the avail ring and the used ring, whatever the number of descriptors (which must be a power of 2).
We add a feature bit so the guest can tell the host that it's writing out the current value there, if it wants to use that.
Quoted 7 lines
Quoted 1 line
Signed-off-by Rusty Russell <[email protected]>I've been looking at this patch some more (more on why later), and I wonder: would it be better to add some alignment to the last used index address, so that if we later add more stuff at the tail, it all fits in a single cache line?
In theory, but not in practice. We don't have many rings, so the difference between 1 and 2 cache lines is not very much.
Quoted 10 lines
We use a new feature bit anyway, so layout change should not be a problem.
Since I raised the question of caches: for used ring, the ring is not aligned to 64 bit, so on CPUs with 64 bit or larger cache lines, used entries will often cross cache line boundaries. Am I right and might it
have been better to align ring entries to cache line boundaries?What do you think?
I think everyone is settled on 128 byte cache lines for the forseeable future, so it's not really an issue.
Cheers,
Rusty.
--
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
maintainerRe: virtio: put last_used and last_avail index into ring itself.
Quoted 19 lines · Rusty Russell
Quoted 18 lines · Michael S. Tsirkin
Quoted 17 lines
virtio: put
last_usedandlast_availindex into ring itself.Generally, the other end of the virtio ring doesn't need to see where you're up to in consuming the ring. However, to completely understand what's going on from the outside, this information must be exposed. For example, if you want to save and restore a
virtio_ring, but you're not the consumer because the kernel is using it directly.Fortunately, we have room to expand: the ring is always a whole number of pages and there's hundreds of bytes of padding after the avail ring and the used ring, whatever the number of descriptors (which must be a power of 2).
We add a feature bit so the guest can tell the host that it's writing out the current value there, if it wants to use that.
Signed-off-by Rusty Russell <[email protected]>
Quoted 8 lines
Quoted 5 lines
I've been looking at this patch some more (more on why later), and I wonder: would it be better to add some alignment to the last used index address, so that if we later add more stuff at the tail, it all fits in a single cache line?
In theory, but not in practice. We don't have many rings, so the difference between 1 and 2 cache lines is not very much.
Fair enough.
Quoted 9 lines
Quoted 9 lines
We use a new feature bit anyway, so layout change should not be a problem.
Since I raised the question of caches: for used ring, the ring is not aligned to 64 bit, so on CPUs with 64 bit or larger cache lines, used entries will often cross cache line boundaries. Am I right and might it
have been better to align ring entries to cache line boundaries?
Quoted 7 lines
Quoted 1 line
What do you think?
I think everyone is settled on 128 byte cache lines for the forseeable future, so it's not really an issue.
Cheers,
Rusty.
You mean with 64 bit descriptors we will be bouncing a cache line between host and guest, anyway?
Signature · 7 lines
MST -- 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>
Re: virtio: put last_used and last_avail index into ring itself.
Quoted 9 lines · Michael S. Tsirkin
Quoted 5 lines · Rusty Russell
Quoted 1 line · Michael S. Tsirkin
What do you think?
I think everyone is settled on 128 byte cache lines for the forseeable future, so it's not really an issue.
You mean with 64 bit descriptors we will be bouncing a cache line between host and guest, anyway?
I'm confused by this entire thread.
Descriptors are 16 bytes. They are at the start, so presumably aligned to cache boundaries.
Available ring follows that at 2 bytes per entry, so it's also packed nicely into cachelines.
Then there's padding to page boundary. That puts us on a cacheline again for the used ring; also 2 bytes per entry.
I don't see how any change in layout could be more cache friendly? Rusty.
--
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
maintainerRe: virtio: put last_used and last_avail index into ring itself.
Quoted 13 lines · Rusty Russell
Quoted 9 lines · Michael S. Tsirkin
Quoted 5 lines · Rusty Russell
Quoted 1 line · Michael S. Tsirkin
What do you think?
I think everyone is settled on 128 byte cache lines for the forseeable future, so it's not really an issue.
You mean with 64 bit descriptors we will be bouncing a cache line between host and guest, anyway?
I'm confused by this entire thread.
Quoted 9 lines
Descriptors are 16 bytes. They are at the start, so presumably aligned to cache boundaries.
Available ring follows that at 2 bytes per entry, so it's also packed nicely into cachelines.
Then there's padding to page boundary. That puts us on a cacheline again for the used ring; also 2 bytes per entry.
Hmm, is used ring really 2 bytes per entry?
/* u32 is used here for ids for padding reasons. */
struct vring_used_elem {
/* Index of start of used descriptor chain. */
__u32 id;
/* Total length of the descriptor chain which was used (written to) */
__u32 len;
};struct vring_used {
__u16 flags;
__u16 idx;
struct vring_used_elem ring[];
};Quoted 2 lines
I don't see how any change in layout could be more cache friendly? Rusty.
I thought that used ring has 8 bytes per entry, and that struct vring_used is aligned at page boundary, this
would mean that ring element is at offset 4 bytes from page boundary. Thus with cacheline size 128 bytes, each 4th element crosses a cacheline boundary. If we had a 4 byte padding after idx, each used element would always be completely within a single cacheline.
What am I missing?
Signature · 7 lines
MST -- 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>
Re: virtio: put last_used and last_avail index into ring itself.
Quoted 6 lines · Michael S. Tsirkin
Quoted 3 lines · Rusty Russell
Then there's padding to page boundary. That puts us on a cacheline again for the used ring; also 2 bytes per entry.
Hmm, is used ring really 2 bytes per entry?
Err, no, I am an idiot.
Quoted 8 lines
/* u32 is used here for ids for padding reasons. */
struct vring_used_elem { /* Index of start of used descriptor chain. */ __u32 id; /* Total length of the descriptor chain which was used (written to) */ __u32 len; };
Quoted 5 lines
struct vring_used {
__u16 flags;
__u16 idx;
struct vring_used_elem ring[];
};OK, now I get it. Sorry, I was focussed on the avail ring.
Quoted 6 lines
I thought that used ring has 8 bytes per entry, and that struct
vring_usedis aligned at page boundary, this
would mean that ring element is at offset 4 bytes from page boundary. Thus with cacheline size 128 bytes, each 4th element crosses a cacheline boundary. If we had a 4 byte padding after idx, each used element would always be completely within a single cacheline.
I think the numbers are: every 16th entry hits two cachelines. So currently the first 15 entries are "free" (assuming we hit the idx cacheline anyway), then 1 in 16 cost 2 cachelines. That makes the aligned version win when N > 240.
But, we access the array linearly. So the extra cacheline cost is in fact amortized. I doubt it could be measured, but maybe vring_get_buf() should prefetch? While you're there, we could use an & rather than a mod on the calculation, which may actually be measurable :)
Cheers,
Rusty.
--
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>
Re: virtio: put last_used and last_avail index into ring itself.
In reply to Michael S. TsirkinQuoted 6 lines · Michael S. Tsirkin
Quoted 3 lines · Rusty Russell
Then there's padding to page boundary. That puts us on a cacheline again for the used ring; also 2 bytes per entry.
Hmm, is used ring really 2 bytes per entry?
Err, no, I am an idiot.
Quoted 8 lines
/* u32 is used here for ids for padding reasons. */
struct vring_used_elem { /* Index of start of used descriptor chain. */ __u32 id; /* Total length of the descriptor chain which was used (written to) */ __u32 len; };
Quoted 5 lines
struct vring_used {
__u16 flags;
__u16 idx;
struct vring_used_elem ring[];
};OK, now I get it. Sorry, I was focussed on the avail ring.
Quoted 6 lines
I thought that used ring has 8 bytes per entry, and that struct
vring_usedis aligned at page boundary, this
would mean that ring element is at offset 4 bytes from page boundary. Thus with cacheline size 128 bytes, each 4th element crosses a cacheline boundary. If we had a 4 byte padding after idx, each used element would always be completely within a single cacheline.
I think the numbers are: every 16th entry hits two cachelines. So currently the first 15 entries are "free" (assuming we hit the idx cacheline anyway), then 1 in 16 cost 2 cachelines. That makes the aligned version win when N > 240.
But, we access the array linearly. So the extra cacheline cost is in fact amortized. I doubt it could be measured, but maybe vring_get_buf() should prefetch? While you're there, we could use an & rather than a mod on the calculation, which may actually be measurable :)
Cheers,
Rusty.
Michael S. Tsirkin
maintainerRe: virtio: put last_used and last_avail index into ring itself.
In reply to Rusty RussellQuoted 13 lines · Rusty Russell
Quoted 9 lines · Michael S. Tsirkin
Quoted 5 lines · Rusty Russell
Quoted 1 line · Michael S. Tsirkin
What do you think?
I think everyone is settled on 128 byte cache lines for the forseeable future, so it's not really an issue.
You mean with 64 bit descriptors we will be bouncing a cache line between host and guest, anyway?
I'm confused by this entire thread.
Quoted 9 lines
Descriptors are 16 bytes. They are at the start, so presumably aligned to cache boundaries.
Available ring follows that at 2 bytes per entry, so it's also packed nicely into cachelines.
Then there's padding to page boundary. That puts us on a cacheline again for the used ring; also 2 bytes per entry.
Hmm, is used ring really 2 bytes per entry?
/* u32 is used here for ids for padding reasons. */
struct vring_used_elem {
/* Index of start of used descriptor chain. */
__u32 id;
/* Total length of the descriptor chain which was used (written to) */
__u32 len;
};struct vring_used {
__u16 flags;
__u16 idx;
struct vring_used_elem ring[];
};Quoted 2 lines
I don't see how any change in layout could be more cache friendly? Rusty.
I thought that used ring has 8 bytes per entry, and that struct vring_used is aligned at page boundary, this
would mean that ring element is at offset 4 bytes from page boundary. Thus with cacheline size 128 bytes, each 4th element crosses a cacheline boundary. If we had a 4 byte padding after idx, each used element would always be completely within a single cacheline.
What am I missing?
Signature · 1 line
MST
Re: virtio: put last_used and last_avail index into ring itself.
In reply to Michael S. TsirkinQuoted 9 lines · Michael S. Tsirkin
Quoted 5 lines · Rusty Russell
Quoted 1 line · Michael S. Tsirkin
What do you think?
I think everyone is settled on 128 byte cache lines for the forseeable future, so it's not really an issue.
You mean with 64 bit descriptors we will be bouncing a cache line between host and guest, anyway?
I'm confused by this entire thread.
Descriptors are 16 bytes. They are at the start, so presumably aligned to cache boundaries.
Available ring follows that at 2 bytes per entry, so it's also packed nicely into cachelines.
Then there's padding to page boundary. That puts us on a cacheline again for the used ring; also 2 bytes per entry.
I don't see how any change in layout could be more cache friendly? Rusty.
Michael S. Tsirkin
maintainerRe: virtio: put last_used and last_avail index into ring itself.
In reply to Rusty RussellQuoted 19 lines · Rusty Russell
Quoted 18 lines · Michael S. Tsirkin
Quoted 17 lines
virtio: put
last_usedandlast_availindex into ring itself.Generally, the other end of the virtio ring doesn't need to see where you're up to in consuming the ring. However, to completely understand what's going on from the outside, this information must be exposed. For example, if you want to save and restore a
virtio_ring, but you're not the consumer because the kernel is using it directly.Fortunately, we have room to expand: the ring is always a whole number of pages and there's hundreds of bytes of padding after the avail ring and the used ring, whatever the number of descriptors (which must be a power of 2).
We add a feature bit so the guest can tell the host that it's writing out the current value there, if it wants to use that.
Signed-off-by Rusty Russell <[email protected]>
Quoted 8 lines
Quoted 5 lines
I've been looking at this patch some more (more on why later), and I wonder: would it be better to add some alignment to the last used index address, so that if we later add more stuff at the tail, it all fits in a single cache line?
In theory, but not in practice. We don't have many rings, so the difference between 1 and 2 cache lines is not very much.
Fair enough.
Quoted 9 lines
Quoted 9 lines
We use a new feature bit anyway, so layout change should not be a problem.
Since I raised the question of caches: for used ring, the ring is not aligned to 64 bit, so on CPUs with 64 bit or larger cache lines, used entries will often cross cache line boundaries. Am I right and might it
have been better to align ring entries to cache line boundaries?
Quoted 7 lines
Quoted 1 line
What do you think?
I think everyone is settled on 128 byte cache lines for the forseeable future, so it's not really an issue.
Cheers,
Rusty.
You mean with 64 bit descriptors we will be bouncing a cache line between host and guest, anyway?
Signature · 1 line
MST
Re: virtio: put last_used and last_avail index into ring itself.
In reply to Michael S. TsirkinQuoted 16 lines · Michael S. Tsirkin
Quoted 16 lines
virtio: put
last_usedandlast_availindex into ring itself.Generally, the other end of the virtio ring doesn't need to see where you're up to in consuming the ring. However, to completely understand what's going on from the outside, this information must be exposed. For example, if you want to save and restore a
virtio_ring, but you're not the consumer because the kernel is using it directly.Fortunately, we have room to expand: the ring is always a whole number of pages and there's hundreds of bytes of padding after the avail ring and the used ring, whatever the number of descriptors (which must be a power of 2).
We add a feature bit so the guest can tell the host that it's writing out the current value there, if it wants to use that.
Quoted 7 lines
Quoted 1 line
Signed-off-by Rusty Russell <[email protected]>I've been looking at this patch some more (more on why later), and I wonder: would it be better to add some alignment to the last used index address, so that if we later add more stuff at the tail, it all fits in a single cache line?
In theory, but not in practice. We don't have many rings, so the difference between 1 and 2 cache lines is not very much.
Quoted 10 lines
We use a new feature bit anyway, so layout change should not be a problem.
Since I raised the question of caches: for used ring, the ring is not aligned to 64 bit, so on CPUs with 64 bit or larger cache lines, used entries will often cross cache line boundaries. Am I right and might it
have been better to align ring entries to cache line boundaries?What do you think?
I think everyone is settled on 128 byte cache lines for the forseeable future, so it's not really an issue.
Cheers,
Rusty.
Michael S. Tsirkin
maintainerRe: virtio: put last_used and last_avail index into ring itself.
In reply to Rusty RussellQuoted 13 lines
virtio: put
last_usedandlast_availindex into ring itself.Generally, the other end of the virtio ring doesn't need to see where you're up to in consuming the ring. However, to completely understand what's going on from the outside, this information must be exposed. For example, if you want to save and restore a
virtio_ring, but you're not the consumer because the kernel is using it directly.Fortunately, we have room to expand: the ring is always a whole number of pages and there's hundreds of bytes of padding after the avail ring and the used ring, whatever the number of descriptors (which must be a power of 2).
Quoted 4 lines
We add a feature bit so the guest can tell the host that it's writing out the current value there, if it wants to use that.
Signed-off-by Rusty Russell <[email protected]>
I've been looking at this patch some more (more on why later), and I wonder: would it be better to add some alignment to the last used index address, so that if we later add more stuff at the tail, it all fits in a single cache line?
We use a new feature bit anyway, so layout change should not be a problem.
Since I raised the question of caches: for used ring, the ring is not aligned to 64 bit, so on CPUs with 64 bit or larger cache lines, used entries will often cross cache line boundaries. Am I right and might it
have been better to align ring entries to cache line boundaries?
What do you think?
Quoted 130 lines
--- drivers/virtio/virtio_ring.c | 23 +++++++++++++++-------- include/linux/virtio_ring.h | 12 +++++++++++- 2 files changed, 26 insertions(+), 9 deletions(-)drivers/virtio/virtio_ring.c +15
-8@@ -71,9 +71,6 @@ struct vring_virtqueue/* Number we've added since last sync. */ unsigned int num_added; -/* Last used index we've seen. */ -u16 last_used_idx; -/* How to notify other side. FIXME: commonalize hcalls! */ void (*notify)(struct virtqueue *vq);@@ -278,12 +275,13 @@ static void detach_buf(struct vring_virtstatic inline bool more_used (const struct vring_virtqueue *vq) { -return vq->last_used_idx != vq->vring.used->idx; +return vring_last_used (&vq->vring) != vq->vring.used->idx; }static void *vring_get_buf (struct virtqueue *_vq,unsigned int *len) {struct vring_virtqueue *vq =to_vvq (_vq); +struct vring_used_elem *u;void *ret;unsigned int i;@@ -300,8 +298,11 @@ static void *vring_get_buf(struct virtqureturn NULL ; } - i = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].id; - *len = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].len; + u = &vq->vring.used->ring[vring_last_used (&vq->vring) % vq->vring.num]; + i = u->id; + *len = u->len; +/* Make sure we don't reload i after doing checks. */ +rmb ();if (unlikely (i >= vq->vring.num)) {BAD_RING (vq,"id %u out of range\n" , i);@@ -315,7 +316,8 @@ static void *vring_get_buf(struct virtqu/* detach_buf clears data, so grab it now. */ ret = vq->data[i];detach_buf (vq, i); - vq->last_used_idx++; +vring_last_used (&vq->vring)++; +END_USE (vq);return ret; }@@ -402,7 +404,6 @@ struct virtqueue *vring_new_virtqueue(un vq->vq.name = name; vq->notify = notify; vq->broken = false; - vq->last_used_idx =0 ; vq->num_added =0 ;list_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG @@ -413,6 +414,10 @@ struct virtqueue *vring_new_virtqueue(un vq->indirect =virtio_has_feature (vdev,VIRTIO_RING_F_INDIRECT_DESC ); +/* We publish indices whether they offer it or not: if not, it's junk +* space anyway. But calling this acknowledges the feature. */ +virtio_has_feature (vdev,VIRTIO_RING_F_PUBLISH_INDICES ); +/* No callback? Tell other side not to bother us. */ if (!callback) vq->vring.avail->flags |=VRING_AVAIL_F_NO_INTERRUPT ;@@ -443,6 +448,8 @@ void vring_transport_features(struct virswitch (i) {case VIRTIO_RING_F_INDIRECT_DESC :break ; +case VIRTIO_RING_F_PUBLISH_INDICES : +break ;default :/* We don't understand this bit. */ clear_bit (i, vdev->features);include/linux/virtio_ring.h +11
-1@@ -29,6 +29,9 @@/* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28 +/* We publish our last-seen used index at the end of the avail ring. */ +#define VIRTIO_RING_F_PUBLISH_INDICES 29 +/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ struct vring_desc {@@ -87,6 +90,7 @@ struct vring {* __u16 avail_flags; * __u16 avail_idx; * __u16 available[num]; +* __u16 last_used_idx; * * // Padding to the next align boundary. * char pad[]; @@ -95,6 +99,7 @@ struct vring {* __u16 used_flags; * __u16 used_idx; * struct vring_used_elem used[num]; +* __u16 last_avail_idx; * }; */ static inline void vring_init(struct vring *vr, unsigned int num, void *p,@@ -111,9 +116,14 @@ static inline unsigned vring_size(unsign {return ((sizeof (struct vring_desc ) * num +sizeof (__u16) * (2 + num) + align -1 ) & ~(align -1 )) - +sizeof (__u16) *2 +sizeof (struct vring_used_elem ) * num; + +sizeof (__u16) *2 +sizeof (struct vring_used_elem ) * num +2 ; } +/* We publish the last-seen used index at the end of the available ring, and +* vice-versa. These are at the end for backwards compatibility. */ +#define vring_last_used (vr) ((vr)->avail->ring[(vr)->num]) +#define vring_last_avail (vr) (*(__u16 *)&(vr)->used->ring[(vr)->num]) +#ifdef __KERNEL__#include <linux/irqreturn.h> struct virtio_device ;
Re: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 9 lines · Michael S. Tsirkin
Quoted 7 lines · Rusty Russell
Review
Quoted 5 lines
(quoted diff) +5
-0+/* Caller must have TX VQ lock */ +static void tx_poll_stop(struct vhost_net *net) +{ + if (likely(net->tx_poll_state != VHOST_NET_POLL_STARTED)) + return;likely? Really?
Quoted 7 lines
Hmm ... yes. tx poll stop is called on each packet (as long as we do not fill up 1/2 backend queue), the first call will stop polling the rest checks state and does nothing.
This is because we normally do not care when the message has left the queue in backend device: we tell backend to send it and forget. We only start polling when backend tx queue fills up.
OK, good.
Quoted 11 lines
Quoted 10 lines
Review
Quoted 7 lines
(quoted diff) +7
-0+static void vhost_net_set_features(struct vhost_net *n, u64 features) +{ + size_t hdr_size = features & (1 << VHOST_NET_F_VIRTIO_NET_HDR) ? + sizeof(struct virtio_net_hdr) : 0; + int i; + mutex_lock(&n->dev.mutex); + n->dev.acked_features = features;Why is this called "
acked_features"? Not just "features"? I expected to see code which exposed these back to userspace, and didn't.
Quoted 2 lines
Not sure how do you mean. Userspace sets them, why does it want to get them exposed back?
There's something about the 'acked' which rubs me the wrong way. "enabled_features" is perhaps a better term than "acked_features"; "acked" seems more a user point-of-view, "enabled" seems more driver POV?
set_features matches your ioctl names, but it sounds like a fn name :(
It's marginal. And 'features' is shorter than both.
Quoted 11 lines
Quoted 6 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ switch (ioctl) { + case VHOST_SET_VRING_NUM:I haven't looked at your userspace implementation, but does a generic
VHOST_SET_VRING_STATE&VHOST_GET_VRING_STATEwith a struct make more sense? It'd be simpler here,Not by much though, right?
Quoted 1 line
but not sure if it'd be simpler to use?
Quoted 7 lines
The problem is with
VHOST_SET_VRING_BASEas well. I want it to be separate because I want to make it possible to relocate e.g. used ring to another address while ring is running. This would be a good debugging tool (you look at kernel's used ring, check descriptor, then update guest's used ring) and also possibly an extra way to do migration. And it's nicer to have vring size separate as well, because it is initialized by host and never changed, right?
Actually, this looks wrong to me:
+ case VHOST_SET_VRING_BASE:
...
+ vq->avail_idx = vq->last_avail_idx = s.num;
The last_avail_idx is part of the state of the driver. It needs to be saved and restored over susp/resume. The only reason it's not in the ring itself is because I figured the other side doesn't need to see it (which is true, but missed debugging opportunities as well as man-in-the-middle issues like this one). I had a patch which put this field at the end of the ring, I might resurrect it to avoid this problem. This is backwards compatible with all implementations. See patch at end.
I would drop avail_idx altogether: get_user is basically free, and simplifies a lot. As most state is in the ring, all you need is an ioctl to save/restore the last_avail_idx.
Quoted 4 lines
We could merge DESC, AVAIL, USED, and it will reduce the amount of code in userspace. With both base, size and fds separate, it seemed a bit more symmetrical to have desc/avail/used separate as well. What's your opinion?
Well, DESC, AVAIL, and USED could easily be turned into SET/GET_LAYOUT.
Quoted 5 lines
Quoted 3 lines
For future reference, this is *exactly* the kind of thing which would have been nice as a followup patch. Easy to separate, easy to review, not critical to the core.
Yes. It's not too late to split it out though: should I do it yet?
Only if you're feeling enthused. It's lightly reviewed now.
Cheers,
Rusty.
virtio: put last_used and last_avail index into ring itself.
Generally, the other end of the virtio ring doesn't need to see where you're up to in consuming the ring. However, to completely understand what's going on from the outside, this information must be exposed. For example, if you want to save and restore a virtio_ring, but you're not the consumer because the kernel is using it directly.
Fortunately, we have room to expand: the ring is always a whole number of pages and there's hundreds of bytes of padding after the avail ring and the used ring, whatever the number of descriptors (which must be a power of 2).
We add a feature bit so the guest can tell the host that it's writing out the current value there, if it wants to use that.
--- drivers/virtio/virtio_ring.c | 23 +++++++++++++++-------- include/linux/virtio_ring.h | 12 +++++++++++- 2 files changed, 26 insertions(+), 9 deletions(-)
drivers/virtio/virtio_ring.c +15 -8
@@ -71,9 +71,6 @@ struct vring_virtqueue/* Number we've added since last sync. */ unsigned int num_added; -/* Last used index we've seen. */ -u16 last_used_idx; -/* How to notify other side. FIXME: commonalize hcalls! */ void (*notify)(struct virtqueue *vq);
@@ -278,12 +275,13 @@ static void detach_buf(struct vring_virtstatic inline bool more_used (const struct vring_virtqueue *vq) { -return vq->last_used_idx != vq->vring.used->idx; +return vring_last_used (&vq->vring) != vq->vring.used->idx; }static void *vring_get_buf (struct virtqueue *_vq,unsigned int *len) {struct vring_virtqueue *vq =to_vvq (_vq); +struct vring_used_elem *u;void *ret;unsigned int i;
@@ -300,8 +298,11 @@ static void *vring_get_buf(struct virtqureturn NULL ; } - i = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].id; - *len = vq->vring.used->ring[vq->last_used_idx%vq->vring.num].len; + u = &vq->vring.used->ring[vring_last_used (&vq->vring) % vq->vring.num]; + i = u->id; + *len = u->len; +/* Make sure we don't reload i after doing checks. */ +rmb ();if (unlikely (i >= vq->vring.num)) {BAD_RING (vq,"id %u out of range\n" , i);
@@ -315,7 +316,8 @@ static void *vring_get_buf(struct virtqu/* detach_buf clears data, so grab it now. */ ret = vq->data[i];detach_buf (vq, i); - vq->last_used_idx++; +vring_last_used (&vq->vring)++; +END_USE (vq);return ret; }
@@ -402,7 +404,6 @@ struct virtqueue *vring_new_virtqueue(un vq->vq.name = name; vq->notify = notify; vq->broken = false; - vq->last_used_idx =0 ; vq->num_added =0 ;list_add_tail (&vq->vq.list, &vdev->vqs);#ifdef DEBUG
@@ -413,6 +414,10 @@ struct virtqueue *vring_new_virtqueue(un vq->indirect =virtio_has_feature (vdev,VIRTIO_RING_F_INDIRECT_DESC ); +/* We publish indices whether they offer it or not: if not, it's junk +* space anyway. But calling this acknowledges the feature. */ +virtio_has_feature (vdev,VIRTIO_RING_F_PUBLISH_INDICES ); +/* No callback? Tell other side not to bother us. */ if (!callback) vq->vring.avail->flags |=VRING_AVAIL_F_NO_INTERRUPT ;
@@ -443,6 +448,8 @@ void vring_transport_features(struct virswitch (i) {case VIRTIO_RING_F_INDIRECT_DESC :break ; +case VIRTIO_RING_F_PUBLISH_INDICES : +break ;default :/* We don't understand this bit. */ clear_bit (i, vdev->features);
include/linux/virtio_ring.h +11 -1
@@ -29,6 +29,9 @@/* We support indirect buffer descriptors */ #define VIRTIO_RING_F_INDIRECT_DESC 28 +/* We publish our last-seen used index at the end of the avail ring. */ +#define VIRTIO_RING_F_PUBLISH_INDICES 29 +/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */ struct vring_desc {
@@ -87,6 +90,7 @@ struct vring {* __u16 avail_flags; * __u16 avail_idx; * __u16 available[num]; +* __u16 last_used_idx; * * // Padding to the next align boundary. * char pad[];
@@ -95,6 +99,7 @@ struct vring {* __u16 used_flags; * __u16 used_idx; * struct vring_used_elem used[num]; +* __u16 last_avail_idx; * }; */ static inline void vring_init(struct vring *vr, unsigned int num, void *p,
@@ -111,9 +116,14 @@ static inline unsigned vring_size(unsign {return ((sizeof (struct vring_desc ) * num +sizeof (__u16) * (2 + num) + align -1 ) & ~(align -1 )) - +sizeof (__u16) *2 +sizeof (struct vring_used_elem ) * num; + +sizeof (__u16) *2 +sizeof (struct vring_used_elem ) * num +2 ; } +/* We publish the last-seen used index at the end of the available ring, and +* vice-versa. These are at the end for backwards compatibility. */ +#define vring_last_used (vr) ((vr)->avail->ring[(vr)->num]) +#define vring_last_avail (vr) (*(__u16 *)&(vr)->used->ring[(vr)->num]) +#ifdef __KERNEL__#include <linux/irqreturn.h> struct virtio_device ;
Michael S. Tsirkin
maintainerRe: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
In reply to Rusty RussellQuoted 9 lines · Rusty Russell
Quoted 2 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.
Hi Michael,
Now everyone else has finally kicked all the tires and it seems to pass,
I've done a fairly complete review. Generally, it's really nice; just one bug and a few minor suggestions for polishing.
Thanks for the review! I'll add more polishing and repost. Answers to some questions below.
Quoted 7 lines
drivers/vhost/net.c line 80
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+/* Caller must have TX VQ lock */ +static void tx_poll_stop (struct vhost_net *net) +{ +if (likely (net->tx_poll_state !=VHOST_NET_POLL_STARTED )) +return ;likely? Really?
Hmm ... yes. tx poll stop is called on each packet (as long as we do not fill up 1/2 backend queue), the first call will stop polling the rest checks state and does nothing.
This is because we normally do not care when the message has left the queue in backend device: we tell backend to send it and forget. We only start polling when backend tx queue fills up.
Makes sense?
Quoted 7 lines
drivers/vhost/net.c line 129
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+for (;;) { + head =vhost_get_vq_desc (&net->dev, vq, vq->iov, &out, &in, +NULL ,NULL );Danger! You need an arg to
vhost_get_vq_descto tell it the max desc size you can handle. Otherwise, it's only limited by ring size, and a malicious guest can overflow you here, and below:
In fact, I think this is not a bug. This happens to work correctly (even with malicious guests) because vhost_get_vq_desc is hard-coded to check VHOST_NET_MAX_SG, so in fact no overflow is possible. I agree that it's mich nicer to pass iovec size to vhost_get_vq_desc.
Quoted 10 lines
Quoted 3 lines
drivers/vhost/net.c new file +2
-0+/* Skip header. TODO: support TSO. */ + s =move_iovec_hdr (vq->iov, vq->hdr, hdr_size, out);...
drivers/vhost/net.c line 121
Quoted 4 lines
drivers/vhost/net.c new file +4
-0+ +use_mm (net->dev.mm); +mutex_lock (&vq->mutex); +vhost_no_notify (vq);I prefer a name like "
vhost_disable_notify()".
Good idea.
Quoted 5 lines
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+/* OK, now we need to know about added descriptors. */ +if (head == vq->num &&vhost_notify (vq)) +/* They could have slipped one in as we were doing that: +* check again. */ +continue ;
Quoted 8 lines
drivers/vhost/net.c line 228
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+/* Nothing new? Wait for eventfd to tell us they refilled. */ +if (head == vq->num) +break ; +/* We don't need to be notified again. */ +vhost_no_notify (vq);Similarly,
vhost_enable_notify. This one is particularly misleading since it doesn't actually notify anything!
Good idea.
Quoted 13 lines
In particular, this code would be neater as:
if (head == vq->num) { if (vhost_enable_notify(vq)) { /* Try again, they could have slipped one in. */ continue; } /* Nothing more to do. */ break; } vhost_disable_notify(vq);
Quoted 3 lines
Now, AFAICT vhost_notify()/enable_notify() would be better rewritten to return true only when there's more pending. Saves a loop around here most of the time.
OKay, I'll look into this. It kind of annoys me that we would do get_user for the same value twice: once in vhost_enable_notify and once in vhost_get_vq_desc. OTOH, all the loop does is call vhost_get_vq_desc again.
Quoted 2 lines
Also, the vhost_no_notify/vhost_disable_notify() can be moved
out of the loop entirely.
I don't think it can, if we enabled notification and then see more descriptors in queue, we want to disable notification again. But it can be
Quoted 2 lines
(It could be under an if (unlikely(enabled)), not
sure if it's worth it).
if (unlikely(vhost_enable_notify(vq))) {
/* Try again, they have slipped one in. */ vhost_disable_notify(vq); continue;
}
Quoted 6 lines
drivers/vhost/net.c line 261
Quoted 3 lines
drivers/vhost/net.c new file +2
-0+ len = err; + err =memcpy_toiovec (vq->hdr, (unsigned char *)&hdr, hdr_size);That unsigned char * arg to
memcpy_toiovecis annoying. A patch might be nice, separate from this effort.
Sounds good.
Quoted 11 lines
drivers/vhost/net.c line 322
Quoted 9 lines
drivers/vhost/net.c new file +9
-0+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;I have a personal dislike of calloc for structures.
You mean zalloc?
Quoted 4 lines
In userspace, it's because valgrind can't spot uninitialized fields. These days a similar argument applies in the kernel, because we have KMEMCHECK now. If someone adds a field to the struct and forgets to initialize it, we can spot it.
OK.
Quoted 10 lines
drivers/vhost/net.c line 348
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+static void vhost_net_enable_vq (struct vhost_net *n,int index) +{ +struct socket *sock = n->vqs[index].private_data;OK, I can't help but this that presenting the vqs as an array doesn't buy us very much. Esp. if you change
vhost_dev_initto take a NULL-terminated varargs. I think readability would improve. It means passing a vq around rather than an index.Not completely sure it'll be a win tho.
Hmm, varargs sounds a bit complex. But I agree readability for vhost_net_enable_vq and friends would benefit from passing a vq around rather than an index. I'll try it out and do it if it's a win.
Quoted 22 lines
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+static long vhost_net_set_backend (struct vhost_net *n,unsigned index,int fd) +{ +struct socket *sock, *oldsock =NULL ;...
Quoted 8 lines
drivers/vhost/net.c new file +8
-0+ sock =get_socket (fd); +if (IS_ERR (sock)) { + r =PTR_ERR (sock); + goto done; + } + +/* start polling new socket */ + oldsock = vq->private_data;...
drivers/vhost/net.c line 503
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+done: +mutex_unlock (&n->dev.mutex); +if (oldsock) { +vhost_net_flush_vq (n, index); +fput (oldsock->file);I dislike this style; I prefer multiple different goto points, one for when oldsock is set, and one for when it's not.
Quoted 1 line
That way, gcc warns us about uninitialized variables if we get it wrong.
OK.
Quoted 7 lines
drivers/vhost/net.c line 512
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+static long vhost_net_reset_owner (struct vhost_net *n) +{ +struct socket *tx_sock =NULL ; +struct socket *rx_sock =NULL ; +long r;This should be called "err", since that's what it is.
OK.
Quoted 10 lines
drivers/vhost/net.c line 535
Quoted 7 lines
drivers/vhost/net.c new file +7
-0+static void vhost_net_set_features (struct vhost_net *n,u64 features) +{ +size_t hdr_size = features & (1 <<VHOST_NET_F_VIRTIO_NET_HDR ) ? +sizeof (struct virtio_net_hdr ) :0 ; +int i; +mutex_lock (&n->dev.mutex); + n->dev.acked_features = features;Why is this called "
acked_features"? Not just "features"? I expected to see code which exposed these back to userspace, and didn't.
Not sure how do you mean. Userspace sets them, why does it want to get them exposed back?
Quoted 6 lines
Quoted 6 lines
drivers/vhost/net.c new file +6
-0+case VHOST_GET_FEATURES : + features =VHOST_FEATURES ; +return put_user (features, featurep); +case VHOST_ACK_FEATURES : + r =get_user (features, featurep); +/* No features for now */
Quoted 8 lines
drivers/vhost/net.c line 571
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+if (r <0 ) +return r; +if (features & ~VHOST_FEATURES ) +return -EOPNOTSUPP ; +vhost_net_set_features (n, features);OK, from the userspace POV it's "get features" then "ack features". But I think "
VHOST_SET_FEATURES" is more consistent, despite this usage.
OK.
Quoted 6 lines
drivers/vhost/vhost.c line 264
Quoted 2 lines
drivers/vhost/vhost.c new file +2
-0+switch (ioctl) { +case VHOST_SET_VRING_NUM :I haven't looked at your userspace implementation, but does a generic
VHOST_SET_VRING_STATE&VHOST_GET_VRING_STATEwith a struct make more sense? It'd be simpler here,
Not by much though, right?
Quoted 1 line
but not sure if it'd be simpler to use?
The problem is with VHOST_SET_VRING_BASE as well. I want it to be separate because I want to make it possible to relocate e.g. used ring to another address while ring is running. This would be a good debugging tool (you look at kernel's used ring, check descriptor, then update guest's used ring) and also possibly an extra way to do migration. And it's nicer to have vring size separate as well, because it is initialized by host and never changed, right?
We could merge DESC, AVAIL, USED, and it will reduce the amount of code in userspace. With both base, size and fds separate, it seemed a bit more symmetrical to have desc/avail/used separate as well. What's your opinion?
Quoted 17 lines
(Not the fd-setting ioctls of course)
Quoted 15 lines
drivers/vhost/vhost.c new file +15
-0+case VHOST_SET_VRING_LOG : + r =copy_from_user (&a, argp,sizeof a); +if (r <0 ) +break ; +if (a.padding) { + r = -EOPNOTSUPP ; +break ; + } +if (a.user_addr ==VHOST_VRING_LOG_DISABLE ) { + vq->log_used = false; +break ; + } +if (a.user_addr & (sizeof *vq->used->ring -1 )) { + r = -EINVAL ; +break ;
Quoted 8 lines
drivers/vhost/vhost.c line 362
Quoted 4 lines
drivers/vhost/vhost.c new file +4
-0+ } + vq->log_used = true; + vq->log_addr = a.user_addr; +break ;For future reference, this is *exactly* the kind of thing which would have been nice as a followup patch. Easy to separate, easy to review, not critical to the core.
Yes. It's not too late to split it out though: should I do it yet?
Quoted 9 lines
drivers/vhost/vhost.c line 525
Quoted 6 lines
drivers/vhost/vhost.c new file +6
-0+/* TODO: This is really inefficient. We need something like get_user() +* (instruction directly accesses the data, with an exception table entry +* returning -EFAULT). See Documentation/x86/exception-tables.txt. +*/ +static int set_bit_to_user (int nr,void __user *addr) +{I guess we won't be dealing with many contiguous pages, otherwise we could get a cheap speedup making this
set_bits_to_user(int nr, int num_bits...).
No idea. Let's keep it as simple as possible for now?
Quoted 4 lines
Quoted 4 lines
drivers/vhost/vhost.c new file +4
-0+/* Each buffer in the virtqueues is actually a chain of descriptors. This +* function returns the next descriptor in the chain, +* or -1 if we're at the end. */ +static unsigned next_desc (struct vring_desc *desc)
Quoted 8 lines
drivers/vhost/vhost.c line 635
Quoted 6 lines
drivers/vhost/vhost.c new file +6
-0+{ +unsigned int next; + +/* If this descriptor says it doesn't chain, we're done. */ +if (!(desc->flags &VRING_DESC_F_NEXT )) +return -1 ;Hmm, prefer s/-1/-1U/ in comment, here, and below. Clarifies a bit.
Good idea.
Quoted 4 lines
Quoted 2 lines
+/* After we've used one of their buffers, we tell them about it. We'll then
+ * want to send them an interrupt, usingvq->call. */This comment has too much cut & paste:
I tried to cut and paste as many comments as possible, this made it easy to audit the code by comparing it with lguest, and made them much more witty. But yes, this is definitely going overboard as we do not have vq->call at all :)
Quoted 7 lines
... want to notify the guest, using the eventfd */
drivers/vhost/vhost.c line 904
Quoted 3 lines
drivers/vhost/vhost.c new file +3
-0+/* This actually sends the interrupt for this virtqueue */ +void vhost_trigger_irq (struct vhost_dev *dev,struct vhost_virtqueue *vq) +{Rename
vhost_notify_eventfd()or something, and fix comments?
Sounds good. Since I'm renaming vhost_notify to vhost_enable_notify, this one can just become vhost_notify.
Quoted 4 lines
drivers/vhost/vhost.h line 19
Quoted 2 lines
drivers/vhost/vhost.h new file +2
-0+enum { +VHOST_NET_MAX_SG =MAX_SKB_FRAGS +2 ,+2? Believable, but is it correct?
+ 1 is for skb head, + 1 is for virtio net header. I'll add a comment.
Quoted 8 lines
drivers/vhost/vhost.h line 24
Quoted 3 lines
drivers/vhost/vhost.h new file +3
-0+/* Poll a file (eventfd or socket) */ +/* Note: there's nothing vhost specific about this structure. */ +struct vhost_poll {This comment really helped while reading the code. Kudos!
Thanks!
Rusty.
Re: [PATCHv8 3/3] vhost_net: a kernel-level virtio server
In reply to Michael S. TsirkinQuoted 2 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.
Hi Michael,
Now everyone else has finally kicked all the tires and it seems to pass,
I've done a fairly complete review. Generally, it's really nice; just one bug and a few minor suggestions for polishing.
drivers/vhost/net.c line 80
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+/* Caller must have TX VQ lock */ +static void tx_poll_stop (struct vhost_net *net) +{ +if (likely (net->tx_poll_state !=VHOST_NET_POLL_STARTED )) +return ;
likely? Really?
drivers/vhost/net.c line 129
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+for (;;) { + head =vhost_get_vq_desc (&net->dev, vq, vq->iov, &out, &in, +NULL ,NULL );
Danger! You need an arg to vhost_get_vq_desc to tell it the max desc size you can handle. Otherwise, it's only limited by ring size, and a malicious guest can overflow you here, and below:
Quoted 2 lines
drivers/vhost/net.c new file +2
-0+/* Skip header. TODO: support TSO. */ + s =move_iovec_hdr (vq->iov, vq->hdr, hdr_size, out);
...
drivers/vhost/net.c line 121
Quoted 4 lines
drivers/vhost/net.c new file +4
-0+ +use_mm (net->dev.mm); +mutex_lock (&vq->mutex); +vhost_no_notify (vq);
I prefer a name like "vhost_disable_notify()".
drivers/vhost/net.c line 228
Quoted 10 lines
drivers/vhost/net.c new file +10
-0+/* OK, now we need to know about added descriptors. */ +if (head == vq->num &&vhost_notify (vq)) +/* They could have slipped one in as we were doing that: +* check again. */ +continue ; +/* Nothing new? Wait for eventfd to tell us they refilled. */ +if (head == vq->num) +break ; +/* We don't need to be notified again. */ +vhost_no_notify (vq);
Similarly, vhost_enable_notify. This one is particularly misleading since it doesn't actually notify anything!
In particular, this code would be neater as:
if (head == vq->num) {
if (vhost_enable_notify(vq)) {
/* Try again, they could have slipped one in. */
continue;
}
/* Nothing more to do. */
break;
}
vhost_disable_notify(vq);Now, AFAICT vhost_notify()/enable_notify() would be better rewritten to return true only when there's more pending. Saves a loop around here most of the time. Also, the vhost_no_notify/vhost_disable_notify() can be moved out of the loop entirely. (It could be under an if (unlikely(enabled)), not sure if it's worth it).
drivers/vhost/net.c line 261
Quoted 2 lines
drivers/vhost/net.c new file +2
-0+ len = err; + err =memcpy_toiovec (vq->hdr, (unsigned char *)&hdr, hdr_size);
That unsigned char * arg to memcpy_toiovec is annoying. A patch might be nice, separate from this effort.
drivers/vhost/net.c line 322
Quoted 9 lines
drivers/vhost/net.c new file +9
-0+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;
I have a personal dislike of calloc for structures. In userspace, it's because valgrind can't spot uninitialized fields. These days a similar argument applies in the kernel, because we have KMEMCHECK now. If someone adds a field to the struct and forgets to initialize it, we can spot it.
drivers/vhost/net.c line 348
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+static void vhost_net_enable_vq (struct vhost_net *n,int index) +{ +struct socket *sock = n->vqs[index].private_data;
OK, I can't help but this that presenting the vqs as an array doesn't buy us very much. Esp. if you change vhost_dev_init to take a NULL-terminated varargs. I think readability would improve. It means passing a vq around rather than an index.
Not completely sure it'll be a win tho.
Quoted 3 lines
drivers/vhost/net.c new file +3
-0+static long vhost_net_set_backend (struct vhost_net *n,unsigned index,int fd) +{ +struct socket *sock, *oldsock =NULL ;
...
Quoted 8 lines
drivers/vhost/net.c new file +8
-0+ sock =get_socket (fd); +if (IS_ERR (sock)) { + r =PTR_ERR (sock); + goto done; + } + +/* start polling new socket */ + oldsock = vq->private_data;
...
drivers/vhost/net.c line 503
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+done: +mutex_unlock (&n->dev.mutex); +if (oldsock) { +vhost_net_flush_vq (n, index); +fput (oldsock->file);
I dislike this style; I prefer multiple different goto points, one for when oldsock is set, and one for when it's not.
That way, gcc warns us about uninitialized variables if we get it wrong.
drivers/vhost/net.c line 512
Quoted 5 lines
drivers/vhost/net.c new file +5
-0+static long vhost_net_reset_owner (struct vhost_net *n) +{ +struct socket *tx_sock =NULL ; +struct socket *rx_sock =NULL ; +long r;
This should be called "err", since that's what it is.
drivers/vhost/net.c line 535
Quoted 7 lines
drivers/vhost/net.c new file +7
-0+static void vhost_net_set_features (struct vhost_net *n,u64 features) +{ +size_t hdr_size = features & (1 <<VHOST_NET_F_VIRTIO_NET_HDR ) ? +sizeof (struct virtio_net_hdr ) :0 ; +int i; +mutex_lock (&n->dev.mutex); + n->dev.acked_features = features;
Why is this called "acked_features"? Not just "features"? I expected to see code which exposed these back to userspace, and didn't.
drivers/vhost/net.c line 571
Quoted 11 lines
drivers/vhost/net.c new file +11
-0+case VHOST_GET_FEATURES : + features =VHOST_FEATURES ; +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 & ~VHOST_FEATURES ) +return -EOPNOTSUPP ; +vhost_net_set_features (n, features);
OK, from the userspace POV it's "get features" then "ack features". But I think "VHOST_SET_FEATURES" is more consistent, despite this usage.
drivers/vhost/vhost.c line 264
Quoted 2 lines
drivers/vhost/vhost.c new file +2
-0+switch (ioctl) { +case VHOST_SET_VRING_NUM :
I haven't looked at your userspace implementation, but does a generic VHOST_SET_VRING_STATE & VHOST_GET_VRING_STATE with a struct make more sense? It'd be simpler here, but not sure if it'd be simpler to use?
(Not the fd-setting ioctls of course)
Quoted 9 lines
drivers/vhost/vhost.c new file +9
-0+case VHOST_SET_VRING_LOG : + r =copy_from_user (&a, argp,sizeof a); +if (r <0 ) +break ; +if (a.padding) { + r = -EOPNOTSUPP ; +break ; + } +if (a.user_addr ==VHOST_VRING_LOG_DISABLE ) {
drivers/vhost/vhost.c line 362
Quoted 10 lines
drivers/vhost/vhost.c new file +10
-0+ vq->log_used = false; +break ; + } +if (a.user_addr & (sizeof *vq->used->ring -1 )) { + r = -EINVAL ; +break ; + } + vq->log_used = true; + vq->log_addr = a.user_addr; +break ;
For future reference, this is *exactly* the kind of thing which would have been nice as a followup patch. Easy to separate, easy to review, not critical to the core.
drivers/vhost/vhost.c line 525
Quoted 6 lines
drivers/vhost/vhost.c new file +6
-0+/* TODO: This is really inefficient. We need something like get_user() +* (instruction directly accesses the data, with an exception table entry +* returning -EFAULT). See Documentation/x86/exception-tables.txt. +*/ +static int set_bit_to_user (int nr,void __user *addr) +{
I guess we won't be dealing with many contiguous pages, otherwise we could get a cheap speedup making this set_bits_to_user(int nr, int num_bits...).
drivers/vhost/vhost.c line 635
Quoted 10 lines
drivers/vhost/vhost.c new file +10
-0+/* Each buffer in the virtqueues is actually a chain of descriptors. This +* function returns the next descriptor in the chain, +* or -1 if we're at the end. */ +static unsigned next_desc (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 -1 ;
Hmm, prefer s/-1/-1U/ in comment, here, and below. Clarifies a bit.
Quoted 2 lines
+/* After we've used one of their buffers, we tell them about it. We'll then
+ * want to send them an interrupt, usingvq->call. */
This comment has too much cut & paste:
... want to notify the guest, using the eventfd */
drivers/vhost/vhost.c line 904
Quoted 3 lines
drivers/vhost/vhost.c new file +3
-0+/* This actually sends the interrupt for this virtqueue */ +void vhost_trigger_irq (struct vhost_dev *dev,struct vhost_virtqueue *vq) +{
Rename vhost_notify_eventfd() or something, and fix comments?
drivers/vhost/vhost.h line 19
Quoted 2 lines
drivers/vhost/vhost.h new file +2
-0+enum { +VHOST_NET_MAX_SG =MAX_SKB_FRAGS +2 ,
+2? Believable, but is it correct?
drivers/vhost/vhost.h line 24
Quoted 3 lines
drivers/vhost/vhost.h new file +3
-0+/* Poll a file (eventfd or socket) */ +/* Note: there's nothing vhost specific about this structure. */ +struct vhost_poll {
This comment really helped while reading the code. Kudos!
Thanks!
Rusty.
Michael S. Tsirkin
maintainerPATCH v8 3/3 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, bug work-arounds in userspace)
- write logging is supported (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 backends 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 tap device. Backend is also configured by userspace, including vlan/mac etc.
Status: This works for me, and I haven't see any crashes. Compared to userspace, people reported improved latency (as I save up to 4 system calls per packet), as well as better bandwidth and CPU utilization.
Features that I plan to look at in the future:
- mergeable buffers
- zero copy
- scalability tuning: figure out the best threading model to use
Note on RCU usage (this is also documented in vhost.h, near private_pointer which is the value protected by this variant of RCU):
what is happening is that the rcu_dereference() is being used in a workqueue item. The role of rcu_read_lock() is taken on by the start of execution of the workqueue item, of rcu_read_unlock() by the end of execution of the workqueue item, and of synchronize_rcu() by flush_workqueue()/flush_work().
---
Paul, I'm Cc you so that you can verify that nothing significant changed in vhost/net.c since you approved it the previous time. Could you please Ack use of rcu_dereference/rcu_assign_pointer there? Eric Dumazet proposed that I open-code these, he suggested that this is an abuse of these macros and not enough like RCU. What is your opinion?
MAINTAINERS | 9 + arch/x86/kvm/Kconfig | 1 + drivers/Makefile | 1 + drivers/vhost/Kconfig | 11 + drivers/vhost/Makefile | 2 + drivers/vhost/net.c | 633 +++++++++++++++++++++++++++++ drivers/vhost/vhost.c | 970 ++++++++++++++++++++++++++++++++++++++++++++ drivers/vhost/vhost.h | 158 +++++++ include/linux/Kbuild | 1 + include/linux/miscdevice.h | 1 + include/linux/vhost.h | 126 ++++++ 11 files changed, 1913 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 +9 -0
@@ -5619,6 +5619,15 @@ S: Maintained F: Documentation/filesystems/vfat.txt F: fs/fat/ +VIRTIO HOST (VHOST) +M: "Michael S. Tsirkin" <[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 M: Roger Luethi <[email protected]> S: Maintained
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
@@ -106,6 +106,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/vhost/Kconfig new file +11 -0
@@ -0,0 +1,11 @@ +config VHOST_NET + tristate "Host kernel accelerator for virtio net (EXPERIMENTAL)" + depends on NET && EVENTFD && EXPERIMENTAL + ---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 +633 -0
633 lines not on this page · raw message
drivers/vhost/vhost.c new file +970 -0
970 lines not on this page · raw message
drivers/vhost/vhost.h new file +158 -0
158 lines not on this page · raw message
include/linux/Kbuild +1 -0
@@ -361,6 +361,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 +126 -0
126 lines not on this page · raw message
Signature · 1 line
1.6.5.2.143.g8cc62