cef61ee34481d982e95814e6fb040f7f2e65cd61
[librecmc/librecmc.git] /
1 From e44de90453bb2b46a523df78c39eb896bab35dcd Mon Sep 17 00:00:00 2001
2 From: Govindaraj Saminathan <quic_gsaminat@quicinc.com>
3 Date: Tue, 29 Nov 2022 13:04:02 +0200
4 Subject: [PATCH] wifi: ath11k: Fix race condition with struct
5  htt_ppdu_stats_info
6
7 A crash happens when running the traffic with multiple clients:
8
9 Crash Signature : Unable to handle kernel paging request at
10 virtual address ffffffd700970918 During the crash, PC points to
11 "ieee80211_tx_rate_update+0x30/0x68 [mac80211]"
12 LR points to "ath11k_dp_htt_htc_t2h_msg_handler+0x5a8/0x8a0 [ath11k]".
13
14 Struct ppdu_stats_info is allocated and accessed from event callback via copy
15 engine tasklet, this has a problem when freeing it from ath11k_mac_op_stop().
16
17 Use data_lock during entire ath11k_dp_htt_get_ppdu_desc() call to protect
18 struct htt_ppdu_stats_info access and to avoid race condition when accessing it
19 from ath11k_mac_op_stop().
20
21 Tested-on: IPQ8074 hw2.0 AHB WLAN.HK.2.7.0.1-01744-QCAHKSWPL_SILICONZ-1
22
23 Signed-off-by: Govindaraj Saminathan <quic_gsaminat@quicinc.com>
24 Co-developed-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
25 Signed-off-by: Karthikeyan Kathirvel <quic_kathirve@quicinc.com>
26 Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com>
27 Link: https://lore.kernel.org/r/20221124071104.22506-1-quic_kathirve@quicinc.com
28 ---
29  drivers/net/wireless/ath/ath11k/dp_rx.c | 22 +++++++++++-----------
30  1 file changed, 11 insertions(+), 11 deletions(-)
31
32 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c
33 +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
34 @@ -1535,13 +1535,12 @@ struct htt_ppdu_stats_info *ath11k_dp_ht
35  {
36         struct htt_ppdu_stats_info *ppdu_info;
37  
38 -       spin_lock_bh(&ar->data_lock);
39 +       lockdep_assert_held(&ar->data_lock);
40 +
41         if (!list_empty(&ar->ppdu_stats_info)) {
42                 list_for_each_entry(ppdu_info, &ar->ppdu_stats_info, list) {
43 -                       if (ppdu_info->ppdu_id == ppdu_id) {
44 -                               spin_unlock_bh(&ar->data_lock);
45 +                       if (ppdu_info->ppdu_id == ppdu_id)
46                                 return ppdu_info;
47 -                       }
48                 }
49  
50                 if (ar->ppdu_stat_list_depth > HTT_PPDU_DESC_MAX_DEPTH) {
51 @@ -1553,16 +1552,13 @@ struct htt_ppdu_stats_info *ath11k_dp_ht
52                         kfree(ppdu_info);
53                 }
54         }
55 -       spin_unlock_bh(&ar->data_lock);
56  
57         ppdu_info = kzalloc(sizeof(*ppdu_info), GFP_ATOMIC);
58         if (!ppdu_info)
59                 return NULL;
60  
61 -       spin_lock_bh(&ar->data_lock);
62         list_add_tail(&ppdu_info->list, &ar->ppdu_stats_info);
63         ar->ppdu_stat_list_depth++;
64 -       spin_unlock_bh(&ar->data_lock);
65  
66         return ppdu_info;
67  }
68 @@ -1586,16 +1582,17 @@ static int ath11k_htt_pull_ppdu_stats(st
69         ar = ath11k_mac_get_ar_by_pdev_id(ab, pdev_id);
70         if (!ar) {
71                 ret = -EINVAL;
72 -               goto exit;
73 +               goto out;
74         }
75  
76         if (ath11k_debugfs_is_pktlog_lite_mode_enabled(ar))
77                 trace_ath11k_htt_ppdu_stats(ar, skb->data, len);
78  
79 +       spin_lock_bh(&ar->data_lock);
80         ppdu_info = ath11k_dp_htt_get_ppdu_desc(ar, ppdu_id);
81         if (!ppdu_info) {
82                 ret = -EINVAL;
83 -               goto exit;
84 +               goto out_unlock_data;
85         }
86  
87         ppdu_info->ppdu_id = ppdu_id;
88 @@ -1604,10 +1601,13 @@ static int ath11k_htt_pull_ppdu_stats(st
89                                      (void *)ppdu_info);
90         if (ret) {
91                 ath11k_warn(ab, "Failed to parse tlv %d\n", ret);
92 -               goto exit;
93 +               goto out_unlock_data;
94         }
95  
96 -exit:
97 +out_unlock_data:
98 +       spin_unlock_bh(&ar->data_lock);
99 +
100 +out:
101         rcu_read_unlock();
102  
103         return ret;