aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/sta_info.c
diff options
context:
space:
mode:
authorEmmanuel Grumbach <emmanuel.grumbach@intel.com>2014-02-20 02:22:11 -0500
committerJohannes Berg <johannes.berg@intel.com>2014-02-20 04:32:29 -0500
commit1d147bfa64293b2723c4fec50922168658e613ba (patch)
treef129b8d8b612181ba5c554e89201420daefe2484 /net/mac80211/sta_info.c
parent50c11eb9982554e9f99b7bab322c517cbe5ce1a1 (diff)
mac80211: fix AP powersave TX vs. wakeup race
There is a race between the TX path and the STA wakeup: while a station is sleeping, mac80211 buffers frames until it wakes up, then the frames are transmitted. However, the RX and TX path are concurrent, so the packet indicating wakeup can be processed while a packet is being transmitted. This can lead to a situation where the buffered frames list is emptied on the one side, while a frame is being added on the other side, as the station is still seen as sleeping in the TX path. As a result, the newly added frame will not be send anytime soon. It might be sent much later (and out of order) when the station goes to sleep and wakes up the next time. Additionally, it can lead to the crash below. Fix all this by synchronising both paths with a new lock. Both path are not fastpath since they handle PS situations. In a later patch we'll remove the extra skb queue locks to reduce locking overhead. BUG: unable to handle kernel NULL pointer dereference at 000000b0 IP: [<ff6f1791>] ieee80211_report_used_skb+0x11/0x3e0 [mac80211] *pde = 00000000 Oops: 0000 [#1] SMP DEBUG_PAGEALLOC EIP: 0060:[<ff6f1791>] EFLAGS: 00210282 CPU: 1 EIP is at ieee80211_report_used_skb+0x11/0x3e0 [mac80211] EAX: e5900da0 EBX: 00000000 ECX: 00000001 EDX: 00000000 ESI: e41d00c0 EDI: e5900da0 EBP: ebe458e4 ESP: ebe458b0 DS: 007b ES: 007b FS: 00d8 GS: 00e0 SS: 0068 CR0: 8005003b CR2: 000000b0 CR3: 25a78000 CR4: 000407d0 DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000 DR6: ffff0ff0 DR7: 00000400 Process iperf (pid: 3934, ti=ebe44000 task=e757c0b0 task.ti=ebe44000) iwlwifi 0000:02:00.0: I iwl_pcie_enqueue_hcmd Sending command LQ_CMD (#4e), seq: 0x0903, 92 bytes at 3[3]:9 Stack: e403b32c ebe458c4 00200002 00200286 e403b338 ebe458cc c10960bb e5900da0 ff76a6ec ebe458d8 00000000 e41d00c0 e5900da0 ebe458f0 ff6f1b75 e403b210 ebe4598c ff723dc1 00000000 ff76a6ec e597c978 e403b758 00000002 00000002 Call Trace: [<ff6f1b75>] ieee80211_free_txskb+0x15/0x20 [mac80211] [<ff723dc1>] invoke_tx_handlers+0x1661/0x1780 [mac80211] [<ff7248a5>] ieee80211_tx+0x75/0x100 [mac80211] [<ff7249bf>] ieee80211_xmit+0x8f/0xc0 [mac80211] [<ff72550e>] ieee80211_subif_start_xmit+0x4fe/0xe20 [mac80211] [<c149ef70>] dev_hard_start_xmit+0x450/0x950 [<c14b9aa9>] sch_direct_xmit+0xa9/0x250 [<c14b9c9b>] __qdisc_run+0x4b/0x150 [<c149f732>] dev_queue_xmit+0x2c2/0xca0 Cc: stable@vger.kernel.org Reported-by: Yaara Rozenblum <yaara.rozenblum@intel.com> Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com> Reviewed-by: Stanislaw Gruszka <sgruszka@redhat.com> [reword commit log, use a separate lock] Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/sta_info.c')
-rw-r--r--net/mac80211/sta_info.c4
1 files changed, 4 insertions, 0 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index decd30c1e290..62a5f0889583 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -330,6 +330,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
330 rcu_read_unlock(); 330 rcu_read_unlock();
331 331
332 spin_lock_init(&sta->lock); 332 spin_lock_init(&sta->lock);
333 spin_lock_init(&sta->ps_lock);
333 INIT_WORK(&sta->drv_unblock_wk, sta_unblock); 334 INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
334 INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); 335 INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
335 mutex_init(&sta->ampdu_mlme.mtx); 336 mutex_init(&sta->ampdu_mlme.mtx);
@@ -1109,6 +1110,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
1109 1110
1110 skb_queue_head_init(&pending); 1111 skb_queue_head_init(&pending);
1111 1112
1113 /* sync with ieee80211_tx_h_unicast_ps_buf */
1114 spin_lock(&sta->ps_lock);
1112 /* Send all buffered frames to the station */ 1115 /* Send all buffered frames to the station */
1113 for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 1116 for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
1114 int count = skb_queue_len(&pending), tmp; 1117 int count = skb_queue_len(&pending), tmp;
@@ -1128,6 +1131,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
1128 } 1131 }
1129 1132
1130 ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta); 1133 ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta);
1134 spin_unlock(&sta->ps_lock);
1131 1135
1132 /* This station just woke up and isn't aware of our SMPS state */ 1136 /* This station just woke up and isn't aware of our SMPS state */
1133 if (!ieee80211_smps_is_restrictive(sta->known_smps_mode, 1137 if (!ieee80211_smps_is_restrictive(sta->known_smps_mode,