From c57b0108af9f32d015af8be9381bfa1f11b8150a Mon Sep 17 00:00:00 2001 From: stsp Date: Fri, 16 Oct 2015 10:04:56 +0000 Subject: [PATCH] In iwm(4), correctly size and map the mbuf used for large firmware commands. Fixes occasional firmware errors while bringing the interface up or scanning. ok phessler@ --- sys/dev/pci/if_iwm.c | 44 ++++++++++++++++++++++++----------------- sys/dev/pci/if_iwmreg.h | 3 ++- 2 files changed, 28 insertions(+), 19 deletions(-) diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c index a6aa84dfca3..3835246c33a 100644 --- a/sys/dev/pci/if_iwm.c +++ b/sys/dev/pci/if_iwm.c @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwm.c,v 1.56 2015/10/12 10:01:27 stsp Exp $ */ +/* $OpenBSD: if_iwm.c,v 1.57 2015/10/16 10:04:56 stsp Exp $ */ /* * Copyright (c) 2014 genua mbh @@ -1108,14 +1108,21 @@ iwm_alloc_tx_ring(struct iwm_softc *sc, struct iwm_tx_ring *ring, int qid) paddr = ring->cmd_dma.paddr; for (i = 0; i < IWM_TX_RING_COUNT; i++) { struct iwm_tx_data *data = &ring->data[i]; + size_t mapsize; data->cmd_paddr = paddr; data->scratch_paddr = paddr + sizeof(struct iwm_cmd_header) + offsetof(struct iwm_tx_cmd, scratch); paddr += sizeof(struct iwm_device_cmd); - error = bus_dmamap_create(sc->sc_dmat, MCLBYTES, - IWM_NUM_OF_TBS - 2, MCLBYTES, 0, BUS_DMA_NOWAIT, + /* FW commands may require more mapped space than packets. */ + if (qid == IWM_MVM_CMD_QUEUE) + mapsize = (sizeof(struct iwm_cmd_header) + + IWM_MAX_CMD_PAYLOAD_SIZE); + else + mapsize = MCLBYTES; + error = bus_dmamap_create(sc->sc_dmat, mapsize, + IWM_NUM_OF_TBS - 2, mapsize, 0, BUS_DMA_NOWAIT, &data->map); if (error != 0) { printf("%s: could not create TX buf DMA map\n", DEVNAME(sc)); @@ -3393,30 +3400,31 @@ iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) data = &ring->data[ring->cur]; if (paylen > sizeof(cmd->data)) { - /* Command is too large */ - if (sizeof(cmd->hdr) + paylen > IWM_RBUF_SIZE) { + /* Command is too large to fit in pre-allocated space. */ + size_t totlen = sizeof(cmd->hdr) + paylen; + if (paylen > IWM_MAX_CMD_PAYLOAD_SIZE) { + printf("%s: firmware command too long (%zd bytes)\n", + DEVNAME(sc), totlen); error = EINVAL; goto out; } - m = m_gethdr(M_DONTWAIT, MT_DATA); + m = MCLGETI(NULL, M_DONTWAIT, NLL, totlen); if (m == NULL) { - error = ENOMEM; - goto out; - } - MCLGETI(m, M_DONTWAIT, NULL, IWM_RBUF_SIZE); - if (!(m->m_flags & M_EXT)) { - m_freem(m); + printf("%s: could not get fw cmd mbuf (%zd bytes)\n", + DEVNAME(sc), totlen); error = ENOMEM; goto out; } cmd = mtod(m, struct iwm_device_cmd *); error = bus_dmamap_load(sc->sc_dmat, data->map, cmd, - hcmd->len[0], NULL, BUS_DMA_NOWAIT | BUS_DMA_WRITE); + totlen, NULL, BUS_DMA_NOWAIT | BUS_DMA_WRITE); if (error != 0) { + printf("%s: could not load fw cmd mbuf (%zd bytes)\n", + DEVNAME(sc), totlen); m_freem(m); goto out; } - data->m = m; + data->m = m; /* mbuf will be freed in iwm_cmd_done() */ paddr = data->map->dm_segs[0].ds_addr; } else { cmd = &ring->cmd[ring->cur]; @@ -3447,13 +3455,13 @@ iwm_send_cmd(struct iwm_softc *sc, struct iwm_host_cmd *hcmd) code, hcmd->len[0] + hcmd->len[1] + sizeof(cmd->hdr), async ? " (async)" : "")); - if (hcmd->len[0] > sizeof(cmd->data)) { - bus_dmamap_sync(sc->sc_dmat, data->map, 0, hcmd->len[0], - BUS_DMASYNC_PREWRITE); + if (paylen > sizeof(cmd->data)) { + bus_dmamap_sync(sc->sc_dmat, data->map, 0, + sizeof(cmd->hdr) + paylen, BUS_DMASYNC_PREWRITE); } else { bus_dmamap_sync(sc->sc_dmat, ring->cmd_dma.map, (char *)(void *)cmd - (char *)(void *)ring->cmd_dma.vaddr, - hcmd->len[0] + 4, BUS_DMASYNC_PREWRITE); + sizeof(cmd->hdr) + hcmd->len[0], BUS_DMASYNC_PREWRITE); } bus_dmamap_sync(sc->sc_dmat, ring->desc_dma.map, (char *)(void *)desc - (char *)(void *)ring->desc_dma.vaddr, diff --git a/sys/dev/pci/if_iwmreg.h b/sys/dev/pci/if_iwmreg.h index cfa39b12952..b688d6f681c 100644 --- a/sys/dev/pci/if_iwmreg.h +++ b/sys/dev/pci/if_iwmreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: if_iwmreg.h,v 1.4 2015/06/15 08:06:11 stsp Exp $ */ +/* $OpenBSD: if_iwmreg.h,v 1.5 2015/10/16 10:04:56 stsp Exp $ */ /****************************************************************************** * @@ -5141,6 +5141,7 @@ enum iwm_power_scheme { }; #define IWM_DEF_CMD_PAYLOAD_SIZE 320 +#define IWM_MAX_CMD_PAYLOAD_SIZE (4096 - sizeof(struct iwm_cmd_header)) #define IWM_CMD_FAILED_MSK 0x40 struct iwm_device_cmd { -- 2.20.1