Hi,
This patch improves the error handling iwm_rx_addbuf(), specifically in
out-of-memory and mbuf exhaustion cases.

Keep an additional/spare bus_dmamap_t object around to make error handling
for bus_dmamap_load_mbuf() failures easier in iwm_rx_addbuf().
This seems like an easy way to avoid having weird panic() cases in the code
(like in iwn, which then tries to bus_dmamap_load_mbuf() the old mbuf and
panics when that fails).

Move mbuf modifications that are needed before calling ieee80211_input()
in iwm_rx_rx_mpdu() to below the iwm_rx_addbuf call. Otherwise we can end
up with a broken mbuf in the rx ring when iwm_rx_rx_mpdu() aborts.

Index: sys/dev/pci/if_iwm.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v
retrieving revision 1.142
diff -u -r1.142 if_iwm.c
--- sys/dev/pci/if_iwm.c        27 Sep 2016 15:54:33 -0000      1.142
+++ sys/dev/pci/if_iwm.c        6 Oct 2016 08:38:18 -0000
@@ -1009,6 +1009,18 @@
        }
        ring->stat = ring->stat_dma.vaddr;
 
+       /*
+        * Allocate one more bus_dmamap_t for iwm_rx_addbuf() usage from
+        * iwm_rx_rx_mpdu().
+        */
+       err = bus_dmamap_create(sc->sc_dmat, IWM_RBUF_SIZE, 1,
+           IWM_RBUF_SIZE, 0, BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
+           &ring->spare_map);
+       if (err) {
+               printf("%s: could not create RX buf DMA map\n",
+                   DEVNAME(sc));
+               goto fail;
+       }
        for (i = 0; i < IWM_RX_RING_COUNT; i++) {
                struct iwm_rx_data *data = &ring->data[i];
 
@@ -1069,6 +1081,10 @@
        iwm_dma_contig_free(&ring->desc_dma);
        iwm_dma_contig_free(&ring->stat_dma);
 
+       if (ring->spare_map != NULL) {
+               bus_dmamap_destroy(sc->sc_dmat, ring->spare_map);
+               ring->spare_map = NULL;
+       }
        for (i = 0; i < IWM_RX_RING_COUNT; i++) {
                struct iwm_rx_data *data = &ring->data[i];
 
@@ -3116,8 +3132,8 @@
        struct iwm_rx_ring *ring = &sc->rxq;
        struct iwm_rx_data *data = &ring->data[idx];
        struct mbuf *m;
+       bus_dmamap_t dmamap;
        int err;
-       int fatal = 0;
 
        m = m_gethdr(M_DONTWAIT, MT_DATA);
        if (m == NULL)
@@ -3133,21 +3149,23 @@
                return ENOBUFS;
        }
 
-       if (data->m != NULL) {
-               bus_dmamap_unload(sc->sc_dmat, data->map);
-               fatal = 1;
-       }
-
        m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
-       err = bus_dmamap_load_mbuf(sc->sc_dmat, data->map, m,
+       err = bus_dmamap_load_mbuf(sc->sc_dmat, ring->spare_map, m,
            BUS_DMA_READ|BUS_DMA_NOWAIT);
        if (err) {
-               /* XXX */
-               if (fatal)
-                       panic("iwm: could not load RX mbuf");
+               printf("%s: could not load RX mbuf\n", DEVNAME(sc));
                m_freem(m);
                return err;
        }
+
+       if (data->m != NULL)
+               bus_dmamap_unload(sc->sc_dmat, data->map);
+
+       /* Swap ring->spare_map with data->map */
+       dmamap = data->map;
+       data->map = ring->spare_map;
+       ring->spare_map = dmamap;
+
        data->m = m;
        bus_dmamap_sync(sc->sc_dmat, data->map, 0, size, BUS_DMASYNC_PREREAD);
 
@@ -3255,7 +3273,7 @@
        struct ieee80211_node *ni;
        struct ieee80211_channel *c = NULL;
        struct ieee80211_rxinfo rxi;
-       struct mbuf *m;
+       struct mbuf *m = data->m;
        struct iwm_rx_phy_info *phy_info;
        struct iwm_rx_mpdu_res_start *rx_res;
        int device_timestamp;
@@ -3273,10 +3291,6 @@
        rx_pkt_status = le32toh(*(uint32_t *)(pkt->data +
            sizeof(*rx_res) + len));
 
-       m = data->m;
-       m->m_data = pkt->data + sizeof(*rx_res);
-       m->m_pkthdr.len = m->m_len = len;
-
        if (__predict_false(phy_info->cfg_phy_cnt > 20))
                return;
 
@@ -3296,6 +3310,9 @@
 
        if (iwm_rx_addbuf(sc, IWM_RBUF_SIZE, sc->rxq.cur) != 0)
                return;
+
+       m->m_data = pkt->data + sizeof(*rx_res);
+       m->m_pkthdr.len = m->m_len = len;
 
        if (le32toh(phy_info->channel) < nitems(ic->ic_channels))
                c = &ic->ic_channels[le32toh(phy_info->channel)];
Index: sys/dev/pci/if_iwmvar.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwmvar.h,v
retrieving revision 1.24
diff -u -r1.24 if_iwmvar.h
--- sys/dev/pci/if_iwmvar.h     21 Sep 2016 13:53:18 -0000      1.24
+++ sys/dev/pci/if_iwmvar.h     6 Oct 2016 08:38:18 -0000
@@ -276,6 +276,7 @@
        uint32_t                *desc;
        struct iwm_rb_status    *stat;
        struct iwm_rx_data      data[IWM_RX_RING_COUNT];
+       bus_dmamap_t            spare_map;
        int                     cur;
 };
 

Reply via email to