diff options
author | adrian <adrian@FreeBSD.org> | 2016-06-23 01:13:30 +0000 |
---|---|---|
committer | adrian <adrian@FreeBSD.org> | 2016-06-23 01:13:30 +0000 |
commit | 64fe91633444a848d22f78d0115894d1855600d5 (patch) | |
tree | 3e9273f8f62f30d3ef93c2effbc2effe3a4871d9 | |
parent | a55236309fdf7ebb707e4ca2b66827ce83be2a69 (diff) | |
download | FreeBSD-src-64fe91633444a848d22f78d0115894d1855600d5.zip FreeBSD-src-64fe91633444a848d22f78d0115894d1855600d5.tar.gz |
[iwm] Use mbuf for large firmware commands, like OpenBSD does.
We also need to consider the size of large firmware commands in iwm_alloc_tx_ring(),
in the dma tag creation, when qid == IWM_MVM_CMD_QUEUE. The old code apparently
only allocated a 2KB (MCLBYTES) sized buffer when it actually expected 4KB.
Submitted by: Imre Vadasz <imre@vdsz.com>
Approved by: re (gjb)
Differential Revision: https://reviews.freebsd.org/D6824
-rw-r--r-- | sys/dev/iwm/if_iwm.c | 15 | ||||
-rw-r--r-- | sys/dev/iwm/if_iwm_util.c | 43 |
2 files changed, 33 insertions, 25 deletions
diff --git a/sys/dev/iwm/if_iwm.c b/sys/dev/iwm/if_iwm.c index dbd1eef..87db721 100644 --- a/sys/dev/iwm/if_iwm.c +++ b/sys/dev/iwm/if_iwm.c @@ -956,6 +956,8 @@ iwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid) { bus_addr_t paddr; bus_size_t size; + size_t maxsize; + int nsegments; int i, error; ring->qid = qid; @@ -988,9 +990,18 @@ iwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid) } ring->cmd = ring->cmd_dma.vaddr; + /* FW commands may require more mapped space than packets. */ + if (qid == IWM_MVM_CMD_QUEUE) { + maxsize = IWM_RBUF_SIZE; + nsegments = 1; + } else { + maxsize = MCLBYTES; + nsegments = IWM_MAX_SCATTER - 2; + } + error = bus_dma_tag_create(sc->sc_dmat, 1, 0, - BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, MCLBYTES, - IWM_MAX_SCATTER - 2, MCLBYTES, 0, NULL, NULL, &ring->data_dmat); + BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL, maxsize, + nsegments, maxsize, 0, NULL, NULL, &ring->data_dmat); if (error != 0) { device_printf(sc->sc_dev, "could not create TX buf DMA tag\n"); goto fail; diff --git a/sys/dev/iwm/if_iwm_util.c b/sys/dev/iwm/if_iwm_util.c index 99002a1..0f0aef8 100644 --- a/sys/dev/iwm/if_iwm_util.c +++ b/sys/dev/iwm/if_iwm_util.c @@ -157,19 +157,6 @@ __FBSDID("$FreeBSD$"); #include <dev/iwm/if_iwm_util.h> #include <dev/iwm/if_iwm_pcie_trans.h> -static void -iwm_dma_map_mem(void *arg, bus_dma_segment_t *segs, int nsegs, int error) -{ - if (error != 0) - return; - KASSERT(nsegs <= 2, ("too many DMA segments, %d should be <= 2", - nsegs)); - if (nsegs > 1) - KASSERT(segs[1].ds_addr == segs[0].ds_addr + segs[0].ds_len, - ("fragmented DMA memory")); - *(bus_addr_t *)arg = segs[0].ds_addr; -} - /* * Send a command to the firmware. We try to implement the Linux * driver interface for the routine. @@ -183,12 +170,15 @@ iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) struct iwm_tx_ring *ring = &sc->txq[IWM_MVM_CMD_QUEUE]; struct iwm_tfd *desc; struct iwm_tx_data *data; - struct iwm_device_cmd *cmd = NULL; + struct iwm_device_cmd *cmd; + struct mbuf *m; + bus_dma_segment_t seg; bus_addr_t paddr; uint32_t addr_lo; int error = 0, i, paylen, off; int code; int async, wantresp; + int nsegs; code = hcmd->id; async = hcmd->flags & IWM_CMD_ASYNC; @@ -231,15 +221,24 @@ iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) error = EINVAL; goto out; } - error = bus_dmamem_alloc(ring->data_dmat, (void **)&cmd, - BUS_DMA_NOWAIT | BUS_DMA_COHERENT, &data->map); - if (error != 0) + m = m_getjcl(M_NOWAIT, MT_DATA, M_PKTHDR, IWM_RBUF_SIZE); + if (m == NULL) { + error = ENOBUFS; goto out; - error = bus_dmamap_load(ring->data_dmat, data->map, - cmd, paylen + sizeof(cmd->hdr), iwm_dma_map_mem, - &paddr, BUS_DMA_NOWAIT); - if (error != 0) + } + + m->m_len = m->m_pkthdr.len = m->m_ext.ext_size; + error = bus_dmamap_load_mbuf_sg(ring->data_dmat, + data->map, m, &seg, &nsegs, BUS_DMA_NOWAIT); + if (error != 0) { + device_printf(sc->sc_dev, + "%s: can't map mbuf, error %d\n", __func__, error); + m_freem(m); goto out; + } + data->m = m; /* mbuf will be freed in iwm_cmd_done() */ + cmd = mtod(m, struct iwm_device_cmd *); + paddr = seg.ds_addr; } else { cmd = &ring->cmd[ring->cur]; paddr = data->cmd_paddr; @@ -319,8 +318,6 @@ iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) } } out: - if (cmd && paylen > sizeof(cmd->data)) - bus_dmamem_free(ring->data_dmat, cmd, data->map); if (wantresp && error != 0) { iwm_free_resp(sc, hcmd); } |