vhost: introduce vhost_exceeds_weight() [Linux 3.16.72]

This Linux kernel change "vhost: introduce vhost_exceeds_weight()" is included in the Linux 3.16.72 release. This change is authored by Jason Wang <jasowang [at] redhat.com> on Fri May 17 00:29:49 2019 -0400. The commit for this change in Linux stable tree is 2a59b04 (patch) which is from upstream commit e82b9b0. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream e82b9b0.

vhost: introduce vhost_exceeds_weight()

commit e82b9b0727ff6d665fff2d326162b460dded554d upstream.

We used to have vhost_exceeds_weight() for vhost-net to:

- prevent vhost kthread from hogging the cpu
- balance the time spent between TX and RX

This function could be useful for vsock and scsi as well. So move it
to vhost.c. Device must specify a weight which counts the number of
requests, or it can also specific a byte_weight which counts the
number of bytes that has been processed.

Signed-off-by: Jason Wang <[email protected]>
Reviewed-by: Stefan Hajnoczi <[email protected]>
Signed-off-by: Michael S. Tsirkin <[email protected]>
[bwh: Backported to 3.16:
 - Drop changes to vhost_vsock
 - In vhost_net, both Tx modes are handled in one loop in handle_tx()
 - Adjust context]
Signed-off-by: Ben Hutchings <[email protected]>

There are 52 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 drivers/vhost/net.c   | 17 ++++-------------
 drivers/vhost/scsi.c  |  9 ++++++++-
 drivers/vhost/vhost.c | 20 +++++++++++++++++++-
 drivers/vhost/vhost.h |  6 +++++-
 4 files changed, 36 insertions(+), 16 deletions(-)

diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index ab10066..2abe7e4 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -335,12 +335,6 @@ static void vhost_zerocopy_callback(struct ubuf_info *ubuf, bool success)
    rcu_read_unlock_bh();
 }

-static bool vhost_exceeds_weight(int pkts, int total_len)
-{
-   return total_len >= VHOST_NET_WEIGHT ||
-          pkts >= VHOST_NET_PKT_WEIGHT;
-}
-
 /* Expects to be always run from workqueue - which acts as
  * read-size critical section for our kind of RCU. */
 static void handle_tx(struct vhost_net *net)
@@ -463,10 +457,8 @@ static void handle_tx(struct vhost_net *net)
            vhost_zerocopy_signal_used(net, vq);
        total_len += len;
        vhost_net_tx_packet(net);
-       if (unlikely(vhost_exceeds_weight(++sent_pkts, total_len))) {
-           vhost_poll_queue(&vq->poll);
+       if (vhost_exceeds_weight(vq, ++sent_pkts, total_len))
            break;
-       }
    }
 out:
    mutex_unlock(&vq->mutex);
@@ -673,10 +665,8 @@ static void handle_rx(struct vhost_net *net)
        if (unlikely(vq_log))
            vhost_log_write(vq, vq_log, log, vhost_len);
        total_len += vhost_len;
-       if (unlikely(vhost_exceeds_weight(++recv_pkts, total_len))) {
-           vhost_poll_queue(&vq->poll);
+       if (unlikely(vhost_exceeds_weight(vq, ++recv_pkts, total_len)))
            break;
-       }
    }
 out:
    mutex_unlock(&vq->mutex);
@@ -746,7 +736,8 @@ static int vhost_net_open(struct inode *inode, struct file *f)
        n->vqs[i].vhost_hlen = 0;
        n->vqs[i].sock_hlen = 0;
    }
-   vhost_dev_init(dev, vqs, VHOST_NET_VQ_MAX);
+   vhost_dev_init(dev, vqs, VHOST_NET_VQ_MAX,
+              VHOST_NET_WEIGHT, VHOST_NET_PKT_WEIGHT);

    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);
diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c
index 0dfb3fd..fc2febf 100644
--- a/drivers/vhost/scsi.c
+++ b/drivers/vhost/scsi.c
@@ -60,6 +60,12 @@
 #define TCM_VHOST_PREALLOC_UPAGES 2048
 #define TCM_VHOST_PREALLOC_PROT_SGLS 512

+/* Max number of requests before requeueing the job.
+ * Using this limit prevents one virtqueue from starving others with
+ * request.
+ */
+#define VHOST_SCSI_WEIGHT 256
+
 struct vhost_scsi_inflight {
    /* Wait for the flush operation to finish */
    struct completion comp;
@@ -1576,7 +1582,8 @@ static int vhost_scsi_open(struct inode *inode, struct file *f)
        vqs[i] = &vs->vqs[i].vq;
        vs->vqs[i].vq.handle_kick = vhost_scsi_handle_kick;
    }
-   vhost_dev_init(&vs->dev, vqs, VHOST_SCSI_MAX_VQ);
+   vhost_dev_init(&vs->dev, vqs, VHOST_SCSI_MAX_VQ,
+              VHOST_SCSI_WEIGHT, 0);

    tcm_vhost_init_inflight(vs, NULL);

diff --git a/drivers/vhost/vhost.c b/drivers/vhost/vhost.c
index 337e1d0..48507ef0 100644
--- a/drivers/vhost/vhost.c
+++ b/drivers/vhost/vhost.c
@@ -292,8 +292,24 @@ static void vhost_dev_free_iovecs(struct vhost_dev *dev)
        vhost_vq_free_iovecs(dev->vqs[i]);
 }

+bool vhost_exceeds_weight(struct vhost_virtqueue *vq,
+             int pkts, int total_len)
+{
+   struct vhost_dev *dev = vq->dev;
+
+   if ((dev->byte_weight && total_len >= dev->byte_weight) ||
+       pkts >= dev->weight) {
+       vhost_poll_queue(&vq->poll);
+       return true;
+   }
+
+   return false;
+}
+EXPORT_SYMBOL_GPL(vhost_exceeds_weight);
+
 void vhost_dev_init(struct vhost_dev *dev,
-           struct vhost_virtqueue **vqs, int nvqs)
+           struct vhost_virtqueue **vqs, int nvqs,
+           int weight, int byte_weight)
 {
    struct vhost_virtqueue *vq;
    int i;
@@ -308,6 +324,8 @@ void vhost_dev_init(struct vhost_dev *dev,
    spin_lock_init(&dev->work_lock);
    INIT_LIST_HEAD(&dev->work_list);
    dev->worker = NULL;
+   dev->weight = weight;
+   dev->byte_weight = byte_weight;

    for (i = 0; i < dev->nvqs; ++i) {
        vq = dev->vqs[i];
diff --git a/drivers/vhost/vhost.h b/drivers/vhost/vhost.h
index 3eda654..6c4c8aa 100644
--- a/drivers/vhost/vhost.h
+++ b/drivers/vhost/vhost.h
@@ -123,9 +123,13 @@ struct vhost_dev {
    spinlock_t work_lock;
    struct list_head work_list;
    struct task_struct *worker;
+   int weight;
+   int byte_weight;
 };

-void vhost_dev_init(struct vhost_dev *, struct vhost_virtqueue **vqs, int nvqs);
+bool vhost_exceeds_weight(struct vhost_virtqueue *vq, int pkts, int total_len);
+void vhost_dev_init(struct vhost_dev *, struct vhost_virtqueue **vqs,
+           int nvqs, int weight, int byte_weight);
 long vhost_dev_set_owner(struct vhost_dev *dev);
 bool vhost_dev_has_owner(struct vhost_dev *dev);
 long vhost_dev_check_owner(struct vhost_dev *);

Leave a Reply

Your email address will not be published. Required fields are marked *