[PATCH v5.1-rc] iwlwifi: make locking in iwl_mvm_tx_mpdu() BH-safe

From: Jiri Kosina
Date: Mon Apr 15 2019 - 07:03:48 EST


From: Jiri Kosina <jkosina@xxxxxxx>

iwl_mvm_sta->lock can be taken from BH, and therefore BH must be disabled if
taking it from other contexts.

This fixes the lockdep warning below.

IPv6: ADDRCONF(NETDEV_CHANGE): wlan0: link becomes ready

========================================================
WARNING: possible irq lock inversion dependency detected
5.1.0-rc5 #3 Not tainted
--------------------------------------------------------
swapper/1/0 just changed the state of lock:
00000000a18af450 (_xmit_ETHER#2){+.-.}, at: __dev_queue_xmit+0x8da/0xbe0
but this lock took another, SOFTIRQ-unsafe lock in the past:
(&(&mvm_sta->lock)->rlock){+.+.}

and interrupts could create inverse lock ordering between them.

other info that might help us debug this:
Possible interrupt unsafe locking scenario:

CPU0 CPU1
---- ----
lock(&(&mvm_sta->lock)->rlock);
local_irq_disable();
lock(_xmit_ETHER#2);
lock(&(&mvm_sta->lock)->rlock);
<Interrupt>
lock(_xmit_ETHER#2);

*** DEADLOCK ***

4 locks held by swapper/1/0:
#0: 000000008e12d112 ((&idev->mc_ifc_timer)){+.-.}, at: call_timer_fn+0x5/0x310
#1: 0000000003eca5ef (rcu_read_lock){....}, at: mld_sendpack+0x5/0x3b0
#2: 000000007eac744b (rcu_read_lock_bh){....}, at: ip6_finish_output2+0x5f/0x960
#3: 000000007eac744b (rcu_read_lock_bh){....}, at: __dev_queue_xmit+0x4e/0xbe0

the shortest dependencies between 2nd lock and 1st lock:
-> (&(&mvm_sta->lock)->rlock){+.+.} {
HARDIRQ-ON-W at:
_raw_spin_lock_bh+0x3d/0x50
iwl_mvm_add_new_dqa_stream_wk+0x102/0x910 [iwlmvm]
process_one_work+0x1f0/0x5b0
worker_thread+0x4c/0x3f0
kthread+0x103/0x140
ret_from_fork+0x3a/0x50
SOFTIRQ-ON-W at:
_raw_spin_lock+0x35/0x50
iwl_mvm_tx_mpdu+0xae/0x5f0 [iwlmvm]
iwl_mvm_tx_skb+0x200/0x470 [iwlmvm]
iwl_mvm_mac_itxq_xmit+0xd5/0x1f0 [iwlmvm]
iwl_mvm_add_new_dqa_stream_wk+0x34c/0x910 [iwlmvm]
process_one_work+0x1f0/0x5b0
worker_thread+0x4c/0x3f0
kthread+0x103/0x140
ret_from_fork+0x3a/0x50
INITIAL USE at:
_raw_spin_lock_bh+0x3d/0x50
iwl_mvm_add_new_dqa_stream_wk+0x102/0x910 [iwlmvm]
process_one_work+0x1f0/0x5b0
worker_thread+0x4c/0x3f0
kthread+0x103/0x140
ret_from_fork+0x3a/0x50
}
... key at: [<ffffffffc0e80d80>] __key.87928+0x0/0xfffffffffffd9280 [iwlmvm]
... acquired at:
_raw_spin_lock+0x35/0x50
iwl_mvm_tx_mpdu+0xae/0x5f0 [iwlmvm]
iwl_mvm_tx_skb+0x200/0x470 [iwlmvm]
iwl_mvm_mac_itxq_xmit+0xd5/0x1f0 [iwlmvm]
ieee80211_queue_skb+0x23a/0x470 [mac80211]
ieee80211_tx+0xe1/0x140 [mac80211]
__ieee80211_subif_start_xmit+0x257/0x310 [mac80211]
ieee80211_subif_start_xmit+0x47/0x300 [mac80211]
dev_hard_start_xmit+0xb7/0x340
__dev_queue_xmit+0x88d/0xbe0
packet_sendmsg+0x1025/0x1920 [af_packet]
sock_sendmsg+0x36/0x50
__sys_sendto+0xdc/0x160
__x64_sys_sendto+0x24/0x30
do_syscall_64+0x5d/0x190
entry_SYSCALL_64_after_hwframe+0x49/0xbe

-> (_xmit_ETHER#2){+.-.} {
HARDIRQ-ON-W at:
_raw_spin_lock+0x35/0x50
dev_deactivate_many+0xec/0x3b0
dev_deactivate+0x4f/0x80
linkwatch_do_dev+0x34/0x50
__linkwatch_run_queue+0xf4/0x190
linkwatch_event+0x21/0x30
process_one_work+0x1f0/0x5b0
worker_thread+0x4c/0x3f0
kthread+0x103/0x140
ret_from_fork+0x3a/0x50
IN-SOFTIRQ-W at:
_raw_spin_lock+0x35/0x50
__dev_queue_xmit+0x8da/0xbe0
ip6_finish_output2+0x22b/0x960
ip6_output+0x1a6/0x2b0
mld_sendpack+0x369/0x3b0
mld_ifc_timer_expire+0x19f/0x2e0
call_timer_fn+0x92/0x310
run_timer_softirq+0x216/0x560
__do_softirq+0x12c/0x447
irq_exit+0xac/0xc0
smp_apic_timer_interrupt+0xac/0x2a0
apic_timer_interrupt+0xf/0x20
cpuidle_enter_state+0xbf/0x450
do_idle+0x1cc/0x290
cpu_startup_entry+0x19/0x20
start_secondary+0x179/0x1c0
secondary_startup_64+0xa4/0xb0
INITIAL USE at:
_raw_spin_lock+0x35/0x50
dev_deactivate_many+0xec/0x3b0
dev_deactivate+0x4f/0x80
linkwatch_do_dev+0x34/0x50
__linkwatch_run_queue+0xf4/0x190
linkwatch_event+0x21/0x30
process_one_work+0x1f0/0x5b0
worker_thread+0x4c/0x3f0
kthread+0x103/0x140
ret_from_fork+0x3a/0x50
}
... key at: [<ffffffffa1679a70>] netdev_xmit_lock_key+0x10/0x390
... acquired at:
__lock_acquire+0x1ae/0xfe0
lock_acquire+0xbd/0x1e0
_raw_spin_lock+0x35/0x50
__dev_queue_xmit+0x8da/0xbe0
ip6_finish_output2+0x22b/0x960
ip6_output+0x1a6/0x2b0
mld_sendpack+0x369/0x3b0
mld_ifc_timer_expire+0x19f/0x2e0
call_timer_fn+0x92/0x310
run_timer_softirq+0x216/0x560
__do_softirq+0x12c/0x447
irq_exit+0xac/0xc0
smp_apic_timer_interrupt+0xac/0x2a0
apic_timer_interrupt+0xf/0x20
cpuidle_enter_state+0xbf/0x450
do_idle+0x1cc/0x290
cpu_startup_entry+0x19/0x20
start_secondary+0x179/0x1c0
secondary_startup_64+0xa4/0xb0

stack backtrace:
CPU: 1 PID: 0 Comm: swapper/1 Not tainted 5.1.0-rc5 #3
Hardware name: LENOVO 20K5S22R00/20K5S22R00, BIOS R0IET38W (1.16 ) 05/31/2017
Call Trace:
<IRQ>
dump_stack+0x78/0xb3
print_irq_inversion_bug.part.40+0x19f/0x1aa
check_usage_forwards+0x11e/0x120
? secondary_startup_64+0xa4/0xb0
? check_usage_backwards+0x120/0x120
mark_lock+0x17c/0x280
__lock_acquire+0x1ae/0xfe0
? find_held_lock+0x31/0xa0
? ieee80211_select_queue+0x124/0x2d0 [mac80211]
lock_acquire+0xbd/0x1e0
? __dev_queue_xmit+0x8da/0xbe0
_raw_spin_lock+0x35/0x50
? __dev_queue_xmit+0x8da/0xbe0
__dev_queue_xmit+0x8da/0xbe0
? __dev_queue_xmit+0x4e/0xbe0
? neigh_resolve_output+0x15a/0x230
? ip6_finish_output2+0x22b/0x960
ip6_finish_output2+0x22b/0x960
? ip6_finish_output2+0x5f/0x960
? ip6_mtu+0x12d/0x1f0
? ip6_mtu+0x71/0x1f0
ip6_output+0x1a6/0x2b0
? ip6_output+0xb8/0x2b0
? ip6_fragment+0xb70/0xb70
mld_sendpack+0x369/0x3b0
? mld_sendpack+0x5/0x3b0
? mld_dad_timer_expire+0x90/0x90
mld_ifc_timer_expire+0x19f/0x2e0
? mld_dad_timer_expire+0x90/0x90
? mld_dad_timer_expire+0x90/0x90
call_timer_fn+0x92/0x310
? call_timer_fn+0x5/0x310
? mld_dad_timer_expire+0x90/0x90
run_timer_softirq+0x216/0x560
__do_softirq+0x12c/0x447
irq_exit+0xac/0xc0
smp_apic_timer_interrupt+0xac/0x2a0
apic_timer_interrupt+0xf/0x20
</IRQ>
RIP: 0010:cpuidle_enter_state+0xbf/0x450
Code: 44 8b 63 04 49 89 c6 0f 1f 44 00 00 31 ff e8 08 3a 9b ff 80 7c 24 0b 00 0f 85 de 02 00 00 e8 e8 a4 a9 ff fb 66 0f 1f 44 00 00 <45> 85 ed 0f 88 b0 02 00 00 49 63 f5 4c 2b 34 24 48 ba cf f7 53 e3
RSP: 0018:ffffa60b00d27ea0 EFLAGS: 00000202 ORIG_RAX: ffffffffffffff13
RAX: ffff8a5686a30340 RBX: ffffc60affc90f50 RCX: 0000000000000000
RDX: ffff8a5686a30340 RSI: 0000000000000006 RDI: ffff8a5686a30340
RBP: ffffffffa02fb360 R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000001 R11: 071c71c71c71c71c R12: 0000000000000001
R13: 0000000000000003 R14: 0000000542fed0f3 R15: 0000000000000001
do_idle+0x1cc/0x290
cpu_startup_entry+0x19/0x20
start_secondary+0x179/0x1c0
secondary_startup_64+0xa4/0xb0

Signed-off-by: Jiri Kosina <jkosina@xxxxxxx>
---
drivers/net/wireless/intel/iwlwifi/mvm/tx.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
index 0c2aabc842f9..8c8ebb5e12bc 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c
@@ -1107,7 +1107,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
*/
info->flags &= ~IEEE80211_TX_STATUS_EOSP;

- spin_lock(&mvmsta->lock);
+ spin_lock_bh(&mvmsta->lock);

/* nullfunc frames should go to the MGMT queue regardless of QOS,
* the condition of !ieee80211_is_qos_nullfunc(fc) keeps the default
@@ -1146,7 +1146,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,

if (WARN_ONCE(txq_id == IWL_MVM_INVALID_QUEUE, "Invalid TXQ id")) {
iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);
- spin_unlock(&mvmsta->lock);
+ spin_unlock_bh(&mvmsta->lock);
return 0;
}

@@ -1182,7 +1182,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,
if (tid < IWL_MAX_TID_COUNT && !ieee80211_has_morefrags(fc))
mvmsta->tid_data[tid].seq_number = seq_number + 0x10;

- spin_unlock(&mvmsta->lock);
+ spin_unlock_bh(&mvmsta->lock);

if (iwl_mvm_tx_pkt_queued(mvm, mvmsta,
tid == IWL_MAX_TID_COUNT ? 0 : tid))
@@ -1192,7 +1192,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb,

drop_unlock_sta:
iwl_trans_free_tx_cmd(mvm->trans, dev_cmd);
- spin_unlock(&mvmsta->lock);
+ spin_unlock_bh(&mvmsta->lock);
drop:
IWL_DEBUG_TX(mvm, "TX to [%d|%d] dropped\n", mvmsta->sta_id, tid);
return -1;

--
Jiri Kosina
SUSE Labs