sis(4): Rework busdma(9) related bits
authorSepherosa Ziehau <sephe@dragonflybsd.org>
Thu, 29 Jan 2009 08:25:56 +0000 (16:25 +0800)
committerSepherosa Ziehau <sephe@dragonflybsd.org>
Tue, 17 Feb 2009 10:35:49 +0000 (18:35 +0800)
- RX/TX descriptor rings need to be 4bytes aligned.
- RX buffers need to be 4bytes aligned.
- Create TX/RX buffer busdma map at attaching time and destroy them
  at detaching time.
- Rework sis_newbuf() by utilizing bus_dmamap_load_mbuf_segment().
- Factor sis_setup_rxdesc() out of sis_newbuf() and call it on RX
  error handling path.
- Rework sis_encap() by utilizing bus_dmamap_load_mbuf_defrag().
- Correct IFF_OACTIVE handling in sis_start()/sis_txeof().

sys/dev/netif/sis/if_sis.c
sys/dev/netif/sis/if_sisreg.h

index 15197bb..991b9e0 100644 (file)
@@ -113,8 +113,9 @@ static int  sis_probe(device_t);
 static int     sis_attach(device_t);
 static int     sis_detach(device_t);
 
-static int     sis_newbuf(struct sis_softc *, int, struct mbuf *);
-static int     sis_encap(struct sis_softc *, struct mbuf *, uint32_t *);
+static int     sis_newbuf(struct sis_softc *, int, int);
+static void    sis_setup_rxdesc(struct sis_softc *, int);
+static int     sis_encap(struct sis_softc *, struct mbuf **, uint32_t *);
 static void    sis_rxeof(struct sis_softc *);
 static void    sis_rxeoc(struct sis_softc *);
 static void    sis_txeof(struct sis_softc *);
@@ -156,9 +157,8 @@ static void sis_reset(struct sis_softc *);
 static int     sis_list_rx_init(struct sis_softc *);
 static int     sis_list_tx_init(struct sis_softc *);
 
-static void    sis_dma_map_desc_ptr(void *, bus_dma_segment_t *, int, int);
-static void    sis_dma_map_desc_next(void *, bus_dma_segment_t *, int, int);
-static void    sis_dma_map_ring(void *, bus_dma_segment_t *, int, int);
+static int     sis_dma_alloc(device_t dev);
+static void    sis_dma_free(device_t dev);
 #ifdef DEVICE_POLLING
 static poll_handler_t sis_poll;
 #endif
@@ -213,33 +213,6 @@ DRIVER_MODULE(miibus, sis, miibus_driver, miibus_devclass, 0, 0);
 #define SIO_CLR(x)                                     \
        CSR_WRITE_4(sc, SIS_EECTL, CSR_READ_4(sc, SIS_EECTL) & ~x)
 
-static void
-sis_dma_map_desc_next(void *arg, bus_dma_segment_t *segs, int nseg, int error)
-{
-       struct sis_desc *r;
-
-       r = arg;
-       r->sis_next = segs->ds_addr;
-}
-
-static void
-sis_dma_map_desc_ptr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
-{
-       struct sis_desc *r;
-
-       r = arg;
-       r->sis_ptr = segs->ds_addr;
-}
-
-static void
-sis_dma_map_ring(void *arg, bus_dma_segment_t *segs, int nseg, int error)
-{
-       uint32_t *p;
-
-       p = arg;
-       *p = segs->ds_addr;
-}
-
 /*
  * Routine to reverse the bits in a word. Stolen almost
  * verbatim from /usr/games/fortune.
@@ -1148,117 +1121,7 @@ sis_attach(device_t dev)
 
        callout_init(&sc->sis_timer);
 
-       /*
-        * Allocate the parent bus DMA tag appropriate for PCI.
-        */
-#define SIS_NSEG_NEW 32
-       error = bus_dma_tag_create(NULL,        /* parent */
-                       1, 0,                   /* alignment, boundary */
-                       BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
-                       BUS_SPACE_MAXADDR,      /* highaddr */
-                       NULL, NULL,             /* filter, filterarg */
-                       MAXBSIZE, SIS_NSEG_NEW, /* maxsize, nsegments */
-                       BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
-                       BUS_DMA_ALLOCNOW,       /* flags */
-                       &sc->sis_parent_tag);
-       if (error)
-               goto fail;
-
-       /*
-        * Now allocate a tag for the DMA descriptor lists and a chunk
-        * of DMA-able memory based on the tag. Also obtain the physical
-        * addresses of the RX and TX ring, which we'll need later.
-        * All of our lists are allocated as a contiguous block of memory.
-        */
-       error = bus_dma_tag_create(sc->sis_parent_tag,  /* parent */
-                       1, 0,                   /* alignment, boundary */
-                       BUS_SPACE_MAXADDR,      /* lowaddr */
-                       BUS_SPACE_MAXADDR,      /* highaddr */
-                       NULL, NULL,             /* filter, filterarg */
-                       SIS_RX_LIST_SZ, 1,      /* maxsize, nsegments */
-                       BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
-                       0,                      /* flags */
-                       &sc->sis_ldata.sis_rx_tag);
-       if (error)
-               goto fail;
-
-       error = bus_dmamem_alloc(sc->sis_ldata.sis_rx_tag,
-                                (void **)&sc->sis_ldata.sis_rx_list,
-                                BUS_DMA_WAITOK | BUS_DMA_ZERO,
-                                &sc->sis_ldata.sis_rx_dmamap);
-
-       if (error) {
-               device_printf(dev, "no memory for rx list buffers!\n");
-               bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag);
-               sc->sis_ldata.sis_rx_tag = NULL;
-               goto fail;
-       }
-
-       error = bus_dmamap_load(sc->sis_ldata.sis_rx_tag,
-                               sc->sis_ldata.sis_rx_dmamap,
-                               sc->sis_ldata.sis_rx_list,
-                               sizeof(struct sis_desc), sis_dma_map_ring,
-                               &sc->sis_ldata.sis_rx_paddr, 0);
-
-       if (error) {
-               device_printf(dev, "cannot get address of the rx ring!\n");
-               bus_dmamem_free(sc->sis_ldata.sis_rx_tag,
-                               sc->sis_ldata.sis_rx_list,
-                               sc->sis_ldata.sis_rx_dmamap);
-               bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag);
-               sc->sis_ldata.sis_rx_tag = NULL;
-               goto fail;
-       }
-
-       error = bus_dma_tag_create(sc->sis_parent_tag,  /* parent */
-                       1, 0,                   /* alignment, boundary */
-                       BUS_SPACE_MAXADDR,      /* lowaddr */
-                       BUS_SPACE_MAXADDR,      /* highaddr */
-                       NULL, NULL,             /* filter, filterarg */
-                       SIS_TX_LIST_SZ, 1,      /* maxsize, nsegments */
-                       BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
-                       0,                      /* flags */
-                       &sc->sis_ldata.sis_tx_tag);
-       if (error)
-               goto fail;
-
-       error = bus_dmamem_alloc(sc->sis_ldata.sis_tx_tag,
-                                (void **)&sc->sis_ldata.sis_tx_list,
-                                BUS_DMA_WAITOK | BUS_DMA_ZERO,
-                                &sc->sis_ldata.sis_tx_dmamap);
-
-       if (error) {
-               device_printf(dev, "no memory for tx list buffers!\n");
-               bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag);
-               sc->sis_ldata.sis_tx_tag = NULL;
-               goto fail;
-       }
-
-       error = bus_dmamap_load(sc->sis_ldata.sis_tx_tag,
-                               sc->sis_ldata.sis_tx_dmamap,
-                               sc->sis_ldata.sis_tx_list,
-                               sizeof(struct sis_desc), sis_dma_map_ring,
-                               &sc->sis_ldata.sis_tx_paddr, 0);
-
-       if (error) {
-               device_printf(dev, "cannot get address of the tx ring!\n");
-               bus_dmamem_free(sc->sis_ldata.sis_tx_tag,
-                               sc->sis_ldata.sis_tx_list,
-                               sc->sis_ldata.sis_tx_dmamap);
-               bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag);
-               sc->sis_ldata.sis_tx_tag = NULL;
-               goto fail;
-       }
-
-       error = bus_dma_tag_create(sc->sis_parent_tag,  /* parent */
-                       1, 0,                   /* alignment, boundary */
-                       BUS_SPACE_MAXADDR,      /* lowaddr */
-                       BUS_SPACE_MAXADDR,      /* highaddr */
-                       NULL, NULL,             /* filter, filterarg */
-                       MCLBYTES, 1,            /* maxsize, nsegments */
-                       BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
-                       0,                      /* flags */
-                       &sc->sis_tag);
+       error = sis_dma_alloc(dev);
        if (error)
                goto fail;
 
@@ -1350,27 +1213,7 @@ sis_detach(device_t dev)
        if (sc->sis_res)
                bus_release_resource(dev, SIS_RES, SIS_RID, sc->sis_res);
 
-       if (sc->sis_ldata.sis_rx_tag) {
-               bus_dmamap_unload(sc->sis_ldata.sis_rx_tag,
-                                 sc->sis_ldata.sis_rx_dmamap);
-               bus_dmamem_free(sc->sis_ldata.sis_rx_tag,
-                               sc->sis_ldata.sis_rx_list,
-                               sc->sis_ldata.sis_rx_dmamap);
-               bus_dma_tag_destroy(sc->sis_ldata.sis_rx_tag);
-       }
-
-       if (sc->sis_ldata.sis_tx_tag) {
-               bus_dmamap_unload(sc->sis_ldata.sis_tx_tag,
-                                 sc->sis_ldata.sis_tx_dmamap);
-               bus_dmamem_free(sc->sis_ldata.sis_tx_tag,
-                               sc->sis_ldata.sis_tx_list,
-                               sc->sis_ldata.sis_tx_dmamap);
-               bus_dma_tag_destroy(sc->sis_ldata.sis_tx_tag);
-       }
-       if (sc->sis_tag)
-               bus_dma_tag_destroy(sc->sis_tag);
-       if (sc->sis_parent_tag)
-               bus_dma_tag_destroy(sc->sis_parent_tag);
+       sis_dma_free(dev);
 
        return(0);
 }
@@ -1381,30 +1224,23 @@ sis_detach(device_t dev)
 static int
 sis_list_tx_init(struct sis_softc *sc)
 {
-       struct sis_list_data *ld;
-       struct sis_chain_data *cd;
+       struct sis_list_data *ld = &sc->sis_ldata;
+       struct sis_chain_data *cd = &sc->sis_cdata;
        int i, nexti;
 
-       cd = &sc->sis_cdata;
-       ld = &sc->sis_ldata;
-
        for (i = 0; i < SIS_TX_LIST_CNT; i++) {
+               bus_addr_t paddr;
+
+               /*
+                * Link the TX desc together
+                */
                nexti = (i == (SIS_TX_LIST_CNT - 1)) ? 0 : i+1;
-               bus_dmamap_load(sc->sis_ldata.sis_tx_tag,
-                               sc->sis_ldata.sis_tx_dmamap,
-                               &ld->sis_tx_list[nexti],
-                               sizeof(struct sis_desc), sis_dma_map_desc_next,
-                               &ld->sis_tx_list[i], 0);
-               ld->sis_tx_list[i].sis_ptr = 0;
-               ld->sis_tx_list[i].sis_ctl = 0;
+               paddr = ld->sis_tx_paddr + (nexti * sizeof(struct sis_desc));
+               ld->sis_tx_list[i].sis_next = paddr;
        }
-
        cd->sis_tx_prod = cd->sis_tx_cons = cd->sis_tx_cnt = 0;
 
-       bus_dmamap_sync(sc->sis_ldata.sis_tx_tag, sc->sis_ldata.sis_tx_dmamap,
-                       BUS_DMASYNC_PREWRITE);
-
-       return(0);
+       return 0;
 }
 
 /*
@@ -1415,59 +1251,89 @@ sis_list_tx_init(struct sis_softc *sc)
 static int
 sis_list_rx_init(struct sis_softc *sc)
 {
-       struct sis_list_data *ld;
-       struct sis_chain_data *cd;
-       int i, nexti;
-
-       ld = &sc->sis_ldata;
-       cd = &sc->sis_cdata;
+       struct sis_list_data *ld = &sc->sis_ldata;
+       struct sis_chain_data *cd = &sc->sis_cdata;
+       int i, error;
 
        for (i = 0; i < SIS_RX_LIST_CNT; i++) {
-               if (sis_newbuf(sc, i, NULL) == ENOBUFS)
-                       return(ENOBUFS);
+               bus_addr_t paddr;
+               int nexti;
 
+               error = sis_newbuf(sc, i, 1);
+               if (error)
+                       return error;
+
+               /*
+                * Link the RX desc together
+                */
                nexti = (i == (SIS_RX_LIST_CNT - 1)) ? 0 : i+1;
-               bus_dmamap_load(sc->sis_ldata.sis_rx_tag,
-                               sc->sis_ldata.sis_rx_dmamap,
-                               &ld->sis_rx_list[nexti],
-                               sizeof(struct sis_desc), sis_dma_map_desc_next,
-                               &ld->sis_rx_list[i], 0);
+               paddr = ld->sis_rx_paddr + (nexti * sizeof(struct sis_desc));
+               ld->sis_rx_list[i].sis_next = paddr;
        }
-
-       bus_dmamap_sync(sc->sis_ldata.sis_rx_tag, sc->sis_ldata.sis_rx_dmamap,
-                       BUS_DMASYNC_PREWRITE);
-
        cd->sis_rx_prod = 0;
 
-       return(0);
+       return 0;
 }
 
 /*
  * Initialize an RX descriptor and attach an MBUF cluster.
  */
 static int
-sis_newbuf(struct sis_softc *sc, int idx, struct mbuf *m)
+sis_newbuf(struct sis_softc *sc, int idx, int init)
 {
-       struct sis_desc *c = &sc->sis_ldata.sis_rx_list[idx];
-       struct sis_rx_data *rd = &sc->sis_cdata.sis_rx_data[idx];
+       struct sis_chain_data *cd = &sc->sis_cdata;
+       struct sis_rx_data *rd = &cd->sis_rx_data[idx];
+       bus_dma_segment_t seg;
+       bus_dmamap_t map;
+       struct mbuf *m;
+       int nseg, error;
 
+       m = m_getcl(init ? MB_WAIT : MB_DONTWAIT, MT_DATA, M_PKTHDR);
        if (m == NULL) {
-               m = m_getcl(MB_DONTWAIT, MT_DATA, M_PKTHDR);
-               if (m == NULL)
-                       return(ENOBUFS);
-       } else {
-               m->m_data = m->m_ext.ext_buf;
+               if (init)
+                       if_printf(&sc->arpcom.ac_if, "can't alloc RX mbuf\n");
+               return ENOBUFS;
        }
+       m->m_len = m->m_pkthdr.len = MCLBYTES;
 
+       /* Try loading the mbuf into tmp DMA map */
+       error = bus_dmamap_load_mbuf_segment(cd->sis_rxbuf_tag,
+                       cd->sis_rx_tmpmap, m, &seg, 1, &nseg, BUS_DMA_NOWAIT);
+       if (error) {
+               m_freem(m);
+               if (init)
+                       if_printf(&sc->arpcom.ac_if, "can't load RX mbuf\n");
+               return error;
+       }
+
+       /* Unload the currently loaded mbuf */
+       if (rd->sis_mbuf != NULL) {
+               bus_dmamap_sync(cd->sis_rxbuf_tag, rd->sis_map,
+                               BUS_DMASYNC_POSTREAD);
+               bus_dmamap_unload(cd->sis_rxbuf_tag, rd->sis_map);
+       }
+
+       /* Swap DMA maps */
+       map = cd->sis_rx_tmpmap;
+       cd->sis_rx_tmpmap = rd->sis_map;
+       rd->sis_map = map;
+
+       /* Save necessary information */
        rd->sis_mbuf = m;
-       c->sis_ctl = SIS_RXLEN;
+       rd->sis_paddr = seg.ds_addr;
 
-       bus_dmamap_create(sc->sis_tag, 0, &rd->sis_map);
-       bus_dmamap_load(sc->sis_tag, rd->sis_map, mtod(m, void *), MCLBYTES,
-                       sis_dma_map_desc_ptr, c, 0);
-       bus_dmamap_sync(sc->sis_tag, rd->sis_map, BUS_DMASYNC_PREWRITE);
+       sis_setup_rxdesc(sc, idx);
+       return 0;
+}
 
-       return(0);
+static void
+sis_setup_rxdesc(struct sis_softc *sc, int idx)
+{
+       struct sis_desc *c = &sc->sis_ldata.sis_rx_list[idx];
+
+       /* Setup the RX desc */
+       c->sis_ctl = SIS_RXLEN;
+       c->sis_ptr = sc->sis_cdata.sis_rx_data[idx].sis_paddr;
 }
 
 /*
@@ -1477,17 +1343,15 @@ sis_newbuf(struct sis_softc *sc, int idx, struct mbuf *m)
 static void
 sis_rxeof(struct sis_softc *sc)
 {
-       struct mbuf *m;
-       struct ifnet *ifp;
+       struct ifnet *ifp = &sc->arpcom.ac_if;
        int i, total_len = 0;
        uint32_t rxstat;
 
-       ifp = &sc->arpcom.ac_if;
        i = sc->sis_cdata.sis_rx_prod;
-
-       while(SIS_OWNDESC(&sc->sis_ldata.sis_rx_list[i])) {
+       while (SIS_OWNDESC(&sc->sis_ldata.sis_rx_list[i])) {
                struct sis_desc *cur_rx;
                struct sis_rx_data *rd;
+               struct mbuf *m;
                int idx = i;
 
 #ifdef DEVICE_POLLING
@@ -1502,15 +1366,9 @@ sis_rxeof(struct sis_softc *sc)
                rd = &sc->sis_cdata.sis_rx_data[idx];
 
                rxstat = cur_rx->sis_rxstat;
-               bus_dmamap_sync(sc->sis_tag, rd->sis_map,
-                               BUS_DMASYNC_POSTWRITE);
-               bus_dmamap_unload(sc->sis_tag, rd->sis_map);
-               bus_dmamap_destroy(sc->sis_tag, rd->sis_map);
+               total_len = SIS_RXBYTES(cur_rx);
 
                m = rd->sis_mbuf;
-               rd->sis_mbuf = NULL;
-
-               total_len = SIS_RXBYTES(cur_rx);
 
                SIS_INC(i, SIS_RX_LIST_CNT);
 
@@ -1524,42 +1382,23 @@ sis_rxeof(struct sis_softc *sc)
                        ifp->if_ierrors++;
                        if (rxstat & SIS_RXSTAT_COLL)
                                ifp->if_collisions++;
-                       sis_newbuf(sc, idx, m);
+                       sis_setup_rxdesc(sc, idx);
                        continue;
                }
 
                /* No errors; receive the packet. */
-#ifdef __i386__
-               /*
-                * On the x86 we do not have alignment problems, so try to
-                * allocate a new buffer for the receive ring, and pass up
-                * the one where the packet is already, saving the expensive
-                * copy done in m_devget().
-                * If we are on an architecture with alignment problems, or
-                * if the allocation fails, then use m_devget and leave the
-                * existing buffer in the receive ring.
-                */
-               if (sis_newbuf(sc, idx, NULL) == 0)
+               if (sis_newbuf(sc, idx, 0) == 0) {
                        m->m_pkthdr.len = m->m_len = total_len;
-               else
-#endif
-               {
-                       struct mbuf *m0;
-                       m0 = m_devget(mtod(m, char *) - ETHER_ALIGN,
-                               total_len + ETHER_ALIGN, 0, ifp, NULL);
-                       sis_newbuf(sc, idx, m);
-                       if (m0 == NULL) {
-                               ifp->if_ierrors++;
-                               continue;
-                       }
-                       m_adj(m0, ETHER_ALIGN);
-                       m = m0;
+                       m->m_pkthdr.rcvif = ifp;
+               } else {
+                       ifp->if_ierrors++;
+                       sis_setup_rxdesc(sc, idx);
+                       continue;
                }
 
                ifp->if_ipackets++;
                ifp->if_input(ifp, m);
        }
-
        sc->sis_cdata.sis_rx_prod = i;
 }
 
@@ -1578,11 +1417,10 @@ sis_rxeoc(struct sis_softc *sc)
 static void
 sis_txeof(struct sis_softc *sc)
 {
-       struct ifnet *ifp;
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       struct sis_chain_data *cd = &sc->sis_cdata;
        uint32_t idx;
 
-       ifp = &sc->arpcom.ac_if;
-
        /*
         * Go through our tx list and free mbufs for those
         * frames that have been transmitted.
@@ -1593,7 +1431,7 @@ sis_txeof(struct sis_softc *sc)
                struct sis_tx_data *td;
 
                cur_tx = &sc->sis_ldata.sis_tx_list[idx];
-               td = &sc->sis_cdata.sis_tx_data[idx];
+               td = &cd->sis_tx_data[idx];
 
                if (SIS_OWNDESC(cur_tx))
                        break;
@@ -1614,20 +1452,21 @@ sis_txeof(struct sis_softc *sc)
 
                ifp->if_opackets++;
                if (td->sis_mbuf != NULL) {
+                       bus_dmamap_unload(cd->sis_txbuf_tag, td->sis_map);
                        m_freem(td->sis_mbuf);
                        td->sis_mbuf = NULL;
-                       bus_dmamap_unload(sc->sis_tag, td->sis_map);
-                       bus_dmamap_destroy(sc->sis_tag, td->sis_map);
                }
        }
 
        if (idx != sc->sis_cdata.sis_tx_cons) {
                /* we freed up some buffers */
                sc->sis_cdata.sis_tx_cons = idx;
-               ifp->if_flags &= ~IFF_OACTIVE;
        }
 
-       ifp->if_timer = (sc->sis_cdata.sis_tx_cnt == 0) ? 0 : 5;
+       if (cd->sis_tx_cnt == 0)
+               ifp->if_timer = 0;
+       if (!SIS_IS_OACTIVE(sc))
+               ifp->if_flags &= ~IFF_OACTIVE;
 }
 
 static void
@@ -1766,54 +1605,54 @@ sis_intr(void *arg)
  * pointers to the fragment pointers.
  */
 static int
-sis_encap(struct sis_softc *sc, struct mbuf *m_head, uint32_t *txidx)
+sis_encap(struct sis_softc *sc, struct mbuf **m_head, uint32_t *txidx)
 {
-       struct sis_desc *f = NULL;
-       struct sis_tx_data *td = NULL;
-       struct mbuf *m;
-       int frag, cur, cnt = 0;
+       struct sis_chain_data *cd = &sc->sis_cdata;
+       struct sis_list_data *ld = &sc->sis_ldata;
+       bus_dma_segment_t segs[SIS_NSEGS];
+       bus_dmamap_t map;
+       int frag, cur, maxsegs, nsegs, error, i;
+
+       maxsegs = SIS_TX_LIST_CNT - SIS_NSEGS_RESERVED - cd->sis_tx_cnt;
+       KASSERT(maxsegs >= 1, ("not enough TX descs\n"));
+       if (maxsegs > SIS_NSEGS)
+               maxsegs = SIS_NSEGS;
+
+       map = cd->sis_tx_data[*txidx].sis_map;
+       error = bus_dmamap_load_mbuf_defrag(cd->sis_txbuf_tag, map, m_head,
+                       segs, maxsegs, &nsegs, BUS_DMA_NOWAIT);
+       if (error) {
+               m_freem(*m_head);
+               *m_head = NULL;
+               return error;
+       }
+       bus_dmamap_sync(cd->sis_txbuf_tag, map, BUS_DMASYNC_PREWRITE);
 
-       /*
-        * Start packing the mbufs in this chain into
-        * the fragment pointers. Stop when we run out
-        * of fragments or hit the end of the mbuf chain.
-        */
        cur = frag = *txidx;
+       for (i = 0; i < nsegs; ++i) {
+               struct sis_desc *f = &ld->sis_tx_list[frag];
 
-       for (m = m_head; m != NULL; m = m->m_next) {
-               if (m->m_len != 0) {
-                       if ((SIS_TX_LIST_CNT -
-                           (sc->sis_cdata.sis_tx_cnt + cnt)) < 2)
-                               break;
+               f->sis_ctl = SIS_CMDSTS_MORE | segs[i].ds_len;
+               f->sis_ptr = segs[i].ds_addr;
+               if (i != 0)
+                       f->sis_ctl |= SIS_CMDSTS_OWN;
 
-                       f = &sc->sis_ldata.sis_tx_list[frag];
-                       td = &sc->sis_cdata.sis_tx_data[frag];
-
-                       f->sis_ctl = SIS_CMDSTS_MORE | m->m_len;
-                       bus_dmamap_create(sc->sis_tag, 0, &td->sis_map);
-                       bus_dmamap_load(sc->sis_tag, td->sis_map,
-                                       mtod(m, void *), m->m_len,
-                                       sis_dma_map_desc_ptr, f, 0);
-                       bus_dmamap_sync(sc->sis_tag, td->sis_map,
-                                       BUS_DMASYNC_PREREAD);
-                       if (cnt != 0)
-                               f->sis_ctl |= SIS_CMDSTS_OWN;
-                       cur = frag;
-                       SIS_INC(frag, SIS_TX_LIST_CNT);
-                       cnt++;
-               }
+               cur = frag;
+               SIS_INC(frag, SIS_TX_LIST_CNT);
        }
-       /* Caller should make sure that 'm_head' is not excessive fragmented */
-       KASSERT(m == NULL, ("too many fragments\n"));
+       ld->sis_tx_list[cur].sis_ctl &= ~SIS_CMDSTS_MORE;
+       ld->sis_tx_list[*txidx].sis_ctl |= SIS_CMDSTS_OWN;
 
-       sc->sis_cdata.sis_tx_data[cur].sis_mbuf = m_head;
+       /* Swap DMA map */
+       cd->sis_tx_data[*txidx].sis_map = cd->sis_tx_data[cur].sis_map;
+       cd->sis_tx_data[cur].sis_map = map;
 
-       sc->sis_ldata.sis_tx_list[cur].sis_ctl &= ~SIS_CMDSTS_MORE;
-       sc->sis_ldata.sis_tx_list[*txidx].sis_ctl |= SIS_CMDSTS_OWN;
-       sc->sis_cdata.sis_tx_cnt += cnt;
+       cd->sis_tx_data[cur].sis_mbuf = *m_head;
+
+       cd->sis_tx_cnt += nsegs;
        *txidx = frag;
 
-       return(0);
+       return 0;
 }
 
 /*
@@ -1826,70 +1665,46 @@ sis_encap(struct sis_softc *sc, struct mbuf *m_head, uint32_t *txidx)
 static void
 sis_start(struct ifnet *ifp)
 {
-       struct sis_softc *sc;
-       struct mbuf *m_head = NULL, *m_defragged;
+       struct sis_softc *sc = ifp->if_softc;
+       int need_trans, error;
        uint32_t idx;
-       int need_trans;
-
-       sc = ifp->if_softc;
 
        if (!sc->sis_link) {
                ifq_purge(&ifp->if_snd);
                return;
        }
 
-       idx = sc->sis_cdata.sis_tx_prod;
-
        if ((ifp->if_flags & (IFF_OACTIVE | IFF_RUNNING)) != IFF_RUNNING)
                return;
 
+       idx = sc->sis_cdata.sis_tx_prod;
        need_trans = 0;
+
        while (sc->sis_cdata.sis_tx_data[idx].sis_mbuf == NULL) {
-               struct mbuf *m;
-               int cnt;
+               struct mbuf *m_head;
 
                /*
                 * If there's no way we can send any packets, return now.
                 */
-               if (SIS_TX_LIST_CNT - sc->sis_cdata.sis_tx_cnt < 2) {
+               if (SIS_IS_OACTIVE(sc)) {
                        ifp->if_flags |= IFF_OACTIVE;
                        break;
                }
 
-               m_defragged = NULL;
                m_head = ifq_dequeue(&ifp->if_snd, NULL);
                if (m_head == NULL)
                        break;
 
-again:
-               cnt = 0;
-               for (m = m_head; m != NULL; m = m->m_next)
-                       ++cnt;
-               if ((SIS_TX_LIST_CNT -
-                   (sc->sis_cdata.sis_tx_cnt + cnt)) < 2) {
-                       if (m_defragged != NULL) {
-                               /*
-                                * Even after defragmentation, there
-                                * are still too many fragments, so
-                                * drop this packet.
-                                */
-                               m_freem(m_head);
+               error = sis_encap(sc, &m_head, &idx);
+               if (error) {
+                       ifp->if_oerrors++;
+                       if (sc->sis_cdata.sis_tx_cnt == 0) {
+                               continue;
+                       } else {
                                ifp->if_flags |= IFF_OACTIVE;
                                break;
                        }
-
-                       m_defragged = m_defrag(m_head, MB_DONTWAIT);
-                       if (m_defragged == NULL) {
-                               m_freem(m_head);
-                               continue;
-                       }
-                       m_head = m_defragged;
-
-                       /* Recount # of fragments */
-                       goto again;
                }
-
-               sis_encap(sc, m_head, &idx);
                need_trans = 1;
 
                /*
@@ -1950,7 +1765,7 @@ sis_init(void *xsc)
        }
 
        /* Init circular RX list. */
-       if (sis_list_rx_init(sc) == ENOBUFS) {
+       if (sis_list_rx_init(sc)) {
                if_printf(ifp, "initialization failed: "
                          "no memory for rx buffers\n");
                sis_stop(sc);
@@ -2180,15 +1995,16 @@ sis_watchdog(struct ifnet *ifp)
 static void
 sis_stop(struct sis_softc *sc)
 {
+       struct ifnet *ifp = &sc->arpcom.ac_if;
+       struct sis_list_data *ld = &sc->sis_ldata;
+       struct sis_chain_data *cd = &sc->sis_cdata;
        int i;
-       struct ifnet *ifp;
-
-       ifp = &sc->arpcom.ac_if;
-       ifp->if_timer = 0;
 
        callout_stop(&sc->sis_timer);
 
        ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+       ifp->if_timer = 0;
+
        CSR_WRITE_4(sc, SIS_IER, 0);
        CSR_WRITE_4(sc, SIS_IMR, 0);
        SIS_SETBIT(sc, SIS_CSR, SIS_CSR_TX_DISABLE|SIS_CSR_RX_DISABLE);
@@ -2202,31 +2018,29 @@ sis_stop(struct sis_softc *sc)
         * Free data in the RX lists.
         */
        for (i = 0; i < SIS_RX_LIST_CNT; i++) {
-               if (sc->sis_cdata.sis_rx_data[i].sis_mbuf != NULL) {
-                       bus_dmamap_unload(sc->sis_tag,
-                               sc->sis_cdata.sis_rx_data[i].sis_map);
-                       bus_dmamap_destroy(sc->sis_tag,
-                               sc->sis_cdata.sis_rx_data[i].sis_map);
-                       m_freem(sc->sis_cdata.sis_rx_data[i].sis_mbuf);
-                       sc->sis_cdata.sis_rx_data[i].sis_mbuf = NULL;
+               struct sis_rx_data *rd = &cd->sis_rx_data[i];
+
+               if (rd->sis_mbuf != NULL) {
+                       bus_dmamap_unload(cd->sis_rxbuf_tag, rd->sis_map);
+                       m_freem(rd->sis_mbuf);
+                       rd->sis_mbuf = NULL;
                }
        }
-       bzero(sc->sis_ldata.sis_rx_list, sizeof(sc->sis_ldata.sis_rx_list));
+       bzero(ld->sis_rx_list, SIS_RX_LIST_SZ);
 
        /*
         * Free the TX list buffers.
         */
        for (i = 0; i < SIS_TX_LIST_CNT; i++) {
-               if (sc->sis_cdata.sis_tx_data[i].sis_mbuf != NULL) {
-                       bus_dmamap_unload(sc->sis_tag,
-                               sc->sis_cdata.sis_tx_data[i].sis_map);
-                       bus_dmamap_destroy(sc->sis_tag,
-                               sc->sis_cdata.sis_tx_data[i].sis_map);
-                       m_freem(sc->sis_cdata.sis_tx_data[i].sis_mbuf);
-                       sc->sis_cdata.sis_tx_data[i].sis_mbuf = NULL;
+               struct sis_tx_data *td = &cd->sis_tx_data[i];
+
+               if (td->sis_mbuf != NULL) {
+                       bus_dmamap_unload(cd->sis_txbuf_tag, td->sis_map);
+                       m_freem(td->sis_mbuf);
+                       td->sis_mbuf = NULL;
                }
        }
-       bzero(sc->sis_ldata.sis_tx_list, sizeof(sc->sis_ldata.sis_tx_list));
+       bzero(ld->sis_tx_list, SIS_TX_LIST_SZ);
 }
 
 /*
@@ -2247,3 +2061,181 @@ sis_shutdown(device_t dev)
        lwkt_serialize_exit(ifp->if_serializer);
 }
 
+static int
+sis_dma_alloc(device_t dev)
+{
+       struct sis_softc *sc = device_get_softc(dev);
+       struct sis_chain_data *cd = &sc->sis_cdata;
+       struct sis_list_data *ld = &sc->sis_ldata;
+       int i, error;
+
+       /* Create top level DMA tag */
+       error = bus_dma_tag_create(NULL,        /* parent */
+                       1, 0,                   /* alignment, boundary */
+                       BUS_SPACE_MAXADDR_32BIT,/* lowaddr */
+                       BUS_SPACE_MAXADDR,      /* highaddr */
+                       NULL, NULL,             /* filter, filterarg */
+                       BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
+                       0,                      /* nsegments */
+                       BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
+                       0,                      /* flags */
+                       &sc->sis_parent_tag);
+       if (error) {
+               device_printf(dev, "could not create parent DMA tag\n");
+               return error;
+       }
+
+       /* Allocate RX ring */
+       ld->sis_rx_list = bus_dmamem_coherent_any(sc->sis_parent_tag,
+                               SIS_RING_ALIGN, SIS_RX_LIST_SZ,
+                               BUS_DMA_WAITOK | BUS_DMA_ZERO,
+                               &ld->sis_rx_tag, &ld->sis_rx_dmamap,
+                               &ld->sis_rx_paddr);
+       if (ld->sis_rx_list == NULL) {
+               device_printf(dev, "could not allocate RX ring\n");
+               return ENOMEM;
+       }
+
+       /* Allocate TX ring */
+       ld->sis_tx_list = bus_dmamem_coherent_any(sc->sis_parent_tag,
+                               SIS_RING_ALIGN, SIS_TX_LIST_SZ,
+                               BUS_DMA_WAITOK | BUS_DMA_ZERO,
+                               &ld->sis_tx_tag, &ld->sis_tx_dmamap,
+                               &ld->sis_tx_paddr);
+       if (ld->sis_tx_list == NULL) {
+               device_printf(dev, "could not allocate TX ring\n");
+               return ENOMEM;
+       }
+
+       /* Create DMA tag for TX mbuf */
+       error = bus_dma_tag_create(sc->sis_parent_tag,/* parent */
+                       1, 0,                   /* alignment, boundary */
+                       BUS_SPACE_MAXADDR,      /* lowaddr */
+                       BUS_SPACE_MAXADDR,      /* highaddr */
+                       NULL, NULL,             /* filter, filterarg */
+                       MCLBYTES,               /* maxsize */
+                       SIS_NSEGS,              /* nsegments */
+                       MCLBYTES,               /* maxsegsize */
+                       BUS_DMA_ALLOCNOW | BUS_DMA_WAITOK,/* flags */
+                       &cd->sis_txbuf_tag);
+       if (error) {
+               device_printf(dev, "could not create TX buf DMA tag\n");
+               return error;
+       }
+
+       /* Create DMA maps for TX mbufs */
+       for (i = 0; i < SIS_TX_LIST_CNT; ++i) {
+               error = bus_dmamap_create(cd->sis_txbuf_tag, BUS_DMA_WAITOK,
+                                         &cd->sis_tx_data[i].sis_map);
+               if (error) {
+                       int j;
+
+                       for (j = 0; j < i; ++j) {
+                               bus_dmamap_destroy(cd->sis_txbuf_tag,
+                                       cd->sis_tx_data[j].sis_map);
+                       }
+                       bus_dma_tag_destroy(cd->sis_txbuf_tag);
+                       cd->sis_txbuf_tag = NULL;
+
+                       device_printf(dev, "could not create %dth "
+                                     "TX buf DMA map\n", i);
+                       return error;
+               }
+       }
+
+       /* Create DMA tag for RX mbuf */
+       error = bus_dma_tag_create(sc->sis_parent_tag,/* parent */
+                       SIS_RXBUF_ALIGN, 0,     /* alignment, boundary */
+                       BUS_SPACE_MAXADDR,      /* lowaddr */
+                       BUS_SPACE_MAXADDR,      /* highaddr */
+                       NULL, NULL,             /* filter, filterarg */
+                       MCLBYTES,               /* maxsize */
+                       1,                      /* nsegments */
+                       MCLBYTES,               /* maxsegsize */
+                       BUS_DMA_ALLOCNOW | BUS_DMA_WAITOK |
+                       BUS_DMA_ALIGNED,        /* flags */
+                       &cd->sis_rxbuf_tag);
+       if (error) {
+               device_printf(dev, "could not create RX buf DMA tag\n");
+               return error;
+       }
+
+       /* Create tmp DMA map for loading RX mbuf */
+       error = bus_dmamap_create(cd->sis_rxbuf_tag, BUS_DMA_WAITOK,
+                                 &cd->sis_rx_tmpmap);
+       if (error) {
+               device_printf(dev, "could not create RX buf tmp DMA map\n");
+               bus_dma_tag_destroy(cd->sis_rxbuf_tag);
+               cd->sis_rxbuf_tag = NULL;
+               return error;
+       }
+
+       /* Create DMA maps for RX mbufs */
+       for (i = 0; i < SIS_RX_LIST_CNT; ++i) {
+               error = bus_dmamap_create(cd->sis_rxbuf_tag, BUS_DMA_WAITOK,
+                                         &cd->sis_rx_data[i].sis_map);
+               if (error) {
+                       int j;
+
+                       for (j = 0; j < i; ++j) {
+                               bus_dmamap_destroy(cd->sis_rxbuf_tag,
+                                       cd->sis_rx_data[j].sis_map);
+                       }
+                       bus_dmamap_destroy(cd->sis_rxbuf_tag,
+                                          cd->sis_rx_tmpmap);
+                       bus_dma_tag_destroy(cd->sis_rxbuf_tag);
+                       cd->sis_rxbuf_tag = NULL;
+
+                       device_printf(dev, "could not create %dth "
+                                     "RX buf DMA map\n", i);
+                       return error;
+               }
+       }
+       return 0;
+}
+
+static void
+sis_dma_free(device_t dev)
+{
+       struct sis_softc *sc = device_get_softc(dev);
+       struct sis_list_data *ld = &sc->sis_ldata;
+       struct sis_chain_data *cd = &sc->sis_cdata;
+       int i;
+
+       /* Free TX ring */
+       if (ld->sis_tx_list != NULL) {
+               bus_dmamap_unload(ld->sis_tx_tag, ld->sis_tx_dmamap);
+               bus_dmamem_free(ld->sis_tx_tag, ld->sis_tx_list,
+                               ld->sis_tx_dmamap);
+               bus_dma_tag_destroy(ld->sis_tx_tag);
+       }
+
+       /* Free RX ring */
+       if (ld->sis_rx_list != NULL) {
+               bus_dmamap_unload(ld->sis_rx_tag, ld->sis_rx_dmamap);
+               bus_dmamem_free(ld->sis_rx_tag, ld->sis_rx_list,
+                               ld->sis_rx_dmamap);
+               bus_dma_tag_destroy(ld->sis_rx_tag);
+       }
+
+       /* Destroy DMA stuffs for TX mbufs */
+       if (cd->sis_txbuf_tag != NULL) {
+               for (i = 0; i < SIS_TX_LIST_CNT; ++i) {
+                       KKASSERT(cd->sis_tx_data[i].sis_mbuf == NULL);
+                       bus_dmamap_destroy(cd->sis_txbuf_tag,
+                                          cd->sis_tx_data[i].sis_map);
+               }
+               bus_dma_tag_destroy(cd->sis_txbuf_tag);
+       }
+
+       /* Destroy DMA stuffs for RX mbufs */
+       if (cd->sis_rxbuf_tag != NULL) {
+               for (i = 0; i < SIS_RX_LIST_CNT; ++i) {
+                       KKASSERT(cd->sis_rx_data[i].sis_mbuf == NULL);
+                       bus_dmamap_destroy(cd->sis_rxbuf_tag,
+                                          cd->sis_rx_data[i].sis_map);
+               }
+               bus_dmamap_destroy(cd->sis_rxbuf_tag, cd->sis_rx_tmpmap);
+               bus_dma_tag_destroy(cd->sis_rxbuf_tag);
+       }
+}
index 76b0093..0b698ab 100644 (file)
 #define NS_FILTADDR_FMEM_HI    0x000003FE
 
 /*
- * DMA descriptor structures. The first part of the descriptor
- * is the hardware descriptor format, which is just three longwords.
- * After this, we include some additional structure members for
- * use by the driver. Note that for this structure will be a different
- * size on the alpha, but that's okay as long as it's a multiple of 4
- * bytes in size.
+ * DMA descriptor structures.
  */
 struct sis_desc {
        /* SiS hardware descriptor section */
@@ -314,6 +309,8 @@ struct sis_desc {
        uint32_t                sis_ptr;
 };
 
+#define SIS_RING_ALIGN         4
+
 #define SIS_CMDSTS_BUFLEN      0x00000FFF
 #define SIS_CMDSTS_PKT_OK      0x08000000
 #define SIS_CMDSTS_CRC         0x10000000
@@ -361,12 +358,12 @@ struct sis_list_data {
        bus_dma_tag_t           sis_rx_tag;
        bus_dmamap_t            sis_rx_dmamap;
        struct sis_desc         *sis_rx_list;
-       uint32_t                sis_rx_paddr;
+       bus_addr_t              sis_rx_paddr;
 
        bus_dma_tag_t           sis_tx_tag;
        bus_dmamap_t            sis_tx_dmamap;
        struct sis_desc         *sis_tx_list;
-       uint32_t                sis_tx_paddr;
+       bus_addr_t              sis_tx_paddr;
 };
 
 struct sis_rx_data {
@@ -381,9 +378,12 @@ struct sis_tx_data {
 };
 
 struct sis_chain_data {
+       bus_dma_tag_t           sis_rxbuf_tag;
+       bus_dmamap_t            sis_rx_tmpmap;
        struct sis_rx_data      sis_rx_data[SIS_RX_LIST_CNT];
        int                     sis_rx_prod;
 
+       bus_dma_tag_t           sis_txbuf_tag;
        struct sis_tx_data      sis_tx_data[SIS_TX_LIST_CNT];
        int                     sis_tx_prod;
        int                     sis_tx_cons;
@@ -442,7 +442,6 @@ struct sis_softc {
        uint8_t                 sis_link;
        struct sis_list_data    sis_ldata;
        bus_dma_tag_t           sis_parent_tag;
-       bus_dma_tag_t           sis_tag;
        struct sis_chain_data   sis_cdata;
        struct callout          sis_timer;
 #ifdef DEVICE_POLLING
@@ -450,6 +449,13 @@ struct sis_softc {
 #endif
 };
 
+#define SIS_RXBUF_ALIGN                4
+#define SIS_NSEGS              32      /* XXX limitted by TX desc cnt */
+#define SIS_NSEGS_RESERVED     1
+
+#define SIS_IS_OACTIVE(sc)     \
+       ((sc)->sis_cdata.sis_tx_cnt + SIS_NSEGS_RESERVED >= SIS_TX_LIST_CNT)
+
 /*
  * register space access macros
  */