mirror of
https://github.com/Divested-Mobile/DivestOS-Build.git
synced 2024-12-11 08:54:28 -05:00
161 lines
6.4 KiB
Diff
161 lines
6.4 KiB
Diff
|
From 1d147bfa64293b2723c4fec50922168658e613ba Mon Sep 17 00:00:00 2001
|
||
|
From: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
|
||
|
Date: Thu, 20 Feb 2014 09:22:11 +0200
|
||
|
Subject: 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>
|
||
|
---
|
||
|
net/mac80211/sta_info.c | 4 ++++
|
||
|
net/mac80211/sta_info.h | 7 +++----
|
||
|
net/mac80211/tx.c | 15 +++++++++++++++
|
||
|
3 files changed, 22 insertions(+), 4 deletions(-)
|
||
|
|
||
|
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
|
||
|
index decd30c..62a5f08 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,
|
||
|
rcu_read_unlock();
|
||
|
|
||
|
spin_lock_init(&sta->lock);
|
||
|
+ spin_lock_init(&sta->ps_lock);
|
||
|
INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
|
||
|
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
|
||
|
mutex_init(&sta->ampdu_mlme.mtx);
|
||
|
@@ -1109,6 +1110,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
|
||
|
|
||
|
skb_queue_head_init(&pending);
|
||
|
|
||
|
+ /* sync with ieee80211_tx_h_unicast_ps_buf */
|
||
|
+ spin_lock(&sta->ps_lock);
|
||
|
/* Send all buffered frames to the station */
|
||
|
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
|
||
|
int count = skb_queue_len(&pending), tmp;
|
||
|
@@ -1128,6 +1131,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
|
||
|
}
|
||
|
|
||
|
ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta);
|
||
|
+ spin_unlock(&sta->ps_lock);
|
||
|
|
||
|
/* This station just woke up and isn't aware of our SMPS state */
|
||
|
if (!ieee80211_smps_is_restrictive(sta->known_smps_mode,
|
||
|
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
|
||
|
index d77ff70..d3a6d82 100644
|
||
|
--- a/net/mac80211/sta_info.h
|
||
|
+++ b/net/mac80211/sta_info.h
|
||
|
@@ -267,6 +267,7 @@ struct ieee80211_tx_latency_stat {
|
||
|
* @drv_unblock_wk: used for driver PS unblocking
|
||
|
* @listen_interval: listen interval of this station, when we're acting as AP
|
||
|
* @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly
|
||
|
+ * @ps_lock: used for powersave (when mac80211 is the AP) related locking
|
||
|
* @ps_tx_buf: buffers (per AC) of frames to transmit to this station
|
||
|
* when it leaves power saving state or polls
|
||
|
* @tx_filtered: buffers (per AC) of frames we already tried to
|
||
|
@@ -356,10 +357,8 @@ struct sta_info {
|
||
|
/* use the accessors defined below */
|
||
|
unsigned long _flags;
|
||
|
|
||
|
- /*
|
||
|
- * STA powersave frame queues, no more than the internal
|
||
|
- * locking required.
|
||
|
- */
|
||
|
+ /* STA powersave lock and frame queues */
|
||
|
+ spinlock_t ps_lock;
|
||
|
struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS];
|
||
|
struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS];
|
||
|
unsigned long driver_buffered_tids;
|
||
|
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
|
||
|
index 97a02d3..4080c61 100644
|
||
|
--- a/net/mac80211/tx.c
|
||
|
+++ b/net/mac80211/tx.c
|
||
|
@@ -478,6 +478,20 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
|
||
|
sta->sta.addr, sta->sta.aid, ac);
|
||
|
if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
|
||
|
purge_old_ps_buffers(tx->local);
|
||
|
+
|
||
|
+ /* sync with ieee80211_sta_ps_deliver_wakeup */
|
||
|
+ spin_lock(&sta->ps_lock);
|
||
|
+ /*
|
||
|
+ * STA woke up the meantime and all the frames on ps_tx_buf have
|
||
|
+ * been queued to pending queue. No reordering can happen, go
|
||
|
+ * ahead and Tx the packet.
|
||
|
+ */
|
||
|
+ if (!test_sta_flag(sta, WLAN_STA_PS_STA) &&
|
||
|
+ !test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
|
||
|
+ spin_unlock(&sta->ps_lock);
|
||
|
+ return TX_CONTINUE;
|
||
|
+ }
|
||
|
+
|
||
|
if (skb_queue_len(&sta->ps_tx_buf[ac]) >= STA_MAX_TX_BUFFER) {
|
||
|
struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf[ac]);
|
||
|
ps_dbg(tx->sdata,
|
||
|
@@ -492,6 +506,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
|
||
|
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
|
||
|
info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
|
||
|
skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb);
|
||
|
+ spin_unlock(&sta->ps_lock);
|
||
|
|
||
|
if (!timer_pending(&local->sta_cleanup))
|
||
|
mod_timer(&local->sta_cleanup,
|
||
|
--
|
||
|
cgit v1.1
|
||
|
|