summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/dev/pci/if_iwm.c148
-rw-r--r--sys/dev/pci/if_iwmvar.h6
2 files changed, 121 insertions, 33 deletions
diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c
index af9e5233958..27067478c13 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.213 2017/08/27 12:38:23 stsp Exp $ */
+/* $OpenBSD: if_iwm.c,v 1.214 2017/10/04 18:00:12 stsp Exp $ */
/*
* Copyright (c) 2014, 2016 genua gmbh <info@genua.de>
@@ -120,6 +120,7 @@
#include <sys/systm.h>
#include <sys/endian.h>
+#include <sys/refcnt.h>
#include <sys/task.h>
#include <machine/bus.h>
#include <machine/intr.h>
@@ -430,6 +431,8 @@ void iwm_mac_ctxt_cmd_fill_sta(struct iwm_softc *, struct iwm_node *,
struct iwm_mac_data_sta *, int);
int iwm_mac_ctxt_cmd(struct iwm_softc *, struct iwm_node *, uint32_t, int);
int iwm_update_quotas(struct iwm_softc *, struct iwm_node *, int);
+void iwm_add_task(struct iwm_softc *, struct taskq *, struct task *);
+void iwm_del_task(struct iwm_softc *, struct taskq *, struct task *);
int iwm_scan(struct iwm_softc *);
int iwm_auth(struct iwm_softc *);
int iwm_deauth(struct iwm_softc *);
@@ -2571,13 +2574,22 @@ iwm_htprot_task(void *arg)
struct iwm_softc *sc = arg;
struct ieee80211com *ic = &sc->sc_ic;
struct iwm_node *in = (void *)ic->ic_bss;
- int err;
+ int err, s = splnet();
+
+ if (sc->sc_flags & IWM_FLAG_SHUTDOWN) {
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
+ return;
+ }
/* This call updates HT protection based on in->in_ni.ni_htop1. */
err = iwm_mac_ctxt_cmd(sc, in, IWM_FW_CTXT_ACTION_MODIFY, 1);
if (err)
printf("%s: could not change HT protection: error %d\n",
DEVNAME(sc), err);
+
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
}
/*
@@ -2590,7 +2602,7 @@ iwm_update_htprot(struct ieee80211com *ic, struct ieee80211_node *ni)
struct iwm_softc *sc = ic->ic_softc;
/* assumes that ni == ic->ic_bss */
- task_add(systq, &sc->htprot_task);
+ iwm_add_task(sc, systq, &sc->htprot_task);
}
void
@@ -2599,11 +2611,21 @@ iwm_ba_task(void *arg)
struct iwm_softc *sc = arg;
struct ieee80211com *ic = &sc->sc_ic;
struct ieee80211_node *ni = ic->ic_bss;
+ int s = splnet();
+
+ if (sc->sc_flags & IWM_FLAG_SHUTDOWN) {
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
+ return;
+ }
if (sc->ba_start)
iwm_sta_rx_agg(sc, ni, sc->ba_tid, sc->ba_ssn, 1);
else
iwm_sta_rx_agg(sc, ni, sc->ba_tid, 0, 0);
+
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
}
/*
@@ -2623,7 +2645,7 @@ iwm_ampdu_rx_start(struct ieee80211com *ic, struct ieee80211_node *ni,
sc->ba_start = 1;
sc->ba_tid = tid;
sc->ba_ssn = htole16(ba->ba_winstart);
- task_add(systq, &sc->ba_task);
+ iwm_add_task(sc, systq, &sc->ba_task);
return EBUSY;
}
@@ -2640,7 +2662,7 @@ iwm_ampdu_rx_stop(struct ieee80211com *ic, struct ieee80211_node *ni,
sc->ba_start = 0;
sc->ba_tid = tid;
- task_add(systq, &sc->ba_task);
+ iwm_add_task(sc, systq, &sc->ba_task);
}
void
@@ -3562,7 +3584,7 @@ iwm_rx_tx_cmd_single(struct iwm_softc *sc, struct iwm_rx_packet *pkt,
* the firwmare's LQ rate table from process context.
*/
if (omcs != ni->ni_txmcs)
- task_add(systq, &sc->setrates_task);
+ iwm_add_task(sc, systq, &sc->setrates_task);
}
if (txfail)
@@ -5408,6 +5430,29 @@ iwm_update_quotas(struct iwm_softc *sc, struct iwm_node *in, int running)
sizeof(cmd), &cmd);
}
+void
+iwm_add_task(struct iwm_softc *sc, struct taskq *taskq, struct task *task)
+{
+ int s = splnet();
+
+ if (sc->sc_flags & IWM_FLAG_SHUTDOWN) {
+ splx(s);
+ return;
+ }
+
+ refcnt_take(&sc->task_refs);
+ if (!task_add(taskq, task))
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
+}
+
+void
+iwm_del_task(struct iwm_softc *sc, struct taskq *taskq, struct task *task)
+{
+ if (task_del(taskq, task))
+ refcnt_rele(&sc->task_refs);
+}
+
int
iwm_scan(struct iwm_softc *sc)
{
@@ -5756,7 +5801,7 @@ iwm_calib_timeout(void *arg)
* the firwmare's LQ rate table from process context.
*/
if (otxrate != ni->ni_txrate)
- task_add(systq, &sc->setrates_task);
+ iwm_add_task(sc, systq, &sc->setrates_task);
}
splx(s);
@@ -5769,9 +5814,18 @@ iwm_setrates_task(void *arg)
struct iwm_softc *sc = arg;
struct ieee80211com *ic = &sc->sc_ic;
struct iwm_node *in = (struct iwm_node *)ic->ic_bss;
+ int s = splnet();
+
+ if (sc->sc_flags & IWM_FLAG_SHUTDOWN) {
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
+ return;
+ }
/* Update rates table based on new TX rate determined by AMRR. */
iwm_setrates(in);
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
}
void
@@ -5930,7 +5984,9 @@ iwm_newstate_task(void *psc)
ieee80211_state_name[ostate],
ieee80211_state_name[nstate]));
- if (nstate == ostate) {
+ if (nstate == ostate || (sc->sc_flags & IWM_FLAG_SHUTDOWN)) {
+ /* No-op state change or iwm_stop() is waiting for us. */
+ refcnt_rele_wake(&sc->task_refs);
splx(s);
return;
}
@@ -5963,6 +6019,13 @@ iwm_newstate_task(void *psc)
case IEEE80211_S_INIT:
break;
}
+
+ /* Die now if iwm_stop() was called while we were sleeping. */
+ if (sc->sc_flags & IWM_FLAG_SHUTDOWN) {
+ refcnt_rele_wake(&sc->task_refs);
+ splx(s);
+ return;
+ }
}
switch (nstate) {
@@ -5973,6 +6036,7 @@ iwm_newstate_task(void *psc)
err = iwm_scan(sc);
if (err)
break;
+ refcnt_rele_wake(&sc->task_refs);
splx(s);
return;
@@ -5987,14 +6051,12 @@ iwm_newstate_task(void *psc)
case IEEE80211_S_RUN:
err = iwm_run(sc);
break;
-
- default:
- break;
}
out:
- if (err == 0)
+ if (err == 0 && (sc->sc_flags & IWM_FLAG_SHUTDOWN) == 0)
sc->sc_newstate(ic, nstate, arg);
+ refcnt_rele_wake(&sc->task_refs);
splx(s);
}
@@ -6005,14 +6067,18 @@ iwm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
struct iwm_softc *sc = ifp->if_softc;
struct iwm_node *in = (void *)ic->ic_bss;
- timeout_del(&sc->sc_calib_to);
- if (ic->ic_state == IEEE80211_S_RUN)
+ if (ic->ic_state == IEEE80211_S_RUN) {
+ timeout_del(&sc->sc_calib_to);
ieee80211_mira_cancel_timeouts(&in->in_mn);
+ iwm_del_task(sc, systq, &sc->setrates_task);
+ iwm_del_task(sc, systq, &sc->ba_task);
+ iwm_del_task(sc, systq, &sc->htprot_task);
+ }
sc->ns_nstate = nstate;
sc->ns_arg = arg;
- task_add(sc->sc_nswq, &sc->newstate_task);
+ iwm_add_task(sc, sc->sc_nswq, &sc->newstate_task);
return 0;
}
@@ -6391,11 +6457,17 @@ iwm_init(struct ifnet *ifp)
struct ieee80211com *ic = &sc->sc_ic;
int err, generation;
- sc->sc_generation++;
+ rw_assert_wrlock(&sc->ioctl_rwl);
+
+ generation = ++sc->sc_generation;
+
+ KASSERT(sc->task_refs.refs == 0);
+ refcnt_init(&sc->task_refs);
err = iwm_init_hw(sc);
if (err) {
- iwm_stop(ifp);
+ if (generation == sc->sc_generation)
+ iwm_stop(ifp);
return err;
}
@@ -6408,7 +6480,6 @@ iwm_init(struct ifnet *ifp)
* ieee80211_begin_scan() ends up scheduling iwm_newstate_task().
* Wait until the transition to SCAN state has completed.
*/
- generation = sc->sc_generation;
do {
err = tsleep(&ic->ic_state, PCATCH, "iwminit", hz);
if (generation != sc->sc_generation)
@@ -6495,6 +6566,23 @@ iwm_stop(struct ifnet *ifp)
struct iwm_node *in = (void *)ic->ic_bss;
int s = splnet();
+ rw_assert_wrlock(&sc->ioctl_rwl);
+
+ sc->sc_flags |= IWM_FLAG_SHUTDOWN; /* Disallow new tasks. */
+
+ /* Cancel scheduled tasks and let any stale tasks finish up. */
+ task_del(systq, &sc->init_task);
+ iwm_del_task(sc, sc->sc_nswq, &sc->newstate_task);
+ iwm_del_task(sc, systq, &sc->setrates_task);
+ iwm_del_task(sc, systq, &sc->ba_task);
+ iwm_del_task(sc, systq, &sc->htprot_task);
+ KASSERT(sc->task_refs.refs >= 1);
+ refcnt_finalize(&sc->task_refs, "iwmstop");
+
+ iwm_stop_device(sc);
+
+ /* Reset soft state. */
+
sc->sc_generation++;
if (ic->ic_scan_lock & IEEE80211_SCAN_REQUEST)
wakeup(&ic->ic_scan_lock);
@@ -6504,13 +6592,7 @@ iwm_stop(struct ifnet *ifp)
in->in_phyctxt = NULL;
if (ic->ic_state == IEEE80211_S_RUN)
- ieee80211_mira_cancel_timeouts(&in->in_mn);
-
- task_del(systq, &sc->init_task);
- task_del(sc->sc_nswq, &sc->newstate_task);
- task_del(systq, &sc->setrates_task);
- task_del(systq, &sc->ba_task);
- task_del(systq, &sc->htprot_task);
+ ieee80211_mira_cancel_timeouts(&in->in_mn); /* XXX refcount? */
sc->sc_flags &= ~IWM_FLAG_SCANNING;
sc->sc_flags &= ~IWM_FLAG_MAC_ACTIVE;
@@ -6518,13 +6600,13 @@ iwm_stop(struct ifnet *ifp)
sc->sc_flags &= ~IWM_FLAG_STA_ACTIVE;
sc->sc_flags &= ~IWM_FLAG_TE_ACTIVE;
sc->sc_flags &= ~IWM_FLAG_HW_ERR;
+ sc->sc_flags &= ~IWM_FLAG_SHUTDOWN;
sc->sc_newstate(ic, IEEE80211_S_INIT, -1);
- timeout_del(&sc->sc_calib_to);
+ timeout_del(&sc->sc_calib_to); /* XXX refcount? */
iwm_led_blink_stop(sc);
ifp->if_timer = sc->sc_tx_timer = 0;
- iwm_stop_device(sc);
splx(s);
}
@@ -6541,7 +6623,8 @@ iwm_watchdog(struct ifnet *ifp)
#ifdef IWM_DEBUG
iwm_nic_error(sc);
#endif
- task_add(systq, &sc->init_task);
+ if ((sc->sc_flags & IWM_FLAG_SHUTDOWN) == 0)
+ task_add(systq, &sc->init_task);
ifp->if_oerrors++;
return;
}
@@ -7247,7 +7330,8 @@ iwm_intr(void *arg)
#endif
printf("%s: fatal firmware error\n", DEVNAME(sc));
- task_add(systq, &sc->init_task);
+ if ((sc->sc_flags & IWM_FLAG_SHUTDOWN) == 0)
+ task_add(systq, &sc->init_task);
rv = 1;
goto out;
@@ -7256,8 +7340,10 @@ iwm_intr(void *arg)
if (r1 & IWM_CSR_INT_BIT_HW_ERR) {
handled |= IWM_CSR_INT_BIT_HW_ERR;
printf("%s: hardware error, stopping device \n", DEVNAME(sc));
- sc->sc_flags |= IWM_FLAG_HW_ERR;
- task_add(systq, &sc->init_task);
+ if ((sc->sc_flags & IWM_FLAG_SHUTDOWN) == 0) {
+ sc->sc_flags |= IWM_FLAG_HW_ERR;
+ task_add(systq, &sc->init_task);
+ }
rv = 1;
goto out;
}
diff --git a/sys/dev/pci/if_iwmvar.h b/sys/dev/pci/if_iwmvar.h
index f6d16b853f7..a843fa491bc 100644
--- a/sys/dev/pci/if_iwmvar.h
+++ b/sys/dev/pci/if_iwmvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwmvar.h,v 1.34 2017/08/27 12:38:23 stsp Exp $ */
+/* $OpenBSD: if_iwmvar.h,v 1.35 2017/10/04 18:00:12 stsp Exp $ */
/*
* Copyright (c) 2014 genua mbh <info@genua.de>
@@ -287,6 +287,7 @@ struct iwm_rx_ring {
#define IWM_FLAG_STA_ACTIVE 0x20 /* AP added to firmware station table */
#define IWM_FLAG_TE_ACTIVE 0x40 /* time event is scheduled */
#define IWM_FLAG_HW_ERR 0x80 /* hardware error occurred */
+#define IWM_FLAG_SHUTDOWN 0x100 /* shutting down; new tasks forbidden */
struct iwm_ucode_status {
uint32_t uc_error_event_table;
@@ -358,7 +359,8 @@ struct iwm_softc {
struct timeout sc_calib_to;
struct timeout sc_led_blink_to;
- struct task init_task;
+ struct task init_task; /* NB: not reference-counted */
+ struct refcnt task_refs;
struct task newstate_task;
struct task setrates_task;
enum ieee80211_state ns_nstate;