"Michael S. Tsirkin" <m...@redhat.com> wrote on 10/08/2014 10:45:59 PM:

> From: "Michael S. Tsirkin" <m...@redhat.com>
> To: Razya Ladelsky/Haifa/IBM@IBMIL, 
> Cc: k...@vger.kernel.org, Alex Glikson/Haifa/IBM@IBMIL, Eran 
> Raichstein/Haifa/IBM@IBMIL, Yossi Kuperman1/Haifa/IBM@IBMIL, Joel 
> Nider/Haifa/IBM@IBMIL, abel.gor...@gmail.com, linux-
> ker...@vger.kernel.org, netdev@vger.kernel.org, 
> virtualizat...@lists.linux-foundation.org
> Date: 10/08/2014 10:45 PM
> Subject: Re: [PATCH] vhost: Add polling mode
> 
> On Sun, Aug 10, 2014 at 11:30:35AM +0300, Razya Ladelsky wrote:
> > From: Razya Ladelsky <ra...@il.ibm.com>
> > Date: Thu, 31 Jul 2014 09:47:20 +0300
> > Subject: [PATCH] vhost: Add polling mode
> > 
> > When vhost is waiting for buffers from the guest driver (e.g., 
> more packets to
> > send in vhost-net's transmit queue), it normally goes to sleep and
> waits for the
> > guest to "kick" it. This kick involves a PIO in the guest, and 
> therefore an exit
> > (and possibly userspace involvement in translating this PIO exit into 
a file
> > descriptor event), all of which hurts performance.
> > 
> > If the system is under-utilized (has cpu time to spare), vhost can
> continuously
> > poll the virtqueues for new buffers, and avoid asking the guest to 
kick us.
> > This patch adds an optional polling mode to vhost, that can be enabled 
via a
> > kernel module parameter, "poll_start_rate".
> > 
> > When polling is active for a virtqueue, the guest is asked to disable
> > notification (kicks), and the worker thread continuously checks 
> for new buffers.
> > When it does discover new buffers, it simulates a "kick" by invoking 
the
> > underlying backend driver (such as vhost-net), which thinks it got
> a real kick
> > from the guest, and acts accordingly. If the underlying driver 
> asks not to be
> > kicked, we disable polling on this virtqueue.
> > 
> > We start polling on a virtqueue when we notice it has work to do. 
Polling on
> > this virtqueue is later disabled after 3 seconds of polling 
> turning up no new
> > work, as in this case we are better off returning to the exit-
> based notification
> > mechanism. The default timeout of 3 seconds can be changed with the
> > "poll_stop_idle" kernel module parameter.
> > 
> > This polling approach makes lot of sense for new HW with posted-
> interrupts for
> > which we have exitless host-to-guest notifications. But even with 
> support for
> > posted interrupts, guest-to-host communication still causes exits.
> Polling adds
> > the missing part.
> > 
> > When systems are overloaded, there won't be enough cpu time for the 
various
> > vhost threads to poll their guests' devices. For these scenarios, 
> we plan to add
> > support for vhost threads that can be shared by multiple devices, even 
of
> > multiple vms.
> > Our ultimate goal is to implement the I/O acceleration features 
> described in:
> > KVM Forum 2013: Efficient and Scalable Virtio (by Abel Gordon)
> > https://www.youtube.com/watch?v=9EyweibHfEs
> > and
> > https://www.mail-archive.com/kvm@vger.kernel.org/msg98179.html
> > 
> > I ran some experiments with TCP stream netperf and filebench 
> (having 2 threads
> > performing random reads) benchmarks on an IBM System x3650 M4.
> > I have two machines, A and B. A hosts the vms, B runs the netserver.
> > The vms (on A) run netperf, its destination server is running on B.
> > All runs loaded the guests in a way that they were (cpu) 
> saturated. For example,
> > I ran netperf with 64B messages, which is heavily loading the vm 
> (which is why
> > its throughput is low).
> > The idea was to get it 100% loaded, so we can see that the polling
> is getting it
> > to produce higher throughput.
> 
> And, did your tests actually produce 100% load on both host CPUs?
> 

The vm indeed utilized 100% cpu, whether polling was enabled or not.
The vhost thread utilized less than 100% (of the other cpu) when polling 
was disabled.
Enabling polling increased its utilization to 100% (in which case both 
cpus were 100% utilized). 
 

> > The system had two cores per guest, as to allow for both the vcpu 
> and the vhost
> > thread to run concurrently for maximum throughput (but I didn't 
> pin the threads
> > to specific cores).
> > My experiments were fair in a sense that for both cases, with or 
without
> > polling, I run both threads, vcpu and vhost, on 2 cores (set their
> affinity that
> > way). The only difference was whether polling was enabled/disabled.
> > 
> > Results:
> > 
> > Netperf, 1 vm:
> > The polling patch improved throughput by ~33% (1516 MB/sec -> 2046 
MB/sec).
> > Number of exits/sec decreased 6x.
> > The same improvement was shown when I tested with 3 vms running 
netperf
> > (4086 MB/sec -> 5545 MB/sec).
> > 
> > filebench, 1 vm:
> > ops/sec improved by 13% with the polling patch. Number of exits 
> was reduced by
> > 31%.
> > The same experiment with 3 vms running filebench showed similar 
numbers.
> > 
> > Signed-off-by: Razya Ladelsky <ra...@il.ibm.com>
> > ---
> >  drivers/vhost/net.c   |    6 +-
> >  drivers/vhost/scsi.c  |    6 +-
> >  drivers/vhost/vhost.c |  245 ++++++++++++++++++++++++++++++++++++
> +++++++++++--
> >  drivers/vhost/vhost.h |   38 +++++++-
> >  4 files changed, 277 insertions(+), 18 deletions(-)
> > 
> > diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
> > index 971a760..558aecb 100644
> > --- a/drivers/vhost/net.c
> > +++ b/drivers/vhost/net.c
> > @@ -742,8 +742,10 @@ static int vhost_net_open(struct inode 
> *inode, struct file *f)
> >     }
> >     vhost_dev_init(dev, vqs, VHOST_NET_VQ_MAX);
> > 
> > -   vhost_poll_init(n->poll + VHOST_NET_VQ_TX, handle_tx_net, POLLOUT, 
dev);
> > -   vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, POLLIN, 
dev);
> > +   vhost_poll_init(n->poll + VHOST_NET_VQ_TX, handle_tx_net, POLLOUT,
> > +         vqs[VHOST_NET_VQ_TX]);
> > +   vhost_poll_init(n->poll + VHOST_NET_VQ_RX, handle_rx_net, POLLIN,
> > +         vqs[VHOST_NET_VQ_RX]);
> > 
> >     f->private_data = n;
> > 
> > diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
> > index 4f4ffa4..665eeeb 100644
> > --- a/drivers/vhost/scsi.c
> > +++ b/drivers/vhost/scsi.c
> > @@ -1528,9 +1528,9 @@ static int vhost_scsi_open(struct inode 
> *inode, struct file *f)
> >     if (!vqs)
> >        goto err_vqs;
> > 
> > -   vhost_work_init(&vs->vs_completion_work, 
vhost_scsi_complete_cmd_work);
> > -   vhost_work_init(&vs->vs_event_work, tcm_vhost_evt_work);
> > -
> > +   vhost_work_init(&vs->vs_completion_work, NULL,
> > +                  vhost_scsi_complete_cmd_work);
> > +   vhost_work_init(&vs->vs_event_work, NULL, tcm_vhost_evt_work);
> >     vs->vs_events_nr = 0;
> >     vs->vs_events_missed = false;
> > 
> > diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
> > index c90f437..fbe8174 100644
> > --- a/drivers/vhost/vhost.c
> > +++ b/drivers/vhost/vhost.c
> > @@ -24,9 +24,17 @@
> >  #include <linux/slab.h>
> >  #include <linux/kthread.h>
> >  #include <linux/cgroup.h>
> > +#include <linux/jiffies.h>
> >  #include <linux/module.h>
> > 
> >  #include "vhost.h"
> > +static int poll_start_rate = 0;
> > +module_param(poll_start_rate, int, S_IRUGO|S_IWUSR);
> > +MODULE_PARM_DESC(poll_start_rate, "Start continuous polling of 
> virtqueue when rate of events is at least this number per jiffy. If 
> 0, never start polling.");
> > +
> > +static int poll_stop_idle = 3*HZ; /* 3 seconds */
> > +module_param(poll_stop_idle, int, S_IRUGO|S_IWUSR);
> > +MODULE_PARM_DESC(poll_stop_idle, "Stop continuous polling of 
> virtqueue after this many jiffies of no work.");
> > 
> >  enum {
> >     VHOST_MEMORY_MAX_NREGIONS = 64,
> > @@ -58,27 +66,28 @@ static int vhost_poll_wakeup(wait_queue_t 
> *wait, unsigned mode, int sync,
> >     return 0;
> >  }
> > 
> > -void vhost_work_init(struct vhost_work *work, vhost_work_fn_t fn)
> > +void vhost_work_init(struct vhost_work *work, struct vhost_virtqueue 
*vq,
> > +                     vhost_work_fn_t fn)
> >  {
> >     INIT_LIST_HEAD(&work->node);
> >     work->fn = fn;
> >     init_waitqueue_head(&work->done);
> >     work->flushing = 0;
> >     work->queue_seq = work->done_seq = 0;
> > +   work->vq = vq;
> >  }
> >  EXPORT_SYMBOL_GPL(vhost_work_init);
> > 
> >  /* Init poll structure */
> >  void vhost_poll_init(struct vhost_poll *poll, vhost_work_fn_t fn,
> > -           unsigned long mask, struct vhost_dev *dev)
> > +           unsigned long mask, struct vhost_virtqueue *vq)
> >  {
> >     init_waitqueue_func_entry(&poll->wait, vhost_poll_wakeup);
> >     init_poll_funcptr(&poll->table, vhost_poll_func);
> >     poll->mask = mask;
> > -   poll->dev = dev;
> > +   poll->dev = vq->dev;
> >     poll->wqh = NULL;
> > -
> > -   vhost_work_init(&poll->work, fn);
> > +   vhost_work_init(&poll->work, vq, fn);
> >  }
> >  EXPORT_SYMBOL_GPL(vhost_poll_init);
> > 
> > @@ -174,6 +183,86 @@ void vhost_poll_queue(struct vhost_poll *poll)
> >  }
> >  EXPORT_SYMBOL_GPL(vhost_poll_queue);
> > 
> > +/* Enable or disable virtqueue polling (vqpoll.enabled) for a 
virtqueue.
> > + *
> > + * Enabling this mode it tells the guest not to notify ("kick") us 
when its
> > + * has made more work available on this virtqueue; Rather, we 
> will continuously
> > + * poll this virtqueue in the worker thread. If multiple 
> virtqueues are polled,
> > + * the worker thread polls them all, e.g., in a round-robin fashion.
> > + * Note that vqpoll.enabled doesn't always mean that this virtqueue 
is
> > + * actually being polled: The backend (e.g., net.c) may 
> temporarily disable it
> > + * using vhost_disable/enable_notify(), while vqpoll.enabled is 
unchanged.
> > + *
> > + * It is assumed that these functions are called relatively 
> rarely, when vhost
> > + * notices that this virtqueue's usage pattern significantly 
> changed in a way
> > + * that makes polling more efficient than notification, or vice 
versa.
> > + * Also, we assume that vhost_vq_disable_vqpoll() is always called on 
vq
> > + * cleanup, so any allocations done by vhost_vq_enable_vqpoll() can 
be
> > + * reclaimed.
> > + */
> > +static void vhost_vq_enable_vqpoll(struct vhost_virtqueue *vq)
> > +{
> > +   if (vq->vqpoll.enabled)
> > +      return; /* already enabled, nothing to do */
> > +   if (!vq->handle_kick)
> > +      return; /* polling will be a waste of time if no callback! */
> > +   if (!(vq->used_flags & VRING_USED_F_NO_NOTIFY)) {
> > +      /* vq has guest notifications enabled. Disable them,
> > +         and instead add vq to the polling list */
> > +      vhost_disable_notify(vq->dev, vq);
> > +      list_add_tail(&vq->vqpoll.link, &vq->dev->vqpoll_list);
> > +   }
> > +   vq->vqpoll.jiffies_last_kick = jiffies;
> > +   __get_user(vq->avail_idx, &vq->avail->idx);
> > +   vq->vqpoll.enabled = true;
> > +
> > +   /* Map userspace's vq->avail to the kernel's memory space. */
> > +   if (get_user_pages_fast((unsigned long)vq->avail, 1, 0,
> > +      &vq->vqpoll.avail_page) != 1) {
> > +      /* TODO: can this happen, as we check access
> > +      to vq->avail in advance? */
> > +      BUG();
> > +   }
> > +   vq->vqpoll.avail_mapped = (struct vring_avail *) (
> > +      (unsigned long)kmap(vq->vqpoll.avail_page) |
> > +      ((unsigned long)vq->avail & ~PAGE_MASK));
> > +}
> > +
> > +/*
> > + * This function doesn't always succeed in changing the mode. 
Sometimes
> > + * a temporary race condition prevents turning on guest 
notifications, so
> > + * vq should be polled next time again.
> > + */
> > +static void vhost_vq_disable_vqpoll(struct vhost_virtqueue *vq)
> > +{
> > +   if (!vq->vqpoll.enabled)
> > +      return; /* already disabled, nothing to do */
> > +
> > +   vq->vqpoll.enabled = false;
> > +
> > +   if (!list_empty(&vq->vqpoll.link)) {
> > +      /* vq is on the polling list, remove it from this list and
> > +       * instead enable guest notifications. */
> > +      list_del_init(&vq->vqpoll.link);
> > +      if (unlikely(vhost_enable_notify(vq->dev, vq))
> > +         && !vq->vqpoll.shutdown) {
> > +         /* Race condition: guest wrote before we enabled
> > +          * notification, so we'll never get a notification for
> > +          * this work - so continue polling mode for a while. */
> > +         vhost_disable_notify(vq->dev, vq);
> > +         vq->vqpoll.enabled = true;
> > +         vhost_enable_notify(vq->dev, vq);
> > +         return;
> > +      }
> > +   }
> > +
> > +   if (vq->vqpoll.avail_mapped) {
> > +      kunmap(vq->vqpoll.avail_page);
> > +      put_page(vq->vqpoll.avail_page);
> > +      vq->vqpoll.avail_mapped = 0;
> > +   }
> > +}
> > +
> >  static void vhost_vq_reset(struct vhost_dev *dev,
> >              struct vhost_virtqueue *vq)
> >  {
> > @@ -199,6 +288,48 @@ static void vhost_vq_reset(struct vhost_dev *dev,
> >     vq->call = NULL;
> >     vq->log_ctx = NULL;
> >     vq->memory = NULL;
> > +   INIT_LIST_HEAD(&vq->vqpoll.link);
> > +   vq->vqpoll.enabled = false;
> > +   vq->vqpoll.shutdown = false;
> > +   vq->vqpoll.avail_mapped = NULL;
> > +}
> > +
> > +/* roundrobin_poll() takes worker->vqpoll_list, and returns one of 
the
> > + * virtqueues which the caller should kick, or NULL in case none 
should be
> > + * kicked. roundrobin_poll() also disables polling on a 
virtqueuewhich has
> > + * been polled for too long without success.
> > + *
> > + * This current implementation (the "round-robin" implementation) 
only
> > + * polls the first vq in the list, returning it or NULL as 
appropriate, and
> > + * moves this vq to the end of the list, so next time a different one 
is
> > + * polled.
> > + */
> > +static struct vhost_virtqueue *roundrobin_poll(struct list_head 
*list)
> > +{
> > +   struct vhost_virtqueue *vq;
> > +   u16 avail_idx;
> > +
> > +   if (list_empty(list))
> > +      return NULL;
> > +
> > +   vq = list_first_entry(list, struct vhost_virtqueue, vqpoll.link);
> > +   WARN_ON(!vq->vqpoll.enabled);
> > +   list_move_tail(&vq->vqpoll.link, list);
> > +
> > +   /* See if there is any new work available from the guest. */
> > +   /* TODO: can check the optional idx feature, and if we haven't
> > +   * reached that idx yet, don't kick... */
> > +   avail_idx = vq->vqpoll.avail_mapped->idx;
> > +   if (avail_idx != vq->last_avail_idx)
> > +      return vq;
> > +
> > +   if (jiffies > vq->vqpoll.jiffies_last_kick + poll_stop_idle) {
> > +      /* We've been polling this virtqueue for a long time with no
> > +      * results, so switch back to guest notification
> > +      */
> > +      vhost_vq_disable_vqpoll(vq);
> > +   }
> > +   return NULL;
> >  }
> > 
> >  static int vhost_worker(void *data)
> > @@ -237,12 +368,62 @@ static int vhost_worker(void *data)
> >        spin_unlock_irq(&dev->work_lock);
> > 
> >        if (work) {
> > +         struct vhost_virtqueue *vq = work->vq;
> >           __set_current_state(TASK_RUNNING);
> >           work->fn(work);
> > +         /* Keep track of the work rate, for deciding when to
> > +          * enable polling */
> > +         if (vq) {
> > +            if (vq->vqpoll.jiffies_last_work != jiffies) {
> > +               vq->vqpoll.jiffies_last_work = jiffies;
> > +               vq->vqpoll.work_this_jiffy = 0;
> > +            }
> > +            vq->vqpoll.work_this_jiffy++;
> > +         }
> > +         /* If vq is in the round-robin list of virtqueues being
> > +          * constantly checked by this thread, move vq the end
> > +          * of the queue, because it had its fair chance now.
> > +          */
> > +         if (vq && !list_empty(&vq->vqpoll.link)) {
> > +            list_move_tail(&vq->vqpoll.link,
> > +               &dev->vqpoll_list);
> > +         }
> > +         /* Otherwise, if this vq is looking for notifications
> > +          * but vq polling is not enabled for it, do it now.
> > +          */
> > +         else if (poll_start_rate && vq && vq->handle_kick &&
> > +            !vq->vqpoll.enabled &&
> > +            !vq->vqpoll.shutdown &&
> > +            !(vq->used_flags & VRING_USED_F_NO_NOTIFY) &&
> > +            vq->vqpoll.work_this_jiffy >=
> > +               poll_start_rate) {
> > +            vhost_vq_enable_vqpoll(vq);
> > +         }
> > +      }
> > +      /* Check one virtqueue from the round-robin list */
> > +      if (!list_empty(&dev->vqpoll_list)) {
> > +         struct vhost_virtqueue *vq;
> > +
> > +         vq = roundrobin_poll(&dev->vqpoll_list);
> > +
> > +         if (vq) {
> > +            vq->handle_kick(&vq->poll.work);
> > +            vq->vqpoll.jiffies_last_kick = jiffies;
> > +         }
> > +
> > +         /* If our polling list isn't empty, ask to continue
> > +          * running this thread, don't yield.
> > +          */
> > +         __set_current_state(TASK_RUNNING);
> >           if (need_resched())
> >              schedule();
> > -      } else
> > -         schedule();
> > +      } else {
> > +         if (work) {
> > +            if (need_resched())
> > +               schedule();
> > +         } else
> > +            schedule();
> > +      }
> > 
> >     }
> >     unuse_mm(dev->mm);
> > @@ -306,6 +487,7 @@ void vhost_dev_init(struct vhost_dev *dev,
> >     dev->mm = NULL;
> >     spin_lock_init(&dev->work_lock);
> >     INIT_LIST_HEAD(&dev->work_list);
> > +   INIT_LIST_HEAD(&dev->vqpoll_list);
> >     dev->worker = NULL;
> > 
> >     for (i = 0; i < dev->nvqs; ++i) {
> > @@ -318,7 +500,7 @@ void vhost_dev_init(struct vhost_dev *dev,
> >        vhost_vq_reset(dev, vq);
> >        if (vq->handle_kick)
> >           vhost_poll_init(&vq->poll, vq->handle_kick,
> > -               POLLIN, dev);
> > +               POLLIN, vq);
> >     }
> >  }
> >  EXPORT_SYMBOL_GPL(vhost_dev_init);
> > @@ -350,7 +532,7 @@ static int vhost_attach_cgroups(struct vhost_dev 
*dev)
> >     struct vhost_attach_cgroups_struct attach;
> > 
> >     attach.owner = current;
> > -   vhost_work_init(&attach.work, vhost_attach_cgroups_work);
> > +   vhost_work_init(&attach.work, NULL, vhost_attach_cgroups_work);
> >     vhost_work_queue(dev, &attach.work);
> >     vhost_work_flush(dev, &attach.work);
> >     return attach.ret;
> > @@ -444,6 +626,26 @@ void vhost_dev_stop(struct vhost_dev *dev)
> >  }
> >  EXPORT_SYMBOL_GPL(vhost_dev_stop);
> > 
> > +/* shutdown_vqpoll() asks the worker thread to shut down virtqueue 
polling
> > + * mode for a given virtqueue which is itself being shut down. We ask 
the
> > + * worker thread to do this rather than doing it directly, so that we 
don't
> > + * race with the worker thread's use of the queue.
> > + */
> > +static void shutdown_vqpoll_work(struct vhost_work *work)
> > +{
> > +   work->vq->vqpoll.shutdown = true;
> > +   vhost_vq_disable_vqpoll(work->vq);
> > +   WARN_ON(work->vq->vqpoll.avail_mapped);
> > +}
> > +
> > +static void shutdown_vqpoll(struct vhost_virtqueue *vq)
> > +{
> > +   struct vhost_work work;
> > +
> > +   vhost_work_init(&work, vq, shutdown_vqpoll_work);
> > +   vhost_work_queue(vq->dev, &work);
> > +   vhost_work_flush(vq->dev, &work);
> > +}
> >  /* Caller should have device mutex if and only if locked is set */
> >  void vhost_dev_cleanup(struct vhost_dev *dev, bool locked)
> >  {
> > @@ -460,6 +662,7 @@ void vhost_dev_cleanup(struct vhost_dev *dev, 
> bool locked)
> >           eventfd_ctx_put(dev->vqs[i]->call_ctx);
> >        if (dev->vqs[i]->call)
> >           fput(dev->vqs[i]->call);
> > +      shutdown_vqpoll(dev->vqs[i]);
> >        vhost_vq_reset(dev, dev->vqs[i]);
> >     }
> >     vhost_dev_free_iovecs(dev);
> > @@ -1491,6 +1694,19 @@ bool vhost_enable_notify(struct vhost_dev 
> *dev, struct vhost_virtqueue *vq)
> >     u16 avail_idx;
> >     int r;
> > 
> > +   /* In polling mode, when the backend (e.g., net.c) asks to enable
> > +    * notifications, we don't enable guest notifications. Instead, 
start
> > +    * polling on this vq by adding it to the round-robin list.
> > +    */
> > +   if (vq->vqpoll.enabled) {
> > +      if (list_empty(&vq->vqpoll.link)) {
> > +         list_add_tail(&vq->vqpoll.link,
> > +            &vq->dev->vqpoll_list);
> > +         vq->vqpoll.jiffies_last_kick = jiffies;
> > +      }
> > +      return false;
> > +   }
> > +
> >     if (!(vq->used_flags & VRING_USED_F_NO_NOTIFY))
> >        return false;
> >     vq->used_flags &= ~VRING_USED_F_NO_NOTIFY;
> > @@ -1528,6 +1744,17 @@ void vhost_disable_notify(struct vhost_dev 
> *dev, struct vhost_virtqueue *vq)
> >  {
> >     int r;
> > 
> > +   /* If this virtqueue is vqpoll.enabled, and on the polling list, 
it
> > +    * will generate notifications even if the guest is asked not to 
send
> > +    * them. So we must remove it from the round-robin polling list.
> > +    * Note that vqpoll.enabled remains set.
> > +    */
> > +   if (vq->vqpoll.enabled) {
> > +      if (!list_empty(&vq->vqpoll.link))
> > +         list_del_init(&vq->vqpoll.link);
> > +      return;
> > +   }
> > +
> >     if (vq->used_flags & VRING_USED_F_NO_NOTIFY)
> >        return;
> >     vq->used_flags |= VRING_USED_F_NO_NOTIFY;
> > diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
> > index 3eda654..11aaaf4 100644
> > --- a/drivers/vhost/vhost.h
> > +++ b/drivers/vhost/vhost.h
> > @@ -24,6 +24,7 @@ struct vhost_work {
> >     int           flushing;
> >     unsigned        queue_seq;
> >     unsigned        done_seq;
> > +   struct vhost_virtqueue    *vq;
> >  };
> > 
> >  /* Poll a file (eventfd or socket) */
> > @@ -37,11 +38,12 @@ struct vhost_poll {
> >     struct vhost_dev    *dev;
> >  };
> > 
> > -void vhost_work_init(struct vhost_work *work, vhost_work_fn_t fn);
> > +void vhost_work_init(struct vhost_work *work, struct vhost_virtqueue 
*vq,
> > +                     vhost_work_fn_t fn);
> >  void vhost_work_queue(struct vhost_dev *dev, struct vhost_work 
*work);
> > 
> >  void vhost_poll_init(struct vhost_poll *poll, vhost_work_fn_t fn,
> > -           unsigned long mask, struct vhost_dev *dev);
> > +           unsigned long mask, struct vhost_virtqueue  *vq);
> >  int vhost_poll_start(struct vhost_poll *poll, struct file *file);
> >  void vhost_poll_stop(struct vhost_poll *poll);
> >  void vhost_poll_flush(struct vhost_poll *poll);
> > @@ -54,8 +56,6 @@ struct vhost_log {
> >     u64 len;
> >  };
> > 
> > -struct vhost_virtqueue;
> > -
> >  /* The virtqueue structure describes a queue attached to a device. */
> >  struct vhost_virtqueue {
> >     struct vhost_dev *dev;
> > @@ -110,6 +110,35 @@ struct vhost_virtqueue {
> >     /* Log write descriptors */
> >     void __user *log_base;
> >     struct vhost_log *log;
> > +   struct {
> > +      /* When a virtqueue is in vqpoll.enabled mode, it declares
> > +       * that instead of using guest notifications (kicks) to
> > +       * discover new work, we prefer to continuously poll this
> > +       * virtqueue in the worker thread.
> > +       * If !enabled, the rest of the fields below are undefined.
> > +       */
> > +      bool enabled;
> > +      /* vqpoll.enabled doesn't always mean that this virtqueue is
> > +       * actually being polled: The backend (e.g., net.c) may
> > +       * temporarily disable it using vhost_disable/enable_notify().
> > +       * vqpoll.link is used to maintain the thread's round-robin
> > +       * list of virtqueues that actually need to be polled.
> > +       * Note list_empty(link) means this virtqueue isn't polled.
> > +       */
> > +      struct list_head link;
> > +      /* If this flag is true, the virtqueue is being shut down,
> > +       * so vqpoll should not be re-enabled.
> > +       */
> > +      bool shutdown;
> > +      /* Various counters used to decide when to enter polling mode
> > +       * or leave it and return to notification mode.
> > +       */
> > +      unsigned long jiffies_last_kick;
> > +      unsigned long jiffies_last_work;
> > +      int work_this_jiffy;
> > +      struct page *avail_page;
> > +      volatile struct vring_avail *avail_mapped;
> > +   } vqpoll;
> >  };
> > 
> >  struct vhost_dev {
> > @@ -123,6 +152,7 @@ struct vhost_dev {
> >     spinlock_t work_lock;
> >     struct list_head work_list;
> >     struct task_struct *worker;
> > +   struct list_head vqpoll_list;
> >  };
> > 
> >  void vhost_dev_init(struct vhost_dev *, struct vhost_virtqueue 
> **vqs, int nvqs);
> > -- 
> > 1.7.9.5
> 


Reply via email to