summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordamien <damien@FreeBSD.org>2006-03-10 18:55:30 +0000
committerdamien <damien@FreeBSD.org>2006-03-10 18:55:30 +0000
commit25007ede444d0488571e3690e6cf9d655c8a089c (patch)
tree1dfcf78c6d2b62aadd7f8e8d704306a33a10f259
parent410473acf0d0463c5c7d38db972a1440064a3b95 (diff)
downloadFreeBSD-src-25007ede444d0488571e3690e6cf9d655c8a089c.zip
FreeBSD-src-25007ede444d0488571e3690e6cf9d655c8a089c.tar.gz
fix the locking.
-rw-r--r--sys/dev/iwi/if_iwi.c98
-rw-r--r--sys/dev/iwi/if_iwivar.h3
2 files changed, 73 insertions, 28 deletions
diff --git a/sys/dev/iwi/if_iwi.c b/sys/dev/iwi/if_iwi.c
index 5d4c7a5..2246409 100644
--- a/sys/dev/iwi/if_iwi.c
+++ b/sys/dev/iwi/if_iwi.c
@@ -151,7 +151,8 @@ static int iwi_scan(struct iwi_softc *);
static int iwi_auth_and_assoc(struct iwi_softc *);
static void iwi_init(void *);
static void iwi_stop(void *);
-static int iwi_read_firmware(const char *, caddr_t *, size_t *);
+static int iwi_read_firmware(struct iwi_softc *, const char *, caddr_t *,
+ size_t *);
static int iwi_sysctl_stats(SYSCTL_HANDLER_ARGS);
static int iwi_sysctl_radio(SYSCTL_HANDLER_ARGS);
@@ -810,7 +811,7 @@ iwi_resume(device_t dev)
struct iwi_softc *sc = device_get_softc(dev);
struct ifnet *ifp = sc->sc_ic.ic_ifp;
- IWI_LOCK(sc);
+ mtx_lock(&sc->sc_mtx);
pci_write_config(dev, 0x41, 0, 1);
@@ -820,7 +821,7 @@ iwi_resume(device_t dev)
ifp->if_start(ifp);
}
- IWI_UNLOCK(sc);
+ mtx_unlock(&sc->sc_mtx);
return 0;
}
@@ -858,18 +859,18 @@ iwi_media_change(struct ifnet *ifp)
struct iwi_softc *sc = ifp->if_softc;
int error;
- IWI_LOCK(sc);
+ mtx_lock(&sc->sc_mtx);
error = ieee80211_media_change(ifp);
if (error != ENETRESET) {
- IWI_UNLOCK(sc);
+ mtx_unlock(&sc->sc_mtx);
return error;
}
if ((ifp->if_flags & IFF_UP) && (ifp->if_drv_flags & IFF_DRV_RUNNING))
iwi_init(sc);
- IWI_UNLOCK(sc);
+ mtx_unlock(&sc->sc_mtx);
return 0;
}
@@ -1411,10 +1412,10 @@ iwi_intr(void *arg)
struct iwi_softc *sc = arg;
uint32_t r;
- IWI_LOCK(sc);
+ mtx_lock(&sc->sc_mtx);
if ((r = CSR_READ_4(sc, IWI_CSR_INTR)) == 0 || r == 0xffffffff) {
- IWI_UNLOCK(sc);
+ mtx_unlock(&sc->sc_mtx);
return;
}
@@ -1462,7 +1463,7 @@ iwi_intr(void *arg)
/* re-enable interrupts */
CSR_WRITE_4(sc, IWI_CSR_INTR_MASK, IWI_INTR_MASK);
- IWI_UNLOCK(sc);
+ mtx_unlock(&sc->sc_mtx);
}
static int
@@ -1659,10 +1660,10 @@ iwi_start(struct ifnet *ifp)
struct ieee80211_node *ni;
int ac;
- IWI_LOCK(sc);
+ mtx_lock(&sc->sc_mtx);
if (ic->ic_state != IEEE80211_S_RUN) {
- IWI_UNLOCK(sc);
+ mtx_unlock(&sc->sc_mtx);
return;
}
@@ -1725,7 +1726,7 @@ iwi_start(struct ifnet *ifp)
ifp->if_timer = 1;
}
- IWI_UNLOCK(sc);
+ mtx_unlock(&sc->sc_mtx);
}
static void
@@ -1734,7 +1735,7 @@ iwi_watchdog(struct ifnet *ifp)
struct iwi_softc *sc = ifp->if_softc;
struct ieee80211com *ic = &sc->sc_ic;
- IWI_LOCK(sc);
+ mtx_lock(&sc->sc_mtx);
ifp->if_timer = 0;
@@ -1744,7 +1745,7 @@ iwi_watchdog(struct ifnet *ifp)
ifp->if_oerrors++;
ifp->if_flags &= ~IFF_UP;
iwi_stop(sc);
- IWI_UNLOCK(sc);
+ mtx_unlock(&sc->sc_mtx);
return;
}
ifp->if_timer = 1;
@@ -1752,7 +1753,7 @@ iwi_watchdog(struct ifnet *ifp)
ieee80211_watchdog(ic);
- IWI_UNLOCK(sc);
+ mtx_unlock(&sc->sc_mtx);
}
static int
@@ -1762,7 +1763,7 @@ iwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
struct ieee80211com *ic = &sc->sc_ic;
int error = 0;
- IWI_LOCK(sc);
+ mtx_lock(&sc->sc_mtx);
switch (cmd) {
case SIOCSIFFLAGS:
@@ -1787,7 +1788,7 @@ iwi_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data)
error = 0;
}
- IWI_UNLOCK(sc);
+ mtx_unlock(&sc->sc_mtx);
return error;
}
@@ -1866,9 +1867,7 @@ iwi_load_ucode(struct iwi_softc *sc, const char *name)
size_t size;
int i, ntries, error;
- IWI_UNLOCK(sc);
- error = iwi_read_firmware(name, &uc, &size);
- IWI_LOCK(sc);
+ error = iwi_read_firmware(sc, name, &uc, &size);
if (error != 0) {
device_printf(sc->sc_dev, "could not read ucode image\n");
goto fail1;
@@ -1961,9 +1960,7 @@ iwi_load_firmware(struct iwi_softc *sc, const char *name)
int ntries, error;
/* read firmware image from filesystem */
- IWI_UNLOCK(sc);
- error = iwi_read_firmware(name, &fw, &size);
- IWI_LOCK(sc);
+ error = iwi_read_firmware(sc, name, &fw, &size);
if (error != 0) {
device_printf(sc->sc_dev, "could not read firmware image\n");
goto fail1;
@@ -1977,12 +1974,16 @@ iwi_load_firmware(struct iwi_softc *sc, const char *name)
fw += sizeof (struct iwi_firmware_hdr);
size -= sizeof (struct iwi_firmware_hdr);
+ /* drop the driver's lock before doing any DMA operation */
+ mtx_unlock(&sc->sc_mtx);
+
/* allocate DMA memory for mapping firmware image */
error = bus_dma_tag_create(NULL, 4, 0, BUS_SPACE_MAXADDR_32BIT,
BUS_SPACE_MAXADDR, NULL, NULL, size, 1, size, 0, NULL, NULL, &dmat);
if (error != 0) {
device_printf(sc->sc_dev,
"could not create firmware DMA tag\n");
+ mtx_lock(&sc->sc_mtx);
goto fail2;
}
@@ -1990,6 +1991,7 @@ iwi_load_firmware(struct iwi_softc *sc, const char *name)
if (error != 0) {
device_printf(sc->sc_dev,
"could not allocate firmware DMA memory\n");
+ mtx_lock(&sc->sc_mtx);
goto fail3;
}
@@ -1997,6 +1999,7 @@ iwi_load_firmware(struct iwi_softc *sc, const char *name)
&physaddr, 0);
if (error != 0) {
device_printf(sc->sc_dev, "could not load firmware DMA map\n");
+ mtx_lock(&sc->sc_mtx);
goto fail4;
}
@@ -2006,6 +2009,9 @@ iwi_load_firmware(struct iwi_softc *sc, const char *name)
/* make sure the adapter will get up-to-date values */
bus_dmamap_sync(dmat, map, BUS_DMASYNC_PREWRITE);
+ /* reacquire the driver's lock before kicking the hardware */
+ mtx_lock(&sc->sc_mtx);
+
/* tell the adapter where the command blocks are stored */
MEM_WRITE_4(sc, 0x3000a0, 0x27000);
@@ -2418,7 +2424,28 @@ iwi_init(void *priv)
struct ifnet *ifp = ic->ic_ifp;
struct iwi_rx_data *data;
const char *uc, *fw;
- int i;
+ int owned, i;
+
+ /*
+ * iwi_init() is exposed through ifp->if_init so it might be called
+ * without the driver's lock held. Since msleep() doesn't like
+ * being called on a recursed mutex, we acquire the driver's lock
+ * only if we're not already holding it.
+ */
+ if (!(owned = mtx_owned(&sc->sc_mtx)))
+ mtx_lock(&sc->sc_mtx);
+
+ /*
+ * Avoid re-entrant calls. We need to release the mutex in iwi_init()
+ * when loading the firmware and allocating DMA memory and we don't
+ * want to be called during these operations.
+ */
+ if (sc->flags & IWI_FLAG_INIT_LOCKED) {
+ if (!owned)
+ mtx_unlock(&sc->sc_mtx);
+ return;
+ }
+ sc->flags |= IWI_FLAG_INIT_LOCKED;
iwi_stop(sc);
@@ -2507,10 +2534,18 @@ iwi_init(void *priv)
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
ifp->if_drv_flags |= IFF_DRV_RUNNING;
+ sc->flags &=~ IWI_FLAG_INIT_LOCKED;
+
+ if (!owned)
+ mtx_unlock(&sc->sc_mtx);
+
return;
fail: ifp->if_flags &= ~IFF_UP;
iwi_stop(sc);
+ sc->flags &=~ IWI_FLAG_INIT_LOCKED;
+ if (!owned)
+ mtx_unlock(&sc->sc_mtx);
}
static void
@@ -2520,6 +2555,8 @@ iwi_stop(void *priv)
struct ieee80211com *ic = &sc->sc_ic;
struct ifnet *ifp = ic->ic_ifp;
+ mtx_lock(&sc->sc_mtx);
+
ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
iwi_stop_master(sc);
@@ -2537,6 +2574,8 @@ iwi_stop(void *priv)
sc->sc_tx_timer = 0;
ifp->if_timer = 0;
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
+
+ mtx_unlock(&sc->sc_mtx);
}
/*
@@ -2544,7 +2583,8 @@ iwi_stop(void *priv)
* Must be called from a process context.
*/
static int
-iwi_read_firmware(const char *name, caddr_t *bufp, size_t *len)
+iwi_read_firmware(struct iwi_softc *sc, const char *name, caddr_t *bufp,
+ size_t *len)
{
char path[64];
struct thread *td = curthread;
@@ -2557,6 +2597,9 @@ iwi_read_firmware(const char *name, caddr_t *bufp, size_t *len)
snprintf(path, sizeof path, "/boot/firmware/%s.fw", name);
+ /* drop the driver's lock before doing VFS operations */
+ mtx_unlock(&sc->sc_mtx);
+
NDINIT(&nd, LOOKUP, NOFOLLOW | LOCKLEAF | MPSAFE, UIO_SYSSPACE, path,
td);
if ((error = namei(&nd)) != 0)
@@ -2598,12 +2641,15 @@ iwi_read_firmware(const char *name, caddr_t *bufp, size_t *len)
vput(nd.ni_vp);
VFS_UNLOCK_GIANT(vfslocked);
+ /* reacquire the driver's lock */
+ mtx_lock(&sc->sc_mtx);
+
return 0;
fail3: free(buf, M_DEVBUF);
fail2: vput(nd.ni_vp);
VFS_UNLOCK_GIANT(vfslocked);
-fail1:
+fail1: mtx_lock(&sc->sc_mtx);
return error;
}
diff --git a/sys/dev/iwi/if_iwivar.h b/sys/dev/iwi/if_iwivar.h
index 604aa64..71f0ce49 100644
--- a/sys/dev/iwi/if_iwivar.h
+++ b/sys/dev/iwi/if_iwivar.h
@@ -121,6 +121,7 @@ struct iwi_softc {
uint32_t flags;
#define IWI_FLAG_FW_INITED (1 << 0)
#define IWI_FLAG_SCANNING (1 << 1)
+#define IWI_FLAG_INIT_LOCKED (1 << 2)
struct iwi_cmd_ring cmdq;
struct iwi_tx_ring txq[WME_NUM_AC];
@@ -157,5 +158,3 @@ struct iwi_softc {
int sc_txtap_len;
};
-#define IWI_LOCK(sc) mtx_lock(&(sc)->sc_mtx)
-#define IWI_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx)
OpenPOWER on IntegriCloud