summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoradrian <adrian@FreeBSD.org>2016-06-23 01:13:30 +0000
committeradrian <adrian@FreeBSD.org>2016-06-23 01:13:30 +0000
commit64fe91633444a848d22f78d0115894d1855600d5 (patch)
tree3e9273f8f62f30d3ef93c2effbc2effe3a4871d9
parenta55236309fdf7ebb707e4ca2b66827ce83be2a69 (diff)
downloadFreeBSD-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.c15
-rw-r--r--sys/dev/iwm/if_iwm_util.c43
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);
}
OpenPOWER on IntegriCloud