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