diff options
| author | pooka <pooka@NetBSD.org> | 2009-02-28 15:28:45 +0000 |
|---|---|---|
| committer | pooka <pooka@NetBSD.org> | 2009-02-28 15:28:45 +0000 |
| commit | a4aff0cbb8732e667fd398827b926b180acbecb9 (patch) | |
| tree | 2d7f8387a3576807dd47180488ab0776e76d5302 /sys/rump/net/lib/libshmif | |
| parent | 95deb7702d9720949d674b1c7943a0bb43617e46 (diff) | |
Add a virtual ethernet interface which uses shared memory as the
bus instead of relying on the host kernel's tap and bridge. This
is much lighterweight approach which does not consume non-pageable
kernel resources.
Diffstat (limited to 'sys/rump/net/lib/libshmif')
| -rw-r--r-- | sys/rump/net/lib/libshmif/Makefile | 11 | ||||
| -rw-r--r-- | sys/rump/net/lib/libshmif/if_shmem.c | 373 | ||||
| -rw-r--r-- | sys/rump/net/lib/libshmif/shlib_version | 4 |
3 files changed, 388 insertions, 0 deletions
diff --git a/sys/rump/net/lib/libshmif/Makefile b/sys/rump/net/lib/libshmif/Makefile new file mode 100644 index 00000000000..ce0a603ceb1 --- /dev/null +++ b/sys/rump/net/lib/libshmif/Makefile @@ -0,0 +1,11 @@ +# $NetBSD: Makefile,v 1.1 2009/02/28 15:28:46 pooka Exp $ +# + +LIB= rumpnet_shmif + +SRCS= if_shmem.c + +CPPFLAGS+= -I${.CURDIR}/../../../librump/rumpkern + +.include <bsd.lib.mk> +.include <bsd.klinks.mk> diff --git a/sys/rump/net/lib/libshmif/if_shmem.c b/sys/rump/net/lib/libshmif/if_shmem.c new file mode 100644 index 00000000000..27bd57ea80c --- /dev/null +++ b/sys/rump/net/lib/libshmif/if_shmem.c @@ -0,0 +1,373 @@ +/* $NetBSD: if_shmem.c,v 1.1 2009/02/28 15:28:46 pooka Exp $ */ + +/* + * Copyright (c) 2009 Antti Kantee. All Rights Reserved. + * + * Development of this software was supported by The Nokia Foundation. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS + * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: if_shmem.c,v 1.1 2009/02/28 15:28:46 pooka Exp $"); + +#include <sys/param.h> +#include <sys/condvar.h> +#include <sys/fcntl.h> +#include <sys/kmem.h> +#include <sys/kthread.h> +#include <sys/lock.h> + +#include <net/if.h> +#include <net/if_ether.h> + +#include <netinet/in.h> +#include <netinet/in_var.h> + +#include <rump/rump.h> +#include <rump/rumpuser.h> + +#include "rump_private.h" + +/* + * A virtual ethernet interface which uses shared memory from a + * memory mapped file as the bus. + */ + +static int shmif_init(struct ifnet *); +static int shmif_ioctl(struct ifnet *, u_long, void *); +static void shmif_start(struct ifnet *); +static void shmif_stop(struct ifnet *, int); + +struct shmif_sc { + struct ethercom sc_ec; + uint8_t sc_myaddr[6]; + uint8_t *sc_busmem; + int sc_memfd; + int sc_kq; + + uint32_t sc_nextpacket; + uint32_t sc_prevgen; +}; +#define IFMEM_LOCK (0) +#define IFMEM_GENERATION (8) +#define IFMEM_LASTPACKET (12) +#define IFMEM_WAKEUP (16) +#define IFMEM_DATA (20) + +#define BUSCTRL_ATOFF(sc, off) ((uint32_t *)(sc->sc_busmem+(off))) + +#define BUSMEM_SIZE 65536 /* enough? */ + +static void shmif_rcv(void *); + +static uint32_t numif; + +static void +lockbus(struct shmif_sc *sc) +{ + + __cpu_simple_lock((__cpu_simple_lock_t *)sc->sc_busmem); +} + +static void +unlockbus(struct shmif_sc *sc) +{ + + __cpu_simple_unlock((__cpu_simple_lock_t *)sc->sc_busmem); +} + +static uint32_t +busread(struct shmif_sc *sc, void *dest, uint32_t off, size_t len) +{ + size_t chunk; + + KASSERT(len < (BUSMEM_SIZE - IFMEM_DATA) && off <= BUSMEM_SIZE); + chunk = MIN(len, BUSMEM_SIZE - off); + memcpy(dest, sc->sc_busmem + off, chunk); + len -= chunk; + + if (len == 0) + return off + chunk; + + /* else, wraps around */ + off = IFMEM_DATA; + sc->sc_prevgen = *BUSCTRL_ATOFF(sc, IFMEM_GENERATION); + + /* finish reading */ + memcpy((uint8_t *)dest + chunk, sc->sc_busmem + off, len); + return off + len; +} + +static uint32_t +buswrite(struct shmif_sc *sc, uint32_t off, void *data, size_t len) +{ + size_t chunk; + + KASSERT(len < (BUSMEM_SIZE - IFMEM_DATA) && off <= BUSMEM_SIZE); + + chunk = MIN(len, BUSMEM_SIZE - off); + memcpy(sc->sc_busmem + off, data, chunk); + len -= chunk; + + if (len == 0) + return off + chunk; + + DPRINTF(("buswrite wrap: wrote %d bytes to %d, left %d to %d", + chunk, off, len, IFMEM_DATA)); + + /* else, wraps around */ + off = IFMEM_DATA; + (*BUSCTRL_ATOFF(sc, IFMEM_GENERATION))++; + sc->sc_prevgen = *BUSCTRL_ATOFF(sc, IFMEM_GENERATION); + + /* finish writing */ + memcpy(sc->sc_busmem + off, (uint8_t *)data + chunk, len); + return off + len; +} + +static inline uint32_t +advance(uint32_t oldoff, uint32_t delta) +{ + uint32_t newoff; + + newoff = oldoff + delta; + if (newoff >= BUSMEM_SIZE) + newoff -= (BUSMEM_SIZE - IFMEM_DATA); + return newoff; + +} + +static uint32_t +nextpktoff(struct shmif_sc *sc, uint32_t oldoff) +{ + uint32_t oldlen; + + busread(sc, &oldlen, oldoff, 4); + KASSERT(oldlen < BUSMEM_SIZE - IFMEM_DATA); + + return advance(oldoff, 4 + oldlen); +} + +int rump_shmif_create(const char *, int *); /* XXX */ + +int +rump_shmif_create(const char *path, int *ifnum) +{ + struct shmif_sc *sc; + struct ifnet *ifp; + uint8_t enaddr[ETHER_ADDR_LEN] = { 0xb2, 0xa0, 0x00, 0x00, 0x00, 0x00 }; + uint32_t randnum; + unsigned mynum; + int error; + + randnum = arc4random(); + memcpy(&enaddr[2], &randnum, 4); + mynum = atomic_inc_uint_nv(&numif)-1; + + sc = kmem_zalloc(sizeof(*sc), KM_SLEEP); + ifp = &sc->sc_ec.ec_if; + memcpy(sc->sc_myaddr, enaddr, sizeof(enaddr)); + + sc->sc_memfd = rumpuser_open(path, O_RDWR | O_CREAT, &error); + if (sc->sc_memfd == -1) + goto fail; + sc->sc_busmem = rumpuser_filemmap(sc->sc_memfd, 0, BUSMEM_SIZE, + 1, 1, &error); + if (error) + goto fail; + + lockbus(sc); + if (*BUSCTRL_ATOFF(sc, IFMEM_LASTPACKET) == 0) + *BUSCTRL_ATOFF(sc, IFMEM_LASTPACKET) = IFMEM_DATA; + sc->sc_nextpacket = *BUSCTRL_ATOFF(sc, IFMEM_LASTPACKET); + sc->sc_prevgen = *BUSCTRL_ATOFF(sc, IFMEM_GENERATION); + unlockbus(sc); + + sc->sc_kq = rumpuser_writewatchfile_setup(-1, sc->sc_memfd, 0, &error); + if (sc->sc_kq == -1) + goto fail; + + if (rump_threads) { + error = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL, + shmif_rcv, ifp, NULL, "shmif"); + if (error) { + goto fail; + } + } else { + printf("WARNING: threads not enabled, shmif NOT working\n"); + } + + sprintf(ifp->if_xname, "shmif%d", mynum); + ifp->if_softc = sc; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_init = shmif_init; + ifp->if_ioctl = shmif_ioctl; + ifp->if_start = shmif_start; + ifp->if_stop = shmif_stop; + ifp->if_mtu = 1518; + + if_attach(ifp); + ether_ifattach(ifp, enaddr); + + *ifnum = mynum; + return 0; + + fail: + panic("rump_shmemif_create: fixme"); +} + +static int +shmif_init(struct ifnet *ifp) +{ + + ifp->if_flags |= IFF_RUNNING; + return 0; +} + +static int +shmif_ioctl(struct ifnet *ifp, u_long cmd, void *data) +{ + int s, rv; + + s = splnet(); + rv = ether_ioctl(ifp, cmd, data); + splx(s); + + return rv; +} + +/* send everything in-context */ +static void +shmif_start(struct ifnet *ifp) +{ + struct shmif_sc *sc = ifp->if_softc; + struct mbuf *m, *m0; + uint32_t lastoff, dataoff, npktlenoff; + uint32_t pktsize = 0; + bool wrote = false; + int error; + + for (;;) { + IF_DEQUEUE(&ifp->if_snd, m0); + if (m0 == NULL) { + break; + } + + lockbus(sc); + lastoff = *BUSCTRL_ATOFF(sc, IFMEM_LASTPACKET); + + npktlenoff = nextpktoff(sc, lastoff); + dataoff = advance(npktlenoff, 4); + + for (m = m0; m != NULL; m = m->m_next) { + pktsize += m->m_len; + dataoff = buswrite(sc, dataoff, mtod(m, void *), + m->m_len); + } + buswrite(sc, npktlenoff, &pktsize, 4); + *BUSCTRL_ATOFF(sc, IFMEM_LASTPACKET) = npktlenoff; + unlockbus(sc); + + m_freem(m0); + wrote = true; + + DPRINTF(("shmif_start: send %d bytes at off %d\n", + pktsize, npktlenoff)); + } + /* wakeup */ + if (wrote) + rumpuser_pwrite(sc->sc_memfd, &error, 4, IFMEM_WAKEUP, &error); +} + +static void +shmif_stop(struct ifnet *ifp, int disable) +{ + + panic("%s: unimpl", __func__); +} + +static void +shmif_rcv(void *arg) +{ + struct ifnet *ifp = arg; + struct shmif_sc *sc = ifp->if_softc; + struct mbuf *m = NULL; + struct ether_header *eth; + uint32_t nextpkt, pktlen, lastpkt, busgen, lastnext; + int error; + + for (;;) { + if (m == NULL) { + m = m_gethdr(M_WAIT, MT_DATA); + MCLGET(m, M_WAIT); + } + + DPRINTF(("waiting %d/%d\n", sc->sc_nextpacket, sc->sc_prevgen)); + + KASSERT(m->m_flags & M_EXT); + lockbus(sc); + lastpkt = *BUSCTRL_ATOFF(sc, IFMEM_LASTPACKET); + busgen = *BUSCTRL_ATOFF(sc, IFMEM_GENERATION); + lastnext = nextpktoff(sc, lastpkt); + if ((lastnext > sc->sc_nextpacket && busgen > sc->sc_prevgen) + || (busgen > sc->sc_prevgen+1)) { + nextpkt = lastpkt; + sc->sc_prevgen = busgen; + printf("DROPPING\n"); + } else { + nextpkt = sc->sc_nextpacket; + } + + /* need more data? */ + if (lastnext == nextpkt && sc->sc_prevgen == busgen){ + unlockbus(sc); + error = 0; + rumpuser_writewatchfile_wait(sc->sc_kq, NULL, &error); + if (__predict_false(error)) + printf("shmif_rcv: wait failed %d\n", error); + continue; + } + + busread(sc, &pktlen, nextpkt, 4); + busread(sc, mtod(m, void *), advance(nextpkt, 4), pktlen); + + DPRINTF(("shmif_rcv: read packet of length %d at %d\n", + pktlen, nextpkt)); + + sc->sc_nextpacket = nextpktoff(sc, nextpkt); + sc->sc_prevgen = busgen; + unlockbus(sc); + + m->m_len = m->m_pkthdr.len = pktlen; + m->m_pkthdr.rcvif = ifp; + + /* if it's to us, don't pass up and reuse storage space */ + eth = mtod(m, struct ether_header *); + if (memcmp(eth->ether_shost, sc->sc_myaddr, 6) != 0) { + ifp->if_input(ifp, m); + m = NULL; + } + } + + panic("shmif_worker is a lazy boy %d\n", error); +} diff --git a/sys/rump/net/lib/libshmif/shlib_version b/sys/rump/net/lib/libshmif/shlib_version new file mode 100644 index 00000000000..3d524cc9bbf --- /dev/null +++ b/sys/rump/net/lib/libshmif/shlib_version @@ -0,0 +1,4 @@ +# $NetBSD: shlib_version,v 1.1 2009/02/28 15:28:46 pooka Exp $ +# +major=0 +minor=0 |
