summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordlg <dlg@openbsd.org>2007-08-15 07:46:02 +0000
committerdlg <dlg@openbsd.org>2007-08-15 07:46:02 +0000
commit5f0aa67a54a1c82a408b317d2f29d6cd41f98770 (patch)
tree8ef0354b8d961adf1ac9cab301f42f3da0ea34cd
parentprogram the mac control thing according the the multicast or promisc (diff)
downloadwireguard-openbsd-5f0aa67a54a1c82a408b317d2f29d6cd41f98770.tar.xz
wireguard-openbsd-5f0aa67a54a1c82a408b317d2f29d6cd41f98770.zip
implement nxe_start. this is enough to put packets on the wire. the sgl
layout of this chip is extremely weird. there's only 4 sg entries in each tx descriptor, but theyre out of order. you can use multiple tx descriptors to tx more than 4 segments, but instead of letting you use all 64bytes that a tx descriptor uses you just use the same 4 sg entries, and up to 8 descs.
-rw-r--r--sys/dev/pci/if_nxe.c153
1 files changed, 152 insertions, 1 deletions
diff --git a/sys/dev/pci/if_nxe.c b/sys/dev/pci/if_nxe.c
index 5a115c46a03..ca6aaf58634 100644
--- a/sys/dev/pci/if_nxe.c
+++ b/sys/dev/pci/if_nxe.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_nxe.c,v 1.37 2007/08/15 07:17:38 dlg Exp $ */
+/* $OpenBSD: if_nxe.c,v 1.38 2007/08/15 07:46:02 dlg Exp $ */
/*
* Copyright (c) 2007 David Gwynne <dlg@openbsd.org>
@@ -762,6 +762,10 @@ int nxe_ring_writeable(struct nxe_ring *, int);
void *nxe_ring_cur(struct nxe_softc *, struct nxe_ring *);
void *nxe_ring_next(struct nxe_softc *, struct nxe_ring *);
+struct mbuf *nxe_load_pkt(struct nxe_softc *, bus_dmamap_t,
+ struct mbuf *);
+struct mbuf *nxe_coalesce_m(struct mbuf *);
+
/* pkts */
struct nxe_pkt_list *nxe_pkt_alloc(struct nxe_softc *, u_int, int);
void nxe_pkt_free(struct nxe_softc *,
@@ -1227,7 +1231,154 @@ nxe_down(struct nxe_softc *sc)
void
nxe_start(struct ifnet *ifp)
{
+ struct nxe_softc *sc = ifp->if_softc;
+ struct nxe_ring *nr = sc->sc_cmd_ring;
+ struct nxe_tx_desc *txd;
+ struct nxe_pkt *pkt;
+ struct mbuf *m;
+ bus_dmamap_t dmap;
+ bus_dma_segment_t *segs;
+ int nsegs;
+
+ if (!ISSET(ifp->if_flags, IFF_RUNNING) ||
+ ISSET(ifp->if_flags, IFF_OACTIVE) ||
+ IFQ_IS_EMPTY(&ifp->if_snd))
+ return;
+
+ if (nxe_ring_writeable(nr, 0 /* XXX */) < NXE_TXD_DESCS) {
+ SET(ifp->if_flags, IFF_OACTIVE);
+ return;
+ }
+
+ nxe_ring_sync(sc, nr, BUS_DMASYNC_POSTWRITE);
+ txd = nxe_ring_cur(sc, nr);
+ bzero(txd, sizeof(struct nxe_tx_desc));
+
+ do {
+ IFQ_POLL(&ifp->if_snd, m);
+ if (m == NULL)
+ break;
+
+ pkt = nxe_pkt_get(sc->sc_tx_pkts);
+ if (pkt == NULL) {
+ SET(ifp->if_flags, IFF_OACTIVE);
+ break;
+ }
+
+ IFQ_DEQUEUE(&ifp->if_snd, m);
+
+ dmap = pkt->pkt_dmap;
+ m = nxe_load_pkt(sc, dmap, m);
+ if (m == NULL) {
+ nxe_pkt_put(sc->sc_tx_pkts, pkt);
+ ifp->if_oerrors++;
+ break;
+ }
+
+#if NBPFILTER > 0
+ if (ifp->if_bpf)
+ bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_OUT);
+#endif
+
+ pkt->pkt_m = m;
+
+ txd->tx_flags = htole16(NXE_TXD_F_OPCODE_TX);
+ txd->tx_nbufs = dmap->dm_nsegs;
+ txd->tx_length = htole16(dmap->dm_mapsize);
+ txd->tx_id = pkt->pkt_id;
+ txd->tx_port = sc->sc_port;
+
+ segs = dmap->dm_segs;
+ nsegs = dmap->dm_nsegs;
+ do {
+ switch ((nsegs > NXE_TXD_SEGS) ?
+ NXE_TXD_SEGS : nsegs) {
+ case 4:
+ txd->tx_addr_4 = htole64(segs[3].ds_addr);
+ txd->tx_slen_4 = htole32(segs[3].ds_len);
+ case 3:
+ txd->tx_addr_3 = htole64(segs[2].ds_addr);
+ txd->tx_slen_3 = htole32(segs[2].ds_len);
+ case 2:
+ txd->tx_addr_2 = htole64(segs[1].ds_addr);
+ txd->tx_slen_2 = htole32(segs[1].ds_len);
+ case 1:
+ txd->tx_addr_1 = htole64(segs[0].ds_addr);
+ txd->tx_slen_1 = htole32(segs[0].ds_len);
+ break;
+ default:
+ panic("%s: unexpected segments in tx map",
+ DEVNAME(sc));
+ }
+
+ nsegs -= NXE_TXD_SEGS;
+ segs += NXE_TXD_SEGS;
+
+ txd = nxe_ring_next(sc, nr);
+ bzero(txd, sizeof(struct nxe_tx_desc));
+ } while (nsegs > 0);
+
+ bus_dmamap_sync(sc->sc_dmat, dmap, 0, dmap->dm_mapsize,
+ BUS_DMASYNC_PREWRITE);
+
+ ifp->if_opackets++;
+ } while (nr->nr_ready >= NXE_TXD_DESCS);
+
+ nxe_ring_sync(sc, nr, BUS_DMASYNC_PREWRITE);
+ nxe_crb_write(sc, NXE_1_SW_CMD_PRODUCER(sc->sc_function), nr->nr_slot);
+}
+
+struct mbuf *
+nxe_coalesce_m(struct mbuf *m)
+{
+ struct mbuf *m0;
+
+ MGETHDR(m0, M_DONTWAIT, MT_DATA);
+ if (m0 == NULL)
+ goto err;
+
+ if (m->m_pkthdr.len > MHLEN) {
+ MCLGET(m0, M_DONTWAIT);
+ if (!(m0->m_flags & M_EXT)) {
+ m_freem(m0);
+ m0 = NULL;
+ goto err;
+ }
+ }
+
+ m_copydata(m, 0, m->m_pkthdr.len, mtod(m0, caddr_t));
+ m0->m_pkthdr.len = m0->m_len = m->m_pkthdr.len;
+
+err:
+ m_freem(m);
+ return (m0);
+}
+
+struct mbuf *
+nxe_load_pkt(struct nxe_softc *sc, bus_dmamap_t dmap, struct mbuf *m)
+{
+ switch (bus_dmamap_load_mbuf(sc->sc_dmat, dmap, m, BUS_DMA_NOWAIT)) {
+ case 0:
+ break;
+
+ case EFBIG:
+ m = nxe_coalesce_m(m);
+ if (m == NULL)
+ break;
+
+ if (bus_dmamap_load_mbuf(sc->sc_dmat, dmap, m,
+ BUS_DMA_NOWAIT) == 0)
+ break;
+
+ /* we get here on error */
+ /* FALLTHROUGH */
+ default:
+ m_freem(m);
+ m = NULL;
+ break;
+ }
+ return (m);
}
void