1 /* $NetBSD: if_shmem.c,v 1.9 2009/10/20 23:29:57 pooka Exp $ */
4 * Copyright (c) 2009 Antti Kantee. All Rights Reserved.
6 * Development of this software was supported by The Nokia Foundation.
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/cdefs.h>
31 __KERNEL_RCSID(0, "$NetBSD: if_shmem.c,v 1.9 2009/10/20 23:29:57 pooka Exp $");
33 #include <sys/param.h>
34 #include <sys/fcntl.h>
36 #include <sys/kthread.h>
38 #include <sys/atomic.h>
41 #include <net/if_ether.h>
43 #include <netinet/in.h>
44 #include <netinet/in_var.h>
46 #include <rump/rump.h>
47 #include <rump/rumpuser.h>
49 #include "rump_private.h"
50 #include "rump_net_private.h"
53 #define DPRINTF(x) rumpuser_dprintf x
59 * A virtual ethernet interface which uses shared memory from a
60 * memory mapped file as the bus.
63 static int shmif_init(struct ifnet
*);
64 static int shmif_ioctl(struct ifnet
*, u_long
, void *);
65 static void shmif_start(struct ifnet
*);
66 static void shmif_stop(struct ifnet
*, int);
69 struct ethercom sc_ec
;
75 uint32_t sc_nextpacket
;
78 #define IFMEM_LOCK (0)
79 #define IFMEM_GENERATION (8)
80 #define IFMEM_LASTPACKET (12)
81 #define IFMEM_WAKEUP (16)
82 #define IFMEM_DATA (20)
84 #define BUSCTRL_ATOFF(sc, off) ((uint32_t *)(sc->sc_busmem+(off)))
86 #define BUSMEM_SIZE 65536 /* enough? */
88 static void shmif_rcv(void *);
90 static uint32_t numif
;
93 * This locking needs work and will misbehave severely if:
94 * 1) the backing memory has to be paged in
95 * 2) some lockholder exits while holding the lock
98 lockbus(struct shmif_sc
*sc
)
101 __cpu_simple_lock((__cpu_simple_lock_t
*)sc
->sc_busmem
);
105 unlockbus(struct shmif_sc
*sc
)
108 __cpu_simple_unlock((__cpu_simple_lock_t
*)sc
->sc_busmem
);
112 busread(struct shmif_sc
*sc
, void *dest
, uint32_t off
, size_t len
)
116 KASSERT(len
< (BUSMEM_SIZE
- IFMEM_DATA
) && off
<= BUSMEM_SIZE
);
117 chunk
= MIN(len
, BUSMEM_SIZE
- off
);
118 memcpy(dest
, sc
->sc_busmem
+ off
, chunk
);
124 /* else, wraps around */
126 sc
->sc_prevgen
= *BUSCTRL_ATOFF(sc
, IFMEM_GENERATION
);
129 memcpy((uint8_t *)dest
+ chunk
, sc
->sc_busmem
+ off
, len
);
134 buswrite(struct shmif_sc
*sc
, uint32_t off
, void *data
, size_t len
)
138 KASSERT(len
< (BUSMEM_SIZE
- IFMEM_DATA
) && off
<= BUSMEM_SIZE
);
140 chunk
= MIN(len
, BUSMEM_SIZE
- off
);
141 memcpy(sc
->sc_busmem
+ off
, data
, chunk
);
147 DPRINTF(("buswrite wrap: wrote %d bytes to %d, left %d to %d",
148 chunk
, off
, len
, IFMEM_DATA
));
150 /* else, wraps around */
152 (*BUSCTRL_ATOFF(sc
, IFMEM_GENERATION
))++;
153 sc
->sc_prevgen
= *BUSCTRL_ATOFF(sc
, IFMEM_GENERATION
);
156 memcpy(sc
->sc_busmem
+ off
, (uint8_t *)data
+ chunk
, len
);
160 static inline uint32_t
161 advance(uint32_t oldoff
, uint32_t delta
)
165 newoff
= oldoff
+ delta
;
166 if (newoff
>= BUSMEM_SIZE
)
167 newoff
-= (BUSMEM_SIZE
- IFMEM_DATA
);
173 nextpktoff(struct shmif_sc
*sc
, uint32_t oldoff
)
177 busread(sc
, &oldlen
, oldoff
, 4);
178 KASSERT(oldlen
< BUSMEM_SIZE
- IFMEM_DATA
);
180 return advance(oldoff
, 4 + oldlen
);
184 rump_shmif_create(const char *path
, int *ifnum
)
188 uint8_t enaddr
[ETHER_ADDR_LEN
] = { 0xb2, 0xa0, 0x00, 0x00, 0x00, 0x00 };
193 randnum
= arc4random();
194 memcpy(&enaddr
[2], &randnum
, 4);
195 mynum
= atomic_inc_uint_nv(&numif
)-1;
197 sc
= kmem_zalloc(sizeof(*sc
), KM_SLEEP
);
198 ifp
= &sc
->sc_ec
.ec_if
;
199 memcpy(sc
->sc_myaddr
, enaddr
, sizeof(enaddr
));
201 sc
->sc_memfd
= rumpuser_open(path
, O_RDWR
| O_CREAT
, &error
);
202 if (sc
->sc_memfd
== -1)
204 sc
->sc_busmem
= rumpuser_filemmap(sc
->sc_memfd
, 0, BUSMEM_SIZE
,
205 RUMPUSER_FILEMMAP_TRUNCATE
| RUMPUSER_FILEMMAP_SHARED
206 | RUMPUSER_FILEMMAP_READ
| RUMPUSER_FILEMMAP_WRITE
, &error
);
211 if (*BUSCTRL_ATOFF(sc
, IFMEM_LASTPACKET
) == 0)
212 *BUSCTRL_ATOFF(sc
, IFMEM_LASTPACKET
) = IFMEM_DATA
;
213 sc
->sc_nextpacket
= *BUSCTRL_ATOFF(sc
, IFMEM_LASTPACKET
);
214 sc
->sc_prevgen
= *BUSCTRL_ATOFF(sc
, IFMEM_GENERATION
);
217 sc
->sc_kq
= rumpuser_writewatchfile_setup(-1, sc
->sc_memfd
, 0, &error
);
221 sprintf(ifp
->if_xname
, "shmif%d", mynum
);
223 ifp
->if_flags
= IFF_BROADCAST
| IFF_SIMPLEX
| IFF_MULTICAST
;
224 ifp
->if_init
= shmif_init
;
225 ifp
->if_ioctl
= shmif_ioctl
;
226 ifp
->if_start
= shmif_start
;
227 ifp
->if_stop
= shmif_stop
;
231 ether_ifattach(ifp
, enaddr
);
238 panic("rump_shmemif_create: fixme");
242 shmif_init(struct ifnet
*ifp
)
247 error
= kthread_create(PRI_NONE
, KTHREAD_MPSAFE
, NULL
,
248 shmif_rcv
, ifp
, NULL
, "shmif");
250 printf("WARNING: threads not enabled, shmif NOT working\n");
253 ifp
->if_flags
|= IFF_RUNNING
;
258 shmif_ioctl(struct ifnet
*ifp
, u_long cmd
, void *data
)
263 rv
= ether_ioctl(ifp
, cmd
, data
);
271 /* send everything in-context */
273 shmif_start(struct ifnet
*ifp
)
275 struct shmif_sc
*sc
= ifp
->if_softc
;
277 uint32_t lastoff
, dataoff
, npktlenoff
;
278 uint32_t pktsize
= 0;
283 IF_DEQUEUE(&ifp
->if_snd
, m0
);
289 lastoff
= *BUSCTRL_ATOFF(sc
, IFMEM_LASTPACKET
);
291 npktlenoff
= nextpktoff(sc
, lastoff
);
292 dataoff
= advance(npktlenoff
, 4);
294 for (m
= m0
; m
!= NULL
; m
= m
->m_next
) {
296 dataoff
= buswrite(sc
, dataoff
, mtod(m
, void *),
299 buswrite(sc
, npktlenoff
, &pktsize
, 4);
300 *BUSCTRL_ATOFF(sc
, IFMEM_LASTPACKET
) = npktlenoff
;
306 DPRINTF(("shmif_start: send %d bytes at off %d\n",
307 pktsize
, npktlenoff
));
311 rumpuser_pwrite(sc
->sc_memfd
, &error
, 4, IFMEM_WAKEUP
, &error
);
315 shmif_stop(struct ifnet
*ifp
, int disable
)
318 panic("%s: unimpl", __func__
);
324 struct ifnet
*ifp
= arg
;
325 struct shmif_sc
*sc
= ifp
->if_softc
;
326 struct mbuf
*m
= NULL
;
327 struct ether_header
*eth
;
328 uint32_t nextpkt
, pktlen
, lastpkt
, busgen
, lastnext
;
333 m
= m_gethdr(M_WAIT
, MT_DATA
);
337 DPRINTF(("waiting %d/%d\n", sc
->sc_nextpacket
, sc
->sc_prevgen
));
339 KASSERT(m
->m_flags
& M_EXT
);
341 lastpkt
= *BUSCTRL_ATOFF(sc
, IFMEM_LASTPACKET
);
342 busgen
= *BUSCTRL_ATOFF(sc
, IFMEM_GENERATION
);
343 lastnext
= nextpktoff(sc
, lastpkt
);
344 if ((lastnext
> sc
->sc_nextpacket
&& busgen
> sc
->sc_prevgen
)
345 || (busgen
> sc
->sc_prevgen
+1)) {
347 sc
->sc_prevgen
= busgen
;
348 rumpuser_dprintf("DROPPING\n");
350 nextpkt
= sc
->sc_nextpacket
;
353 /* need more data? */
354 if (lastnext
== nextpkt
&& sc
->sc_prevgen
== busgen
){
357 rumpuser_writewatchfile_wait(sc
->sc_kq
, NULL
, &error
);
358 if (__predict_false(error
))
359 printf("shmif_rcv: wait failed %d\n", error
);
363 busread(sc
, &pktlen
, nextpkt
, 4);
364 busread(sc
, mtod(m
, void *), advance(nextpkt
, 4), pktlen
);
366 DPRINTF(("shmif_rcv: read packet of length %d at %d\n",
369 sc
->sc_nextpacket
= nextpktoff(sc
, nextpkt
);
370 sc
->sc_prevgen
= busgen
;
373 m
->m_len
= m
->m_pkthdr
.len
= pktlen
;
374 m
->m_pkthdr
.rcvif
= ifp
;
376 /* if it's from us, don't pass up and reuse storage space */
377 eth
= mtod(m
, struct ether_header
*);
378 if (memcmp(eth
->ether_shost
, sc
->sc_myaddr
, 6) != 0) {
379 ifp
->if_input(ifp
, m
);
384 panic("shmif_worker is a lazy boy %d\n", error
);