On Sun, Jul 19, 2015 at 04:32:39AM +0200, Stefan Sperling wrote:
> Please test this if you use iwm(4). It should make the driver more
> reliable, e.g. when bringing the interface up which sometimes fails
> because of... reasons.
Please test this updated diff instead. The previous one had races between
the ioctl handler and the newstate function, pointed out by kettenis@.
I believe claudio@ has already hit them. I got help from mpi@, too.
Index: if_iwm.c
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwm.c,v
retrieving revision 1.45
diff -u -p -r1.45 if_iwm.c
--- if_iwm.c 15 Jun 2015 08:06:11 -0000 1.45
+++ if_iwm.c 20 Jul 2015 22:33:16 -0000
@@ -195,14 +195,6 @@ const struct iwm_rate {
#define IWM_RIDX_IS_CCK(_i_) ((_i_) < IWM_RIDX_OFDM)
#define IWM_RIDX_IS_OFDM(_i_) ((_i_) >= IWM_RIDX_OFDM)
-struct iwm_newstate_state {
- struct task ns_wk;
- struct ieee80211com *ns_ic;
- enum ieee80211_state ns_nstate;
- int ns_arg;
- int ns_generation;
-};
-
int iwm_store_cscheme(struct iwm_softc *, uint8_t *, size_t);
int iwm_firmware_store_section(struct iwm_softc *, enum iwm_ucode_type,
uint8_t *, size_t);
@@ -406,13 +398,13 @@ struct ieee80211_node *iwm_node_alloc(st
void iwm_calib_timeout(void *);
void iwm_setrates(struct iwm_node *);
int iwm_media_change(struct ifnet *);
-void iwm_newstate_cb(void *);
+void iwm_newstate_task(void *);
int iwm_newstate(struct ieee80211com *, enum ieee80211_state, int);
void iwm_endscan_cb(void *);
int iwm_init_hw(struct iwm_softc *);
int iwm_init(struct ifnet *);
void iwm_start(struct ifnet *);
-void iwm_stop(struct ifnet *, int);
+void iwm_stop(struct ifnet *);
void iwm_watchdog(struct ifnet *);
int iwm_ioctl(struct ifnet *, u_long, iwm_caddr_t);
const char *iwm_desc_lookup(uint32_t);
@@ -425,9 +417,9 @@ int iwm_match(struct device *, void *, v
int iwm_preinit(struct iwm_softc *);
void iwm_attach_hook(iwm_hookarg_t);
void iwm_attach(struct device *, struct device *, void *);
-void iwm_init_task(void *);
int iwm_activate(struct device *, int);
-void iwm_wakeup(struct iwm_softc *);
+void iwm_suspend(struct iwm_softc *);
+void iwm_resume(struct iwm_softc *);
#if NBPFILTER > 0
void iwm_radiotap_attach(struct iwm_softc *);
@@ -5250,40 +5242,27 @@ iwm_media_change(struct ifnet *ifp)
sc->sc_fixed_ridx = ridx;
}
- if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
- (IFF_UP | IFF_RUNNING)) {
- iwm_stop(ifp, 0);
- error = iwm_init(ifp);
- }
- return error;
+ /*
+ * No need to do anything on the hardware side.
+ * Channel changes will be applied during the
+ * association sequence in iwm_auth().
+ */
+
+ return (0);
}
void
-iwm_newstate_cb(void *wk)
+iwm_newstate_task(void *arg)
{
- struct iwm_newstate_state *iwmns = (void *)wk;
- struct ieee80211com *ic = iwmns->ns_ic;
- enum ieee80211_state nstate = iwmns->ns_nstate;
- int generation = iwmns->ns_generation;
+ struct iwm_softc *sc = arg;
+ struct ieee80211com *ic = &sc->sc_ic;
+ struct ifnet *ifp = IC2IFP(&sc->sc_ic);
+ struct iwm_newstate_task_arg *task_arg = &sc->sc_newstate_task_arg;
+ enum ieee80211_state nstate = task_arg->state;
struct iwm_node *in;
- int arg = iwmns->ns_arg;
- struct ifnet *ifp = IC2IFP(ic);
- struct iwm_softc *sc = ifp->if_softc;
int error;
- free(iwmns, M_DEVBUF, sizeof(*iwmns));
-
- DPRINTF(("Prepare to switch state %d->%d\n", ic->ic_state, nstate));
- if (sc->sc_generation != generation) {
- DPRINTF(("newstate_cb: someone pulled the plug meanwhile\n"));
- if (nstate == IEEE80211_S_INIT) {
- DPRINTF(("newstate_cb: nstate == IEEE80211_S_INIT:
calling sc_newstate()\n"));
- sc->sc_newstate(ic, nstate, arg);
- }
- return;
- }
-
- DPRINTF(("switching state %d->%d\n", ic->ic_state, nstate));
+ sc->sc_newstate_errno = 0;
if (ic->ic_state == IEEE80211_S_SCAN && nstate != ic->ic_state)
iwm_led_blink_stop(sc);
@@ -5292,6 +5271,8 @@ iwm_newstate_cb(void *wk)
if (ic->ic_state == IEEE80211_S_RUN && nstate != ic->ic_state) {
iwm_mvm_disable_beacon_filter(sc, (void *)ic->ic_bss);
+ timeout_del(&sc->sc_calib_to);
+
if (((in = (void *)ic->ic_bss) != NULL))
in->in_assoc = 0;
iwm_release(sc, NULL);
@@ -5310,8 +5291,9 @@ iwm_newstate_cb(void *wk)
if (nstate == IEEE80211_S_SCAN ||
nstate == IEEE80211_S_AUTH ||
nstate == IEEE80211_S_ASSOC) {
- DPRINTF(("Force transition to INIT; MGT=%d\n", arg));
- sc->sc_newstate(ic, IEEE80211_S_INIT, arg);
+ DPRINTF(("Force transition to INIT; MGT=%d\n",
+ task_arg->arg));
+ sc->sc_newstate(ic, IEEE80211_S_INIT, task_arg->arg);
DPRINTF(("Going INIT->SCAN\n"));
nstate = IEEE80211_S_SCAN;
}
@@ -5319,10 +5301,25 @@ iwm_newstate_cb(void *wk)
switch (nstate) {
case IEEE80211_S_INIT:
- sc->sc_scanband = 0;
+ if (ifp->if_flags & IFF_RUNNING) {
+ /* Stop the device. */
+ sc->sc_scanband = 0;
+ sc->sc_auth_prot = 0;
+ iwm_stop(ifp);
+ }
+
+ if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP)
+ iwm_init(ifp);
break;
case IEEE80211_S_SCAN:
+ /* Allow scan only if the interface is up */
+ if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) !=
+ (IFF_UP | IFF_RUNNING)) {
+ sc->sc_newstate_errno = ENXIO;
+ return;
+ }
+
if (sc->sc_scanband)
break;
@@ -5330,11 +5327,13 @@ iwm_newstate_cb(void *wk)
ic->ic_des_esslen != 0,
ic->ic_des_essid, ic->ic_des_esslen)) != 0) {
printf("%s: could not initiate scan\n", DEVNAME(sc));
- return;
+ sc->sc_newstate_errno = EIO;
+ ieee80211_end_scan(&ic->ic_if);
+ break;
}
- ic->ic_state = nstate;
+
iwm_led_blink_start(sc);
- return;
+ break;
case IEEE80211_S_AUTH:
if ((error = iwm_auth(sc)) != 0) {
@@ -5374,36 +5373,30 @@ iwm_newstate_cb(void *wk)
timeout_add_msec(&sc->sc_calib_to, 500);
iwm_mvm_led_enable(sc);
break; }
-
- default:
- break;
}
- sc->sc_newstate(ic, nstate, arg);
+ sc->sc_newstate(ic, nstate, task_arg->arg);
+
+ /* Wake up sleeping process waiting for state transition completion. */
+ wakeup(&sc->sc_newstate);
+
+ if (nstate == IEEE80211_S_INIT &&
+ (ifp->if_flags & (IFF_UP | IFF_RUNNING)) == (IFF_UP | IFF_RUNNING))
+ ieee80211_begin_scan(ifp);
}
int
iwm_newstate(struct ieee80211com *ic, enum ieee80211_state nstate, int arg)
{
- struct iwm_newstate_state *iwmns;
- struct ifnet *ifp = IC2IFP(ic);
- struct iwm_softc *sc = ifp->if_softc;
-
- timeout_del(&sc->sc_calib_to);
-
- iwmns = malloc(sizeof(*iwmns), M_DEVBUF, M_NOWAIT);
- if (!iwmns) {
- DPRINTF(("%s: allocating state cb mem failed\n", DEVNAME(sc)));
- return ENOMEM;
- }
+ struct iwm_softc *sc = IC2IFP(ic)->if_softc;
+ struct iwm_newstate_task_arg *task_arg = &sc->sc_newstate_task_arg;
- iwmns->ns_ic = ic;
- iwmns->ns_nstate = nstate;
- iwmns->ns_arg = arg;
- iwmns->ns_generation = sc->sc_generation;
+ /* Remove any already scheduled transition. */
+ task_del(systq, &sc->sc_newstate_task);
- task_set(&iwmns->ns_wk, iwm_newstate_cb, iwmns);
- task_add(sc->sc_nswq, &iwmns->ns_wk);
+ task_arg->state = nstate;
+ task_arg->arg = arg;
+ task_add(systq, &sc->sc_newstate_task);
return 0;
}
@@ -5432,11 +5425,8 @@ iwm_endscan_cb(void *arg)
}
if (done) {
- if (!sc->sc_scanband) {
- ic->ic_scan_lock = IEEE80211_SCAN_UNLOCKED;
- } else {
+ if (sc->sc_scanband)
ieee80211_end_scan(&ic->ic_if);
- }
sc->sc_scanband = 0;
}
}
@@ -5541,24 +5531,18 @@ iwm_allow_mcast(struct iwm_softc *sc)
return error;
}
-/*
- * ifnet interfaces
- */
-
+/* Initialize the hardware and mark the interface as RUNNING. */
int
iwm_init(struct ifnet *ifp)
{
struct iwm_softc *sc = ifp->if_softc;
int error;
- if (sc->sc_flags & IWM_FLAG_HW_INITED) {
- return 0;
- }
sc->sc_generation++;
sc->sc_flags &= ~IWM_FLAG_STOPPED;
if ((error = iwm_init_hw(sc)) != 0) {
- iwm_stop(ifp, 1);
+ iwm_stop(ifp);
return error;
}
@@ -5569,9 +5553,6 @@ iwm_init(struct ifnet *ifp)
ifp->if_flags &= ~IFF_OACTIVE;
ifp->if_flags |= IFF_RUNNING;
- ieee80211_begin_scan(ifp);
- sc->sc_flags |= IWM_FLAG_HW_INITED;
-
return 0;
}
@@ -5648,25 +5629,15 @@ iwm_start(struct ifnet *ifp)
}
void
-iwm_stop(struct ifnet *ifp, int disable)
+iwm_stop(struct ifnet *ifp)
{
struct iwm_softc *sc = ifp->if_softc;
- struct ieee80211com *ic = &sc->sc_ic;
- sc->sc_flags &= ~IWM_FLAG_HW_INITED;
- sc->sc_flags |= IWM_FLAG_STOPPED;
- sc->sc_generation++;
- sc->sc_scanband = 0;
- sc->sc_auth_prot = 0;
- ic->ic_scan_lock = IEEE80211_SCAN_UNLOCKED;
ifp->if_flags &= ~(IFF_RUNNING | IFF_OACTIVE);
+ sc->sc_flags |= IWM_FLAG_STOPPED;
+ ifp->if_timer = 0;
+ sc->sc_tx_timer = 0; /* cancel watchdog */
- if (ic->ic_state != IEEE80211_S_INIT)
- ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
-
- timeout_del(&sc->sc_calib_to);
- iwm_led_blink_stop(sc);
- ifp->if_timer = sc->sc_tx_timer = 0;
iwm_stop_device(sc);
}
@@ -5674,6 +5645,7 @@ void
iwm_watchdog(struct ifnet *ifp)
{
struct iwm_softc *sc = ifp->if_softc;
+ struct ieee80211com *ic = &sc->sc_ic;
ifp->if_timer = 0;
if (sc->sc_tx_timer > 0) {
@@ -5682,7 +5654,7 @@ iwm_watchdog(struct ifnet *ifp)
#ifdef IWM_DEBUG
iwm_nic_error(sc);
#endif
- task_add(systq, &sc->init_task);
+ ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
ifp->if_oerrors++;
return;
}
@@ -5703,18 +5675,6 @@ iwm_ioctl(struct ifnet *ifp, u_long cmd,
s = splnet();
- /*
- * Prevent processes from entering this function while another
- * process is tsleep'ing in it.
- */
- while ((sc->sc_flags & IWM_FLAG_BUSY) && error == 0)
- error = tsleep(&sc->sc_flags, PCATCH, "iwmioc", 0);
- if (error != 0) {
- splx(s);
- return error;
- }
- sc->sc_flags |= IWM_FLAG_BUSY;
-
switch (cmd) {
case SIOCSIFADDR:
ifp->if_flags |= IFF_UP;
@@ -5725,12 +5685,16 @@ iwm_ioctl(struct ifnet *ifp, u_long cmd,
case SIOCSIFFLAGS:
if (ifp->if_flags & IFF_UP) {
if (!(ifp->if_flags & IFF_RUNNING)) {
- if ((error = iwm_init(ifp)) != 0)
- ifp->if_flags &= ~IFF_UP;
+ ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+ tsleep(&sc->sc_newstate, 0, "iwmstart", 0);
+ error = sc->sc_newstate_errno;
}
} else {
- if (ifp->if_flags & IFF_RUNNING)
- iwm_stop(ifp, 1);
+ if (ifp->if_flags & IFF_RUNNING) {
+ ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
+ tsleep(&sc->sc_newstate, 0, "iwmstop", 0);
+ error = sc->sc_newstate_errno;
+ }
}
break;
@@ -5748,17 +5712,9 @@ iwm_ioctl(struct ifnet *ifp, u_long cmd,
error = ieee80211_ioctl(ifp, cmd, data);
}
- if (error == ENETRESET) {
+ if (error == ENETRESET)
error = 0;
- if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) ==
- (IFF_UP | IFF_RUNNING)) {
- iwm_stop(ifp, 0);
- error = iwm_init(ifp);
- }
- }
- sc->sc_flags &= ~IWM_FLAG_BUSY;
- wakeup(&sc->sc_flags);
splx(s);
return error;
}
@@ -6158,6 +6114,7 @@ iwm_intr(void *arg)
{
struct iwm_softc *sc = arg;
struct ifnet *ifp = IC2IFP(&sc->sc_ic);
+ struct ieee80211com *ic = &sc->sc_ic;
int handled = 0;
int r1, r2, rv = 0;
int isperiodic = 0;
@@ -6227,7 +6184,7 @@ iwm_intr(void *arg)
printf("%s: fatal firmware error\n", DEVNAME(sc));
ifp->if_flags &= ~IFF_UP;
- iwm_stop(ifp, 1);
+ ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
rv = 1;
goto out;
@@ -6237,7 +6194,7 @@ iwm_intr(void *arg)
handled |= IWM_CSR_INT_BIT_HW_ERR;
printf("%s: hardware error, stopping device \n", DEVNAME(sc));
ifp->if_flags &= ~IFF_UP;
- iwm_stop(ifp, 1);
+ ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
rv = 1;
goto out;
}
@@ -6257,7 +6214,7 @@ iwm_intr(void *arg)
DPRINTF(("%s: rfkill switch, disabling interface\n",
DEVNAME(sc)));
ifp->if_flags &= ~IFF_UP;
- iwm_stop(ifp, 1);
+ ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
}
}
@@ -6533,9 +6490,6 @@ iwm_attach(struct device *parent, struct
sc->sc_eswq = taskq_create("iwmes", 1, IPL_NET, 0);
if (sc->sc_eswq == NULL)
goto fail4;
- sc->sc_nswq = taskq_create("iwmns", 1, IPL_NET, 0);
- if (sc->sc_nswq == NULL)
- goto fail4;
/* Clear pending interrupts. */
IWM_WRITE(sc, IWM_CSR_INT, 0xffffffff);
@@ -6586,7 +6540,7 @@ iwm_attach(struct device *parent, struct
#endif
timeout_set(&sc->sc_calib_to, iwm_calib_timeout, sc);
timeout_set(&sc->sc_led_blink_to, iwm_led_blink_timeout, sc);
- task_set(&sc->init_task, iwm_init_task, sc);
+ task_set(&sc->sc_newstate_task, iwm_newstate_task, sc);
/*
* We cannot read the MAC address without loading the
@@ -6631,37 +6585,37 @@ iwm_radiotap_attach(struct iwm_softc *sc
#endif
void
-iwm_init_task(void *arg1)
+iwm_suspend(struct iwm_softc *sc)
{
- struct iwm_softc *sc = arg1;
struct ifnet *ifp = &sc->sc_ic.ic_if;
- int s;
+ struct ieee80211com *ic = &sc->sc_ic;
- s = splnet();
- while (sc->sc_flags & IWM_FLAG_BUSY)
- tsleep(&sc->sc_flags, 0, "iwmpwr", 0);
- sc->sc_flags |= IWM_FLAG_BUSY;
-
- iwm_stop(ifp, 0);
- if ((ifp->if_flags & (IFF_UP | IFF_RUNNING)) == IFF_UP)
- iwm_init(ifp);
+ /* Remove any already scheduled transition. */
+ task_del(systq, &sc->sc_newstate_task);
- sc->sc_flags &= ~IWM_FLAG_BUSY;
- wakeup(&sc->sc_flags);
- splx(s);
+ /* Cancel pending timeouts. */
+ if (ic->ic_state == IEEE80211_S_SCAN)
+ iwm_led_blink_stop(sc);
+ if (ic->ic_state == IEEE80211_S_RUN)
+ timeout_del(&sc->sc_calib_to);
+
+ iwm_stop(ifp);
+
+ /* Reset net80211 state machine. */
+ sc->sc_newstate(ic, IEEE80211_S_INIT, -1);
}
void
-iwm_wakeup(struct iwm_softc *sc)
+iwm_resume(struct iwm_softc *sc)
{
pcireg_t reg;
+ struct ieee80211com *ic = &sc->sc_ic;
/* Clear device-specific "PCI retry timeout" register (41h). */
reg = pci_conf_read(sc->sc_pct, sc->sc_pcitag, 0x40);
pci_conf_write(sc->sc_pct, sc->sc_pcitag, 0x40, reg & ~0xff00);
- iwm_init_task(sc);
-
+ ieee80211_new_state(ic, IEEE80211_S_INIT, -1);
}
int
@@ -6673,10 +6627,10 @@ iwm_activate(struct device *self, int ac
switch (act) {
case DVACT_SUSPEND:
if (ifp->if_flags & IFF_RUNNING)
- iwm_stop(ifp, 0);
+ iwm_suspend(sc);
break;
- case DVACT_WAKEUP:
- iwm_wakeup(sc);
+ case DVACT_RESUME:
+ iwm_resume(sc);
break;
}
Index: if_iwmvar.h
===================================================================
RCS file: /cvs/src/sys/dev/pci/if_iwmvar.h,v
retrieving revision 1.9
diff -u -p -r1.9 if_iwmvar.h
--- if_iwmvar.h 15 Jun 2015 08:06:12 -0000 1.9
+++ if_iwmvar.h 20 Jul 2015 22:11:16 -0000
@@ -288,10 +288,8 @@ struct iwm_rx_ring {
};
#define IWM_FLAG_USE_ICT 0x01
-#define IWM_FLAG_HW_INITED 0x02
-#define IWM_FLAG_STOPPED 0x04
-#define IWM_FLAG_RFKILL 0x08
-#define IWM_FLAG_BUSY 0x10
+#define IWM_FLAG_STOPPED 0x02
+#define IWM_FLAG_RFKILL 0x04
struct iwm_ucode_status {
uint32_t uc_error_event_table;
@@ -353,18 +351,23 @@ struct iwm_bf_data {
int last_cqm_event;
};
+struct iwm_newstate_task_arg {
+ enum ieee80211_state state;
+ int arg;
+};
+
struct iwm_softc {
struct device sc_dev;
struct ieee80211com sc_ic;
int (*sc_newstate)(struct ieee80211com *, enum ieee80211_state, int);
- int sc_newstate_pending;
+ struct task sc_newstate_task;
+ struct iwm_newstate_task_arg sc_newstate_task_arg;
+ int sc_newstate_errno;
struct ieee80211_amrr sc_amrr;
struct timeout sc_calib_to;
struct timeout sc_led_blink_to;
- struct task init_task;
-
bus_space_tag_t sc_st;
bus_space_handle_t sc_sh;
bus_size_t sc_sz;
@@ -448,7 +451,7 @@ struct iwm_softc {
uint8_t sc_cmd_resp[IWM_CMD_RESP_MAX];
int sc_wantresp;
- struct taskq *sc_nswq, *sc_eswq;
+ struct taskq *sc_eswq;
struct task sc_eswk;
struct iwm_rx_phy_info sc_last_phy_info;