svn commit: r343489 - stable/11/sys/net80211
Andriy Voskoboinyk
avos at FreeBSD.org
Sun Jan 27 13:03:50 UTC 2019
Author: avos
Date: Sun Jan 27 13:03:48 2019
New Revision: 343489
URL: https://svnweb.freebsd.org/changeset/base/343489
Log:
MFC r343213:
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 KBI stability the last element of iv_spare[] array was reused.
Modified:
stable/11/sys/net80211/ieee80211.c
stable/11/sys/net80211/ieee80211_freebsd.c
stable/11/sys/net80211/ieee80211_freebsd.h
stable/11/sys/net80211/ieee80211_ioctl.c
stable/11/sys/net80211/ieee80211_var.h
Directory Properties:
stable/11/ (props changed)
Modified: stable/11/sys/net80211/ieee80211.c
==============================================================================
--- stable/11/sys/net80211/ieee80211.c Sun Jan 27 12:39:35 2019 (r343488)
+++ stable/11/sys/net80211/ieee80211.c Sun Jan 27 13:03:48 2019 (r343489)
@@ -375,8 +375,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: stable/11/sys/net80211/ieee80211_freebsd.c
==============================================================================
--- stable/11/sys/net80211/ieee80211_freebsd.c Sun Jan 27 12:39:35 2019 (r343488)
+++ stable/11/sys/net80211/ieee80211_freebsd.c Sun Jan 27 13:03:48 2019 (r343489)
@@ -305,6 +305,55 @@ ieee80211_sysctl_vdetach(struct ieee80211vap *vap)
}
}
+#define MS(_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: stable/11/sys/net80211/ieee80211_freebsd.h
==============================================================================
--- stable/11/sys/net80211/ieee80211_freebsd.h Sun Jan 27 12:39:35 2019 (r343488)
+++ stable/11/sys/net80211/ieee80211_freebsd.h Sun Jan 27 13:03:48 2019 (r343489)
@@ -222,6 +222,11 @@ typedef struct mtx ieee80211_rt_lock_t;
*/
#include <machine/atomic.h>
+struct ieee80211vap;
+int ieee80211_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) \
@@ -233,7 +238,6 @@ int ieee80211_node_dectestref(struct ieee80211_node *n
#define ieee80211_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: stable/11/sys/net80211/ieee80211_ioctl.c
==============================================================================
--- stable/11/sys/net80211/ieee80211_ioctl.c Sun Jan 27 12:39:35 2019 (r343488)
+++ stable/11/sys/net80211/ieee80211_ioctl.c Sun Jan 27 13:03:48 2019 (r343489)
@@ -3337,10 +3337,14 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t
{
struct ieee80211vap *vap = ifp->if_softc;
struct ieee80211com *ic = vap->iv_ic;
- int error = 0, wait = 0;
+ int error = 0, wait = 0, ic_used;
struct ifreq *ifr;
struct ifaddr *ifa; /* XXX */
+ ic_used = (cmd != SIOCSIFMTU && cmd != SIOCG80211STATS);
+ if (ic_used && (error = ieee80211_com_vincref(vap)) != 0)
+ return (error);
+
switch (cmd) {
case SIOCSIFFLAGS:
IEEE80211_LOCK(ic);
@@ -3477,5 +3481,9 @@ ieee80211_ioctl(struct ifnet *ifp, u_long cmd, caddr_t
error = ether_ioctl(ifp, cmd, data);
break;
}
+
+ if (ic_used)
+ ieee80211_com_vdecref(vap);
+
return (error);
}
Modified: stable/11/sys/net80211/ieee80211_var.h
==============================================================================
--- stable/11/sys/net80211/ieee80211_var.h Sun Jan 27 12:39:35 2019 (r343488)
+++ stable/11/sys/net80211/ieee80211_var.h Sun Jan 27 13:03:48 2019 (r343489)
@@ -523,7 +523,9 @@ struct ieee80211vap {
/* 802.3 output method for raw frame xmit */
int (*iv_output)(struct ifnet *, struct mbuf *,
const struct sockaddr *, struct route *);
- uint64_t iv_spare[6];
+ uint64_t iv_spare[5];
+ uint32_t iv_com_state; /* com usage / detached flag */
+ uint32_t iv_spare1;
};
MALLOC_DECLARE(M_80211_VAP);
@@ -696,6 +698,12 @@ MALLOC_DECLARE(M_80211_VAP);
#define IEEE80211_C_HTCAP_BITS \
"\20\1LDPC\2CHWIDTH40\5GREENFIELD\6SHORTGI20\7SHORTGI40\10TXSTBC" \
"\21AMPDU\22AMSDU\23HT\24SMPS\25RIFS"
+
+#define IEEE80211_COM_DETACHED 0x00000001 /* ieee80211_ifdetach called */
+#define IEEE80211_COM_REF_ADD 0x00000002 /* add / remove reference */
+#define IEEE80211_COM_REF_M 0xfffffffe /* reference counter bits */
+#define IEEE80211_COM_REF_S 1
+#define IEEE80211_COM_REF_MAX (IEEE80211_COM_REF_M >> IEEE80211_COM_REF_S)
int ic_printf(struct ieee80211com *, const char *, ...) __printflike(2, 3);
void ieee80211_ifattach(struct ieee80211com *);
More information about the svn-src-stable
mailing list