[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Xen-devel] [PATCH 21/22] NC2 VMQ support.



This only includes the transmit half, because the receiver uses an
unmodified posted buffers mode implementation.

This includes various bits of patches which were

Signed-off-by: Jose Renato Santos <jsantos@xxxxxxxxxx>
Signed-off-by: Mitch Williams <mitch.a.williams@xxxxxxxxx>
Signed-off-by: Steven Smith <steven.smith@xxxxxxxxxx>

All bugs are mine, of course.
---
 drivers/xen/Kconfig                        |    5 +
 drivers/xen/netchannel2/Makefile           |    4 +
 drivers/xen/netchannel2/chan.c             |    7 +-
 drivers/xen/netchannel2/netback2.c         |    9 +
 drivers/xen/netchannel2/netchannel2_core.h |   10 +
 drivers/xen/netchannel2/posted_buffer.h    |   50 ++
 drivers/xen/netchannel2/posted_buffers.c   |   20 +-
 drivers/xen/netchannel2/util.c             |    8 +-
 drivers/xen/netchannel2/vmq.c              |  805 ++++++++++++++++++++++++++++
 drivers/xen/netchannel2/vmq.h              |   58 ++
 drivers/xen/netchannel2/vmq_def.h          |   68 +++
 drivers/xen/netchannel2/xmit_packet.c      |    6 +
 12 files changed, 1029 insertions(+), 21 deletions(-)
 create mode 100644 drivers/xen/netchannel2/posted_buffer.h
 create mode 100644 drivers/xen/netchannel2/vmq.c
 create mode 100644 drivers/xen/netchannel2/vmq.h
 create mode 100644 drivers/xen/netchannel2/vmq_def.h

diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig
index a7e5b5c..a37b0cd 100644
--- a/drivers/xen/Kconfig
+++ b/drivers/xen/Kconfig
@@ -234,6 +234,11 @@ config XEN_NETDEV2_FRONTEND
        depends on XEN_NETCHANNEL2
        default y
 
+config XEN_NETDEV2_VMQ
+       bool "Net channel 2 support for multi-queue devices"
+       depends on XEN_NETDEV2_BACKEND && NET_VMQ
+       default y
+
 config XEN_NETDEV2_BYPASSABLE
        bool "Net channel 2 bypassee support"
        depends on XEN_NETDEV2_BACKEND
diff --git a/drivers/xen/netchannel2/Makefile b/drivers/xen/netchannel2/Makefile
index 11a257e..918d8d8 100644
--- a/drivers/xen/netchannel2/Makefile
+++ b/drivers/xen/netchannel2/Makefile
@@ -12,6 +12,10 @@ ifeq ($(CONFIG_XEN_NETDEV2_FRONTEND),y)
 netchannel2-objs += netfront2.o
 endif
 
+ifeq ($(CONFIG_XEN_NETDEV2_VMQ),y)
+netchannel2-objs += vmq.o
+endif
+
 ifeq ($(CONFIG_XEN_NETDEV2_BYPASSABLE),y)
 netchannel2-objs += bypassee.o
 endif
diff --git a/drivers/xen/netchannel2/chan.c b/drivers/xen/netchannel2/chan.c
index 060b49b..8dad6fe 100644
--- a/drivers/xen/netchannel2/chan.c
+++ b/drivers/xen/netchannel2/chan.c
@@ -13,6 +13,7 @@
 
 #include "netchannel2_endpoint.h"
 #include "netchannel2_core.h"
+#include "vmq.h"
 
 static int process_ring(struct napi_struct *napi,
                        int work_avail);
@@ -810,6 +811,8 @@ static int process_ring(struct napi_struct *napi,
        /* Pick up incoming messages. */
        work_done = nc2_poll(ncrp, work_avail, &rx_queue);
 
+       do_vmq_work(nc);
+
        /* Transmit pending packets. */
        if (!skb_queue_empty(&ncrp->pending_tx_queue)) {
                skb = __skb_dequeue(&ncrp->pending_tx_queue);
@@ -828,9 +831,11 @@ static int process_ring(struct napi_struct *napi,
                   This must happen before we flush the rings, since
                   that's when the PACKET messages will be made
                   visible to the other end. */
-               if (ncrp == &nc->rings)
+               if (ncrp == &nc->rings) {
                        flush_hypercall_batcher(&nc->batcher,
                                                nc2_posted_on_gntcopy_fail);
+                       vmq_flush_unmap_hypercall();
+               }
 
                flush_rings(ncrp);
 
diff --git a/drivers/xen/netchannel2/netback2.c 
b/drivers/xen/netchannel2/netback2.c
index 129ef81..eb2a781 100644
--- a/drivers/xen/netchannel2/netback2.c
+++ b/drivers/xen/netchannel2/netback2.c
@@ -10,8 +10,13 @@
 #include "netchannel2_core.h"
 #include "netchannel2_endpoint.h"
 #include "netchannel2_uspace.h"
+#include "vmq.h"
 
+#ifdef CONFIG_XEN_NETDEV2_VMQ
+#define NR_TX_BUFS (VMQ_MAX_BUFFERS+256)
+#else
 #define NR_TX_BUFS 256
+#endif
 
 static atomic_t next_handle;
 /* A list of all currently-live netback2 interfaces. */
@@ -168,6 +173,8 @@ static int attach_to_frontend(struct netback2 *nd)
                return err;
        }
 
+       nc2_vmq_connect(nc);
+
        /* All done */
        nd->attached = 1;
 
@@ -176,6 +183,8 @@ static int attach_to_frontend(struct netback2 *nd)
 
 static void nb2_shutdown(struct netchannel2 *nc)
 {
+       nc2_vmq_disconnect(nc);
+
        nc2_set_nr_tx_buffers(nc, 0);
 }
 
diff --git a/drivers/xen/netchannel2/netchannel2_core.h 
b/drivers/xen/netchannel2/netchannel2_core.h
index 1939cbb..8e1657d 100644
--- a/drivers/xen/netchannel2/netchannel2_core.h
+++ b/drivers/xen/netchannel2/netchannel2_core.h
@@ -7,6 +7,8 @@
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 
+#include "vmq_def.h"
+
 /* After we send this number of frags, we request the other end to
  * notify us when sending the corresponding finish packet message */
 #define MAX_MAX_COUNT_FRAGS_NO_EVENT 192
@@ -43,6 +45,9 @@ enum transmit_policy {
        transmit_policy_grant = transmit_policy_first,
        transmit_policy_post,
        transmit_policy_map,
+#ifdef CONFIG_XEN_NETDEV2_VMQ
+       transmit_policy_vmq,
+#endif
        transmit_policy_small,
        transmit_policy_last = transmit_policy_small
 };
@@ -437,6 +442,11 @@ struct netchannel2 {
 
        struct hypercall_batcher batcher;
 
+#ifdef CONFIG_XEN_NETDEV2_VMQ
+       /* vmq data for supporting multi-queue devices */
+       nc2_vmq_t vmq;
+#endif
+
 #ifdef CONFIG_XEN_NETDEV2_AUTOMATIC_BYPASS
        struct nc2_auto_bypass auto_bypass;
 #endif
diff --git a/drivers/xen/netchannel2/posted_buffer.h 
b/drivers/xen/netchannel2/posted_buffer.h
new file mode 100644
index 0000000..e249777
--- /dev/null
+++ b/drivers/xen/netchannel2/posted_buffer.h
@@ -0,0 +1,50 @@
+/* Buffer management related bits, shared between vmq.c and
+ * posted_buffer.c */
+#ifndef NC2_POSTED_BUFFER_H__
+#define NC2_POSTED_BUFFER_H__
+
+/* A buffer which the other end has provided us which we can use to
+   transmit packets to it. */
+struct nc2_tx_buffer {
+       struct list_head list;
+       uint32_t id; /* ID assigned by the remote endpoint. */
+       grant_ref_t gref;
+       uint16_t off_in_page;
+       uint16_t size;
+       grant_handle_t grant_handle;
+};
+
+/* add a buffer to the pending list to be returned to the other end buffer */
+static inline void return_tx_buffer(struct netchannel2 *nc,
+                                   struct nc2_tx_buffer *buffer)
+{
+       list_add(&buffer->list, &nc->pending_tx_buffer_return);
+}
+
+static inline struct nc2_tx_buffer *_get_tx_buffer(struct netchannel2 *nc)
+{
+       struct nc2_tx_buffer *buffer;
+       struct list_head *entry = nc->avail_tx_buffers.next;
+       list_del(entry);
+       buffer = list_entry(entry, struct nc2_tx_buffer, list);
+       nc->nr_avail_tx_buffers--;
+       return buffer;
+}
+
+/* recycle a posted buffer: return it to the list of available buffers */
+static inline void recycle_tx_buffer(struct netchannel2 *nc,
+                                   struct nc2_tx_buffer *buffer)
+{
+       list_add(&buffer->list, &nc->avail_tx_buffers);
+       nc->nr_avail_tx_buffers++;
+}
+
+/* add a buffer slot to list of unused buffer slots after it has been
+ * returned to other end */
+static inline void free_tx_buffer(struct netchannel2 *nc,
+                                 struct nc2_tx_buffer *buffer)
+{
+       list_add(&buffer->list, &nc->unused_tx_buffer_slots);
+}
+
+#endif /* !NC2_POSTED_BUFFER_H__ */
diff --git a/drivers/xen/netchannel2/posted_buffers.c 
b/drivers/xen/netchannel2/posted_buffers.c
index 96de7da..9fb7570 100644
--- a/drivers/xen/netchannel2/posted_buffers.c
+++ b/drivers/xen/netchannel2/posted_buffers.c
@@ -9,6 +9,7 @@
 #include <xen/live_maps.h>
 #include "netchannel2_endpoint.h"
 #include "netchannel2_core.h"
+#include "posted_buffer.h"
 
 #define POSTED_BUFFER_SIZE PAGE_SIZE
 
@@ -350,17 +351,6 @@ void nc2_handle_set_nr_posted_buffers(struct netchannel2 
*nc,
 
 /* -------------------------- Transmit ------------------------------- */
 
-/* A buffer which the other end has provided us which we can use to
-   transmit packets to it. */
-struct nc2_tx_buffer {
-       struct list_head list;
-       uint32_t id; /* ID assigned by the remote endpoint. */
-       grant_ref_t gref;
-       uint16_t off_in_page;
-       uint16_t size;
-       grant_handle_t grant_handle;
-};
-
 /* A representation of a packet which is halfway through being
    prepared for transmission. */
 struct post_packet_plan {
@@ -373,14 +363,6 @@ struct post_packet_plan {
        volatile struct netchannel2_fragment *output_frag;
 };
 
-/* add a buffer slot to list of unused buffer slots after it has been
- * returned to other end */
-static void free_tx_buffer(struct netchannel2 *nc,
-                          struct nc2_tx_buffer *buffer)
-{
-       list_add(&buffer->list, &nc->unused_tx_buffer_slots);
-}
-
 /* A grant copy failed while we were transmitting a packet.  That
    indicates that the *receiving* domain gave us a bad RX buffer.
    We're too late to send them an error, so there isn't really
diff --git a/drivers/xen/netchannel2/util.c b/drivers/xen/netchannel2/util.c
index 79d9f09..1d96256 100644
--- a/drivers/xen/netchannel2/util.c
+++ b/drivers/xen/netchannel2/util.c
@@ -34,7 +34,13 @@ int allocate_txp_slot(struct netchannel2_ring_pair *ncrp,
 static void nc2_free_skb(struct netchannel2 *nc,
                         struct sk_buff *skb)
 {
-       dev_kfree_skb(skb);
+#ifdef CONFIG_XEN_NETDEV2_VMQ
+       nc2_vmq_t *vmq = &nc->vmq;
+       if (get_skb_overlay(skb)->policy == transmit_policy_vmq)
+               skb_queue_tail(&vmq->dealloc_queue, skb);
+       else
+#endif
+               dev_kfree_skb(skb);
 }
 
 void release_txp_slot(struct netchannel2_ring_pair *ncrp,
diff --git a/drivers/xen/netchannel2/vmq.c b/drivers/xen/netchannel2/vmq.c
new file mode 100644
index 0000000..e36962b
--- /dev/null
+++ b/drivers/xen/netchannel2/vmq.c
@@ -0,0 +1,805 @@
+/*****************************************************************************
+ * vmq.c
+ *
+ * Support multi-queue network devices.
+ *
+ * Copyright (c) 2008, Kaushik Kumar Ram, Rice University.
+ * Copyright (c) 2008, Jose Renato Santos, Hewlett-Packard Co.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation; or, when distributed
+ * separately from the Linux kernel or incorporated into other
+ * software packages, subject to the following license:
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this source file (the "Software"), to deal in the Software without
+ * restriction, including without limitation the rights to use, copy, modify,
+ * merge, publish, distribute, sublicense, and/or sell copies of the Software,
+ * and to permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ *
+ */
+/* This only implements the transmit half of the method; receive is
+ * handled by posted_buffers.c */
+#include <linux/kernel.h>
+#include <linux/netvmq.h>
+#include <linux/skbuff.h>
+#include <xen/xenbus.h>
+#include <xen/balloon.h>
+#include "netchannel2_core.h"
+
+#include "posted_buffer.h"
+#include "vmq.h"
+
+/* state of device queue when operating in vmq mode */
+#define VMQ_QUEUE_DISABLED  0
+#define VMQ_QUEUE_STARTING  1
+#define VMQ_QUEUE_ENABLED   2
+#define VMQ_QUEUE_CLOSING   3
+
+#define VMQ_MAX_UNMAP_OPS 256
+struct vmq_unmap_grants {
+       unsigned n;
+       gnttab_unmap_grant_ref_t gop[VMQ_MAX_UNMAP_OPS];
+};
+typedef struct vmq_unmap_grants vmq_unmap_grants_t;
+
+vmq_unmap_grants_t vmq_unmap_grants;
+
+static inline void vmq_flush_unmap_grants(void)
+{
+       if (vmq_unmap_grants.n == 0)
+               return;
+
+       if (HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref,
+                                     vmq_unmap_grants.gop,
+                                     vmq_unmap_grants.n))
+               BUG();
+       vmq_unmap_grants.n = 0;
+}
+
+static inline gnttab_unmap_grant_ref_t *vmq_next_unmap_gop(void)
+{
+       if (vmq_unmap_grants.n == VMQ_MAX_UNMAP_OPS)
+               vmq_flush_unmap_grants();
+       return &vmq_unmap_grants.gop[vmq_unmap_grants.n++];
+}
+
+void vmq_flush_unmap_hypercall(void)
+{
+       vmq_flush_unmap_grants();
+}
+
+static inline unsigned long vmq_idx_to_pfn(nc2_vmq_t *vmq, unsigned int idx)
+{
+       return page_to_pfn(vmq->pages[idx]);
+}
+
+static inline unsigned long vmq_idx_to_kaddr(nc2_vmq_t *vmq, unsigned int idx)
+{
+       return (unsigned long)pfn_to_kaddr(vmq_idx_to_pfn(vmq, idx));
+}
+
+/* get vmq idx from page struct         */
+static long nc2_vmq_page_index(struct page *page)
+{
+       nc2_vmq_buf_t *vmq_buf;
+       vmq_buf = (nc2_vmq_buf_t *)page->mapping;
+       return vmq_buf - vmq_buf->nc->vmq.buffer;
+}
+
+/* Read a physical device name from xenstore and
+ * returns a pointer to the associated net_device structure.
+ *  Returns NULL on error. */
+static struct net_device *read_pdev(struct xenbus_device *dev)
+{
+       char *pdevstr;
+       struct net_device *pdev = NULL;
+
+       pdevstr = xenbus_read(XBT_NIL, dev->nodename, "pdev", NULL);
+       if (IS_ERR(pdevstr))
+               return NULL;
+
+       if (pdevstr)
+               pdev = dev_get_by_name(&init_net, pdevstr);
+
+       kfree(pdevstr);
+
+       return pdev;
+}
+
+static void nc2_vmq_page_release(struct page *page, unsigned int order)
+{
+       printk(KERN_CRIT "%s: ERROR: Unexpected release of netchannel2 vmq 
page",
+              __func__);
+       BUG_ON(1);
+}
+
+static inline int nc2_vmq_is_disabled(struct netchannel2 *nc)
+{
+       return nc->vmq.vmq_state == VMQ_QUEUE_DISABLED;
+}
+
+static inline int nc2_vmq_is_starting(struct netchannel2 *nc)
+{
+       return nc->vmq.vmq_state == VMQ_QUEUE_STARTING;
+}
+
+static inline int nc2_vmq_is_enabled(struct netchannel2 *nc)
+{
+       return nc->vmq.vmq_state == VMQ_QUEUE_ENABLED;
+}
+
+static inline int nc2_vmq_is_closing(struct netchannel2 *nc)
+{
+       return nc->vmq.vmq_state == VMQ_QUEUE_CLOSING;
+}
+
+static inline void nc2_vmq_enable(struct netchannel2 *nc)
+{
+       nc2_vmq_t *vmq = &nc->vmq;
+       vmq_get(vmq);
+       vmq_enable_queue(vmq->pdev, vmq->vmq_id);
+       vmq->vmq_state = VMQ_QUEUE_ENABLED;
+}
+
+void nc2_vmq_disconnect(struct netchannel2 *nc)
+{
+       nc2_vmq_t *vmq = &nc->vmq;
+
+       if (nc2_vmq_is_enabled(nc)) {
+               vmq_disable_queue(vmq->pdev, vmq->vmq_id);
+               vmq_free_queue(vmq->pdev, vmq->vmq_id);
+               vmq->vmq_state = VMQ_QUEUE_CLOSING;
+               /* wait until all buffers have been returned by dev driver */
+               wait_event(vmq->waiting_to_free,
+                          atomic_read(&vmq->refcnt) == 0);
+               return;
+       }
+
+       if (nc2_vmq_is_starting(nc)) {
+               vmq_free_queue(vmq->pdev, vmq->vmq_id);
+               vmq->vmq_state = VMQ_QUEUE_CLOSING;
+               return;
+       }
+
+}
+
+
+static void nc2_vmq_end_map_buffers(gnttab_map_grant_ref_t *mop, int count,
+                                   struct netchannel2 *nc, u16 *alloc_idx)
+{
+       int i, err;
+       u16 idx;
+       unsigned int prod;
+       nc2_vmq_t *vmq = &nc->vmq;
+
+       prod = vmq->mapped_pages_prod;
+
+       for (i = 0; i < count; i++) {
+               idx = alloc_idx[i];
+
+               /* Check error status */
+               err = mop->status;
+               if (likely(!err)) {
+                       set_phys_to_machine(
+                                           __pa(vmq_idx_to_kaddr(vmq, idx))
+                                           >> PAGE_SHIFT,
+                                           FOREIGN_FRAME(mop->dev_bus_addr
+                                                         >> PAGE_SHIFT));
+                       /* Store the handle */
+                       vmq->buffer[idx].buf->grant_handle = mop->handle;
+
+                       /* Add it to the mapped pages list */
+                       vmq->mapped_pages[VMQ_IDX_MASK(prod++)] = idx;
+                       mop++;
+                       continue;
+               }
+
+               /* Error mapping page: return posted buffer to other end.
+                * TODO: We might need an error field on the return buffer
+                * message */
+               return_tx_buffer(nc, vmq->buffer[idx].buf);
+
+               /* Add the page back to the free list */
+               vmq->unmapped_pages[VMQ_IDX_MASK(vmq->unmapped_pages_prod++)]
+                       = idx;
+
+               mop++;
+       }
+
+       smp_wmb();
+       vmq->mapped_pages_prod = prod;
+
+       return;
+}
+
+/* Map guest buffers and place them in the mapped buffers list. The mapped
+ * pages in this list are used when allocating a skb (vmq_alloc_skb()).
+ */
+static void nc2_vmq_map_buffers(struct netchannel2 *nc)
+{
+       u16 idx;
+       int count = 0;
+       unsigned int cons;
+       int nbufs;
+       int buf_avail;
+       struct nc2_tx_buffer *buf;
+       struct nc2_vmq *vmq = &nc->vmq;
+       int n_mapped = nr_vmq_bufs(nc);
+
+
+       /*
+        * Putting hundreds of bytes on the stack is considered rude.
+        * Static works because a tasklet can only be on one CPU at any time.
+        */
+       static gnttab_map_grant_ref_t rx_map_ops[VMQ_MAX_BUFFERS];
+       static u16 alloc_idx[VMQ_MAX_BUFFERS];
+
+       /* If there is at least VMQ_MIN_BUFFERS buffers, no work to do */
+       if (n_mapped >= VMQ_MIN_BUFFERS)
+               return;
+
+       /* Try to get VMQ_MAX_BUFFERS mapped buffers, if there are
+          sufficient buffers posted by the other end  */
+       nbufs = VMQ_MAX_BUFFERS - n_mapped;
+       buf_avail = nc->nr_avail_tx_buffers;
+       if (nbufs > buf_avail)
+               nbufs = buf_avail;
+
+       /* Xen cannot handle more than 512 grant ops in a single hypercall */
+       if (nbufs > 512)
+               nbufs = 512;
+
+       /* give up if there are no buffers available */
+       if (nbufs <= 0)
+               return;
+
+       /* Note that we *should* have free pages to consume here
+        * and no checks are needed.
+        */
+       cons = vmq->unmapped_pages_cons;
+
+       while (count < nbufs) {
+               idx = vmq->unmapped_pages[VMQ_IDX_MASK(cons++)];
+               buf = vmq->buffer[idx].buf = _get_tx_buffer(nc);
+               /* Setup grant map operation */
+               gnttab_set_map_op(&rx_map_ops[count],
+                                 vmq_idx_to_kaddr(vmq, idx),
+                                 GNTMAP_host_map,
+                                 buf->gref,
+                                 nc->rings.otherend_id);
+               alloc_idx[count] = idx;
+               count++;
+       }
+
+       vmq->unmapped_pages_cons = cons;
+
+       /* Map all the pages */
+       BUG_ON(HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref,
+                                        rx_map_ops, nbufs));
+
+       /* Finalize buffer mapping after checking if the grant operations
+          succeeded */
+       nc2_vmq_end_map_buffers(rx_map_ops, nbufs, nc, alloc_idx);
+
+       vmq->nbufs += nbufs;
+}
+
+static void nc2_vmq_unmap_buf(struct netchannel2 *nc,
+                             unsigned int idx, int recycle)
+{
+       nc2_vmq_t *vmq = &nc->vmq;
+       unsigned long pfn;
+       gnttab_unmap_grant_ref_t *gop;
+       unsigned prod;
+
+       pfn = vmq_idx_to_pfn(vmq, idx);
+       /* Already unmapped? */
+       if (!phys_to_machine_mapping_valid(pfn))
+               return;
+
+       gop = vmq_next_unmap_gop();
+       gnttab_set_unmap_op(gop, vmq_idx_to_kaddr(vmq, idx),
+                           GNTMAP_host_map,
+                           vmq->buffer[idx].buf->grant_handle);
+
+       vmq->nbufs--;
+
+       set_phys_to_machine(__pa(vmq_idx_to_kaddr(vmq, idx)) >>
+                           PAGE_SHIFT,
+                           INVALID_P2M_ENTRY);
+       /* Ready for next use. */
+       gnttab_reset_grant_page(vmq->pages[idx]);
+       /* Add the page back to the unmapped list */
+       prod = vmq->unmapped_pages_prod;
+       vmq->unmapped_pages[VMQ_IDX_MASK(prod++)] = idx;
+       if (recycle)
+               recycle_tx_buffer(nc, vmq->buffer[idx].buf);
+       else
+               free_tx_buffer(nc, vmq->buffer[idx].buf);
+       smp_wmb();
+       vmq->unmapped_pages_prod = prod;
+}
+
+static void nc2_vmq_free_mapped_bufs(struct netchannel2 *nc)
+{
+       nc2_vmq_t *vmq = &nc->vmq;
+       unsigned int idx;
+       unsigned prod, cons;
+
+       /* The queue should be disabled before this function is called */
+       BUG_ON(vmq->vmq_state == VMQ_QUEUE_ENABLED);
+
+       cons = vmq->mapped_pages_cons;
+       prod = vmq->mapped_pages_prod;
+       smp_rmb();
+
+       while (cons != prod) {
+               idx = vmq->mapped_pages[VMQ_IDX_MASK(cons++)];
+               nc2_vmq_unmap_buf(nc, idx, 1);
+       }
+
+       vmq_flush_unmap_grants();
+
+       vmq->mapped_pages_cons = cons;
+
+}
+
+static void nc2_vmq_free_skb(struct sk_buff *skb)
+{
+       struct netchannel2 *nc;
+       nc2_vmq_t *vmq;
+       unsigned int idx;
+       int nr_frags, i;
+       struct skb_shared_info *shinfo = skb_shinfo(skb);
+       skb_frag_t *frags = shinfo->frags;
+
+       nc = netdev_priv(skb->dev);
+       vmq = &nc->vmq;
+
+       nr_frags = shinfo->nr_frags;
+       for (i = 0; i < nr_frags; i++) {
+               idx = nc2_vmq_page_index(frags[i].page);
+               nc2_vmq_unmap_buf(nc, idx, 1);
+       }
+
+       vmq_flush_unmap_grants();
+
+       shinfo->frag_list = NULL;
+       shinfo->nr_frags = 0;
+
+       /* Add the skb back to the free pool */
+       skb_queue_tail(&vmq->free_skb_list, skb);
+}
+
+/* Initialize the free socket buffer list */
+static int vmq_init_free_skb_list(int n, struct sk_buff_head *free_skb_list)
+{
+       int i;
+       struct sk_buff *skb;
+
+       skb_queue_head_init(free_skb_list);
+
+       for (i = 0; i < n; i++) {
+               skb = alloc_skb(VMQ_SKB_SIZE, GFP_ATOMIC);
+               if (!skb) {
+                       printk("Netchannel2 vmq: Failed to allocate socket "
+                              "buffer %d (max=%d)\n", i, (int)n);
+                       goto error;
+               }
+               skb_queue_tail(free_skb_list, skb);
+       }
+
+       return 0;
+error:
+       /* Free all the allocated buffers and return Error */
+       while (!skb_queue_empty(free_skb_list))
+               kfree_skb(skb_dequeue(free_skb_list));
+
+       return -1;
+}
+
+/* Initialize vmq. Return 1 if vmq is used and 0 otherwise */
+int nc2_vmq_connect(struct netchannel2 *nc)
+{
+       nc2_vmq_t *vmq = &nc->vmq;
+       struct page *page;
+       int q_id;
+       int size;
+       int i;
+
+       vmq->vmq_mode = 0;
+       vmq->pdev = read_pdev(nc->xenbus_device);
+
+       /* cannot use vmq mode if physical device not found */
+       if (!vmq->pdev)
+               return 0;
+
+       /* Allocate a RX queue */
+       q_id = vmq_alloc_queue(vmq->pdev, VMQ_TYPE_RX);
+       if (q_id < 0)
+               /* Allocation failed, cannot use multi-queue */
+               goto free_pdev;
+
+       vmq->vmq_id = q_id;
+
+       /* Set the size of the queue */
+       size = vmq_get_maxsize(vmq->pdev);
+       if (size > VMQ_QUEUE_SIZE)
+               size = VMQ_QUEUE_SIZE;
+       if (vmq_set_size(vmq->pdev, q_id, size) < 0) {
+               /* Failure, free up the queue and return error */
+               printk(KERN_ERR "%s: could not set queue size on net device\n",
+                      __func__);
+               goto free_queue;
+       }
+       vmq->vmq_size = size;
+
+       /* Set the mac address of the queue */
+       if (vmq_set_mac(vmq->pdev, q_id, nc->rings.remote_mac) < 0) {
+               /* Failure, free up the queue and return error */
+               printk(KERN_ERR "%s: could not set MAC address for net device 
queue\n",
+                      __func__);
+               goto free_queue;
+       }
+
+       vmq->pages = alloc_empty_pages_and_pagevec(VMQ_MAX_BUFFERS);
+       if (vmq->pages == NULL) {
+               printk(KERN_ERR "%s: out of memory\n", __func__);
+               goto free_queue;
+       }
+
+       skb_queue_head_init(&vmq->dealloc_queue);
+       skb_queue_head_init(&vmq->rx_queue);
+
+       if (vmq_init_free_skb_list(VMQ_MAX_BUFFERS,
+                                  &vmq->free_skb_list)) {
+               printk(KERN_ERR "%s: Could not allocate free socket buffers",
+                       __func__);
+               goto free_pagevec;
+       }
+
+       for (i = 0; i < VMQ_MAX_BUFFERS; i++) {
+               vmq->buffer[i].nc = nc;
+               page = vmq->pages[i];
+               SetPageForeign(page, nc2_vmq_page_release);
+               page->mapping = (void *)&vmq->buffer[i];
+               vmq->unmapped_pages[i] = i;
+       }
+
+       vmq->unmapped_pages_prod = VMQ_MAX_BUFFERS;
+       vmq->unmapped_pages_cons = 0;
+
+       vmq->mapped_pages_prod = 0;
+       vmq->mapped_pages_cons = 0;
+
+       vmq->nbufs = 0;
+       vmq->vmq_mode = 1;
+
+       /* Store the pointer to netchannel2 device in pdev */
+       BUG_ON((vmq->pdev->vmq == NULL) || (vmq->pdev->vmq->queue == NULL));
+       vmq->pdev->vmq->queue[q_id].guest = (void *)nc->net_device;
+
+       atomic_set(&vmq->refcnt, 0);
+       init_waitqueue_head(&vmq->waiting_to_free);
+
+       printk(KERN_INFO "Netchannel2 using vmq mode for guest %d\n",
+              nc->xenbus_device->otherend_id);
+
+       vmq->vmq_state = VMQ_QUEUE_STARTING;
+
+       return 1;       /* Success */
+
+
+free_pagevec:
+       free_empty_pages_and_pagevec(vmq->pages, VMQ_MAX_BUFFERS);
+free_queue:
+       vmq_free_queue(vmq->pdev, vmq->vmq_id);
+free_pdev:
+       dev_put(vmq->pdev);
+       vmq->pdev = NULL;
+       return 0;
+}
+
+void nc2_vmq_shutdown(struct netchannel2 *nc)
+{
+       nc2_vmq_t *vmq = &nc->vmq;
+       int i;
+
+       if (!vmq->vmq_mode)
+               return;
+
+       /* All posted bufs should have been returned */
+       BUG_ON(nr_vmq_bufs(nc) != nr_vmq_mapped_bufs(nc));
+
+       /* free the mapped bufs */
+       nc2_vmq_free_mapped_bufs(nc);
+
+       /* Free the vmq pages */
+       if (vmq->pages) {
+               for (i = 0; i < VMQ_MAX_BUFFERS; i++) {
+                       if (PageForeign(vmq->pages[i]))
+                               ClearPageForeign(vmq->pages[i]);
+                       vmq->pages[i]->mapping = NULL;
+               }
+               free_empty_pages_and_pagevec(vmq->pages, VMQ_MAX_BUFFERS);
+               vmq->pages = NULL;
+       }
+
+       while (!skb_queue_empty(&vmq->free_skb_list)) {
+               /* Free the socket buffer pool */
+               kfree_skb(skb_dequeue(&vmq->free_skb_list));
+       }
+       vmq->vmq_state = VMQ_QUEUE_DISABLED;
+       vmq->vmq_mode = 0;
+
+       if (vmq->pdev) {
+               dev_put(vmq->pdev);
+               vmq->pdev = NULL;
+       }
+
+       vmq_put(vmq);
+}
+
+static int prepare_xmit_allocate_vmq(struct netchannel2 *nc,
+                                    struct sk_buff *skb)
+{
+       unsigned msg_size;
+
+       msg_size = get_transmitted_packet_msg_size(skb);
+       if (!nc2_reserve_payload_bytes(&nc->rings.prod_ring, msg_size))
+               return -1;
+       return 0;
+}
+
+void do_vmq_work(struct netchannel2 *nc)
+{
+       nc2_vmq_t *vmq = &nc->vmq;
+       struct sk_buff *skb;
+       unsigned long flags;
+
+       /* if not in vmq mode do nothing */
+       if (!nc2_in_vmq_mode(nc))
+               return;
+
+       /* Map guest buffers for dedicated NIC RX queue if needed */
+       if (nr_vmq_bufs(nc) < VMQ_MIN_BUFFERS) {
+               nc2_vmq_map_buffers(nc);
+               /* We delay enabling the queue until we have enough
+                  posted buffers. Check if it is time to enable it */
+               if (nc2_vmq_is_starting(nc) &&
+                   (nr_vmq_bufs(nc) >= VMQ_MIN_BUFFERS)) {
+                       nc2_vmq_enable(nc);
+               }
+       }
+
+       /* free vmq skb's returned by the physical device driver */
+       while (!skb_queue_empty(&nc->vmq.dealloc_queue))
+               nc2_vmq_free_skb(skb_dequeue(&nc->vmq.dealloc_queue));
+
+       /* complete vmq closing after all packets returned by physical
+        * device driver */
+
+       if (nc2_vmq_is_closing(nc) &&
+           (nr_vmq_bufs(nc) == nr_vmq_mapped_bufs(nc))) {
+               nc->vmq.vmq_state = VMQ_QUEUE_DISABLED;
+               nc2_vmq_shutdown(nc);
+       }
+
+       spin_lock_irqsave(&vmq->rx_queue.lock, flags);
+       while (!skb_queue_empty(&vmq->rx_queue)) {
+               skb = __skb_dequeue(&nc->vmq.rx_queue);
+               if (prepare_xmit_allocate_vmq(nc, skb) < 0) {
+                       __skb_queue_head(&vmq->rx_queue, skb);
+                       spin_unlock_irqrestore(&vmq->rx_queue.lock, flags);
+                       return;
+               }
+               __skb_queue_tail(&nc->rings.pending_tx_queue, skb);
+       }
+       spin_unlock_irqrestore(&vmq->rx_queue.lock, flags);
+}
+
+/* Return the netchannel2 device corresponding to the given queue in pdev */
+static inline struct net_device *nc2_vmq_queue_to_vif(struct net_device *pdev,
+                                                     int queue_id)
+{
+       net_vmq_t *n_vmq;
+       vmq_queue_t *vmq_q;
+
+       n_vmq = pdev->vmq;
+       BUG_ON(n_vmq == NULL);
+       vmq_q = &n_vmq->queue[queue_id];
+       BUG_ON(vmq_q == NULL);
+
+       return (struct net_device *)vmq_q->guest;
+}
+
+/* Handle incoming vmq packet */
+int vmq_netif_rx(struct sk_buff *skb, int queue_id)
+{
+       struct skb_cb_overlay *skb_co = get_skb_overlay(skb);
+       struct net_device *dev;
+       struct netchannel2 *nc;
+       nc2_vmq_t *vmq;
+
+       memset(skb_co, 0, sizeof(*skb_co));
+
+       skb_co->nr_fragments = skb_shinfo(skb)->nr_frags;
+       skb_co->type = NC2_PACKET_TYPE_pre_posted;
+       skb_co->policy = transmit_policy_vmq;
+
+       /* get the netchannel2 interface corresponding to this queue */
+       dev = nc2_vmq_queue_to_vif(skb->dev, queue_id);
+       nc = netdev_priv(dev);
+       vmq = &nc->vmq;
+
+       /* replace source dev with destination dev */
+       skb->dev = dev;
+       /* add skb to rx_queue */
+       skb_queue_tail(&vmq->rx_queue, skb);
+
+       /* Trigger thread excution to procees new packets */
+       nc2_kick(&nc->rings);
+
+       return 0;
+}
+EXPORT_SYMBOL(vmq_netif_rx);
+
+
+/* Allocate a socket buffer from the free list, get a guest posted
+ * buffer, attach it to the skb, and return it.
+ */
+struct sk_buff *vmq_alloc_skb(struct net_device *netdevice, int queue_id,
+                             unsigned int length)
+{
+       struct sk_buff *skb;
+       struct netchannel2 *nc;
+       nc2_vmq_t *vmq;
+       unsigned int idx;
+       int nr_bufs, i;
+       unsigned int cons;
+       unsigned int prod;
+
+       /* get the netchannel2 interface corresponding to this queue */
+       nc = netdev_priv(nc2_vmq_queue_to_vif(netdevice, queue_id));
+
+       vmq = &nc->vmq;
+
+       /* Get a free buffer from the pool */
+       if (skb_queue_empty(&vmq->free_skb_list)) {
+               /* No buffers to allocate */
+               return NULL;
+       }
+
+
+       skb = skb_dequeue(&vmq->free_skb_list);
+       BUG_ON(skb == NULL);
+
+       nr_bufs = VMQ_NUM_BUFFERS(length);
+
+       cons = vmq->mapped_pages_cons;
+       prod = vmq->mapped_pages_prod;
+       smp_rmb();
+
+       if (nr_bufs > (prod - cons))
+               /* Not enough mapped buffers in the pool */
+               goto kick_nc2;
+
+       if (nr_bufs > MAX_SKB_FRAGS)
+               goto error;
+
+       for (i = 0; i < nr_bufs; i++) {
+               idx = vmq->mapped_pages[VMQ_IDX_MASK(cons)];
+               /* FIX ME: This can be simplified */
+               skb_shinfo(skb)->frags[i].page =
+                       virt_to_page(vmq_idx_to_kaddr(vmq, idx));
+               skb_shinfo(skb)->frags[i].page_offset = 0;
+               skb_shinfo(skb)->frags[i].size = PAGE_SIZE;
+               skb_shinfo(skb)->nr_frags++;
+               skb->dev = netdevice;
+               cons++;
+       }
+
+       vmq->mapped_pages_cons = cons;
+
+       /* if number of buffers get low run tasklet to map more buffers */
+       if (nr_vmq_bufs(nc)  < VMQ_MIN_BUFFERS)
+               nc2_kick(&nc->rings);
+
+       return skb;
+
+kick_nc2:
+       /* kick netchannel2 interface to get any recently posted buffers */
+       nc2_kick(&nc->rings);
+error:
+       /* Add the skb back to the free pool */
+       skb_queue_tail(&vmq->free_skb_list, skb);
+       return NULL;
+}
+EXPORT_SYMBOL(vmq_alloc_skb);
+
+/* Detach the guest pages and free the socket buffer */
+void vmq_free_skb(struct sk_buff *skb, int queue_id)
+{
+       struct net_device *dev;
+       struct netchannel2 *nc;
+       nc2_vmq_t *vmq;
+
+       /* get the netchannel2 interface corresponding to this queue */
+       dev = nc2_vmq_queue_to_vif(skb->dev, queue_id);
+
+       nc = netdev_priv(dev);
+       vmq = &nc->vmq;
+
+       /* Add skb to the dealloc queue */
+       skb->dev = dev;
+       skb_queue_tail(&vmq->dealloc_queue, skb);
+
+       /* kick netchannel2 interface  */
+       nc2_kick(&nc->rings);
+
+}
+EXPORT_SYMBOL(vmq_free_skb);
+
+int nc2_is_vmq_packet(struct netchannel2 *nc, struct sk_buff *skb)
+{
+       int nr_frags;
+       long idx;
+       nc2_vmq_t *vmq = &nc->vmq;
+
+       nr_frags = skb_shinfo(skb)->nr_frags;
+       if (vmq->vmq_mode && nr_frags &&
+           PageForeign(skb_shinfo(skb)->frags[0].page)) {
+               idx = nc2_vmq_page_index(skb_shinfo(skb)->frags[0].page);
+               if ((idx >= 0) && (idx < VMQ_MAX_BUFFERS))
+                       return 1;
+       }
+
+       return 0;
+}
+
+/* Prepare to transmit a vmq packet */
+void xmit_vmq(struct netchannel2 *nc, struct sk_buff *skb,
+             volatile void *msg_buf)
+{
+       volatile struct netchannel2_msg_packet *msg = msg_buf;
+       volatile struct netchannel2_fragment *out_frag;
+       nc2_vmq_t *vmq = &nc->vmq;
+       skb_frag_t *frag;
+       struct nc2_tx_buffer *txbuf;
+       int nr_frags;
+       unsigned int idx;
+       unsigned x;
+
+       nr_frags = skb_shinfo(skb)->nr_frags;
+       for (x = 0; x < nr_frags; x++) {
+               frag = &skb_shinfo(skb)->frags[x];
+               out_frag = &msg->frags[x];
+
+               idx = nc2_vmq_page_index(frag->page);
+               txbuf = vmq->buffer[idx].buf;
+               out_frag->pre_post.id = txbuf->id;
+               out_frag->off  = frag->page_offset;
+               out_frag->size = frag->size;
+               /* TODO: need to batch unmap grants */
+               nc2_vmq_unmap_buf(nc, idx, 0);
+       }
+
+       /* Avoid unmapping frags grants when skb is freed later */
+       /* by nc2_vmq_free_skb() */
+       skb_shinfo(skb)->nr_frags = 0;
+}
+
diff --git a/drivers/xen/netchannel2/vmq.h b/drivers/xen/netchannel2/vmq.h
new file mode 100644
index 0000000..fa1cc8a
--- /dev/null
+++ b/drivers/xen/netchannel2/vmq.h
@@ -0,0 +1,58 @@
+#ifndef VMQ_H__
+#define VMQ_H__
+
+#include "netchannel2_core.h"
+
+#ifdef CONFIG_XEN_NETDEV2_VMQ
+
+int nc2_vmq_connect(struct netchannel2 *nc);
+void nc2_vmq_disconnect(struct netchannel2 *nc);
+void do_vmq_work(struct netchannel2 *nc);
+int nc2_is_vmq_packet(struct netchannel2 *nc, struct sk_buff *skb);
+void xmit_vmq(struct netchannel2 *nc, struct sk_buff *skb,
+             volatile void *msg);
+void vmq_flush_unmap_hypercall(void);
+
+#define vmq_get(_b)                                            \
+               atomic_inc(&(_b)->refcnt);
+
+#define vmq_put(_b)                                            \
+       do {                                                    \
+               if (atomic_dec_and_test(&(_b)->refcnt)) {       \
+                       wake_up(&(_b)->waiting_to_free);        \
+               }                                               \
+       } while (0)
+
+static inline int nr_vmq_mapped_bufs(struct netchannel2 *nc)
+{
+       return nc->vmq.mapped_pages_prod -
+               nc->vmq.mapped_pages_cons;
+}
+
+static inline int nr_vmq_bufs(struct netchannel2 *nc)
+{
+       return nc->vmq.nbufs;
+}
+
+static inline int nc2_in_vmq_mode(struct netchannel2 *nc)
+{
+       return nc->vmq.vmq_mode;
+}
+
+#else
+static inline int nc2_vmq_connect(struct netchannel2 *nc)
+{
+       return 0;
+}
+static inline void nc2_vmq_disconnect(struct netchannel2 *nc)
+{
+}
+static inline void do_vmq_work(struct netchannel2 *nc)
+{
+}
+static inline void vmq_flush_unmap_hypercall(void)
+{
+}
+#endif /* CONFIG_XEN_NETDEV2_VMQ */
+
+#endif /* !VMQ_H__ */
diff --git a/drivers/xen/netchannel2/vmq_def.h 
b/drivers/xen/netchannel2/vmq_def.h
new file mode 100644
index 0000000..60f1ccb
--- /dev/null
+++ b/drivers/xen/netchannel2/vmq_def.h
@@ -0,0 +1,68 @@
+#ifndef VMQ_DEF_H__
+#define VMQ_DEF_H__
+
+
+/* size of HW queue in VMQ device */
+#define VMQ_QUEUE_SIZE 1024
+
+/* Mimimum amount of buffers needed for VMQ
+ * This is the lower water mark that triggers mapping more guest buffers
+ * Should be larger than the queue size to allow for in flight packets
+ */
+#define VMQ_MIN_BUFFERS 1920
+
+/* Maximum amount of posted buffers which are reserved for VMQ
+ * Should be less than MAX_POSTED_BUFFERS. For now, the difference can be used
+ * for intra-node guest to guest traffic. When we map guest buffers we try to
+ * have VMQ_MAX_BUFFERS mapped. The difference 
(VMQ_MAX_BUFFERS-VMQ_MIN_BUFFERS)
+ * helps batch multiple grant map operattions
+ * VMQ_QUEUE_SIZE < VMQ_MIN_BUFFER < VMQ_MAX_BUFFER < MAX_POSTED_BUFFERS
+ * VMQ_MAX_BUFFERS must be a power of 2
+ */
+#define VMQ_MAX_BUFFERS 2048
+
+/* skb size is zero since packet data uses fragments */
+#define VMQ_SKB_SIZE 0
+
+#define VMQ_NUM_BUFFERS(len) ((len + PAGE_SIZE - 1) / PAGE_SIZE)
+
+#define VMQ_IDX_MASK(_i) ((_i)&(VMQ_MAX_BUFFERS-1))
+
+typedef struct nc2_vmq_buf {
+       struct nc2_tx_buffer *buf;
+       struct netchannel2   *nc;
+} nc2_vmq_buf_t;
+
+typedef struct nc2_vmq {
+       struct net_device *pdev;        /* Pointer to physical device */
+       int vmq_mode;                   /* indicate if vif is in vmq mode   */
+       struct page **pages;            /* pages for mapping guest RX bufs  */
+       struct sk_buff_head free_skb_list;     /* Free socket buffer pool   */
+       struct sk_buff_head dealloc_queue;     /* list of skb's to be free  */
+       struct sk_buff_head rx_queue;          /* list of received packets  */
+
+       /* guest mapped buffers */
+       nc2_vmq_buf_t buffer[VMQ_MAX_BUFFERS];
+
+       /* Ring with free pages available for mapping guest RX buffers */
+       u16 unmapped_pages[VMQ_MAX_BUFFERS];
+       unsigned int unmapped_pages_prod;
+       unsigned int unmapped_pages_cons;
+
+       /* Ring of mapped RX  pages avaialable for vmq device */
+       u16 mapped_pages[VMQ_MAX_BUFFERS];
+       unsigned int mapped_pages_prod;
+       unsigned int mapped_pages_cons;
+
+       unsigned int nbufs;           /* number of vmq buffers: posted to   */
+                                     /* HW queue or available to be posted */
+       int vmq_id;                   /* Queue id    */
+       int vmq_size;                 /* Queue size  */
+       int vmq_state;                /* queue stste */
+
+       atomic_t         refcnt;
+       wait_queue_head_t waiting_to_free;
+
+} nc2_vmq_t;
+
+#endif /* !VMQ_DEF_H__ */
diff --git a/drivers/xen/netchannel2/xmit_packet.c 
b/drivers/xen/netchannel2/xmit_packet.c
index 1a879aa..09827fc 100644
--- a/drivers/xen/netchannel2/xmit_packet.c
+++ b/drivers/xen/netchannel2/xmit_packet.c
@@ -3,6 +3,7 @@
 #include <linux/kernel.h>
 #include <linux/version.h>
 #include "netchannel2_core.h"
+#include "vmq.h"
 
 /* You don't normally want to transmit in posted buffers mode, because
    grant mode is usually faster, but it's sometimes useful for testing
@@ -189,6 +190,11 @@ int nc2_really_start_xmit(struct netchannel2_ring_pair 
*ncrp,
        set_offload_flags(skb, msg);
 
        switch (skb_co->policy) {
+#ifdef CONFIG_XEN_NETDEV2_VMQ
+       case transmit_policy_vmq:
+               xmit_vmq(nc, skb, msg);
+               break;
+#endif
        case transmit_policy_small:
                /* Nothing to do */
                break;
-- 
1.6.3.1


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.