summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstsp <stsp@openbsd.org>2015-12-08 17:10:02 +0000
committerstsp <stsp@openbsd.org>2015-12-08 17:10:02 +0000
commit9b167141195d2d23e6793b7e8fd3c190a78b84d1 (patch)
treedeb01cfae384c520bb521dbf36f034bc5e0bf46e
parentsync (diff)
downloadwireguard-openbsd-9b167141195d2d23e6793b7e8fd3c190a78b84d1.tar.xz
wireguard-openbsd-9b167141195d2d23e6793b7e8fd3c190a78b84d1.zip
When iwm(4) moves to AUTH state it asks the firmware for a "time event" to
prevent it from moving off-channel during association. The firmware issues interrupts at beginning and end of the time event. The driver tried detecting the beginning with a tsleep() in the newstate task followed by a wakeup() from the interrupt handler. However, sometimes the newstate task did not get scheduled until the time event had already passed, and association was aborted. In rare cases the newstate task would even sleep forever and the iwm(4) interface would stop working until reboot. Fix these issues by issuing the time event and continuing association without checking for a "go" from the firmware. Our kernel does not provide the scheduling guarantees required for such precise synchronization so association is more likely to fail with the additional check than without. ok mpi@ tedu@
-rw-r--r--sys/dev/pci/if_iwm.c54
-rw-r--r--sys/dev/pci/if_iwmreg.h39
-rw-r--r--sys/dev/pci/if_iwmvar.h6
3 files changed, 53 insertions, 46 deletions
diff --git a/sys/dev/pci/if_iwm.c b/sys/dev/pci/if_iwm.c
index 92f9828066d..fcfc69a13ec 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.68 2015/11/25 03:09:59 dlg Exp $ */
+/* $OpenBSD: if_iwm.c,v 1.69 2015/12/08 17:10:02 stsp Exp $ */
/*
* Copyright (c) 2014 genua mbh <info@genua.de>
@@ -272,7 +272,7 @@ int iwm_mvm_send_time_event_cmd(struct iwm_softc *,
int iwm_mvm_time_event_send_add(struct iwm_softc *, struct iwm_node *,
void *, struct iwm_time_event_cmd_v2 *);
void iwm_mvm_protect_session(struct iwm_softc *, struct iwm_node *,
- uint32_t, uint32_t, uint32_t);
+ uint32_t, uint32_t);
int iwm_nvm_read_chunk(struct iwm_softc *, uint16_t, uint16_t, uint16_t,
uint8_t *, uint16_t *);
int iwm_nvm_read_section(struct iwm_softc *, uint16_t, uint8_t *,
@@ -2216,7 +2216,7 @@ iwm_mvm_time_event_send_add(struct iwm_softc *sc, struct iwm_node *in,
void
iwm_mvm_protect_session(struct iwm_softc *sc, struct iwm_node *in,
- uint32_t duration, uint32_t min_duration, uint32_t max_delay)
+ uint32_t duration, uint32_t max_delay)
{
struct iwm_time_event_cmd_v2 time_cmd;
@@ -4988,7 +4988,6 @@ iwm_auth(struct iwm_softc *sc)
struct ieee80211com *ic = &sc->sc_ic;
struct iwm_node *in = (void *)ic->ic_bss;
uint32_t duration;
- uint32_t min_duration;
int error;
in->in_assoc = 0;
@@ -5014,32 +5013,14 @@ iwm_auth(struct iwm_softc *sc)
return error;
}
- /* a bit superfluous? */
- while (sc->sc_auth_prot)
- tsleep(&sc->sc_auth_prot, 0, "iwmauth", 0);
- sc->sc_auth_prot = 1;
-
- duration = min(IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS,
- 200 + in->in_ni.ni_intval);
- min_duration = min(IWM_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS,
- 100 + in->in_ni.ni_intval);
- iwm_mvm_protect_session(sc, in, duration, min_duration, 500);
-
- while (sc->sc_auth_prot != 2) {
- /*
- * well, meh, but if the kernel is sleeping for half a
- * second, we have bigger problems
- */
- if (sc->sc_auth_prot == 0) {
- DPRINTF(("%s: missed auth window!\n", DEVNAME(sc)));
- return ETIMEDOUT;
- } else if (sc->sc_auth_prot == -1) {
- DPRINTF(("%s: no time event, denied!\n", DEVNAME(sc)));
- sc->sc_auth_prot = 0;
- return EAUTH;
- }
- tsleep(&sc->sc_auth_prot, 0, "iwmau2", 0);
- }
+ /*
+ * Prevent the FW from wandering off channel during association
+ * by "protecting" the session with a time event.
+ */
+ /* XXX duration is in units of TU, not MS */
+ duration = IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS;
+ iwm_mvm_protect_session(sc, in, duration, 500 /* XXX magic number */);
+ DELAY(100);
return 0;
}
@@ -5642,7 +5623,6 @@ iwm_stop(struct ifnet *ifp, int disable)
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;
ifq_clr_oactive(&ifp->if_snd);
@@ -6082,16 +6062,8 @@ iwm_notif_intr(struct iwm_softc *sc)
struct iwm_time_event_notif *notif;
SYNC_RESP_STRUCT(notif, pkt);
- if (notif->status) {
- if (le32toh(notif->action) &
- IWM_TE_V2_NOTIF_HOST_EVENT_START)
- sc->sc_auth_prot = 2;
- else
- sc->sc_auth_prot = 0;
- } else {
- sc->sc_auth_prot = -1;
- }
- wakeup(&sc->sc_auth_prot);
+ DPRINTF(("%s: TE notif status = 0x%x action = 0x%x\n",
+ DEVNAME(sc), notif->status, notif->action));
break; }
case IWM_MCAST_FILTER_CMD:
diff --git a/sys/dev/pci/if_iwmreg.h b/sys/dev/pci/if_iwmreg.h
index 2ee45a4a344..3e86253135f 100644
--- a/sys/dev/pci/if_iwmreg.h
+++ b/sys/dev/pci/if_iwmreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: if_iwmreg.h,v 1.6 2015/11/15 09:39:09 stsp Exp $ */
+/* $OpenBSD: if_iwmreg.h,v 1.7 2015/12/08 17:10:02 stsp Exp $ */
/******************************************************************************
*
@@ -2008,6 +2008,43 @@ struct iwm_time_event_cmd_v1 {
/* Time event - defines for command API v2 */
+/**
+ * DOC: Time Events - what is it?
+ *
+ * Time Events are a fw feature that allows the driver to control the presence
+ * of the device on the channel. Since the fw supports multiple channels
+ * concurrently, the fw may choose to jump to another channel at any time.
+ * In order to make sure that the fw is on a specific channel at a certain time
+ * and for a certain duration, the driver needs to issue a time event.
+ *
+ * The simplest example is for BSS association. The driver issues a time event,
+ * waits for it to start, and only then tells mac80211 that we can start the
+ * association. This way, we make sure that the association will be done
+ * smoothly and won't be interrupted by channel switch decided within the fw.
+ */
+
+ /**
+ * DOC: The flow against the fw
+ *
+ * When the driver needs to make sure we are in a certain channel, at a certain
+ * time and for a certain duration, it sends a Time Event. The flow against the
+ * fw goes like this:
+ * 1) Driver sends a TIME_EVENT_CMD to the fw
+ * 2) Driver gets the response for that command. This response contains the
+ * Unique ID (UID) of the event.
+ * 3) The fw sends notification when the event starts.
+ *
+ * Of course the API provides various options that allow to cover parameters
+ * of the flow.
+ * What is the duration of the event?
+ * What is the start time of the event?
+ * Is there an end-time for the event?
+ * How much can the event be delayed?
+ * Can the event be split?
+ * If yes what is the maximal number of chunks?
+ * etc...
+ */
+
/*
* @IWM_TE_V2_FRAG_NONE: fragmentation of the time event is NOT allowed.
* @IWM_TE_V2_FRAG_SINGLE: fragmentation of the time event is allowed, but only
diff --git a/sys/dev/pci/if_iwmvar.h b/sys/dev/pci/if_iwmvar.h
index d49f928b5ad..62ad6592fbb 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.12 2015/10/22 11:51:28 jsg Exp $ */
+/* $OpenBSD: if_iwmvar.h,v 1.13 2015/12/08 17:10:02 stsp Exp $ */
/*
* Copyright (c) 2014 genua mbh <info@genua.de>
@@ -305,7 +305,7 @@ struct iwm_ucode_status {
#define IWM_OTP_LOW_IMAGE_SIZE 2048
-#define IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 500
+#define IWM_MVM_TE_SESSION_PROTECTION_MAX_TIME_MS 1000
#define IWM_MVM_TE_SESSION_PROTECTION_MIN_TIME_MS 400
enum IWM_CMD_MODE {
@@ -441,8 +441,6 @@ struct iwm_softc {
int sc_scan_last_antenna;
int sc_scanband;
- int sc_auth_prot;
-
int sc_fixed_ridx;
int sc_staid;