Fixes in the iwi(4) driver to better handle errors:
- bus_dmamap fixes, with checks against NULL - move the rings' allocation code later in iwi_attach() to avoid NULL pointer dereference if allocation fails - avoid double free() in case of failure during attach. If an allocation fails, do not free the ring directly, as it is handled by iwi_detach() - only set ring->count for RX/TX rings when ring allocation is successful, or else the for loop during detach will fail with a NULL dereference. - call pci_intr_disestablish() if iwi_reset() fails during attach; driver is in an incoherent state, interrupt handler should not be used. Proposed by me on tech-net@, approved by christos@. Compiled and tested with GENERIC and XEN3_DOM0 kernels.
This commit is contained in:
parent
1894a7d27b
commit
96f9080096
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: if_iwi.c,v 1.79 2009/02/13 21:11:47 bouyer Exp $ */
|
||||
/* $NetBSD: if_iwi.c,v 1.80 2009/03/13 21:57:07 jym Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2004, 2005
|
||||
@ -28,7 +28,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: if_iwi.c,v 1.79 2009/02/13 21:11:47 bouyer Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: if_iwi.c,v 1.80 2009/03/13 21:57:07 jym Exp $");
|
||||
|
||||
/*-
|
||||
* Intel(R) PRO/Wireless 2200BG/2225BG/2915ABG driver
|
||||
@ -278,51 +278,11 @@ iwi_attach(device_t parent, device_t self, void *aux)
|
||||
aprint_normal_dev(self, "interrupting at %s\n", intrstr);
|
||||
|
||||
if (iwi_reset(sc) != 0) {
|
||||
pci_intr_disestablish(sc->sc_pct, sc->sc_ih);
|
||||
aprint_error_dev(self, "could not reset adapter\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate rings.
|
||||
*/
|
||||
if (iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT) != 0) {
|
||||
aprint_error_dev(self, "could not allocate command ring\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error = iwi_alloc_tx_ring(sc, &sc->txq[0], IWI_TX_RING_COUNT,
|
||||
IWI_CSR_TX1_RIDX, IWI_CSR_TX1_WIDX);
|
||||
if (error != 0) {
|
||||
aprint_error_dev(self, "could not allocate Tx ring 1\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error = iwi_alloc_tx_ring(sc, &sc->txq[1], IWI_TX_RING_COUNT,
|
||||
IWI_CSR_TX2_RIDX, IWI_CSR_TX2_WIDX);
|
||||
if (error != 0) {
|
||||
aprint_error_dev(self, "could not allocate Tx ring 2\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error = iwi_alloc_tx_ring(sc, &sc->txq[2], IWI_TX_RING_COUNT,
|
||||
IWI_CSR_TX3_RIDX, IWI_CSR_TX3_WIDX);
|
||||
if (error != 0) {
|
||||
aprint_error_dev(self, "could not allocate Tx ring 3\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error = iwi_alloc_tx_ring(sc, &sc->txq[3], IWI_TX_RING_COUNT,
|
||||
IWI_CSR_TX4_RIDX, IWI_CSR_TX4_WIDX);
|
||||
if (error != 0) {
|
||||
aprint_error_dev(self, "could not allocate Tx ring 4\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) {
|
||||
aprint_error_dev(self, "could not allocate Rx ring\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ic->ic_ifp = ifp;
|
||||
ic->ic_wme.wme_update = iwi_wme_update;
|
||||
ic->ic_phytype = IEEE80211_T_OFDM; /* not only, but not used */
|
||||
@ -413,6 +373,47 @@ iwi_attach(device_t parent, device_t self, void *aux)
|
||||
ic->ic_newstate = iwi_newstate;
|
||||
ieee80211_media_init(ic, iwi_media_change, iwi_media_status);
|
||||
|
||||
/*
|
||||
* Allocate rings.
|
||||
*/
|
||||
if (iwi_alloc_cmd_ring(sc, &sc->cmdq, IWI_CMD_RING_COUNT) != 0) {
|
||||
aprint_error_dev(self, "could not allocate command ring\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error = iwi_alloc_tx_ring(sc, &sc->txq[0], IWI_TX_RING_COUNT,
|
||||
IWI_CSR_TX1_RIDX, IWI_CSR_TX1_WIDX);
|
||||
if (error != 0) {
|
||||
aprint_error_dev(self, "could not allocate Tx ring 1\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error = iwi_alloc_tx_ring(sc, &sc->txq[1], IWI_TX_RING_COUNT,
|
||||
IWI_CSR_TX2_RIDX, IWI_CSR_TX2_WIDX);
|
||||
if (error != 0) {
|
||||
aprint_error_dev(self, "could not allocate Tx ring 2\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error = iwi_alloc_tx_ring(sc, &sc->txq[2], IWI_TX_RING_COUNT,
|
||||
IWI_CSR_TX3_RIDX, IWI_CSR_TX3_WIDX);
|
||||
if (error != 0) {
|
||||
aprint_error_dev(self, "could not allocate Tx ring 3\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
error = iwi_alloc_tx_ring(sc, &sc->txq[3], IWI_TX_RING_COUNT,
|
||||
IWI_CSR_TX4_RIDX, IWI_CSR_TX4_WIDX);
|
||||
if (error != 0) {
|
||||
aprint_error_dev(self, "could not allocate Tx ring 4\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (iwi_alloc_rx_ring(sc, &sc->rxq, IWI_RX_RING_COUNT) != 0) {
|
||||
aprint_error_dev(self, "could not allocate Rx ring\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#if NBPFILTER > 0
|
||||
bpfattach2(ifp, DLT_IEEE802_11_RADIO,
|
||||
sizeof (struct ieee80211_frame) + 64, &sc->sc_drvbpf);
|
||||
@ -494,6 +495,7 @@ iwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring,
|
||||
if (error != 0) {
|
||||
aprint_error_dev(sc->sc_dev,
|
||||
"could not create command ring DMA map\n");
|
||||
ring->desc_map = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -529,8 +531,7 @@ iwi_alloc_cmd_ring(struct iwi_softc *sc, struct iwi_cmd_ring *ring,
|
||||
|
||||
return 0;
|
||||
|
||||
fail: iwi_free_cmd_ring(sc, ring);
|
||||
return error;
|
||||
fail: return error;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -571,7 +572,7 @@ iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring,
|
||||
{
|
||||
int i, error, nsegs;
|
||||
|
||||
ring->count = count;
|
||||
ring->count = 0;
|
||||
ring->queued = 0;
|
||||
ring->cur = ring->next = 0;
|
||||
ring->csr_ridx = csr_ridx;
|
||||
@ -587,6 +588,7 @@ iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring,
|
||||
if (error != 0) {
|
||||
aprint_error_dev(sc->sc_dev,
|
||||
"could not create tx ring DMA map\n");
|
||||
ring->desc_map = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -626,6 +628,7 @@ iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring,
|
||||
error = ENOMEM;
|
||||
goto fail;
|
||||
}
|
||||
ring->count = count;
|
||||
|
||||
/*
|
||||
* Allocate Tx buffers DMA maps
|
||||
@ -636,13 +639,13 @@ iwi_alloc_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring,
|
||||
if (error != 0) {
|
||||
aprint_error_dev(sc->sc_dev,
|
||||
"could not create tx buf DMA map");
|
||||
ring->data[i].map = NULL;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
|
||||
fail: iwi_free_tx_ring(sc, ring);
|
||||
return error;
|
||||
fail: return error;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -655,11 +658,14 @@ iwi_reset_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
|
||||
data = &ring->data[i];
|
||||
|
||||
if (data->m != NULL) {
|
||||
m_freem(data->m);
|
||||
data->m = NULL;
|
||||
}
|
||||
|
||||
if (data->map != NULL) {
|
||||
bus_dmamap_sync(sc->sc_dmat, data->map, 0,
|
||||
data->map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
|
||||
bus_dmamap_unload(sc->sc_dmat, data->map);
|
||||
m_freem(data->m);
|
||||
data->m = NULL;
|
||||
}
|
||||
|
||||
if (data->ni != NULL) {
|
||||
@ -676,6 +682,7 @@ static void
|
||||
iwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
|
||||
{
|
||||
int i;
|
||||
struct iwi_tx_data *data;
|
||||
|
||||
if (ring->desc_map != NULL) {
|
||||
if (ring->desc != NULL) {
|
||||
@ -688,11 +695,16 @@ iwi_free_tx_ring(struct iwi_softc *sc, struct iwi_tx_ring *ring)
|
||||
}
|
||||
|
||||
for (i = 0; i < ring->count; i++) {
|
||||
if (ring->data[i].m != NULL) {
|
||||
bus_dmamap_unload(sc->sc_dmat, ring->data[i].map);
|
||||
m_freem(ring->data[i].m);
|
||||
data = &ring->data[i];
|
||||
|
||||
if (data->m != NULL) {
|
||||
m_freem(data->m);
|
||||
}
|
||||
|
||||
if (data->map != NULL) {
|
||||
bus_dmamap_unload(sc->sc_dmat, data->map);
|
||||
bus_dmamap_destroy(sc->sc_dmat, data->map);
|
||||
}
|
||||
bus_dmamap_destroy(sc->sc_dmat, ring->data[i].map);
|
||||
}
|
||||
}
|
||||
|
||||
@ -701,7 +713,7 @@ iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count)
|
||||
{
|
||||
int i, error;
|
||||
|
||||
ring->count = count;
|
||||
ring->count = 0;
|
||||
ring->cur = 0;
|
||||
|
||||
ring->data = malloc(count * sizeof (struct iwi_rx_data), M_DEVBUF,
|
||||
@ -712,6 +724,8 @@ iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
ring->count = count;
|
||||
|
||||
/*
|
||||
* Allocate and map Rx buffers
|
||||
*/
|
||||
@ -722,6 +736,7 @@ iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count)
|
||||
if (error != 0) {
|
||||
aprint_error_dev(sc->sc_dev,
|
||||
"could not create rx buf DMA map");
|
||||
ring->data[i].map = NULL;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
@ -744,8 +759,7 @@ iwi_alloc_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring, int count)
|
||||
|
||||
return 0;
|
||||
|
||||
fail: iwi_free_rx_ring(sc, ring);
|
||||
return error;
|
||||
fail: return error;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -758,13 +772,20 @@ static void
|
||||
iwi_free_rx_ring(struct iwi_softc *sc, struct iwi_rx_ring *ring)
|
||||
{
|
||||
int i;
|
||||
struct iwi_rx_data *data;
|
||||
|
||||
for (i = 0; i < ring->count; i++) {
|
||||
if (ring->data[i].m != NULL) {
|
||||
bus_dmamap_unload(sc->sc_dmat, ring->data[i].map);
|
||||
m_freem(ring->data[i].m);
|
||||
data = &ring->data[i];
|
||||
|
||||
if (data->m != NULL) {
|
||||
m_freem(data->m);
|
||||
}
|
||||
bus_dmamap_destroy(sc->sc_dmat, ring->data[i].map);
|
||||
|
||||
if (data->map != NULL) {
|
||||
bus_dmamap_unload(sc->sc_dmat, data->map);
|
||||
bus_dmamap_destroy(sc->sc_dmat, data->map);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -2028,6 +2049,7 @@ iwi_load_firmware(struct iwi_softc *sc, void *fw, int size)
|
||||
if (error != 0) {
|
||||
aprint_error_dev(sc->sc_dev,
|
||||
"could not create firmware DMA map\n");
|
||||
map = NULL;
|
||||
goto fail1;
|
||||
}
|
||||
|
||||
@ -2137,7 +2159,8 @@ fail3:
|
||||
bus_dmamap_sync(sc->sc_dmat, map, 0, size, BUS_DMASYNC_POSTWRITE);
|
||||
bus_dmamap_unload(sc->sc_dmat, map);
|
||||
fail2:
|
||||
bus_dmamap_destroy(sc->sc_dmat, map);
|
||||
if (map != NULL)
|
||||
bus_dmamap_destroy(sc->sc_dmat, map);
|
||||
|
||||
fail1:
|
||||
return error;
|
||||
|
Loading…
Reference in New Issue
Block a user