We can add a IOQNET loop-back device and register it with the PVBUS to test
many aspects of the system (IOQ, PVBUS, and IOQNET itself).

Signed-off-by: Gregory Haskins <[EMAIL PROTECTED]>
---

 drivers/net/Kconfig           |   10 +
 drivers/net/ioqnet/Makefile   |    3 
 drivers/net/ioqnet/loopback.c |  502 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 515 insertions(+), 0 deletions(-)

diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 7ee7454..426947d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -2957,6 +2957,16 @@ config IOQNET_DEBUG
        depends on IOQNET
        default n
 
+config IOQNET_LOOPBACK
+       tristate "IOQNET loopback device test harness"
+       depends on IOQNET
+       default n
+       ---help---
+       This will install a special PVBUS device that implements two IOQNET
+       devices.  The devices are, of course, linked to one another forming a
+       loopback mechanism.  This allows many subsystems to be tested: IOQ,
+       PVBUS, and IOQNET itself.  If unsure, say N.
+
 endif #NETDEVICES
 
 config NETPOLL
diff --git a/drivers/net/ioqnet/Makefile b/drivers/net/ioqnet/Makefile
index d7020ee..7d2d156 100644
--- a/drivers/net/ioqnet/Makefile
+++ b/drivers/net/ioqnet/Makefile
@@ -4,8 +4,11 @@
 
 ioqnet-objs = driver.o
 obj-$(CONFIG_IOQNET) += ioqnet.o
+ioqnet-loopback-objs = loopback.o
+obj-$(CONFIG_IOQNET_LOOPBACK) += ioqnet-loopback.o
 
 
 ifeq ($(CONFIG_IOQNET_DEBUG),y)
 EXTRA_CFLAGS += -DIOQNET_DEBUG
 endif
+
diff --git a/drivers/net/ioqnet/loopback.c b/drivers/net/ioqnet/loopback.c
new file mode 100644
index 0000000..0e36b43
--- /dev/null
+++ b/drivers/net/ioqnet/loopback.c
@@ -0,0 +1,502 @@
+/*
+ * ioqnet test harness
+ *
+ * Copyright (C) 2007 Novell, Gregory Haskins <[EMAIL PROTECTED]>
+ *
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/pvbus.h>
+#include <linux/ioq.h>
+#include <linux/kthread.h>
+#include <linux/ioqnet.h>
+#include <linux/interrupt.h>
+
+MODULE_AUTHOR("Gregory Haskins");
+MODULE_LICENSE("GPL");
+
+#ifndef ETH_ALEN
+#define ETH_ALEN 6
+#endif
+
+#undef PDEBUG             /* undef it, just in case */
+#ifdef IOQNET_DEBUG
+#  define PDEBUG(fmt, args...) printk( KERN_DEBUG "ioqnet: " fmt, ## args)
+#else
+#  define PDEBUG(fmt, args...) /* not debugging: nothing */
+#endif
+
+/*
+ * ---------------------------------------------------------------------
+ * First we must create an IOQ implementation to use while under test
+ * since these operations will all be local to the same host
+ * ---------------------------------------------------------------------
+ */
+
+struct ioqnet_lb_ioq {
+       struct ioq               ioq;
+       struct ioqnet_lb_ioq    *peer;
+       struct tasklet_struct    task;
+};
+
+struct ioqnet_lb_ioqmgr {
+       struct ioq_mgr  mgr;
+
+       /*
+        * Since this is just a test harness, we know ahead of time that
+        * we aren't going to need more than a handful of IOQs.  So to keep
+        * lookups simple we will simply create a static array of them
+        */
+       struct ioqnet_lb_ioq ioqs[8];
+       int pos;
+};
+
+static struct ioqnet_lb_ioqmgr lb_ioqmgr;
+
+struct ioqnet_lb_ioq* to_ioq(struct ioq *ioq)
+{
+       return container_of(ioq, struct ioqnet_lb_ioq, ioq);
+}
+
+struct ioqnet_lb_ioqmgr* to_mgr(struct ioq_mgr *mgr)
+{
+       return container_of(mgr, struct ioqnet_lb_ioqmgr, mgr);
+}
+
+/*
+ * ------------------
+ * ioq implementation
+ * ------------------
+ */
+static void ioqnet_lb_ioq_wake(unsigned long data)
+{
+       struct ioqnet_lb_ioq *_ioq = (struct ioqnet_lb_ioq*)data;
+
+       if (_ioq->peer)
+               ioq_wakeup(&_ioq->peer->ioq);
+}
+
+static int ioqnet_lb_ioq_signal(struct ioq *ioq)
+{
+       struct ioqnet_lb_ioq *_ioq = to_ioq(ioq);
+
+       if (_ioq->peer)
+               tasklet_schedule(&_ioq->task);
+
+       return 0;
+}
+
+static void ioqnet_lb_ioq_destroy(struct ioq *ioq)
+{
+       struct ioqnet_lb_ioq *_ioq = to_ioq(ioq);
+
+       if (_ioq->peer) {
+               _ioq->peer->peer = NULL;
+               _ioq->peer       = NULL;
+       }
+
+       if (_ioq->ioq.locale == ioq_locality_north) {
+               kfree(_ioq->ioq.ring);
+               kfree(_ioq->ioq.head_desc);
+       } else
+               kfree(_ioq);
+}
+
+/*
+ * ------------------
+ * ioqmgr implementation
+ * ------------------
+ */
+static int ioqnet_lb_ioq_create(struct ioq_mgr *t, struct ioq **ioq,
+                               size_t ringsize, int flags)
+{
+       struct ioqnet_lb_ioqmgr *mgr       = to_mgr(t);
+       struct ioqnet_lb_ioq    *_ioq      = NULL;
+       struct ioq_ring_head    *head_desc = NULL;
+       void                    *ring      = NULL;
+       int                      ret       = -ENOMEM;
+       size_t                   ringlen;
+       ioq_id_t                 id;
+
+       ringlen = sizeof(struct ioq_ring_desc) * ringsize;
+
+       BUG_ON(mgr->pos >= (sizeof(mgr->ioqs)/sizeof(mgr->ioqs[0])));
+
+       id = (ioq_id_t)mgr->pos++;
+
+       _ioq = &mgr->ioqs[id];
+       if (!_ioq)
+               goto error;
+
+       head_desc = kzalloc(sizeof(*head_desc), GFP_KERNEL);
+       if (!head_desc)
+               goto error;
+
+       ring = kzalloc(ringlen, GFP_KERNEL);
+       if (!ring)
+               goto error;
+
+       head_desc->magic     = IOQ_RING_MAGIC;
+       head_desc->ver       = IOQ_RING_VER;
+       head_desc->id        = id;
+       head_desc->count     = ringsize;
+       head_desc->ptr       = (u64)ring;
+
+       ioq_init(&_ioq->ioq);
+
+       _ioq->ioq.signal     = ioqnet_lb_ioq_signal;
+       _ioq->ioq.destroy    = ioqnet_lb_ioq_destroy;
+
+       _ioq->ioq.id         = head_desc->id;
+       _ioq->ioq.locale     = ioq_locality_north;
+       _ioq->ioq.mgr        = t;
+       _ioq->ioq.head_desc  = head_desc;
+       _ioq->ioq.ring       = ring;
+
+       tasklet_init(&_ioq->task, ioqnet_lb_ioq_wake, (unsigned long)_ioq);
+
+       *ioq = &_ioq->ioq;
+
+       return 0;
+
+ error:
+       if (head_desc)
+               kfree(head_desc);
+       if (ring)
+               kfree(ring);
+
+       return ret;
+}
+
+static int ioqnet_lb_ioq_connect(struct ioq_mgr *t, ioq_id_t id,
+                                struct ioq **ioq, int flags)
+{
+       struct ioqnet_lb_ioqmgr *mgr      = to_mgr(t);
+       struct ioqnet_lb_ioq    *peer_ioq = &mgr->ioqs[id];
+       struct ioqnet_lb_ioq    *_ioq;
+
+       if (peer_ioq->peer)
+               return -EEXIST;
+
+       _ioq = kzalloc(sizeof(*_ioq), GFP_KERNEL);
+       if (!_ioq)
+               return -ENOMEM;
+
+       ioq_init(&_ioq->ioq);
+
+       _ioq->ioq.signal     = ioqnet_lb_ioq_signal;
+       _ioq->ioq.destroy    = ioqnet_lb_ioq_destroy;
+
+       _ioq->ioq.id         = id;
+       _ioq->ioq.locale     = ioq_locality_south;
+       _ioq->ioq.mgr        = t;
+       _ioq->ioq.head_desc  = peer_ioq->ioq.head_desc;
+       _ioq->ioq.ring       = peer_ioq->ioq.ring;
+
+       _ioq->peer           = peer_ioq;
+       peer_ioq->peer       = _ioq;
+       tasklet_init(&_ioq->task, ioqnet_lb_ioq_wake, (unsigned long)_ioq);
+
+       *ioq = &_ioq->ioq;
+
+       return 0;
+}
+
+static void ioqnet_lb_ioqmgr_init(void)
+{
+       struct ioqnet_lb_ioqmgr *mgr = &lb_ioqmgr;
+
+       memset(mgr, 0, sizeof(mgr));
+
+       mgr->mgr.create  = ioqnet_lb_ioq_create;
+       mgr->mgr.connect = ioqnet_lb_ioq_connect;
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * Next we create the loopback device in terms of our ioqnet_lb_ioq
+ * subsystem
+ * ---------------------------------------------------------------------
+ */
+
+struct ioqnet_lb_device {
+       int                      idx; /* 0 or 1 */
+       struct ioq              *rxq;
+       struct ioq              *txq;
+       char                     mac[ETH_ALEN];
+       struct task_struct      *task;
+       struct ioqnet_lb_device *peer;
+
+       struct pvbus_device      dev;
+};
+
+struct ioqnet_lb_device* to_dev(struct pvbus_device *dev)
+{
+       return container_of(dev, struct ioqnet_lb_device, dev);
+}
+
+static void ioqnet_lb_xmit(struct ioqnet_lb_device *dev,
+                          struct ioqnet_tx_ptr *ptr,
+                          size_t count)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       struct ioq_iterator iter;
+       int ret;
+       int i;
+       char *dest;
+
+       add_wait_queue(&dev->txq->wq, &wait);
+
+       /* We want to iterate on the head of the in-use index for reading */
+       ret = ioq_iter_init(dev->txq, &iter, ioq_idxtype_inuse,
+                           IOQ_ITER_AUTOUPDATE);
+       BUG_ON(ret < 0);
+
+       ret = ioq_iter_seek(&iter, ioq_seek_head, 0, 0);
+       BUG_ON(ret < 0);
+
+       set_current_state(TASK_UNINTERRUPTIBLE);
+
+       while (!iter.desc->valid || !iter.desc->sown)
+               schedule();
+
+       set_current_state(TASK_RUNNING);
+
+       dest = __va(iter.desc->ptr);
+
+       for (i = 0; i < count; ++i) {
+               struct ioqnet_tx_ptr *p = &ptr[i];
+               void                 *d = __va(p->data);
+
+               memcpy(dest, d, p->len);
+               dest += p->len;
+       }
+
+       mb();
+       iter.desc->sown = 0;
+       mb();
+       
+       /* Advance the in-use head */
+       ret = ioq_iter_push(&iter, 0);
+       BUG_ON(ret < 0);
+}
+
+/*
+ * This is the daemon thread for each device that gets created once the guest
+ * side connects to us (via the pvbus_device->call(IOQNET_CONNECT) operation).
+ * We want to wait on packets to arrive on the rxq, and then send them to our
+ * peer's txq.
+ */
+static int ioqnet_lb_thread(void *data)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       struct ioqnet_lb_device *dev = (struct ioqnet_lb_device*)data;
+       struct ioq_iterator iter;
+       int ret;
+
+       add_wait_queue(&dev->rxq->wq, &wait);
+
+       /* We want to iterate on the tail of the in-use index for reading */
+       ret = ioq_iter_init(dev->rxq, &iter, ioq_idxtype_inuse,
+                           IOQ_ITER_AUTOUPDATE);
+       BUG_ON(ret < 0);
+
+       ret = ioq_iter_seek(&iter, ioq_seek_tail, 0, 0);
+       BUG_ON(ret < 0);
+
+       while (1) {
+               struct ioq_ring_desc *desc = iter.desc;
+               struct ioqnet_tx_ptr *ptr;
+
+               PDEBUG("%d: Waiting...\n", dev->idx);
+
+               set_current_state(TASK_UNINTERRUPTIBLE);
+
+               while (!desc->sown)
+                       schedule();
+
+               set_current_state(TASK_RUNNING);
+
+               PDEBUG("%d: Got a packet\n", dev->idx);
+
+               ptr = __va(desc->ptr);
+               
+               /*
+                * If the peer is connected, we transmit it to their
+                * queue...otherwise we just drop it on the floor
+                */
+               if (dev->peer->txq)
+                       ioqnet_lb_xmit(dev->peer, ptr, desc->alen);
+               
+               mb();
+               desc->sown = 0;
+               mb();
+               
+               /* Advance the in-use tail */
+               ret = ioq_iter_pop(&iter, 0);
+               BUG_ON(ret < 0);
+       }
+
+       return 0;
+}
+
+static int ioqnet_lb_dev_createqueue(struct pvbus_device *dev,
+                                    struct ioq **ioq,
+                                    size_t ringsize, int flags)
+{
+       struct ioq_mgr *ioqmgr = &lb_ioqmgr.mgr;
+
+       return ioqmgr->create(ioqmgr, ioq, ringsize, flags);
+}
+
+static int ioqnet_lb_queue_connect(ioq_id_t id, struct ioq **ioq)
+{
+       int ret;
+       struct ioq_mgr *ioqmgr = &lb_ioqmgr.mgr;
+
+       ret = ioqmgr->connect(ioqmgr, id, ioq, 0);
+       if (ret < 0)
+               return ret;
+
+       ioq_start(*ioq, 0);
+
+       return 0;
+}
+
+static int ioqnet_lb_dev_connect(struct ioqnet_lb_device *dev,
+                                void *data, size_t len)
+{
+       struct ioqnet_connect *cnct = (struct ioqnet_connect*)data;
+       int ret;
+
+       /* We connect the north's rxq to our txq */
+       ret = ioqnet_lb_queue_connect(cnct->rxq, &dev->txq);
+       if (ret < 0)
+               return ret;
+
+       /* And vice-versa */
+       ret = ioqnet_lb_queue_connect(cnct->txq, &dev->rxq);
+       if (ret < 0)
+               return ret;
+
+       dev->task = kthread_create(ioqnet_lb_thread, dev,
+                                 "ioqnet-lb/%d", dev->idx);
+       wake_up_process(dev->task);
+
+       return 0;
+}
+
+static int ioqnet_lb_dev_query_mac(struct ioqnet_lb_device *dev,
+                                  void *data, size_t len)
+{
+       if (len != ETH_ALEN)
+               return -EINVAL;
+
+       memcpy(data, dev->mac, ETH_ALEN);
+
+       return 0;
+}
+
+/*
+ * This function is invoked whenever a guest calls pvbus_ops->call() against
+ * our instance ID
+ */
+static int ioqnet_lb_dev_call(struct pvbus_device *dev, u32 func, void *data,
+                             size_t len, int flags)
+{
+       struct ioqnet_lb_device *_dev = to_dev(dev);
+       int ret = -EINVAL;
+
+       switch (func) {
+       case IOQNET_CONNECT:
+               ret = ioqnet_lb_dev_connect(_dev, data, len);
+               break;
+       case IOQNET_QUERY_MAC:
+               ret = ioqnet_lb_dev_query_mac(_dev, data, len);
+               break;
+       }
+
+       return ret;
+}
+
+static int ioqnet_lb_dev_init(struct ioqnet_lb_device *dev,
+                             int idx,
+                             struct ioqnet_lb_device *peer)
+{
+       char mac[] = { 0x00, 0x30, 0xcc, 0x00, 0x00, idx };
+
+       memset(dev, 0, sizeof(*dev));
+       dev->idx   = idx;
+       dev->peer  = peer;
+       memcpy(dev->mac, mac, ETH_ALEN);
+
+       dev->dev.name        = IOQNET_NAME;
+       dev->dev.id          = idx;
+       dev->dev.createqueue = ioqnet_lb_dev_createqueue;
+       dev->dev.call        = ioqnet_lb_dev_call;
+       sprintf(dev->dev.dev.bus_id, "%d", idx);
+
+       return 0;
+}
+
+/*
+ * ---------------------------------------------------------------------
+ * Finally we create the top-level object that binds it all together
+ * ---------------------------------------------------------------------
+ */
+
+
+struct ioqnet_lb {
+       struct ioqnet_lb_device devs[2];
+};
+
+static struct ioqnet_lb ioqnet_lb;
+
+__init int ioqnet_lb_init_module(void)
+{
+       int ret;
+       int i;
+
+       ioqnet_lb_ioqmgr_init();
+
+       /* First initialize both devices */
+       for (i = 0; i < 2; i++) {
+               ret = ioqnet_lb_dev_init(&ioqnet_lb.devs[i],
+                                        i,
+                                        &ioqnet_lb.devs[!i]);
+               BUG_ON(ret < 0);
+       }
+
+       /* Then register them together */
+       for (i = 0; i < 2; i++) {
+               ret = pvbus_device_register(&ioqnet_lb.devs[i].dev);
+               BUG_ON(ret < 0);
+       }
+
+       return 0;
+}
+
+__exit void ioqnet_lb_cleanup(void)
+{
+       int i;
+
+       for (i = 0; i < 2; i++)
+               pvbus_device_unregister(&ioqnet_lb.devs[i].dev);
+
+}
+
+
+module_init(ioqnet_lb_init_module);
+module_exit(ioqnet_lb_cleanup);


-------------------------------------------------------------------------
This SF.net email is sponsored by: Splunk Inc.
Still grepping through log files to find problems?  Stop.
Now Search log events and configuration files using AJAX and a browser.
Download your FREE copy of Splunk now >>  http://get.splunk.com/
_______________________________________________
kvm-devel mailing list
kvm-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/kvm-devel

Reply via email to