Author: avos
Date: Sun Jan 20 13:39:18 2019
New Revision: 343213
URL: https://svnweb.freebsd.org/changeset/base/343213
Log:
net80211: resolve ioctl <-> detach race for ieee80211com structure
Since r287197 ieee80211com is a part of drivers softc; as a result,
after detach all pointers to it (iv_ic, ni_ic) are invalid. Most
possible users (tasks, interrupt handlers) are blocked / removed
when device is stopped; however, ioctl handlers were not tracked
and may crash if ieee80211com structure is accessed.
Since ieee80211com pointer access from ieee80211vap structure is not
protected by lock (constant after interface creation) and used in
many other places just use reference counting for ioctl handlers;
on detach set 'detached' flag and wait until reference counter goes to 0.
For HEAD ieee80211vap size was changed (__FreeBSD_version bumped);
however, in stable branches I'm going to split / reuse the last
iv_spare field for KBI stability.
Tested with:
- rsu(4), SIOCSIFCAP (-rxcsum) ioctl;
- rtwn_pci(4), SIOCG80211 / IEEE80211_IOC_HTPROTMODE ioctl.
MFC after:1 week
Modified:
head/sys/net80211/ieee80211.c
head/sys/net80211/ieee80211_freebsd.c
head/sys/net80211/ieee80211_freebsd.h
head/sys/net80211/ieee80211_ioctl.c
head/sys/net80211/ieee80211_var.h
head/sys/sys/param.h
Modified: head/sys/net80211/ieee80211.c
==
--- head/sys/net80211/ieee80211.c Sun Jan 20 13:16:36 2019
(r343212)
+++ head/sys/net80211/ieee80211.c Sun Jan 20 13:39:18 2019
(r343213)
@@ -405,8 +405,10 @@ ieee80211_ifdetach(struct ieee80211com *ic)
* The VAP is responsible for setting and clearing
* the VIMAGE context.
*/
- while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL)
+ while ((vap = TAILQ_FIRST(&ic->ic_vaps)) != NULL) {
+ ieee80211_com_vdetach(vap);
ieee80211_vap_destroy(vap);
+ }
ieee80211_waitfor_parent(ic);
ieee80211_sysctl_detach(ic);
Modified: head/sys/net80211/ieee80211_freebsd.c
==
--- head/sys/net80211/ieee80211_freebsd.c Sun Jan 20 13:16:36 2019
(r343212)
+++ head/sys/net80211/ieee80211_freebsd.c Sun Jan 20 13:39:18 2019
(r343213)
@@ -307,6 +307,55 @@ ieee80211_sysctl_vdetach(struct ieee80211vap *vap)
}
}
+#defineMS(_v, _f) (((_v) & _f##_M) >> _f##_S)
+int
+ieee80211_com_vincref(struct ieee80211vap *vap)
+{
+ uint32_t ostate;
+
+ ostate = atomic_fetchadd_32(&vap->iv_com_state, IEEE80211_COM_REF_ADD);
+
+ if (ostate & IEEE80211_COM_DETACHED) {
+ atomic_subtract_32(&vap->iv_com_state, IEEE80211_COM_REF_ADD);
+ return (ENETDOWN);
+ }
+
+ if (MS(ostate, IEEE80211_COM_REF) == IEEE80211_COM_REF_MAX) {
+ atomic_subtract_32(&vap->iv_com_state, IEEE80211_COM_REF_ADD);
+ return (EOVERFLOW);
+ }
+
+ return (0);
+}
+
+void
+ieee80211_com_vdecref(struct ieee80211vap *vap)
+{
+ uint32_t ostate;
+
+ ostate = atomic_fetchadd_32(&vap->iv_com_state, -IEEE80211_COM_REF_ADD);
+
+ KASSERT(MS(ostate, IEEE80211_COM_REF) != 0,
+ ("com reference counter underflow"));
+
+ (void) ostate;
+}
+
+void
+ieee80211_com_vdetach(struct ieee80211vap *vap)
+{
+ int sleep_time;
+
+ sleep_time = msecs_to_ticks(250);
+ if (sleep_time == 0)
+ sleep_time = 1;
+
+ atomic_set_32(&vap->iv_com_state, IEEE80211_COM_DETACHED);
+ while (MS(atomic_load_32(&vap->iv_com_state), IEEE80211_COM_REF) != 0)
+ pause("comref", sleep_time);
+}
+#undef MS
+
int
ieee80211_node_dectestref(struct ieee80211_node *ni)
{
Modified: head/sys/net80211/ieee80211_freebsd.h
==
--- head/sys/net80211/ieee80211_freebsd.h Sun Jan 20 13:16:36 2019
(r343212)
+++ head/sys/net80211/ieee80211_freebsd.h Sun Jan 20 13:39:18 2019
(r343213)
@@ -224,6 +224,11 @@ typedef struct mtx ieee80211_rt_lock_t;
*/
#include
+struct ieee80211vap;
+intieee80211_com_vincref(struct ieee80211vap *);
+void ieee80211_com_vdecref(struct ieee80211vap *);
+void ieee80211_com_vdetach(struct ieee80211vap *);
+
#define ieee80211_node_initref(_ni) \
do { ((_ni)->ni_refcnt = 1); } while (0)
#define ieee80211_node_incref(_ni) \
@@ -235,7 +240,6 @@ int ieee80211_node_dectestref(struct ieee80211_node *n
#defineieee80211_node_refcnt(_ni) (_ni)->ni_refcnt
struct ifqueue;
-struct ieee80211vap;
void ieee80211_drain_ifq(struct ifqueue *);
void ieee80211_flush_ifq(struct ifqueue *, struct ieee80211vap *);
Modified: head/sys/net80211/ieee80211_ioctl.c
==