From e6679a69507823c6ade1cfb86297972ba53127d3 Mon Sep 17 00:00:00 2001
From: Sepherosa Ziehau <sephe@dragonflybsd.org>
Date: Wed, 4 Feb 2015 22:01:19 +0800
Subject: [PATCH] wlan: Make sure that WLAN serializer is not held for
 if_{detach,attach}()

---
 sys/dev/netif/ath/ath/if_ath.c                 |  9 +++++-
 sys/netproto/802_11/ieee80211_dragonfly.h      |  6 ++++
 sys/netproto/802_11/wlan/ieee80211.c           | 38 ++++++++++++++++++++++++++
 sys/netproto/802_11/wlan/ieee80211_dragonfly.c |  1 +
 4 files changed, 53 insertions(+), 1 deletion(-)

diff --git a/sys/dev/netif/ath/ath/if_ath.c b/sys/dev/netif/ath/ath/if_ath.c
index 2ed6576..a664bf4 100644
--- a/sys/dev/netif/ath/ath/if_ath.c
+++ b/sys/dev/netif/ath/ath/if_ath.c
@@ -1180,8 +1180,15 @@ ath_attach(u_int16_t devid, struct ath_softc *sc)
 
 	/* NB: used to size node table key mapping array */
 	ic->ic_max_keyix = sc->sc_keymax;
-	/* call MI attach routine. */
+	/*
+	 * Call MI attach routine.
+	 *
+	 * WLAN serializer must _not_ be held for ieee80211_ifattach(),
+	 * since it could dead-lock the domsg to netisrs.
+	 */
+	wlan_serialize_exit();
 	ieee80211_ifattach(ic, macaddr);
+	wlan_serialize_enter();
 	ic->ic_setregdomain = ath_setregdomain;
 	ic->ic_getradiocaps = ath_getradiocaps;
 	sc->sc_opmode = HAL_M_STA;
diff --git a/sys/netproto/802_11/ieee80211_dragonfly.h b/sys/netproto/802_11/ieee80211_dragonfly.h
index 33099e3..bb7cb10 100644
--- a/sys/netproto/802_11/ieee80211_dragonfly.h
+++ b/sys/netproto/802_11/ieee80211_dragonfly.h
@@ -83,6 +83,12 @@ wlan_assert_serialized(void)
 	ASSERT_SERIALIZED(&wlan_global_serializer);
 }
 
+static __inline void
+wlan_assert_notserialized(void)
+{
+	ASSERT_NOT_SERIALIZED(&wlan_global_serializer);
+}
+
 /*
  * Node reference counting definitions.
  *
diff --git a/sys/netproto/802_11/wlan/ieee80211.c b/sys/netproto/802_11/wlan/ieee80211.c
index 784dca0..609f773 100644
--- a/sys/netproto/802_11/wlan/ieee80211.c
+++ b/sys/netproto/802_11/wlan/ieee80211.c
@@ -300,6 +300,12 @@ ieee80211_ifattach(struct ieee80211com *ic,
 	struct sockaddr_dl *sdl;
 	struct ifaddr *ifa;
 
+	/*
+	 * This function must _not_ be serialized by the WLAN serializer,
+	 * since it could dead-lock the domsg to netisrs in if_attach().
+	 */
+	wlan_assert_notserialized();
+
 	KASSERT(ifp->if_type == IFT_IEEE80211, ("if_type %d", ifp->if_type));
 
 	IEEE80211_LOCK_INIT(ic, ifp->if_xname);
@@ -385,6 +391,23 @@ ieee80211_ifdetach(struct ieee80211com *ic)
 	struct ieee80211vap *vap;
 
 	/*
+	 * WLAN serializer must _not_ be held for if_detach()
+	 * or ieee80211_vap_destroy(), since it could dead-lock
+	 * the domsg to netisrs.
+	 *
+	 * NOTE:
+	 * It is safe to release the WLAN serializer for the
+	 * ic_vaps, since the physical/parent ifnet is no longer
+	 * visible after the if_detach().
+	 *
+	 * XXX
+	 * This function actually should _not_ be serialized
+	 * by the WLAN serializer, however, all 802.11 device
+	 * drivers serialize it ...
+	 */
+	wlan_serialize_exit();
+
+	/*
 	 * This detaches the main interface, but not the vaps.
 	 * Each VAP may be in a separate VIMAGE.
 	 */
@@ -398,6 +421,9 @@ ieee80211_ifdetach(struct ieee80211com *ic)
 	 */
 	while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL)
 		ieee80211_vap_destroy(vap);
+
+	wlan_serialize_enter();
+
 	ieee80211_waitfor_parent(ic);
 
 	ieee80211_sysctl_detach(ic);
@@ -582,6 +608,12 @@ ieee80211_vap_attach(struct ieee80211vap *vap,
 	struct ifmediareq imr;
 	int maxrate;
 
+	/*
+	 * This function must _not_ be serialized by the WLAN serializer,
+	 * since it could dead-lock the domsg to netisrs in ether_ifattach().
+	 */
+	wlan_assert_notserialized();
+
 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE,
 	    "%s: %s parent %s flags 0x%x flags_ext 0x%x\n",
 	    __func__, ieee80211_opmode_name[vap->iv_opmode],
@@ -642,6 +674,12 @@ ieee80211_vap_detach(struct ieee80211vap *vap)
 	struct ieee80211com *ic = vap->iv_ic;
 	struct ifnet *ifp = vap->iv_ifp;
 
+	/*
+	 * This function must _not_ be serialized by the WLAN serializer,
+	 * since it could dead-lock the domsg to netisrs in ether_ifdettach().
+	 */
+	wlan_assert_notserialized();
+
 	CURVNET_SET(ifp->if_vnet);
 
 	IEEE80211_DPRINTF(vap, IEEE80211_MSG_STATE, "%s: %s parent %s\n",
diff --git a/sys/netproto/802_11/wlan/ieee80211_dragonfly.c b/sys/netproto/802_11/wlan/ieee80211_dragonfly.c
index 1142161..890f197 100644
--- a/sys/netproto/802_11/wlan/ieee80211_dragonfly.c
+++ b/sys/netproto/802_11/wlan/ieee80211_dragonfly.c
@@ -314,6 +314,7 @@ ieee80211_parent_xmitpkt(struct ieee80211com *ic, struct mbuf *m)
 void
 ieee80211_vap_destroy(struct ieee80211vap *vap)
 {
+	wlan_assert_notserialized();
 	if_clone_destroy(vap->iv_ifp->if_xname);
 }
 
-- 
2.2.1

