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