From 78f1a8b758d57c2d2c9f3db7199cd30803854c82 Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Mon, 27 Jul 2009 08:38:25 -0700 Subject: mac80211: do not queue work after suspend in the dynamic ps timer Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/mlme.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net/mac80211') diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index aca22b00b6a3..07e7e41816be 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -721,7 +721,7 @@ void ieee80211_dynamic_ps_timer(unsigned long data) { struct ieee80211_local *local = (void *) data; - if (local->quiescing) + if (local->quiescing || local->suspended) return; queue_work(local->hw.workqueue, &local->dynamic_ps_enable_work); -- cgit v1.2.2 From 89c3a8aca28e6d57f2ae945d97858a372d624b81 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Tue, 28 Jul 2009 18:10:17 +0200 Subject: mac80211: fix suspend Jan reported that his b43-based laptop hangs during suspend. The problem turned out to be mac80211 asking the driver to stop the hardware before removing interfaces, and interface removal caused b43 to touch the hardware (while down, which causes the hang). This patch fixes mac80211 to do reorder these operations to have them in the correct order -- first remove interfaces and then stop the hardware. Some more code is necessary to be able to do so in a race-free manner, in particular it is necessary to not process frames received during quiescing. Fixes http://bugzilla.kernel.org/show_bug.cgi?id=13337. Reported-by: Jan Scholz Signed-off-by: Johannes Berg Signed-off-by: John W. Linville --- net/mac80211/pm.c | 24 +++++++++++++++--------- net/mac80211/rx.c | 12 ++++++++++++ 2 files changed, 27 insertions(+), 9 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/pm.c b/net/mac80211/pm.c index 7a549f9deb96..5e3d476972f9 100644 --- a/net/mac80211/pm.c +++ b/net/mac80211/pm.c @@ -55,15 +55,6 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) rcu_read_unlock(); - /* flush again, in case driver queued work */ - flush_workqueue(local->hw.workqueue); - - /* stop hardware - this must stop RX */ - if (local->open_count) { - ieee80211_led_radio(local, false); - drv_stop(local); - } - /* remove STAs */ spin_lock_irqsave(&local->sta_lock, flags); list_for_each_entry(sta, &local->sta_list, list) { @@ -111,7 +102,22 @@ int __ieee80211_suspend(struct ieee80211_hw *hw) drv_remove_interface(local, &conf); } + /* stop hardware - this must stop RX */ + if (local->open_count) { + ieee80211_led_radio(local, false); + drv_stop(local); + } + + /* + * flush again, in case driver queued work -- it + * shouldn't be doing (or cancel everything in the + * stop callback) that but better safe than sorry. + */ + flush_workqueue(local->hw.workqueue); + local->suspended = true; + /* need suspended to be visible before quiescing is false */ + barrier(); local->quiescing = false; return 0; diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c index de5bba7f910a..0936fc24942d 100644 --- a/net/mac80211/rx.c +++ b/net/mac80211/rx.c @@ -2453,6 +2453,18 @@ void __ieee80211_rx(struct ieee80211_hw *hw, struct sk_buff *skb, return; } + /* + * If we're suspending, it is possible although not too likely + * that we'd be receiving frames after having already partially + * quiesced the stack. We can't process such frames then since + * that might, for example, cause stations to be added or other + * driver callbacks be invoked. + */ + if (unlikely(local->quiescing || local->suspended)) { + kfree_skb(skb); + return; + } + if (status->flag & RX_FLAG_HT) { /* rate_idx is MCS index */ if (WARN_ON(status->rate_idx < 0 || -- cgit v1.2.2 From 416fbdff2137e8d8cc8f23f517bee3a26b11526f Mon Sep 17 00:00:00 2001 From: "Luis R. Rodriguez" Date: Tue, 11 Aug 2009 13:10:33 -0700 Subject: mac80211: fix panic when splicing unprepared TIDs We splice skbs from the pending queue for a TID onto the local pending queue when tearing down a block ack request. This is not necessary unless we actually have received a request to start a block ack request (rate control, for example). If we never received that request we should not be splicing the tid pending queue as it would be null, causing a panic. Not sure yet how exactly we allowed through a call when the tid state does not have at least HT_ADDBA_REQUESTED_MSK set, that will require some further review as it is not quite obvious. For more information see the bug report: http://bugzilla.kernel.org/show_bug.cgi?id=13922 This fixes this oops: BUG: unable to handle kernel NULL pointer dereference at 00000030 IP: [] ieee80211_agg_splice_packets+0x40/0xc0 [mac80211] *pdpt = 0000000002d1e001 *pde = 0000000000000000 Thread overran stack, or stack corrupted Oops: 0000 [#1] SMP last sysfs file: /sys/module/aes_generic/initstate Modules linked in: Pid: 0, comm: swapper Not tainted (2.6.31-rc5-wl #2) Dell DV051 EIP: 0060:[] EFLAGS: 00010292 CPU: 0 EIP is at ieee80211_agg_splice_packets+0x40/0xc0 [mac80211] EAX: 00000030 EBX: 0000004c ECX: 00000003 EDX: 00000000 ESI: c1c98000 EDI: f745a1c0 EBP: c076be58 ESP: c076be38 DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068 Process swapper (pid: 0, ti=c076a000 task=c0709160 task.ti=c076a000) Stack: Call Trace: [] ? ieee80211_stop_tx_ba_cb+0xab/0x150 [mac80211] [] ? ieee80211_tasklet_handler+0xce/0x110 [mac80211] [] ? net_rx_action+0xef/0x1d0 [] ? tasklet_action+0x58/0xc0 [] ? __do_softirq+0xc2/0x190 [] ? handle_IRQ_event+0x58/0x140 [] ? ack_apic_level+0x7e/0x270 [] ? do_softirq+0x3d/0x40 [] ? irq_exit+0x65/0x90 [] ? do_IRQ+0x4f/0xc0 [] ? irq_exit+0x7d/0x90 [] ? smp_apic_timer_interrupt+0x57/0x90 [] ? common_interrupt+0x29/0x30 [] ? mwait_idle+0xbe/0x100 [] ? cpu_idle+0x52/0x90 [] ? rest_init+0x55/0x60 [] ? start_kernel+0x315/0x37d [] ? unknown_bootoption+0x0/0x1f9 [] ? i386_start_kernel+0x79/0x81 Code: EIP: [] ieee80211_agg_splice_packets+0x40/0xc0 [mac80211] SS:ESP 0068:c076be38 CR2: 0000000000000030 Cc: stable@kernel.org Testedy-by: Jack Lau Signed-off-by: Luis R. Rodriguez Signed-off-by: John W. Linville --- net/mac80211/agg-tx.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'net/mac80211') diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c index 9e5762ad307d..a24e59816b93 100644 --- a/net/mac80211/agg-tx.c +++ b/net/mac80211/agg-tx.c @@ -381,6 +381,14 @@ static void ieee80211_agg_splice_packets(struct ieee80211_local *local, &local->hw, queue, IEEE80211_QUEUE_STOP_REASON_AGGREGATION); + if (!(sta->ampdu_mlme.tid_state_tx[tid] & HT_ADDBA_REQUESTED_MSK)) + return; + + if (WARN(!sta->ampdu_mlme.tid_tx[tid], + "TID %d gone but expected when splicing aggregates from" + "the pending queue\n", tid)) + return; + if (!skb_queue_empty(&sta->ampdu_mlme.tid_tx[tid]->pending)) { spin_lock_irqsave(&local->queue_stop_reason_lock, flags); /* mark queue as pending, it is stopped already */ -- cgit v1.2.2 From 523d2f6982136d332c9b7dd00e9e16da1091f060 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 1 Jul 2009 21:26:43 +0200 Subject: mac80211: fix todo lock The key todo lock can be taken from different locks that require it to be _bh to avoid lock inversion due to (soft)irqs. This should fix the two problems reported by Bob and Gabor: http://mid.gmane.org/20090619113049.GB18956@hash.localnet http://mid.gmane.org/4A3FA376.8020307@openwrt.org Signed-off-by: Johannes Berg Cc: Bob Copeland Cc: Gabor Juhos Signed-off-by: John W. Linville --- net/mac80211/key.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'net/mac80211') diff --git a/net/mac80211/key.c b/net/mac80211/key.c index ce267565e180..659a42d529e3 100644 --- a/net/mac80211/key.c +++ b/net/mac80211/key.c @@ -67,6 +67,8 @@ static DECLARE_WORK(todo_work, key_todo); * * @key: key to add to do item for * @flag: todo flag(s) + * + * Must be called with IRQs or softirqs disabled. */ static void add_todo(struct ieee80211_key *key, u32 flag) { @@ -140,9 +142,9 @@ static void ieee80211_key_enable_hw_accel(struct ieee80211_key *key) ret = drv_set_key(key->local, SET_KEY, &sdata->vif, sta, &key->conf); if (!ret) { - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); key->flags |= KEY_FLAG_UPLOADED_TO_HARDWARE; - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); } if (ret && ret != -ENOSPC && ret != -EOPNOTSUPP) @@ -164,12 +166,12 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) if (!key || !key->local->ops->set_key) return; - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); if (!(key->flags & KEY_FLAG_UPLOADED_TO_HARDWARE)) { - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); return; } - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); sta = get_sta_for_key(key); sdata = key->sdata; @@ -188,9 +190,9 @@ static void ieee80211_key_disable_hw_accel(struct ieee80211_key *key) wiphy_name(key->local->hw.wiphy), key->conf.keyidx, sta ? sta->addr : bcast_addr, ret); - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); key->flags &= ~KEY_FLAG_UPLOADED_TO_HARDWARE; - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); } static void __ieee80211_set_default_key(struct ieee80211_sub_if_data *sdata, @@ -437,14 +439,14 @@ void ieee80211_key_link(struct ieee80211_key *key, __ieee80211_key_replace(sdata, sta, old_key, key); - spin_unlock_irqrestore(&sdata->local->key_lock, flags); - /* free old key later */ add_todo(old_key, KEY_FLAG_TODO_DELETE); add_todo(key, KEY_FLAG_TODO_ADD_DEBUGFS); if (netif_running(sdata->dev)) add_todo(key, KEY_FLAG_TODO_HWACCEL_ADD); + + spin_unlock_irqrestore(&sdata->local->key_lock, flags); } static void __ieee80211_key_free(struct ieee80211_key *key) @@ -547,7 +549,7 @@ static void __ieee80211_key_todo(void) */ synchronize_rcu(); - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); while (!list_empty(&todo_list)) { key = list_first_entry(&todo_list, struct ieee80211_key, todo); list_del_init(&key->todo); @@ -558,7 +560,7 @@ static void __ieee80211_key_todo(void) KEY_FLAG_TODO_HWACCEL_REMOVE | KEY_FLAG_TODO_DELETE); key->flags &= ~todoflags; - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); work_done = false; @@ -591,9 +593,9 @@ static void __ieee80211_key_todo(void) WARN_ON(!work_done); - spin_lock(&todo_lock); + spin_lock_bh(&todo_lock); } - spin_unlock(&todo_lock); + spin_unlock_bh(&todo_lock); } void ieee80211_key_todo(void) -- cgit v1.2.2