2023-08-03 07:27:19

by Wen Gong

[permalink] [raw]
Subject: [PATCH v5 0/5] fix wrong TX power and frequency in regdomain by dynamic switch 6 GHz reg rules of LPI/SP/VLP for station mode

Currently when wlan load up, station always use the 6 GHz reg rules of
AP mode by commit 91fa00fa6922 ("wifi: ath11k: add support to parse new
WMI event for 6 GHz"), then the tx power and frequency are not correct,
so fix it by dynamic switch to client mode LPI/SP/VLP for station after
interface added and connected for station.

v5: change per Kalle and rebased to ath.git ath-202306211808
1. ath11k_ieee80211_ap_pwr_type_convert() to ath11k_reg_ap_pwr_convert()
2. used list_first_entry_or_null() and add comments
3. ath11k_dbg() to ath11k_warn()
4. ath11k_hw_supports_6g_cc_ext() to ath11k_mac_supports_6g_cc_ext()
5. add mesh in commit log

v4: (NOT depends to any patch now).
1. removed patches which depends on
wifi: cfg80211: save Power Spectral Density (PSD) of the regulatory rule
https://lore.kernel.org/linux-wireless/[email protected]/
removed:
[v3,08/15] wifi: ath11k: save power spectral density(psd) of regulatory rule
[v3,09/15] wifi: ath11k: add parse of transmit power envelope element
[v3,10/15] wifi: ath11k: save max tx power in vdev start response event from firmware
[v3,11/15] wifi: ath11k: fill parameters for vdev_set_tpc_power wmi command
[v3,12/15] wifi: ath11k: add WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT service bit
[v3,13/15] wifi: ath11k: discard BSS_CHANGED_TXPOWER when EXT_TPC_REG_SUPPORT for 6 GHz
[v3,14/15] wifi: ath11k: add handler for WMI_VDEV_SET_TPC_POWER_CMDID
[v3,15/15] wifi: ath11k: send TPC power to firmware for 6 GHz station

2. rebased to ath.git ath-202304281700

3. deleted "wifi: ath11k: Add support to parse new wmi event for 6 GHz regulatory" which is alreay upstream.

link of v3:
[v3,00/15] wifi: ath11k: add support for 6 GHz station for various modes : LPI, SP and VLP
https://patchwork.kernel.org/project/linux-wireless/cover/[email protected]/

v3:
1. added "ath11k: fix a possible dead lock caused by ab->base_lock".
3. deleted "ath11k: add support for extended wmi service bit" which is alreay upstream.

v2:
1. change some minor comments by Kalle.
2. rebased to ath.git ath-202112220603

Baochen Qiang (1):
wifi: ath11k: fix a possible dead lock caused by ab->base_lock

Wen Gong (4):
wifi: ath11k: add support to select 6 GHz regulatory type
wifi: ath11k: store cur_regulatory_info for each radio
wifi: ath11k: update regulatory rules when interface added
wifi: ath11k: update regulatory rules when connect to AP on 6 GHz band
for station

drivers/net/wireless/ath/ath11k/core.h | 1 +
drivers/net/wireless/ath/ath11k/mac.c | 28 +++++
drivers/net/wireless/ath/ath11k/mac.h | 3 +-
drivers/net/wireless/ath/ath11k/reg.c | 72 +++++++++---
drivers/net/wireless/ath/ath11k/reg.h | 4 +-
drivers/net/wireless/ath/ath11k/wmi.c | 150 +++++++++++++++++--------
drivers/net/wireless/ath/ath11k/wmi.h | 5 +
7 files changed, 201 insertions(+), 62 deletions(-)


base-commit: b21fe5be53eb873c02e7479372726c8aeed171e3
--
2.40.1



2023-08-03 07:29:36

by Wen Gong

[permalink] [raw]
Subject: [PATCH v5 2/5] wifi: ath11k: store cur_regulatory_info for each radio

The regulatory info of WMI_REG_CHAN_LIST_CC_EXT_EVENTID is not saved
in ath11k now, the info should be saved in ath11k. Save the info for
each radio and support switch regulatory rules dynamically.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/core.h | 1 +
drivers/net/wireless/ath/ath11k/mac.h | 1 -
drivers/net/wireless/ath/ath11k/reg.c | 6 ++
drivers/net/wireless/ath/ath11k/wmi.c | 141 ++++++++++++++++++-------
drivers/net/wireless/ath/ath11k/wmi.h | 5 +
5 files changed, 113 insertions(+), 41 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h
index 9d15b4390b9c..2e017141b62b 100644
--- a/drivers/net/wireless/ath/ath11k/core.h
+++ b/drivers/net/wireless/ath/ath11k/core.h
@@ -923,6 +923,7 @@ struct ath11k_base {
* This may or may not be used during the runtime
*/
struct ieee80211_regdomain *new_regd[MAX_RADIOS];
+ struct cur_regulatory_info *reg_info_store;

/* Current DFS Regulatory */
enum ath11k_dfs_region dfs_region;
diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h
index 0231783ad754..e4835e0b7998 100644
--- a/drivers/net/wireless/ath/ath11k/mac.h
+++ b/drivers/net/wireless/ath/ath11k/mac.h
@@ -158,7 +158,6 @@ struct ath11k_vif *ath11k_mac_get_vif_up(struct ath11k_base *ab);

struct ath11k *ath11k_mac_get_ar_by_vdev_id(struct ath11k_base *ab, u32 vdev_id);
struct ath11k *ath11k_mac_get_ar_by_pdev_id(struct ath11k_base *ab, u32 pdev_id);
-
void ath11k_mac_drain_tx(struct ath11k *ar);
void ath11k_mac_peer_cleanup_all(struct ath11k *ar);
int ath11k_mac_tx_mgmt_pending_free(int buf_id, void *skb, void *ctx);
diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c
index 516cbea37aa6..8106caf53a92 100644
--- a/drivers/net/wireless/ath/ath11k/reg.c
+++ b/drivers/net/wireless/ath/ath11k/reg.c
@@ -809,6 +809,12 @@ void ath11k_reg_free(struct ath11k_base *ab)
{
int i;

+ for (i = 0; i < ab->num_radios; i++)
+ ath11k_reg_reset_info(&ab->reg_info_store[i]);
+
+ kfree(ab->reg_info_store);
+ ab->reg_info_store = NULL;
+
for (i = 0; i < ab->hw_params.max_radios; i++) {
kfree(ab->default_regd[i]);
kfree(ab->new_regd[i]);
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 5a6578865b25..1fb445106872 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -4749,6 +4749,11 @@ static int ath11k_wmi_tlv_ext_soc_hal_reg_caps_parse(struct ath11k_base *soc,
soc->pdevs[0].pdev_id = 0;
}

+ if (!soc->reg_info_store)
+ soc->reg_info_store = kcalloc(soc->num_radios,
+ sizeof(*soc->reg_info_store),
+ GFP_ATOMIC);
+
return 0;
}

@@ -7070,33 +7075,54 @@ static bool ath11k_reg_is_world_alpha(char *alpha)
return false;
}

-static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
- struct sk_buff *skb,
- enum wmi_reg_chan_list_cmd_type id)
+void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info)
{
- struct cur_regulatory_info *reg_info = NULL;
- struct ieee80211_regdomain *regd = NULL;
- bool intersect = false;
- int ret = 0, pdev_idx, i, j;
- struct ath11k *ar;
+ int i, j;

- reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC);
- if (!reg_info) {
- ret = -ENOMEM;
- goto fallback;
- }
+ if (reg_info) {
+ kfree(reg_info->reg_rules_2ghz_ptr);

- if (id == WMI_REG_CHAN_LIST_CC_ID)
- ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info);
- else
- ret = ath11k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info);
+ kfree(reg_info->reg_rules_5ghz_ptr);

- if (ret) {
- ath11k_warn(ab, "failed to extract regulatory info from received event\n");
- goto fallback;
+ for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) {
+ kfree(reg_info->reg_rules_6ghz_ap_ptr[i]);
+ for (j = 0; j < WMI_REG_MAX_CLIENT_TYPE; j++)
+ kfree(reg_info->reg_rules_6ghz_client_ptr[i][j]);
+ }
+
+ memset(reg_info, 0, sizeof(*reg_info));
}
+}
+
+static
+enum wmi_vdev_type ath11k_reg_get_ar_vdev_type(struct ath11k *ar)
+{
+ struct ath11k_vif *arvif;
+
+ /* Currently each struct ath11k maps to one struct ieee80211_hw/wiphy
+ * and one struct ieee80211_regdomain, so it could only store one group
+ * reg rules. It means muti-interface concurrency in the same ath11k is
+ * not support for the regdomain. So get the vdev type of the first entry
+ * now. After concurrency support for the regdomain, this should change.
+ */
+ arvif = list_first_entry_or_null(&ar->arvifs, struct ath11k_vif, list);
+ if (arvif)
+ return arvif->vdev_type;
+
+ return WMI_VDEV_TYPE_UNSPEC;
+}

- ath11k_dbg(ab, ATH11K_DBG_WMI, "event reg chan list id %d", id);
+int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
+ struct cur_regulatory_info *reg_info,
+ enum ieee80211_ap_reg_power power_type)
+{
+ struct ieee80211_regdomain *regd;
+ bool intersect = false;
+ int pdev_idx;
+ struct ath11k *ar;
+ enum wmi_vdev_type vdev_type;
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI, "event reg handle chan list");

if (reg_info->status_code != REG_SET_CC_STATUS_PASS) {
/* In case of failure to set the requested ctry,
@@ -7104,7 +7130,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
* and return from here.
*/
ath11k_warn(ab, "Failed to set the requested Country regulatory setting\n");
- goto mem_free;
+ return -EINVAL;
}

pdev_idx = reg_info->phy_id;
@@ -7116,7 +7142,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags) &&
ab->default_regd[pdev_idx]) {
spin_unlock(&ab->base_lock);
- goto mem_free;
+ goto retfail;
}
spin_unlock(&ab->base_lock);

@@ -7127,7 +7153,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
*/
if (ab->hw_params.single_pdev_only &&
pdev_idx < ab->hw_params.num_rxmda_per_pdev)
- goto mem_free;
+ return 0;
else
goto fallback;
}
@@ -7138,7 +7164,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] &&
!memcmp((char *)ab->default_regd[pdev_idx]->alpha2,
(char *)reg_info->alpha2, 2))
- goto mem_free;
+ goto retfail;

/* Intersect new rules with default regd if a new country setting was
* requested, i.e a default regd was already set during initialization
@@ -7150,13 +7176,24 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
!ath11k_reg_is_world_alpha((char *)reg_info->alpha2))
intersect = true;

- regd = ath11k_reg_build_regd(ab, reg_info, intersect,
- WMI_VDEV_TYPE_AP, IEEE80211_REG_LPI_AP);
+ ar = ab->pdevs[pdev_idx].ar;
+ vdev_type = ath11k_reg_get_ar_vdev_type(ar);
+
+ ath11k_dbg(ab, ATH11K_DBG_WMI,
+ "wmi handle chan list power type %d vdev type %d intersect %d\n",
+ power_type, vdev_type, intersect);
+
+ regd = ath11k_reg_build_regd(ab, reg_info, intersect, vdev_type, power_type);
if (!regd) {
ath11k_warn(ab, "failed to build regd from reg_info\n");
goto fallback;
}

+ if (power_type == IEEE80211_REG_UNSET_AP) {
+ ath11k_reg_reset_info(&ab->reg_info_store[pdev_idx]);
+ ab->reg_info_store[pdev_idx] = *reg_info;
+ }
+
spin_lock(&ab->base_lock);
if (ab->default_regd[pdev_idx]) {
/* The initial rules from FW after WMI Init is to build
@@ -7179,7 +7216,7 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
ab->dfs_region = reg_info->dfs_region;
spin_unlock(&ab->base_lock);

- goto mem_free;
+ return 0;

fallback:
/* Fallback to older reg (by sending previous country setting
@@ -7191,20 +7228,44 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
*/
/* TODO: This is rare, but still should also be handled */
WARN_ON(1);
-mem_free:
- if (reg_info) {
- kfree(reg_info->reg_rules_2ghz_ptr);
- kfree(reg_info->reg_rules_5ghz_ptr);
- if (reg_info->is_ext_reg_event) {
- for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++)
- kfree(reg_info->reg_rules_6ghz_ap_ptr[i]);

- for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++)
- for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++)
- kfree(reg_info->reg_rules_6ghz_client_ptr[j][i]);
- }
- kfree(reg_info);
+retfail:
+
+ return -EINVAL;
+}
+
+static int ath11k_reg_chan_list_event(struct ath11k_base *ab, struct sk_buff *skb,
+ enum wmi_reg_chan_list_cmd_type id)
+{
+ struct cur_regulatory_info *reg_info;
+ int ret;
+
+ reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC);
+ if (!reg_info)
+ return -ENOMEM;
+
+ if (id == WMI_REG_CHAN_LIST_CC_ID)
+ ret = ath11k_pull_reg_chan_list_update_ev(ab, skb, reg_info);
+ else
+ ret = ath11k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info);
+
+ if (ret) {
+ ath11k_warn(ab, "failed to extract regulatory info\n");
+ goto mem_free;
}
+
+ ret = ath11k_reg_handle_chan_list(ab, reg_info, IEEE80211_REG_UNSET_AP);
+ if (ret) {
+ ath11k_warn(ab, "failed to process regulatory info %d\n", ret);
+ goto mem_free;
+ }
+
+ kfree(reg_info);
+ return 0;
+
+mem_free:
+ ath11k_reg_reset_info(reg_info);
+ kfree(reg_info);
return ret;
}

diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h
index 100bb816b592..a34a04bd4612 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.h
+++ b/drivers/net/wireless/ath/ath11k/wmi.h
@@ -4975,6 +4975,7 @@ struct ath11k_targ_cap {
};

enum wmi_vdev_type {
+ WMI_VDEV_TYPE_UNSPEC = 0,
WMI_VDEV_TYPE_AP = 1,
WMI_VDEV_TYPE_STA = 2,
WMI_VDEV_TYPE_IBSS = 3,
@@ -6505,4 +6506,8 @@ int ath11k_wmi_pdev_set_bios_geo_table_param(struct ath11k *ar);
int ath11k_wmi_sta_keepalive(struct ath11k *ar,
const struct wmi_sta_keepalive_arg *arg);

+void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info);
+int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
+ struct cur_regulatory_info *reg_info,
+ enum ieee80211_ap_reg_power power_type);
#endif
--
2.40.1


2023-08-03 07:30:30

by Wen Gong

[permalink] [raw]
Subject: [PATCH v5 1/5] wifi: ath11k: add support to select 6 GHz regulatory type

There are 3 types of regulatory rules for AP mode and 6 type for
station mode. Add wmi_vdev_type and ieee80211_ap_reg_power to
select the exact reg rules.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23

Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/reg.c | 66 +++++++++++++++++++++------
drivers/net/wireless/ath/ath11k/reg.h | 4 +-
drivers/net/wireless/ath/ath11k/wmi.c | 3 +-
3 files changed, 56 insertions(+), 17 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c
index 7f9fb968dac6..516cbea37aa6 100644
--- a/drivers/net/wireless/ath/ath11k/reg.c
+++ b/drivers/net/wireless/ath/ath11k/reg.c
@@ -607,25 +607,64 @@ ath11k_reg_update_weather_radar_band(struct ath11k_base *ab,
*rule_idx = i;
}

+static enum wmi_reg_6ghz_ap_type
+ath11k_reg_ap_pwr_convert(enum ieee80211_ap_reg_power power_type)
+{
+ switch (power_type) {
+ case IEEE80211_REG_LPI_AP:
+ return WMI_REG_INDOOR_AP;
+ case IEEE80211_REG_SP_AP:
+ return WMI_REG_STANDARD_POWER_AP;
+ case IEEE80211_REG_VLP_AP:
+ return WMI_REG_VERY_LOW_POWER_AP;
+ default:
+ return WMI_REG_MAX_AP_TYPE;
+ }
+}
+
struct ieee80211_regdomain *
ath11k_reg_build_regd(struct ath11k_base *ab,
- struct cur_regulatory_info *reg_info, bool intersect)
+ struct cur_regulatory_info *reg_info, bool intersect,
+ enum wmi_vdev_type vdev_type,
+ enum ieee80211_ap_reg_power power_type)
{
struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL;
- struct cur_reg_rule *reg_rule;
+ struct cur_reg_rule *reg_rule, *reg_rule_6ghz;
u8 i = 0, j = 0, k = 0;
u8 num_rules;
u16 max_bw;
- u32 flags;
+ u32 flags, reg_6ghz_number, max_bw_6ghz;
char alpha2[3];

num_rules = reg_info->num_5ghz_reg_rules + reg_info->num_2ghz_reg_rules;

- /* FIXME: Currently taking reg rules for 6 GHz only from Indoor AP mode list.
- * This can be updated after complete 6 GHz regulatory support is added.
- */
- if (reg_info->is_ext_reg_event)
- num_rules += reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP];
+ if (reg_info->is_ext_reg_event) {
+ if (vdev_type == WMI_VDEV_TYPE_STA) {
+ enum wmi_reg_6ghz_ap_type ap_type;
+
+ ap_type = ath11k_reg_ap_pwr_convert(power_type);
+
+ if (ap_type == WMI_REG_MAX_AP_TYPE)
+ ap_type = WMI_REG_INDOOR_AP;
+ reg_6ghz_number = reg_info->num_6ghz_rules_client
+ [ap_type][WMI_REG_DEFAULT_CLIENT];
+ if (reg_6ghz_number == 0) {
+ ap_type = WMI_REG_INDOOR_AP;
+ reg_6ghz_number = reg_info->num_6ghz_rules_client
+ [ap_type][WMI_REG_DEFAULT_CLIENT];
+ }
+ reg_rule_6ghz = reg_info->reg_rules_6ghz_client_ptr
+ [ap_type][WMI_REG_DEFAULT_CLIENT];
+ max_bw_6ghz = reg_info->max_bw_6ghz_client
+ [ap_type][WMI_REG_DEFAULT_CLIENT];
+ } else {
+ reg_6ghz_number = reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP];
+ reg_rule_6ghz =
+ reg_info->reg_rules_6ghz_ap_ptr[WMI_REG_INDOOR_AP];
+ max_bw_6ghz = reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP];
+ }
+ num_rules += reg_6ghz_number;
+ }

if (!num_rules)
goto ret;
@@ -672,13 +711,10 @@ ath11k_reg_build_regd(struct ath11k_base *ab,
* per other BW rule flags we pass from here
*/
flags = NL80211_RRF_AUTO_BW;
- } else if (reg_info->is_ext_reg_event &&
- reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP] &&
- (k < reg_info->num_6ghz_rules_ap[WMI_REG_INDOOR_AP])) {
- reg_rule = reg_info->reg_rules_6ghz_ap_ptr[WMI_REG_INDOOR_AP] +
- k++;
- max_bw = min_t(u16, reg_rule->max_bw,
- reg_info->max_bw_6ghz_ap[WMI_REG_INDOOR_AP]);
+ } else if (reg_info->is_ext_reg_event && reg_6ghz_number &&
+ (k < reg_6ghz_number)) {
+ reg_rule = reg_rule_6ghz + k++;
+ max_bw = min_t(u16, reg_rule->max_bw, max_bw_6ghz);
flags = NL80211_RRF_AUTO_BW;
} else {
break;
diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h
index 2f284f26378d..4930b5c93e1e 100644
--- a/drivers/net/wireless/ath/ath11k/reg.h
+++ b/drivers/net/wireless/ath/ath11k/reg.h
@@ -30,7 +30,9 @@ void ath11k_reg_free(struct ath11k_base *ab);
void ath11k_regd_update_work(struct work_struct *work);
struct ieee80211_regdomain *
ath11k_reg_build_regd(struct ath11k_base *ab,
- struct cur_regulatory_info *reg_info, bool intersect);
+ struct cur_regulatory_info *reg_info, bool intersect,
+ enum wmi_vdev_type vdev_type,
+ enum ieee80211_ap_reg_power power_type);
int ath11k_regd_update(struct ath11k *ar);
int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait);
#endif
diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 23ad6825e5be..5a6578865b25 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -7150,7 +7150,8 @@ static int ath11k_reg_chan_list_event(struct ath11k_base *ab,
!ath11k_reg_is_world_alpha((char *)reg_info->alpha2))
intersect = true;

- regd = ath11k_reg_build_regd(ab, reg_info, intersect);
+ regd = ath11k_reg_build_regd(ab, reg_info, intersect,
+ WMI_VDEV_TYPE_AP, IEEE80211_REG_LPI_AP);
if (!regd) {
ath11k_warn(ab, "failed to build regd from reg_info\n");
goto fallback;
--
2.40.1


2023-08-03 07:31:21

by Wen Gong

[permalink] [raw]
Subject: [PATCH v5 3/5] wifi: ath11k: fix a possible dead lock caused by ab->base_lock

From: Baochen Qiang <[email protected]>

spin_lock/spin_unlock are used in ath11k_reg_chan_list_event to
acquire/release ab->base_lock, for now this is safe because that
function is only called in soft IRQ context.

But ath11k_reg_chan_list_event() will be called from process
context in an upcoming patch, and this can result in a deadlock if
ab->base_lock is acquired in process context and then soft IRQ occurs
on the same CPU and tries to acquire that lock.

Fix it by using spin_lock_bh and spin_unlock_bh instead.

Tested-on: WCN6855 hw2.0 PCI WLAN.HSP.1.1-03125-QCAHSPSWPL_V1_V2_SILICONZ_LITE-3.6510.23

Fixes: 69a0fcf8a9f2 ("ath11k: Avoid reg rules update during firmware recovery")
Signed-off-by: Baochen Qiang <[email protected]>
Signed-off-by: Wen Gong <[email protected]>
---
drivers/net/wireless/ath/ath11k/wmi.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c
index 1fb445106872..c427299b7202 100644
--- a/drivers/net/wireless/ath/ath11k/wmi.c
+++ b/drivers/net/wireless/ath/ath11k/wmi.c
@@ -7138,13 +7138,13 @@ int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
/* Avoid default reg rule updates sent during FW recovery if
* it is already available
*/
- spin_lock(&ab->base_lock);
+ spin_lock_bh(&ab->base_lock);
if (test_bit(ATH11K_FLAG_RECOVERY, &ab->dev_flags) &&
ab->default_regd[pdev_idx]) {
- spin_unlock(&ab->base_lock);
+ spin_unlock_bh(&ab->base_lock);
goto retfail;
}
- spin_unlock(&ab->base_lock);
+ spin_unlock_bh(&ab->base_lock);

if (pdev_idx >= ab->num_radios) {
/* Process the event for phy0 only if single_pdev_only
@@ -7194,7 +7194,7 @@ int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
ab->reg_info_store[pdev_idx] = *reg_info;
}

- spin_lock(&ab->base_lock);
+ spin_lock_bh(&ab->base_lock);
if (ab->default_regd[pdev_idx]) {
/* The initial rules from FW after WMI Init is to build
* the default regd. From then on, any rules updated for
@@ -7214,7 +7214,7 @@ int ath11k_reg_handle_chan_list(struct ath11k_base *ab,
ab->default_regd[pdev_idx] = regd;
}
ab->dfs_region = reg_info->dfs_region;
- spin_unlock(&ab->base_lock);
+ spin_unlock_bh(&ab->base_lock);

return 0;

--
2.40.1