[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
This commit is contained in:
Adrian Chadd 2016-06-23 01:13:30 +00:00
parent 8a1660e16a
commit 1eccf20376
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=302104
2 changed files with 33 additions and 25 deletions

View File

@ -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;

View File

@ -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);
}