Skip to content

Commit 1d147bf

Browse files
egrumbachjmberg-intel
authored andcommitted
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: e5900da EBX: 00000000 ECX: 00000001 EDX: 00000000 ESI: e41d00c0 EDI: e5900da 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 e5900da ff76a6ec ebe458d8 00000000 e41d00c0 e5900da 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: [email protected] Reported-by: Yaara Rozenblum <[email protected]> Signed-off-by: Emmanuel Grumbach <[email protected]> Reviewed-by: Stanislaw Gruszka <[email protected]> [reword commit log, use a separate lock] Signed-off-by: Johannes Berg <[email protected]>
1 parent 50c11eb commit 1d147bf

File tree

3 files changed

+22
-4
lines changed

3 files changed

+22
-4
lines changed

net/mac80211/sta_info.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -330,6 +330,7 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
330330
rcu_read_unlock();
331331

332332
spin_lock_init(&sta->lock);
333+
spin_lock_init(&sta->ps_lock);
333334
INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
334335
INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
335336
mutex_init(&sta->ampdu_mlme.mtx);
@@ -1109,6 +1110,8 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
11091110

11101111
skb_queue_head_init(&pending);
11111112

1113+
/* sync with ieee80211_tx_h_unicast_ps_buf */
1114+
spin_lock(&sta->ps_lock);
11121115
/* Send all buffered frames to the station */
11131116
for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) {
11141117
int count = skb_queue_len(&pending), tmp;
@@ -1128,6 +1131,7 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta)
11281131
}
11291132

11301133
ieee80211_add_pending_skbs_fn(local, &pending, clear_sta_ps_flags, sta);
1134+
spin_unlock(&sta->ps_lock);
11311135

11321136
/* This station just woke up and isn't aware of our SMPS state */
11331137
if (!ieee80211_smps_is_restrictive(sta->known_smps_mode,

net/mac80211/sta_info.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ struct ieee80211_tx_latency_stat {
267267
* @drv_unblock_wk: used for driver PS unblocking
268268
* @listen_interval: listen interval of this station, when we're acting as AP
269269
* @_flags: STA flags, see &enum ieee80211_sta_info_flags, do not use directly
270+
* @ps_lock: used for powersave (when mac80211 is the AP) related locking
270271
* @ps_tx_buf: buffers (per AC) of frames to transmit to this station
271272
* when it leaves power saving state or polls
272273
* @tx_filtered: buffers (per AC) of frames we already tried to
@@ -356,10 +357,8 @@ struct sta_info {
356357
/* use the accessors defined below */
357358
unsigned long _flags;
358359

359-
/*
360-
* STA powersave frame queues, no more than the internal
361-
* locking required.
362-
*/
360+
/* STA powersave lock and frame queues */
361+
spinlock_t ps_lock;
363362
struct sk_buff_head ps_tx_buf[IEEE80211_NUM_ACS];
364363
struct sk_buff_head tx_filtered[IEEE80211_NUM_ACS];
365364
unsigned long driver_buffered_tids;

net/mac80211/tx.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,20 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
478478
sta->sta.addr, sta->sta.aid, ac);
479479
if (tx->local->total_ps_buffered >= TOTAL_MAX_TX_BUFFER)
480480
purge_old_ps_buffers(tx->local);
481+
482+
/* sync with ieee80211_sta_ps_deliver_wakeup */
483+
spin_lock(&sta->ps_lock);
484+
/*
485+
* STA woke up the meantime and all the frames on ps_tx_buf have
486+
* been queued to pending queue. No reordering can happen, go
487+
* ahead and Tx the packet.
488+
*/
489+
if (!test_sta_flag(sta, WLAN_STA_PS_STA) &&
490+
!test_sta_flag(sta, WLAN_STA_PS_DRIVER)) {
491+
spin_unlock(&sta->ps_lock);
492+
return TX_CONTINUE;
493+
}
494+
481495
if (skb_queue_len(&sta->ps_tx_buf[ac]) >= STA_MAX_TX_BUFFER) {
482496
struct sk_buff *old = skb_dequeue(&sta->ps_tx_buf[ac]);
483497
ps_dbg(tx->sdata,
@@ -492,6 +506,7 @@ ieee80211_tx_h_unicast_ps_buf(struct ieee80211_tx_data *tx)
492506
info->flags |= IEEE80211_TX_INTFL_NEED_TXPROCESSING;
493507
info->flags &= ~IEEE80211_TX_TEMPORARY_FLAGS;
494508
skb_queue_tail(&sta->ps_tx_buf[ac], tx->skb);
509+
spin_unlock(&sta->ps_lock);
495510

496511
if (!timer_pending(&local->sta_cleanup))
497512
mod_timer(&local->sta_cleanup,

0 commit comments

Comments
 (0)