static void shmif_rcv(void *arg) { struct ifnet *ifp = arg; struct shmif_sc *sc = ifp->if_softc; struct shmif_mem *busmem; struct mbuf *m = NULL; struct ether_header *eth; uint32_t nextpkt; bool wrap, passup; int error; const int align = ALIGN(sizeof(struct ether_header)) - sizeof(struct ether_header); reup: mutex_enter(&sc->sc_mtx); while ((ifp->if_flags & IFF_RUNNING) == 0 && !sc->sc_dying) cv_wait(&sc->sc_cv, &sc->sc_mtx); mutex_exit(&sc->sc_mtx); busmem = sc->sc_busmem; while (ifp->if_flags & IFF_RUNNING) { struct shmif_pkthdr sp; if (m == NULL) { m = m_gethdr(M_WAIT, MT_DATA); MCLGET(m, M_WAIT); m->m_data += align; } DPRINTF(("waiting %d/%" PRIu64 "\n", sc->sc_nextpacket, sc->sc_devgen)); KASSERT(m->m_flags & M_EXT); shmif_lockbus(busmem); KASSERT(busmem->shm_magic == SHMIF_MAGIC); KASSERT(busmem->shm_gen >= sc->sc_devgen); /* need more data? */ if (sc->sc_devgen == busmem->shm_gen && shmif_nextpktoff(busmem, busmem->shm_last) == sc->sc_nextpacket) { shmif_unlockbus(busmem); error = 0; rumpcomp_shmif_watchwait(sc->sc_kq); if (__predict_false(error)) printf("shmif_rcv: wait failed %d\n", error); membar_consumer(); continue; } if (stillvalid_p(sc)) { nextpkt = sc->sc_nextpacket; } else { KASSERT(busmem->shm_gen > 0); nextpkt = busmem->shm_first; if (busmem->shm_first > busmem->shm_last) sc->sc_devgen = busmem->shm_gen - 1; else sc->sc_devgen = busmem->shm_gen; DPRINTF(("dev %p overrun, new data: %d/%" PRIu64 "\n", sc, nextpkt, sc->sc_devgen)); } /* * If our read pointer is ahead the bus last write, our * generation must be one behind. */ KASSERT(!(nextpkt > busmem->shm_last && sc->sc_devgen == busmem->shm_gen)); wrap = false; nextpkt = shmif_busread(busmem, &sp, nextpkt, sizeof(sp), &wrap); KASSERT(sp.sp_len <= ETHERMTU + ETHER_HDR_LEN); nextpkt = shmif_busread(busmem, mtod(m, void *), nextpkt, sp.sp_len, &wrap); DPRINTF(("shmif_rcv: read packet of length %d at %d\n", sp.sp_len, nextpkt)); sc->sc_nextpacket = nextpkt; shmif_unlockbus(sc->sc_busmem); if (wrap) { sc->sc_devgen++; DPRINTF(("dev %p generation now %" PRIu64 "\n", sc, sc->sc_devgen)); } /* * Ignore packets too short to possibly be valid. * This is hit at least for the first frame on a new bus. */ if (__predict_false(sp.sp_len < ETHER_HDR_LEN)) { DPRINTF(("shmif read packet len %d < ETHER_HDR_LEN\n", sp.sp_len)); continue; } m->m_len = m->m_pkthdr.len = sp.sp_len; m->m_pkthdr.rcvif = ifp; /* * Test if we want to pass the packet upwards */ eth = mtod(m, struct ether_header *); if (memcmp(eth->ether_dhost, CLLADDR(ifp->if_sadl), ETHER_ADDR_LEN) == 0) { passup = true; } else if (ETHER_IS_MULTICAST(eth->ether_dhost)) { passup = true; } else if (ifp->if_flags & IFF_PROMISC) { m->m_flags |= M_PROMISC; passup = true; } else { passup = false; } if (passup) { KERNEL_LOCK(1, NULL); bpf_mtap(ifp, m); ifp->if_input(ifp, m); KERNEL_UNLOCK_ONE(NULL); m = NULL; } /* else: reuse mbuf for a future packet */ } m_freem(m); m = NULL; if (!sc->sc_dying) goto reup; kthread_exit(0); }
int main(int argc, char *argv[]) { struct stat sb; void *busmem; const char *pcapfile = NULL; uint32_t curbus, buslast; struct shmif_mem *bmem; int fd, i, ch; int bonus; char *buf; bool hflag = false, doswap = false; pcap_dumper_t *pdump; FILE *dumploc = stdout; #ifdef PLATFORM_HAS_SETGETPROGNAME setprogname(argv[0]); #endif while ((ch = getopt(argc, argv, "hp:")) != -1) { switch (ch) { case 'h': hflag = true; break; case 'p': pcapfile = optarg; break; default: usage(); } } argc -= optind; argv += optind; if (argc != 1) usage(); buf = malloc(BUFSIZE); if (buf == NULL) err(1, "malloc"); fd = open(argv[0], O_RDONLY); if (fd == -1) err(1, "open bus"); if (fstat(fd, &sb) == -1) err(1, "stat"); busmem = mmap(NULL, sb.st_size, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0); if (busmem == MAP_FAILED) err(1, "mmap"); bmem = busmem; if (bmem->shm_magic != SHMIF_MAGIC) { if (bmem->shm_magic != bswap32(SHMIF_MAGIC)) errx(1, "%s not a shmif bus", argv[0]); doswap = 1; } if (SWAPME(bmem->shm_version) != SHMIF_VERSION) errx(1, "bus vesrsion %d, program %d", SWAPME(bmem->shm_version), SHMIF_VERSION); if (pcapfile && strcmp(pcapfile, "-") == 0) dumploc = stderr; fprintf(dumploc, "bus version %d, lock: %d, generation: %" PRIu64 ", firstoff: 0x%04x, lastoff: 0x%04x\n", SWAPME(bmem->shm_version), SWAPME(bmem->shm_lock), SWAPME64(bmem->shm_gen), SWAPME(bmem->shm_first), SWAPME(bmem->shm_last)); if (hflag) exit(0); if (pcapfile) { pcap_t *pcap = pcap_open_dead(DLT_EN10MB, 1518); pdump = pcap_dump_open(pcap, pcapfile); if (pdump == NULL) err(1, "cannot open pcap dump file"); } else { /* XXXgcc */ pdump = NULL; } curbus = SWAPME(bmem->shm_first); buslast = SWAPME(bmem->shm_last); if (curbus == BUSMEM_DATASIZE) curbus = 0; bonus = 0; if (buslast < curbus) bonus = 1; i = 0; while (curbus <= buslast || bonus) { struct pcap_pkthdr packhdr; struct shmif_pkthdr sp; uint32_t oldoff; uint32_t curlen; bool wrap; assert(curbus < sb.st_size); wrap = false; oldoff = curbus; curbus = shmif_busread(bmem, &sp, oldoff, sizeof(sp), &wrap); if (wrap) bonus = 0; assert(curbus < sb.st_size); curlen = SWAPME(sp.sp_len); if (curlen == 0) { continue; } fprintf(dumploc, "packet %d, offset 0x%04x, length 0x%04x, " "ts %d/%06d\n", i++, curbus, curlen, SWAPME(sp.sp_sec), SWAPME(sp.sp_usec)); if (!pcapfile) { curbus = shmif_busread(bmem, buf, curbus, curlen, &wrap); if (wrap) bonus = 0; continue; } memset(&packhdr, 0, sizeof(packhdr)); packhdr.caplen = packhdr.len = curlen; packhdr.ts.tv_sec = SWAPME(sp.sp_sec); packhdr.ts.tv_usec = SWAPME(sp.sp_usec); assert(curlen <= BUFSIZE); curbus = shmif_busread(bmem, buf, curbus, curlen, &wrap); pcap_dump((u_char *)pdump, &packhdr, (u_char *)buf); if (wrap) bonus = 0; } if (pcapfile) pcap_dump_close(pdump); return 0; }