summaryrefslogtreecommitdiffstats
path: root/package/kernel/mac80211/patches/301-mac80211-lock-rate-control.patch
diff options
context:
space:
mode:
Diffstat (limited to 'package/kernel/mac80211/patches/301-mac80211-lock-rate-control.patch')
-rw-r--r--package/kernel/mac80211/patches/301-mac80211-lock-rate-control.patch125
1 files changed, 125 insertions, 0 deletions
diff --git a/package/kernel/mac80211/patches/301-mac80211-lock-rate-control.patch b/package/kernel/mac80211/patches/301-mac80211-lock-rate-control.patch
new file mode 100644
index 0000000000..6b6903215d
--- /dev/null
+++ b/package/kernel/mac80211/patches/301-mac80211-lock-rate-control.patch
@@ -0,0 +1,125 @@
+From: Johannes Berg <johannes.berg@intel.com>
+Date: Wed, 11 Mar 2015 09:14:15 +0100
+Subject: [PATCH] mac80211: lock rate control
+
+Both minstrel (reported by Sven Eckelmann) and the iwlwifi rate
+control aren't properly taking concurrency into account. It's
+likely that the same is true for other rate control algorithms.
+
+In the case of minstrel this manifests itself in crashes when an
+update and other data access are run concurrently, for example
+when the stations change bandwidth or similar. In iwlwifi, this
+can cause firmware crashes.
+
+Since fixing all rate control algorithms will be very difficult,
+just provide locking for invocations. This protects the internal
+data structures the algorithms maintain.
+
+I've manipulated hostapd to test this, by having it change its
+advertised bandwidth roughly ever 150ms. At the same time, I'm
+running a flood ping between the client and the AP, which causes
+this race of update vs. get_rate/status to easily happen on the
+client. With this change, the system survives this test.
+
+Reported-by: Sven Eckelmann <sven@open-mesh.com>
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+---
+
+--- a/net/mac80211/rate.c
++++ b/net/mac80211/rate.c
+@@ -683,7 +683,13 @@ void rate_control_get_rate(struct ieee80
+ if (sdata->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
+ return;
+
+- ref->ops->get_rate(ref->priv, ista, priv_sta, txrc);
++ if (ista) {
++ spin_lock_bh(&sta->rate_ctrl_lock);
++ ref->ops->get_rate(ref->priv, ista, priv_sta, txrc);
++ spin_unlock_bh(&sta->rate_ctrl_lock);
++ } else {
++ ref->ops->get_rate(ref->priv, NULL, NULL, txrc);
++ }
+
+ if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_RC_TABLE)
+ return;
+--- a/net/mac80211/rate.h
++++ b/net/mac80211/rate.h
+@@ -42,10 +42,12 @@ static inline void rate_control_tx_statu
+ if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
+ return;
+
++ spin_lock_bh(&sta->rate_ctrl_lock);
+ if (ref->ops->tx_status)
+ ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
+ else
+ ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info);
++ spin_unlock_bh(&sta->rate_ctrl_lock);
+ }
+
+ static inline void
+@@ -64,7 +66,9 @@ rate_control_tx_status_noskb(struct ieee
+ if (WARN_ON_ONCE(!ref->ops->tx_status_noskb))
+ return;
+
++ spin_lock_bh(&sta->rate_ctrl_lock);
+ ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info);
++ spin_unlock_bh(&sta->rate_ctrl_lock);
+ }
+
+ static inline void rate_control_rate_init(struct sta_info *sta)
+@@ -91,8 +95,10 @@ static inline void rate_control_rate_ini
+
+ sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
+
++ spin_lock_bh(&sta->rate_ctrl_lock);
+ ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
+ priv_sta);
++ spin_unlock_bh(&sta->rate_ctrl_lock);
+ rcu_read_unlock();
+ set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
+ }
+@@ -115,18 +121,20 @@ static inline void rate_control_rate_upd
+ return;
+ }
+
++ spin_lock_bh(&sta->rate_ctrl_lock);
+ ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
+ ista, priv_sta, changed);
++ spin_unlock_bh(&sta->rate_ctrl_lock);
+ rcu_read_unlock();
+ }
+ drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
+ }
+
+ static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
+- struct ieee80211_sta *sta,
+- gfp_t gfp)
++ struct sta_info *sta, gfp_t gfp)
+ {
+- return ref->ops->alloc_sta(ref->priv, sta, gfp);
++ spin_lock_init(&sta->rate_ctrl_lock);
++ return ref->ops->alloc_sta(ref->priv, &sta->sta, gfp);
+ }
+
+ static inline void rate_control_free_sta(struct sta_info *sta)
+--- a/net/mac80211/sta_info.c
++++ b/net/mac80211/sta_info.c
+@@ -280,7 +280,7 @@ static int sta_prepare_rate_control(stru
+
+ sta->rate_ctrl = local->rate_ctrl;
+ sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl,
+- &sta->sta, gfp);
++ sta, gfp);
+ if (!sta->rate_ctrl_priv)
+ return -ENOMEM;
+
+--- a/net/mac80211/sta_info.h
++++ b/net/mac80211/sta_info.h
+@@ -348,6 +348,7 @@ struct sta_info {
+ u8 ptk_idx;
+ struct rate_control_ref *rate_ctrl;
+ void *rate_ctrl_priv;
++ spinlock_t rate_ctrl_lock;
+ spinlock_t lock;
+
+ struct work_struct drv_deliver_wk;