Thread (14 messages) 14 messages, 2 authors, 2010-05-10

Re: [PATCHv7] add mergeable buffers support to vhost_net

From: David Stevens <hidden>
Date: 2010-05-10 17:09:03
Also in: kvm, netdev

Since "datalen" carries the difference and will be negative by that amount
from the original loop, what about just adding something like:

        }
        if (headcount)
                heads[headcount-1].len += datalen;
[and really, headcount >0 since datalen > 0, so just:

        heads[headcount-1].len += datalen;

                                                +-DLS


kvm-owner@vger.kernel.org wrote on 05/10/2010 09:43:03 AM:
quoted hunk ↗ jump to hunk
On Wed, Apr 28, 2010 at 01:57:12PM -0700, David L Stevens wrote:
quoted
@@ -218,18 +248,19 @@ static void handle_rx(struct vhost_net *
    use_mm(net->dev.mm);
    mutex_lock(&vq->mutex);
    vhost_disable_notify(vq);
-   hdr_size = vq->hdr_size;
+   vhost_hlen = vq->vhost_hlen;

    vq_log = unlikely(vhost_has_feature(&net->dev, VHOST_F_LOG_ALL)) ?
       vq->log : NULL;

-   for (;;) {
-      head = vhost_get_vq_desc(&net->dev, vq, vq->iov,
-                ARRAY_SIZE(vq->iov),
-                &out, &in,
-                vq_log, &log);
+   while ((datalen = vhost_head_len(vq, sock->sk))) {
+      headcount = vhost_get_desc_n(vq, vq->heads,
+                    datalen + vhost_hlen,
+                    &in, vq_log, &log);
+      if (headcount < 0)
+         break;
       /* OK, now we need to know about added descriptors. */
-      if (head == vq->num) {
+      if (!headcount) {
          if (unlikely(vhost_enable_notify(vq))) {
             /* They have slipped one in as we were
              * doing that: check again. */
@@ -241,46 +272,53 @@ static void handle_rx(struct vhost_net *
          break;
       }
       /* We don't need to be notified again. */
-      if (out) {
-         vq_err(vq, "Unexpected descriptor format for RX: "
-                "out %d, int %d\n",
-                out, in);
-         break;
-      }
-      /* Skip header. TODO: support TSO/mergeable rx buffers. */
-      s = move_iovec_hdr(vq->iov, vq->hdr, hdr_size, in);
+      if (vhost_hlen)
+         /* Skip header. TODO: support TSO. */
+         s = move_iovec_hdr(vq->iov, vq->hdr, vhost_hlen, in);
+      else
+         s = copy_iovec_hdr(vq->iov, vq->hdr, vq->sock_hlen, in);
       msg.msg_iovlen = in;
       len = iov_length(vq->iov, in);
       /* Sanity check */
       if (!len) {
          vq_err(vq, "Unexpected header len for RX: "
                 "%zd expected %zd\n",
-                iov_length(vq->hdr, s), hdr_size);
+                iov_length(vq->hdr, s), vhost_hlen);
          break;
       }
       err = sock->ops->recvmsg(NULL, sock, &msg,
                 len, MSG_DONTWAIT | MSG_TRUNC);
       /* TODO: Check specific error and bomb out unless EAGAIN? */
       if (err < 0) {
-         vhost_discard_vq_desc(vq);
+         vhost_discard_desc(vq, headcount);
          break;
       }
-      /* TODO: Should check and handle checksum. */
-      if (err > len) {
-         pr_err("Discarded truncated rx packet: "
-                " len %d > %zd\n", err, len);
-         vhost_discard_vq_desc(vq);
+      if (err != datalen) {
+         pr_err("Discarded rx packet: "
+                " len %d, expected %zd\n", err, datalen);
+         vhost_discard_desc(vq, headcount);
          continue;
       }
       len = err;
-      err = memcpy_toiovec(vq->hdr, (unsigned char *)&hdr, hdr_size);
-      if (err) {
-         vq_err(vq, "Unable to write vnet_hdr at addr %p: %d\n",
-                vq->iov->iov_base, err);
+      if (vhost_hlen &&
+          memcpy_toiovecend(vq->hdr, (unsigned char *)&hdr, 0,
+                  vhost_hlen)) {
+         vq_err(vq, "Unable to write vnet_hdr at addr %p\n",
+                vq->iov->iov_base);
          break;
       }
-      len += hdr_size;
-      vhost_add_used_and_signal(&net->dev, vq, head, len);
+      /* TODO: Should check and handle checksum. */
+      if (vhost_has_feature(&net->dev, VIRTIO_NET_F_MRG_RXBUF) &&
+          memcpy_toiovecend(vq->hdr, (unsigned char *)&headcount,
+                  offsetof(typeof(hdr), num_buffers),
+                  sizeof(hdr.num_buffers))) {
+         vq_err(vq, "Failed num_buffers write");
+         vhost_discard_desc(vq, headcount);
+         break;
+      }
+      len += vhost_hlen;
+      vhost_add_used_and_signal_n(&net->dev, vq, vq->heads,
+                   headcount);
       if (unlikely(vq_log))
          vhost_log_write(vq, vq_log, log, len);
       total_len += len;
OK I think I see the bug here: vhost_add_used_and_signal_n
does not get the actual length, it gets the iovec length from vhost.
Guest virtio uses this as packet length, with bad results.

So I have applied the follows and it seems to have fixed the problem:
diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c
index c16db02..9d7496d 100644
--- a/drivers/vhost/net.c
+++ b/drivers/vhost/net.c
@@ -219,7 +219,7 @@ static int peek_head_len(struct vhost_virtqueue *vq, 
quoted hunk ↗ jump to hunk
struct sock *sk)
 /* This is a multi-buffer version of vhost_get_desc, that works if
  *   vq has read descriptors only.
  * @vq      - the relevant virtqueue
- * @datalen   - data length we'll be reading
+ * @datalen   - data length we'll be reading. must be > 0
  * @iovcount   - returned count of io vectors we fill
  * @log      - vhost log
  * @log_num   - log offset
@@ -236,9 +236,10 @@ static int get_rx_bufs(struct vhost_virtqueue *vq,
    int seg = 0;
    int headcount = 0;
    unsigned d;
+   size_t len;
    int r, nlogs = 0;

-   while (datalen > 0) {
+   for (;;) {
       if (unlikely(headcount >= VHOST_NET_MAX_SG)) {
          r = -ENOBUFS;
          goto err;
@@ -260,16 +261,20 @@ static int get_rx_bufs(struct vhost_virtqueue *vq,
          nlogs += *log_num;
          log += *log_num;
       }
+      len = iov_length(vq->iov + seg, in);
+      seg += in;
       heads[headcount].id = d;
-      heads[headcount].len = iov_length(vq->iov + seg, in);
-      datalen -= heads[headcount].len;
+      if (datalen <= len)
+         break;
+      heads[headcount].len = len;
       ++headcount;
-      seg += in;
+      datalen -= len;
    }
+   heads[headcount].len = datalen;
    *iovcount = seg;
    if (unlikely(log))
       *log_num = nlogs;
-   return headcount;
+   return headcount + 1;
 err:
    vhost_discard_desc(vq, headcount);
    return r;
-- 
MST
--
To unsubscribe from this list: send the line "unsubscribe kvm" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
  
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help