xref: /netbsd-src/sys/rump/net/lib/libshmif/if_shmem.c (revision da9817918ec7e88db2912a2882967c7570a83f47)
1 /*	$NetBSD: if_shmem.c,v 1.7 2009/05/26 19:03:05 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.7 2009/05/26 19:03:05 pooka Exp $");
32 
33 #include <sys/param.h>
34 #include <sys/fcntl.h>
35 #include <sys/kmem.h>
36 #include <sys/kthread.h>
37 #include <sys/lock.h>
38 #include <sys/atomic.h>
39 
40 #include <net/if.h>
41 #include <net/if_ether.h>
42 
43 #include <netinet/in.h>
44 #include <netinet/in_var.h>
45 
46 #include <rump/rump.h>
47 #include <rump/rumpuser.h>
48 
49 #include "rump_private.h"
50 
51 #if 0
52 #define DPRINTF(x) printf x
53 #else
54 #define DPRINTF(x)
55 #endif
56 
57 /*
58  * A virtual ethernet interface which uses shared memory from a
59  * memory mapped file as the bus.
60  */
61 
62 static int	shmif_init(struct ifnet *);
63 static int	shmif_ioctl(struct ifnet *, u_long, void *);
64 static void	shmif_start(struct ifnet *);
65 static void	shmif_stop(struct ifnet *, int);
66 
67 struct shmif_sc {
68 	struct ethercom sc_ec;
69 	uint8_t sc_myaddr[6];
70 	uint8_t *sc_busmem;
71 	int sc_memfd;
72 	int sc_kq;
73 
74 	uint32_t sc_nextpacket;
75 	uint32_t sc_prevgen;
76 };
77 #define IFMEM_LOCK		(0)
78 #define IFMEM_GENERATION	(8)
79 #define IFMEM_LASTPACKET	(12)
80 #define IFMEM_WAKEUP		(16)
81 #define IFMEM_DATA		(20)
82 
83 #define BUSCTRL_ATOFF(sc, off)	((uint32_t *)(sc->sc_busmem+(off)))
84 
85 #define BUSMEM_SIZE 65536 /* enough? */
86 
87 static void shmif_rcv(void *);
88 
89 static uint32_t numif;
90 
91 /*
92  * This locking needs work and will misbehave severely if:
93  * 1) the backing memory has to be paged in
94  * 2) some lockholder exits while holding the lock
95  */
96 static void
97 lockbus(struct shmif_sc *sc)
98 {
99 
100 	__cpu_simple_lock((__cpu_simple_lock_t *)sc->sc_busmem);
101 }
102 
103 static void
104 unlockbus(struct shmif_sc *sc)
105 {
106 
107 	__cpu_simple_unlock((__cpu_simple_lock_t *)sc->sc_busmem);
108 }
109 
110 static uint32_t
111 busread(struct shmif_sc *sc, void *dest, uint32_t off, size_t len)
112 {
113 	size_t chunk;
114 
115 	KASSERT(len < (BUSMEM_SIZE - IFMEM_DATA) && off <= BUSMEM_SIZE);
116 	chunk = MIN(len, BUSMEM_SIZE - off);
117 	memcpy(dest, sc->sc_busmem + off, chunk);
118 	len -= chunk;
119 
120 	if (len == 0)
121 		return off + chunk;
122 
123 	/* else, wraps around */
124 	off = IFMEM_DATA;
125 	sc->sc_prevgen = *BUSCTRL_ATOFF(sc, IFMEM_GENERATION);
126 
127 	/* finish reading */
128 	memcpy((uint8_t *)dest + chunk, sc->sc_busmem + off, len);
129 	return off + len;
130 }
131 
132 static uint32_t
133 buswrite(struct shmif_sc *sc, uint32_t off, void *data, size_t len)
134 {
135 	size_t chunk;
136 
137 	KASSERT(len < (BUSMEM_SIZE - IFMEM_DATA) && off <= BUSMEM_SIZE);
138 
139 	chunk = MIN(len, BUSMEM_SIZE - off);
140 	memcpy(sc->sc_busmem + off, data, chunk);
141 	len -= chunk;
142 
143 	if (len == 0)
144 		return off + chunk;
145 
146 	DPRINTF(("buswrite wrap: wrote %d bytes to %d, left %d to %d",
147 	    chunk, off, len, IFMEM_DATA));
148 
149 	/* else, wraps around */
150 	off = IFMEM_DATA;
151 	(*BUSCTRL_ATOFF(sc, IFMEM_GENERATION))++;
152 	sc->sc_prevgen = *BUSCTRL_ATOFF(sc, IFMEM_GENERATION);
153 
154 	/* finish writing */
155 	memcpy(sc->sc_busmem + off, (uint8_t *)data + chunk, len);
156 	return off + len;
157 }
158 
159 static inline uint32_t
160 advance(uint32_t oldoff, uint32_t delta)
161 {
162 	uint32_t newoff;
163 
164 	newoff = oldoff + delta;
165 	if (newoff >= BUSMEM_SIZE)
166 		newoff -= (BUSMEM_SIZE - IFMEM_DATA);
167 	return newoff;
168 
169 }
170 
171 static uint32_t
172 nextpktoff(struct shmif_sc *sc, uint32_t oldoff)
173 {
174 	uint32_t oldlen;
175 
176 	busread(sc, &oldlen, oldoff, 4);
177 	KASSERT(oldlen < BUSMEM_SIZE - IFMEM_DATA);
178 
179 	return advance(oldoff, 4 + oldlen);
180 }
181 
182 int rump_shmif_create(const char *, int *); /* XXX */
183 
184 int
185 rump_shmif_create(const char *path, int *ifnum)
186 {
187 	struct shmif_sc *sc;
188 	struct ifnet *ifp;
189 	uint8_t enaddr[ETHER_ADDR_LEN] = { 0xb2, 0xa0, 0x00, 0x00, 0x00, 0x00 };
190 	uint32_t randnum;
191 	unsigned mynum;
192 	int error;
193 
194 	randnum = arc4random();
195 	memcpy(&enaddr[2], &randnum, 4);
196 	mynum = atomic_inc_uint_nv(&numif)-1;
197 
198 	sc = kmem_zalloc(sizeof(*sc), KM_SLEEP);
199 	ifp = &sc->sc_ec.ec_if;
200 	memcpy(sc->sc_myaddr, enaddr, sizeof(enaddr));
201 
202 	sc->sc_memfd = rumpuser_open(path, O_RDWR | O_CREAT, &error);
203 	if (sc->sc_memfd == -1)
204 		goto fail;
205 	sc->sc_busmem = rumpuser_filemmap(sc->sc_memfd, 0, BUSMEM_SIZE,
206 	    RUMPUSER_FILEMMAP_TRUNCATE | RUMPUSER_FILEMMAP_SHARED
207 	    | RUMPUSER_FILEMMAP_READ | RUMPUSER_FILEMMAP_WRITE, &error);
208 	if (error)
209 		goto fail;
210 
211 	lockbus(sc);
212 	if (*BUSCTRL_ATOFF(sc, IFMEM_LASTPACKET) == 0)
213 		*BUSCTRL_ATOFF(sc, IFMEM_LASTPACKET) = IFMEM_DATA;
214 	sc->sc_nextpacket = *BUSCTRL_ATOFF(sc, IFMEM_LASTPACKET);
215 	sc->sc_prevgen = *BUSCTRL_ATOFF(sc, IFMEM_GENERATION);
216 	unlockbus(sc);
217 
218 	sc->sc_kq = rumpuser_writewatchfile_setup(-1, sc->sc_memfd, 0, &error);
219 	if (sc->sc_kq == -1)
220 		goto fail;
221 
222 	sprintf(ifp->if_xname, "shmif%d", mynum);
223 	ifp->if_softc = sc;
224 	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
225 	ifp->if_init = shmif_init;
226 	ifp->if_ioctl = shmif_ioctl;
227 	ifp->if_start = shmif_start;
228 	ifp->if_stop = shmif_stop;
229 	ifp->if_mtu = 1518;
230 
231 	if_attach(ifp);
232 	ether_ifattach(ifp, enaddr);
233 
234 	if (ifnum)
235 		*ifnum = mynum;
236 	return 0;
237 
238  fail:
239 	panic("rump_shmemif_create: fixme");
240 }
241 
242 static int
243 shmif_init(struct ifnet *ifp)
244 {
245 	int error = 0;
246 
247 	if (rump_threads) {
248 		error = kthread_create(PRI_NONE, KTHREAD_MPSAFE, NULL,
249 		    shmif_rcv, ifp, NULL, "shmif");
250 	} else {
251 		printf("WARNING: threads not enabled, shmif NOT working\n");
252 	}
253 
254 	ifp->if_flags |= IFF_RUNNING;
255 	return error;
256 }
257 
258 static int
259 shmif_ioctl(struct ifnet *ifp, u_long cmd, void *data)
260 {
261 	int s, rv;
262 
263 	s = splnet();
264 	rv = ether_ioctl(ifp, cmd, data);
265 	if (rv == ENETRESET)
266 		rv = 0;
267 	splx(s);
268 
269 	return rv;
270 }
271 
272 /* send everything in-context */
273 static void
274 shmif_start(struct ifnet *ifp)
275 {
276 	struct shmif_sc *sc = ifp->if_softc;
277 	struct mbuf *m, *m0;
278 	uint32_t lastoff, dataoff, npktlenoff;
279 	uint32_t pktsize = 0;
280 	bool wrote = false;
281 	int error;
282 
283 	for (;;) {
284 		IF_DEQUEUE(&ifp->if_snd, m0);
285 		if (m0 == NULL) {
286 			break;
287 		}
288 
289 		lockbus(sc);
290 		lastoff = *BUSCTRL_ATOFF(sc, IFMEM_LASTPACKET);
291 
292 		npktlenoff = nextpktoff(sc, lastoff);
293 		dataoff = advance(npktlenoff, 4);
294 
295 		for (m = m0; m != NULL; m = m->m_next) {
296 			pktsize += m->m_len;
297 			dataoff = buswrite(sc, dataoff, mtod(m, void *),
298 			    m->m_len);
299 		}
300 		buswrite(sc, npktlenoff, &pktsize, 4);
301 		*BUSCTRL_ATOFF(sc, IFMEM_LASTPACKET) = npktlenoff;
302 		unlockbus(sc);
303 
304 		m_freem(m0);
305 		wrote = true;
306 
307 		DPRINTF(("shmif_start: send %d bytes at off %d\n",
308 		    pktsize, npktlenoff));
309 	}
310 	/* wakeup */
311 	if (wrote)
312 		rumpuser_pwrite(sc->sc_memfd, &error, 4, IFMEM_WAKEUP, &error);
313 }
314 
315 static void
316 shmif_stop(struct ifnet *ifp, int disable)
317 {
318 
319 	panic("%s: unimpl", __func__);
320 }
321 
322 static void
323 shmif_rcv(void *arg)
324 {
325 	struct ifnet *ifp = arg;
326 	struct shmif_sc *sc = ifp->if_softc;
327 	struct mbuf *m = NULL;
328 	struct ether_header *eth;
329 	uint32_t nextpkt, pktlen, lastpkt, busgen, lastnext;
330 	int error;
331 
332 	for (;;) {
333 		if (m == NULL) {
334 			m = m_gethdr(M_WAIT, MT_DATA);
335 			MCLGET(m, M_WAIT);
336 		}
337 
338 		DPRINTF(("waiting %d/%d\n", sc->sc_nextpacket, sc->sc_prevgen));
339 
340 		KASSERT(m->m_flags & M_EXT);
341 		lockbus(sc);
342 		lastpkt = *BUSCTRL_ATOFF(sc, IFMEM_LASTPACKET);
343 		busgen = *BUSCTRL_ATOFF(sc, IFMEM_GENERATION);
344 		lastnext = nextpktoff(sc, lastpkt);
345 		if ((lastnext > sc->sc_nextpacket && busgen > sc->sc_prevgen)
346 		    || (busgen > sc->sc_prevgen+1)) {
347 			nextpkt = lastpkt;
348 			sc->sc_prevgen = busgen;
349 			printf("DROPPING\n");
350 		} else {
351 			nextpkt = sc->sc_nextpacket;
352 		}
353 
354 		/* need more data? */
355 		if (lastnext == nextpkt && sc->sc_prevgen == busgen){
356 			unlockbus(sc);
357 			error = 0;
358 			rumpuser_writewatchfile_wait(sc->sc_kq, NULL, &error);
359 			if (__predict_false(error))
360 				printf("shmif_rcv: wait failed %d\n", error);
361 			continue;
362 		}
363 
364 		busread(sc, &pktlen, nextpkt, 4);
365 		busread(sc, mtod(m, void *), advance(nextpkt, 4), pktlen);
366 
367 		DPRINTF(("shmif_rcv: read packet of length %d at %d\n",
368 		    pktlen, nextpkt));
369 
370 		sc->sc_nextpacket = nextpktoff(sc, nextpkt);
371 		sc->sc_prevgen = busgen;
372 		unlockbus(sc);
373 
374 		m->m_len = m->m_pkthdr.len = pktlen;
375 		m->m_pkthdr.rcvif = ifp;
376 
377 		/* if it's to us, don't pass up and reuse storage space */
378 		eth = mtod(m, struct ether_header *);
379 		if (memcmp(eth->ether_shost, sc->sc_myaddr, 6) != 0) {
380 			ifp->if_input(ifp, m);
381 			m = NULL;
382 		}
383 	}
384 
385 	panic("shmif_worker is a lazy boy %d\n", error);
386 }
387