add chaos_calmer branch
[15.05/openwrt.git] / package / kernel / mac80211 / patches / 301-mac80211-lock-rate-control.patch
1 From: Johannes Berg <johannes.berg@intel.com>
2 Date: Wed, 11 Mar 2015 09:14:15 +0100
3 Subject: [PATCH] mac80211: lock rate control
4
5 Both minstrel (reported by Sven Eckelmann) and the iwlwifi rate
6 control aren't properly taking concurrency into account. It's
7 likely that the same is true for other rate control algorithms.
8
9 In the case of minstrel this manifests itself in crashes when an
10 update and other data access are run concurrently, for example
11 when the stations change bandwidth or similar. In iwlwifi, this
12 can cause firmware crashes.
13
14 Since fixing all rate control algorithms will be very difficult,
15 just provide locking for invocations. This protects the internal
16 data structures the algorithms maintain.
17
18 I've manipulated hostapd to test this, by having it change its
19 advertised bandwidth roughly ever 150ms. At the same time, I'm
20 running a flood ping between the client and the AP, which causes
21 this race of update vs. get_rate/status to easily happen on the
22 client. With this change, the system survives this test.
23
24 Reported-by: Sven Eckelmann <sven@open-mesh.com>
25 Signed-off-by: Johannes Berg <johannes.berg@intel.com>
26 ---
27
28 --- a/net/mac80211/rate.c
29 +++ b/net/mac80211/rate.c
30 @@ -683,7 +683,13 @@ void rate_control_get_rate(struct ieee80
31         if (sdata->local->hw.flags & IEEE80211_HW_HAS_RATE_CONTROL)
32                 return;
33  
34 -       ref->ops->get_rate(ref->priv, ista, priv_sta, txrc);
35 +       if (ista) {
36 +               spin_lock_bh(&sta->rate_ctrl_lock);
37 +               ref->ops->get_rate(ref->priv, ista, priv_sta, txrc);
38 +               spin_unlock_bh(&sta->rate_ctrl_lock);
39 +       } else {
40 +               ref->ops->get_rate(ref->priv, NULL, NULL, txrc);
41 +       }
42  
43         if (sdata->local->hw.flags & IEEE80211_HW_SUPPORTS_RC_TABLE)
44                 return;
45 --- a/net/mac80211/rate.h
46 +++ b/net/mac80211/rate.h
47 @@ -42,10 +42,12 @@ static inline void rate_control_tx_statu
48         if (!ref || !test_sta_flag(sta, WLAN_STA_RATE_CONTROL))
49                 return;
50  
51 +       spin_lock_bh(&sta->rate_ctrl_lock);
52         if (ref->ops->tx_status)
53                 ref->ops->tx_status(ref->priv, sband, ista, priv_sta, skb);
54         else
55                 ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info);
56 +       spin_unlock_bh(&sta->rate_ctrl_lock);
57  }
58  
59  static inline void
60 @@ -64,7 +66,9 @@ rate_control_tx_status_noskb(struct ieee
61         if (WARN_ON_ONCE(!ref->ops->tx_status_noskb))
62                 return;
63  
64 +       spin_lock_bh(&sta->rate_ctrl_lock);
65         ref->ops->tx_status_noskb(ref->priv, sband, ista, priv_sta, info);
66 +       spin_unlock_bh(&sta->rate_ctrl_lock);
67  }
68  
69  static inline void rate_control_rate_init(struct sta_info *sta)
70 @@ -91,8 +95,10 @@ static inline void rate_control_rate_ini
71  
72         sband = local->hw.wiphy->bands[chanctx_conf->def.chan->band];
73  
74 +       spin_lock_bh(&sta->rate_ctrl_lock);
75         ref->ops->rate_init(ref->priv, sband, &chanctx_conf->def, ista,
76                             priv_sta);
77 +       spin_unlock_bh(&sta->rate_ctrl_lock);
78         rcu_read_unlock();
79         set_sta_flag(sta, WLAN_STA_RATE_CONTROL);
80  }
81 @@ -115,18 +121,20 @@ static inline void rate_control_rate_upd
82                         return;
83                 }
84  
85 +               spin_lock_bh(&sta->rate_ctrl_lock);
86                 ref->ops->rate_update(ref->priv, sband, &chanctx_conf->def,
87                                       ista, priv_sta, changed);
88 +               spin_unlock_bh(&sta->rate_ctrl_lock);
89                 rcu_read_unlock();
90         }
91         drv_sta_rc_update(local, sta->sdata, &sta->sta, changed);
92  }
93  
94  static inline void *rate_control_alloc_sta(struct rate_control_ref *ref,
95 -                                          struct ieee80211_sta *sta,
96 -                                          gfp_t gfp)
97 +                                          struct sta_info *sta, gfp_t gfp)
98  {
99 -       return ref->ops->alloc_sta(ref->priv, sta, gfp);
100 +       spin_lock_init(&sta->rate_ctrl_lock);
101 +       return ref->ops->alloc_sta(ref->priv, &sta->sta, gfp);
102  }
103  
104  static inline void rate_control_free_sta(struct sta_info *sta)
105 --- a/net/mac80211/sta_info.c
106 +++ b/net/mac80211/sta_info.c
107 @@ -286,7 +286,7 @@ static int sta_prepare_rate_control(stru
108  
109         sta->rate_ctrl = local->rate_ctrl;
110         sta->rate_ctrl_priv = rate_control_alloc_sta(sta->rate_ctrl,
111 -                                                    &sta->sta, gfp);
112 +                                                    sta, gfp);
113         if (!sta->rate_ctrl_priv)
114                 return -ENOMEM;
115  
116 --- a/net/mac80211/sta_info.h
117 +++ b/net/mac80211/sta_info.h
118 @@ -349,6 +349,7 @@ struct sta_info {
119         u8 ptk_idx;
120         struct rate_control_ref *rate_ctrl;
121         void *rate_ctrl_priv;
122 +       spinlock_t rate_ctrl_lock;
123         spinlock_t lock;
124  
125         struct work_struct drv_deliver_wk;