Home | History | Annotate | Line # | Download | only in libshmif
if_shmem.c revision 1.20
      1 /*	$NetBSD: if_shmem.c,v 1.20 2010/08/13 10:13:44 pooka Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2009 Antti Kantee.  All Rights Reserved.
      5  *
      6  * Development of this software was supported by The Nokia Foundation.
      7  *
      8  * Redistribution and use in source and binary forms, with or without
      9  * modification, are permitted provided that the following conditions
     10  * are met:
     11  * 1. Redistributions of source code must retain the above copyright
     12  *    notice, this list of conditions and the following disclaimer.
     13  * 2. Redistributions in binary form must reproduce the above copyright
     14  *    notice, this list of conditions and the following disclaimer in the
     15  *    documentation and/or other materials provided with the distribution.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
     18  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
     23  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     27  * SUCH DAMAGE.
     28  */
     29 
     30 #include <sys/cdefs.h>
     31 __KERNEL_RCSID(0, "$NetBSD: if_shmem.c,v 1.20 2010/08/13 10:13:44 pooka Exp $");
     32 
     33 #include <sys/param.h>
     34 #include <sys/atomic.h>
     35 #include <sys/fcntl.h>
     36 #include <sys/kmem.h>
     37 #include <sys/kthread.h>
     38 #include <sys/lock.h>
     39 #include <sys/atomic.h>
     40 
     41 #include <net/if.h>
     42 #include <net/if_ether.h>
     43 
     44 #include <netinet/in.h>
     45 #include <netinet/in_var.h>
     46 
     47 #include <rump/rump.h>
     48 #include <rump/rumpuser.h>
     49 
     50 #include "rump_private.h"
     51 #include "rump_net_private.h"
     52 
     53 /*
     54  * A virtual ethernet interface which uses shared memory from a
     55  * memory mapped file as the bus.
     56  */
     57 
     58 static int	shmif_init(struct ifnet *);
     59 static int	shmif_ioctl(struct ifnet *, u_long, void *);
     60 static void	shmif_start(struct ifnet *);
     61 static void	shmif_stop(struct ifnet *, int);
     62 
     63 #include "shmifvar.h"
     64 
     65 struct shmif_sc {
     66 	struct ethercom sc_ec;
     67 	uint8_t sc_myaddr[6];
     68 	struct shmif_mem *sc_busmem;
     69 	int sc_memfd;
     70 	int sc_kq;
     71 
     72 	uint32_t sc_nextpacket;
     73 	uint32_t sc_prevgen;
     74 };
     75 
     76 static const uint32_t busversion = SHMIF_VERSION;
     77 
     78 static void shmif_rcv(void *);
     79 
     80 static uint32_t numif;
     81 
     82 int
     83 rump_shmif_create(const char *path, int *ifnum)
     84 {
     85 	struct shmif_sc *sc;
     86 	struct ifnet *ifp;
     87 	uint8_t enaddr[ETHER_ADDR_LEN] = { 0xb2, 0xa0, 0x00, 0x00, 0x00, 0x00 };
     88 	uint32_t randnum;
     89 	unsigned mynum;
     90 	int error;
     91 
     92 	randnum = arc4random();
     93 	memcpy(&enaddr[2], &randnum, sizeof(randnum));
     94 	mynum = atomic_inc_uint_nv(&numif)-1;
     95 
     96 	sc = kmem_zalloc(sizeof(*sc), KM_SLEEP);
     97 	ifp = &sc->sc_ec.ec_if;
     98 	memcpy(sc->sc_myaddr, enaddr, sizeof(enaddr));
     99 
    100 	sc->sc_memfd = rumpuser_open(path, O_RDWR | O_CREAT, &error);
    101 	if (sc->sc_memfd == -1)
    102 		goto fail;
    103 	sc->sc_busmem = rumpuser_filemmap(sc->sc_memfd, 0, BUSMEM_SIZE,
    104 	    RUMPUSER_FILEMMAP_TRUNCATE | RUMPUSER_FILEMMAP_SHARED
    105 	    | RUMPUSER_FILEMMAP_READ | RUMPUSER_FILEMMAP_WRITE, &error);
    106 	if (error)
    107 		goto fail;
    108 
    109 	if (sc->sc_busmem->shm_magic && sc->sc_busmem->shm_magic != SHMIF_MAGIC)
    110 		panic("bus is not magical");
    111 
    112 	shmif_lockbus(sc->sc_busmem);
    113 	/* we're first?  initialize bus */
    114 	if (sc->sc_busmem->shm_magic == 0) {
    115 		sc->sc_busmem->shm_magic = SHMIF_MAGIC;
    116 		sc->sc_busmem->shm_first = BUSMEM_DATASIZE;
    117 	}
    118 
    119 	sc->sc_nextpacket = sc->sc_busmem->shm_last;
    120 	sc->sc_prevgen = sc->sc_busmem->shm_gen;
    121 	shmif_unlockbus(sc->sc_busmem);
    122 
    123 	sc->sc_kq = rumpuser_writewatchfile_setup(-1, sc->sc_memfd, 0, &error);
    124 	if (sc->sc_kq == -1)
    125 		goto fail;
    126 
    127 	sprintf(ifp->if_xname, "shmif%d", mynum);
    128 	ifp->if_softc = sc;
    129 	ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST;
    130 	ifp->if_init = shmif_init;
    131 	ifp->if_ioctl = shmif_ioctl;
    132 	ifp->if_start = shmif_start;
    133 	ifp->if_stop = shmif_stop;
    134 	ifp->if_mtu = 1518;
    135 
    136 	if_attach(ifp);
    137 	ether_ifattach(ifp, enaddr);
    138 
    139 	aprint_verbose("shmif%d: bus %s\n", mynum, path);
    140 	aprint_verbose("shmif%d: Ethernet address %s\n",
    141 	    mynum, ether_sprintf(enaddr));
    142 
    143 	if (ifnum)
    144 		*ifnum = mynum;
    145 	return 0;
    146 
    147  fail:
    148 	panic("rump_shmemif_create: fixme");
    149 }
    150 
    151 static int
    152 shmif_init(struct ifnet *ifp)
    153 {
    154 	int error = 0;
    155 
    156 	if (rump_threads) {
    157 		error = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
    158 		    shmif_rcv, ifp, NULL, "shmif");
    159 	} else {
    160 		printf("WARNING: threads not enabled, shmif NOT working\n");
    161 	}
    162 
    163 	ifp->if_flags |= IFF_RUNNING;
    164 	return error;
    165 }
    166 
    167 static int
    168 shmif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
    169 {
    170 	int s, rv;
    171 
    172 	s = splnet();
    173 	rv = ether_ioctl(ifp, cmd, data);
    174 	if (rv == ENETRESET)
    175 		rv = 0;
    176 	splx(s);
    177 
    178 	return rv;
    179 }
    180 
    181 /* send everything in-context */
    182 static void
    183 shmif_start(struct ifnet *ifp)
    184 {
    185 	struct shmif_sc *sc = ifp->if_softc;
    186 	struct mbuf *m, *m0;
    187 	uint32_t lastoff, dataoff, npktlenoff;
    188 	uint32_t pktsize = 0;
    189 	bool wrote = false;
    190 	bool wrap = false;
    191 	int error;
    192 
    193 	for (;;) {
    194 		struct shmif_pkthdr sp;
    195 		struct timeval tv;
    196 
    197 		IF_DEQUEUE(&ifp->if_snd, m0);
    198 		if (m0 == NULL) {
    199 			break;
    200 		}
    201 
    202 		for (m = m0; m != NULL; m = m->m_next) {
    203 			pktsize += m->m_len;
    204 		}
    205 
    206 		shmif_lockbus(sc->sc_busmem);
    207 		lastoff = sc->sc_busmem->shm_last;
    208 		npktlenoff = shmif_nextpktoff(sc->sc_busmem, lastoff);
    209 
    210 		getmicrouptime(&tv);
    211 
    212 		sp.sp_len = pktsize;
    213 		sp.sp_sec = tv.tv_sec;
    214 		sp.sp_usec = tv.tv_usec;
    215 
    216 		dataoff = shmif_buswrite(sc->sc_busmem,
    217 		    npktlenoff, &sp, sizeof(sp), &wrap);
    218 		for (m = m0; m != NULL; m = m->m_next) {
    219 			dataoff = shmif_buswrite(sc->sc_busmem, dataoff,
    220 			    mtod(m, void *), m->m_len, &wrap);
    221 		}
    222 
    223 		if (wrap)
    224 			sc->sc_busmem->shm_gen++;
    225 		sc->sc_busmem->shm_last = npktlenoff;
    226 		shmif_unlockbus(sc->sc_busmem);
    227 
    228 		m_freem(m0);
    229 		wrote = true;
    230 
    231 		DPRINTF(("shmif_start: send %d bytes at off %d\n",
    232 		    pktsize, npktlenoff));
    233 	}
    234 	/* wakeup */
    235 	if (wrote)
    236 		rumpuser_pwrite(sc->sc_memfd,
    237 		    &busversion, sizeof(busversion), IFMEM_WAKEUP, &error);
    238 }
    239 
    240 static void
    241 shmif_stop(struct ifnet *ifp, int disable)
    242 {
    243 
    244 	panic("%s: unimpl", __func__);
    245 }
    246 
    247 static void
    248 shmif_rcv(void *arg)
    249 {
    250 	struct ifnet *ifp = arg;
    251 	struct shmif_sc *sc = ifp->if_softc;
    252 	struct mbuf *m = NULL;
    253 	struct ether_header *eth;
    254 	uint32_t nextpkt, lastpkt, busgen, lastnext;
    255 	bool wrap = false;
    256 	int error;
    257 
    258 	for (;;) {
    259 		struct shmif_pkthdr sp;
    260 
    261 		if (m == NULL) {
    262 			m = m_gethdr(M_WAIT, MT_DATA);
    263 			MCLGET(m, M_WAIT);
    264 		}
    265 
    266 		DPRINTF(("waiting %d/%d\n", sc->sc_nextpacket, sc->sc_prevgen));
    267 
    268 		KASSERT(m->m_flags & M_EXT);
    269 		shmif_lockbus(sc->sc_busmem);
    270 		lastpkt = sc->sc_busmem->shm_last;
    271 		busgen = sc->sc_busmem->shm_gen;
    272 		lastnext = shmif_nextpktoff(sc->sc_busmem, lastpkt);
    273 		if ((lastnext > sc->sc_nextpacket && busgen > sc->sc_prevgen)
    274 		    || (busgen > sc->sc_prevgen+1)) {
    275 			nextpkt = lastpkt;
    276 			sc->sc_prevgen = busgen;
    277 			rumpuser_dprintf("shmif_rcv: generation overrun, "
    278 			    "skipping invalid packets\n");
    279 		} else {
    280 			nextpkt = sc->sc_nextpacket;
    281 		}
    282 
    283 		/* need more data? */
    284 		if (lastnext == nextpkt && sc->sc_prevgen == busgen){
    285 			shmif_unlockbus(sc->sc_busmem);
    286 			error = 0;
    287 			rumpuser_writewatchfile_wait(sc->sc_kq, NULL, &error);
    288 			if (__predict_false(error))
    289 				printf("shmif_rcv: wait failed %d\n", error);
    290 			continue;
    291 		}
    292 
    293 		shmif_busread(sc->sc_busmem,
    294 		    &sp, nextpkt, sizeof(sp), &wrap);
    295 		shmif_busread(sc->sc_busmem, mtod(m, void *),
    296 		    shmif_advance(nextpkt, sizeof(sp)), sp.sp_len, &wrap);
    297 		if (wrap)
    298 			sc->sc_prevgen = sc->sc_busmem->shm_gen;
    299 
    300 		DPRINTF(("shmif_rcv: read packet of length %d at %d\n",
    301 		    sp.sp_len, nextpkt));
    302 
    303 		sc->sc_nextpacket = shmif_nextpktoff(sc->sc_busmem, nextpkt);
    304 		sc->sc_prevgen = busgen;
    305 		shmif_unlockbus(sc->sc_busmem);
    306 
    307 		m->m_len = m->m_pkthdr.len = sp.sp_len;
    308 		m->m_pkthdr.rcvif = ifp;
    309 
    310 		/* if it's from us, don't pass up and reuse storage space */
    311 		eth = mtod(m, struct ether_header *);
    312 		if (memcmp(eth->ether_shost, sc->sc_myaddr, 6) != 0) {
    313 			ifp->if_input(ifp, m);
    314 			m = NULL;
    315 		}
    316 	}
    317 
    318 	panic("shmif_worker is a lazy boy %d\n", error);
    319 }
    320