From owner-svn-src-all@FreeBSD.ORG Sat Jun 18 11:51:18 2011 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 25463106566B; Sat, 18 Jun 2011 11:51:18 +0000 (UTC) (envelope-from bschmidt@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id 129188FC0A; Sat, 18 Jun 2011 11:51:18 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.4/8.14.4) with ESMTP id p5IBpIgp039429; Sat, 18 Jun 2011 11:51:18 GMT (envelope-from bschmidt@svn.freebsd.org) Received: (from bschmidt@localhost) by svn.freebsd.org (8.14.4/8.14.4/Submit) id p5IBpIjl039426; Sat, 18 Jun 2011 11:51:18 GMT (envelope-from bschmidt@svn.freebsd.org) Message-Id: <201106181151.p5IBpIjl039426@svn.freebsd.org> From: Bernhard Schmidt Date: Sat, 18 Jun 2011 11:51:18 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-8@freebsd.org X-SVN-Group: stable-8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r223244 - stable/8/sys/dev/iwn X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sat, 18 Jun 2011 11:51:18 -0000 Author: bschmidt Date: Sat Jun 18 11:51:17 2011 New Revision: 223244 URL: http://svn.freebsd.org/changeset/base/223244 Log: MFC r220691-220694,220700-220702,220704,220710-220711: - Remove the flags argument of iwn_dma_contig_alloc(), it is always set as BUS_DMA_NOWAIT. While here also set BUS_DMA_COHERENT. - OpenBSD uses IWN_RBUF_SIZE not MJUMPAGESIZE for the RX path, also replace caddr_t with void * to be in sync. - In case a new mbuf can't be loaded, reuse the old one. - scratch_paddr has the same address pre-assigned, use that instead. - Rewrite DMA segment handling to be more inline with the OpenBSD code. Also change the m_len == 0 hack to have less code churn. - Make sure to destroy all DMA tags and maps. - Unify TX/RX ring allocation, finish the descriptior DMA stuff before starting with data. - Add missing bus_dmamap_sync calls as well as remove two duplicate ones. - Prevent double-free, also use the same error codes as OpenBSD. - Replace RX/TX ring allocation error messages with something more sane and remove those where the caller already prints one. Modified: stable/8/sys/dev/iwn/if_iwn.c Directory Properties: stable/8/sys/ (props changed) stable/8/sys/amd64/include/xen/ (props changed) stable/8/sys/cddl/contrib/opensolaris/ (props changed) stable/8/sys/contrib/dev/acpica/ (props changed) stable/8/sys/contrib/pf/ (props changed) Modified: stable/8/sys/dev/iwn/if_iwn.c ============================================================================== --- stable/8/sys/dev/iwn/if_iwn.c Sat Jun 18 11:44:54 2011 (r223243) +++ stable/8/sys/dev/iwn/if_iwn.c Sat Jun 18 11:51:17 2011 (r223244) @@ -88,7 +88,7 @@ static int iwn_init_otprom(struct iwn_so static int iwn_read_prom_data(struct iwn_softc *, uint32_t, void *, int); static void iwn_dma_map_addr(void *, bus_dma_segment_t *, int, int); static int iwn_dma_contig_alloc(struct iwn_softc *, struct iwn_dma_info *, - void **, bus_size_t, bus_size_t, int); + void **, bus_size_t, bus_size_t); static void iwn_dma_contig_free(struct iwn_dma_info *); static int iwn_alloc_sched(struct iwn_softc *); static void iwn_free_sched(struct iwn_softc *); @@ -1117,7 +1117,7 @@ iwn_dma_map_addr(void *arg, bus_dma_segm static int iwn_dma_contig_alloc(struct iwn_softc *sc, struct iwn_dma_info *dma, - void **kvap, bus_size_t size, bus_size_t alignment, int flags) + void **kvap, bus_size_t size, bus_size_t alignment) { int error; @@ -1126,27 +1126,21 @@ iwn_dma_contig_alloc(struct iwn_softc *s error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), alignment, 0, BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, size, - 1, size, flags, NULL, NULL, &dma->tag); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: bus_dma_tag_create failed, error %d\n", - __func__, error); + 1, size, BUS_DMA_NOWAIT, NULL, NULL, &dma->tag); + if (error != 0) goto fail; - } + error = bus_dmamem_alloc(dma->tag, (void **)&dma->vaddr, - flags | BUS_DMA_ZERO, &dma->map); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: bus_dmamem_alloc failed, error %d\n", __func__, error); + BUS_DMA_NOWAIT | BUS_DMA_ZERO | BUS_DMA_COHERENT, &dma->map); + if (error != 0) goto fail; - } - error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, - size, iwn_dma_map_addr, &dma->paddr, flags); - if (error != 0) { - device_printf(sc->sc_dev, - "%s: bus_dmamap_load failed, error %d\n", __func__, error); + + error = bus_dmamap_load(dma->tag, dma->map, dma->vaddr, size, + iwn_dma_map_addr, &dma->paddr, BUS_DMA_NOWAIT); + if (error != 0) goto fail; - } + + bus_dmamap_sync(dma->tag, dma->map, BUS_DMASYNC_PREWRITE); if (kvap != NULL) *kvap = dma->vaddr; @@ -1159,16 +1153,20 @@ fail: static void iwn_dma_contig_free(struct iwn_dma_info *dma) { - if (dma->tag != NULL) { - if (dma->map != NULL) { - if (dma->paddr == 0) { - bus_dmamap_sync(dma->tag, dma->map, - BUS_DMASYNC_POSTREAD|BUS_DMASYNC_POSTWRITE); - bus_dmamap_unload(dma->tag, dma->map); - } + if (dma->map != NULL) { + if (dma->vaddr != NULL) { + bus_dmamap_sync(dma->tag, dma->map, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dma->tag, dma->map); bus_dmamem_free(dma->tag, &dma->vaddr, dma->map); + dma->vaddr = NULL; } + bus_dmamap_destroy(dma->tag, dma->map); + dma->map = NULL; + } + if (dma->tag != NULL) { bus_dma_tag_destroy(dma->tag); + dma->tag = NULL; } } @@ -1176,8 +1174,8 @@ static int iwn_alloc_sched(struct iwn_softc *sc) { /* TX scheduler rings must be aligned on a 1KB boundary. */ - return iwn_dma_contig_alloc(sc, &sc->sched_dma, - (void **)&sc->sched, sc->sc_hal->schedsz, 1024, BUS_DMA_NOWAIT); + return iwn_dma_contig_alloc(sc, &sc->sched_dma, (void **)&sc->sched, + sc->sc_hal->schedsz, 1024); } static void @@ -1190,8 +1188,7 @@ static int iwn_alloc_kw(struct iwn_softc *sc) { /* "Keep Warm" page must be aligned on a 4KB boundary. */ - return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, 4096, 4096, - BUS_DMA_NOWAIT); + return iwn_dma_contig_alloc(sc, &sc->kw_dma, NULL, 4096, 4096); } static void @@ -1204,8 +1201,8 @@ static int iwn_alloc_ict(struct iwn_softc *sc) { /* ICT table must be aligned on a 4KB boundary. */ - return iwn_dma_contig_alloc(sc, &sc->ict_dma, - (void **)&sc->ict, IWN_ICT_SIZE, 4096, BUS_DMA_NOWAIT); + return iwn_dma_contig_alloc(sc, &sc->ict_dma, (void **)&sc->ict, + IWN_ICT_SIZE, 4096); } static void @@ -1218,8 +1215,8 @@ static int iwn_alloc_fwmem(struct iwn_softc *sc) { /* Must be aligned on a 16-byte boundary. */ - return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, - sc->sc_hal->fwsz, 16, BUS_DMA_NOWAIT); + return iwn_dma_contig_alloc(sc, &sc->fw_dma, NULL, sc->sc_hal->fwsz, + 16); } static void @@ -1238,33 +1235,33 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, /* Allocate RX descriptors (256-byte aligned). */ size = IWN_RX_RING_COUNT * sizeof (uint32_t); - error = iwn_dma_contig_alloc(sc, &ring->desc_dma, - (void **)&ring->desc, size, 256, BUS_DMA_NOWAIT); + error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, + size, 256); if (error != 0) { device_printf(sc->sc_dev, - "%s: could not allocate Rx ring DMA memory, error %d\n", + "%s: could not allocate RX ring DMA memory, error %d\n", __func__, error); goto fail; } - error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, - BUS_SPACE_MAXADDR_32BIT, - BUS_SPACE_MAXADDR, NULL, NULL, MJUMPAGESIZE, 1, - MJUMPAGESIZE, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); + /* Allocate RX status area (16-byte aligned). */ + error = iwn_dma_contig_alloc(sc, &ring->stat_dma, (void **)&ring->stat, + sizeof (struct iwn_rx_status), 16); if (error != 0) { device_printf(sc->sc_dev, - "%s: bus_dma_tag_create_failed, error %d\n", + "%s: could not allocate RX status DMA memory, error %d\n", __func__, error); goto fail; } - /* Allocate RX status area (16-byte aligned). */ - error = iwn_dma_contig_alloc(sc, &ring->stat_dma, - (void **)&ring->stat, sizeof (struct iwn_rx_status), - 16, BUS_DMA_NOWAIT); + /* Create RX buffer DMA tag. */ + error = bus_dma_tag_create(bus_get_dma_tag(sc->sc_dev), 1, 0, + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, + IWN_RBUF_SIZE, 1, IWN_RBUF_SIZE, BUS_DMA_NOWAIT, NULL, NULL, + &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, - "%s: could not allocate Rx status DMA memory, error %d\n", + "%s: could not create RX buf DMA tag, error %d\n", __func__, error); goto fail; } @@ -1279,33 +1276,29 @@ iwn_alloc_rx_ring(struct iwn_softc *sc, error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, - "%s: bus_dmamap_create failed, error %d\n", + "%s: could not create RX buf DMA map, error %d\n", __func__, error); goto fail; } - data->m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + data->m = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, + IWN_RBUF_SIZE); if (data->m == NULL) { device_printf(sc->sc_dev, - "%s: could not allocate rx mbuf\n", __func__); - error = ENOMEM; + "%s: could not allocate RX mbuf\n", __func__); + error = ENOBUFS; goto fail; } - /* Map page. */ error = bus_dmamap_load(ring->data_dmat, data->map, - mtod(data->m, caddr_t), MJUMPAGESIZE, - iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); + mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr, + &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, - "%s: bus_dmamap_load failed, error %d\n", - __func__, error); - m_freem(data->m); - error = ENOMEM; /* XXX unique code */ + "%s: can't not map mbuf, error %d\n", __func__, + error); goto fail; } - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_PREWRITE); /* Set physical address of RX buffer (256-byte aligned). */ ring->desc[i] = htole32(paddr >> 8); @@ -1358,10 +1351,15 @@ iwn_free_rx_ring(struct iwn_softc *sc, s BUS_DMASYNC_POSTREAD); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); + data->m = NULL; } if (data->map != NULL) bus_dmamap_destroy(ring->data_dmat, data->map); } + if (ring->data_dmat != NULL) { + bus_dma_tag_destroy(ring->data_dmat); + ring->data_dmat = NULL; + } } static int @@ -1377,8 +1375,8 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, /* Allocate TX descriptors (256-byte aligned.) */ size = IWN_TX_RING_COUNT * sizeof(struct iwn_tx_desc); - error = iwn_dma_contig_alloc(sc, &ring->desc_dma, - (void **)&ring->desc, size, 256, BUS_DMA_NOWAIT); + error = iwn_dma_contig_alloc(sc, &ring->desc_dma, (void **)&ring->desc, + size, 256); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate TX ring DMA memory, error %d\n", @@ -1394,8 +1392,8 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, return 0; size = IWN_TX_RING_COUNT * sizeof(struct iwn_tx_cmd); - error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, - (void **)&ring->cmd, size, 4, BUS_DMA_NOWAIT); + error = iwn_dma_contig_alloc(sc, &ring->cmd_dma, (void **)&ring->cmd, + size, 4); if (error != 0) { device_printf(sc->sc_dev, "%s: could not allocate TX cmd DMA memory, error %d\n", @@ -1409,7 +1407,7 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, MCLBYTES, BUS_DMA_NOWAIT, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, - "%s: bus_dma_tag_create_failed, error %d\n", + "%s: could not create TX buf DMA tag, error %d\n", __func__, error); goto fail; } @@ -1425,12 +1423,10 @@ iwn_alloc_tx_ring(struct iwn_softc *sc, error = bus_dmamap_create(ring->data_dmat, 0, &data->map); if (error != 0) { device_printf(sc->sc_dev, - "%s: bus_dmamap_create failed, error %d\n", + "%s: could not create TX buf DMA map, error %d\n", __func__, error); goto fail; } - bus_dmamap_sync(ring->data_dmat, data->map, - BUS_DMASYNC_PREWRITE); } return 0; fail: @@ -1447,6 +1443,8 @@ iwn_reset_tx_ring(struct iwn_softc *sc, struct iwn_tx_data *data = &ring->data[i]; if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; @@ -1481,6 +1479,10 @@ iwn_free_tx_ring(struct iwn_softc *sc, s if (data->map != NULL) bus_dmamap_destroy(ring->data_dmat, data->map); } + if (ring->data_dmat != NULL) { + bus_dma_tag_destroy(ring->data_dmat); + ring->data_dmat = NULL; + } } static void @@ -2095,8 +2097,7 @@ iwn_rx_done(struct iwn_softc *sc, struct return; } - /* XXX don't need mbuf, just dma buffer */ - m1 = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, MJUMPAGESIZE); + m1 = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, IWN_RBUF_SIZE); if (m1 == NULL) { DPRINTF(sc, IWN_DEBUG_ANY, "%s: no mbuf to restock ring\n", __func__); @@ -2105,13 +2106,24 @@ iwn_rx_done(struct iwn_softc *sc, struct } bus_dmamap_unload(ring->data_dmat, data->map); - error = bus_dmamap_load(ring->data_dmat, data->map, - mtod(m1, caddr_t), MJUMPAGESIZE, - iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); + error = bus_dmamap_load(ring->data_dmat, data->map, mtod(m1, void *), + IWN_RBUF_SIZE, iwn_dma_map_addr, &paddr, BUS_DMA_NOWAIT); if (error != 0 && error != EFBIG) { device_printf(sc->sc_dev, "%s: bus_dmamap_load failed, error %d\n", __func__, error); m_freem(m1); + + /* Try to reload the old mbuf. */ + error = bus_dmamap_load(ring->data_dmat, data->map, + mtod(data->m, void *), IWN_RBUF_SIZE, iwn_dma_map_addr, + &paddr, BUS_DMA_NOWAIT); + if (error != 0 && error != EFBIG) { + panic("%s: could not load old RX mbuf", __func__); + } + /* Physical address may have changed. */ + ring->desc[ring->cur] = htole32(paddr >> 8); + bus_dmamap_sync(ring->data_dmat, ring->desc_dma.map, + BUS_DMASYNC_PREWRITE); ifp->if_ierrors++; return; } @@ -2187,6 +2199,8 @@ iwn_rx_compressed_ba(struct iwn_softc *s struct iwn_compressed_ba *ba = (struct iwn_compressed_ba *)(desc + 1); struct iwn_tx_ring *txq; + bus_dmamap_sync(sc->rxq.data_dmat, data->map, BUS_DMASYNC_POSTREAD); + txq = &sc->txq[letoh16(ba->qid)]; /* XXX TBD */ } @@ -2437,6 +2451,8 @@ iwn_cmd_done(struct iwn_softc *sc, struc /* If the command was mapped in an mbuf, free it. */ if (data->m != NULL) { + bus_dmamap_sync(ring->data_dmat, data->map, + BUS_DMASYNC_POSTWRITE); bus_dmamap_unload(ring->data_dmat, data->map); m_freem(data->m); data->m = NULL; @@ -2898,8 +2914,8 @@ iwn_tx_data(struct iwn_softc *sc, struct struct iwn_cmd_data *tx; struct ieee80211_frame *wh; struct ieee80211_key *k = NULL; - struct mbuf *mnew; - bus_dma_segment_t segs[IWN_MAX_SCATTER]; + struct mbuf *m1; + bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER]; uint32_t flags; u_int hdrlen; int totlen, error, pad, nsegs = 0, i, rate; @@ -3055,26 +3071,30 @@ iwn_tx_data(struct iwn_softc *sc, struct tx->security = 0; tx->flags = htole32(flags); - if (m->m_len > 0) { - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, - m, segs, &nsegs, BUS_DMA_NOWAIT); - if (error == EFBIG) { - /* too many fragments, linearize */ - mnew = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER); - if (mnew == NULL) { - device_printf(sc->sc_dev, - "%s: could not defrag mbuf\n", __func__); - m_freem(m); - return ENOBUFS; - } - m = mnew; - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, - data->map, m, segs, &nsegs, BUS_DMA_NOWAIT); + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs, + &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + if (error != EFBIG) { + device_printf(sc->sc_dev, + "%s: can't map mbuf (error %d)\n", __func__, error); + m_freem(m); + return error; } + /* Too many DMA segments, linearize mbuf. */ + m1 = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER); + if (m1 == NULL) { + device_printf(sc->sc_dev, + "%s: could not defrag mbuf\n", __func__); + m_freem(m); + return ENOBUFS; + } + m = m1; + + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, + segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, - "%s: bus_dmamap_load_mbuf_sg failed, error %d\n", - __func__, error); + "%s: can't map mbuf (error %d)\n", __func__, error); m_freem(m); return error; } @@ -3087,16 +3107,20 @@ iwn_tx_data(struct iwn_softc *sc, struct __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs); /* Fill TX descriptor. */ - desc->nsegs = 1 + nsegs; + desc->nsegs = 1; + if (m->m_len != 0) + desc->nsegs += nsegs; /* First DMA segment is used by the TX command. */ desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr)); desc->segs[0].len = htole16(IWN_HIADDR(data->cmd_paddr) | (4 + sizeof (*tx) + hdrlen + pad) << 4); /* Other DMA segments are for data payload. */ + seg = &segs[0]; for (i = 1; i <= nsegs; i++) { - desc->segs[i].addr = htole32(IWN_LOADDR(segs[i - 1].ds_addr)); - desc->segs[i].len = htole16(IWN_HIADDR(segs[i - 1].ds_addr) | - segs[i - 1].ds_len << 4); + desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr)); + desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) | + seg->ds_len << 4); + seg++; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE); @@ -3136,9 +3160,8 @@ iwn_tx_data_raw(struct iwn_softc *sc, st struct ieee80211_frame *wh; struct iwn_tx_desc *desc; struct iwn_tx_data *data; - struct mbuf *mnew; - bus_addr_t paddr; - bus_dma_segment_t segs[IWN_MAX_SCATTER]; + struct mbuf *m1; + bus_dma_segment_t *seg, segs[IWN_MAX_SCATTER]; uint32_t flags; u_int hdrlen; int totlen, error, pad, nsegs = 0, i, rate; @@ -3238,9 +3261,8 @@ iwn_tx_data_raw(struct iwn_softc *sc, st txant = IWN_LSB(sc->txchainmask); tx->rflags |= IWN_RFLAG_ANT(txant); /* Set physical address of "scratch area". */ - paddr = ring->cmd_dma.paddr + ring->cur * sizeof (struct iwn_tx_cmd); - tx->loaddr = htole32(IWN_LOADDR(paddr)); - tx->hiaddr = IWN_HIADDR(paddr); + tx->loaddr = htole32(IWN_LOADDR(data->scratch_paddr)); + tx->hiaddr = IWN_HIADDR(data->scratch_paddr); /* Copy 802.11 header in TX command. */ memcpy((uint8_t *)(tx + 1), wh, hdrlen); @@ -3250,26 +3272,30 @@ iwn_tx_data_raw(struct iwn_softc *sc, st tx->security = 0; tx->flags = htole32(flags); - if (m->m_len > 0) { - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, - m, segs, &nsegs, BUS_DMA_NOWAIT); - if (error == EFBIG) { - /* Too many fragments, linearize. */ - mnew = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER); - if (mnew == NULL) { - device_printf(sc->sc_dev, - "%s: could not defrag mbuf\n", __func__); - m_freem(m); - return ENOBUFS; - } - m = mnew; - error = bus_dmamap_load_mbuf_sg(ring->data_dmat, - data->map, m, segs, &nsegs, BUS_DMA_NOWAIT); + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, segs, + &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + if (error != EFBIG) { + device_printf(sc->sc_dev, + "%s: can't map mbuf (error %d)\n", __func__, error); + m_freem(m); + return error; } + /* Too many DMA segments, linearize mbuf. */ + m1 = m_collapse(m, M_DONTWAIT, IWN_MAX_SCATTER); + if (m1 == NULL) { + device_printf(sc->sc_dev, + "%s: could not defrag mbuf\n", __func__); + m_freem(m); + return ENOBUFS; + } + m = m1; + + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, data->map, m, + segs, &nsegs, BUS_DMA_NOWAIT); if (error != 0) { device_printf(sc->sc_dev, - "%s: bus_dmamap_load_mbuf_sg failed, error %d\n", - __func__, error); + "%s: can't map mbuf (error %d)\n", __func__, error); m_freem(m); return error; } @@ -3282,16 +3308,20 @@ iwn_tx_data_raw(struct iwn_softc *sc, st __func__, ring->qid, ring->cur, m->m_pkthdr.len, nsegs); /* Fill TX descriptor. */ - desc->nsegs = 1 + nsegs; + desc->nsegs = 1; + if (m->m_len != 0) + desc->nsegs += nsegs; /* First DMA segment is used by the TX command. */ desc->segs[0].addr = htole32(IWN_LOADDR(data->cmd_paddr)); desc->segs[0].len = htole16(IWN_HIADDR(data->cmd_paddr) | (4 + sizeof (*tx) + hdrlen + pad) << 4); /* Other DMA segments are for data payload. */ + seg = &segs[0]; for (i = 1; i <= nsegs; i++) { - desc->segs[i].addr = htole32(IWN_LOADDR(segs[i - 1].ds_addr)); - desc->segs[i].len = htole16(IWN_HIADDR(segs[i - 1].ds_addr) | - segs[i - 1].ds_len << 4); + desc->segs[i].addr = htole32(IWN_LOADDR(seg->ds_addr)); + desc->segs[i].len = htole16(IWN_HIADDR(seg->ds_addr) | + seg->ds_len << 4); + seg++; } bus_dmamap_sync(ring->data_dmat, data->map, BUS_DMASYNC_PREWRITE);