wlan-ap-Telecominfraproject/feeds/ipq95xx/mac80211/patches/qca/773-002-ath12k-add-peer-channel-width-switch-command-support.patch
John Crispin 144c5d00f4 ipq95xx/mac80211: update to ATH12.3-CS
Signed-off-by: John Crispin <john@phrozen.org>
2024-02-28 18:56:21 +01:00

425 lines
12 KiB
Diff

From e2a727bd1d752b315b15decd1312e0bd7e536050 Mon Sep 17 00:00:00 2001
From: Aditya Kumar Singh <quic_adisi@quicinc.com>
Date: Thu, 11 May 2023 16:29:28 +0530
Subject: [PATCH] ath12k: add peer channel width switch command support
Currently, separate set peer param command for each peer is being sent
for changing channel width. However this leads to firmware buffer overflow
during multi-client testing.
This can be optimised by using WMI_PEER_CHAN_WIDTH_SWITCH_CMDID. This command
clubs N number of peers into single command. The value of N is advertised
by firmware in service ready event 2.
Add support to form and send WMI_PEER_CHAN_WIDTH_SWITCH_CMDID during peer
channel width change.
Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com>
---
drivers/net/wireless/ath/ath12k/core.h | 11 ++
drivers/net/wireless/ath/ath12k/mac.c | 94 +++++++++++
drivers/net/wireless/ath/ath12k/wmi.c | 225 +++++++++++++++++++++++++
drivers/net/wireless/ath/ath12k/wmi.h | 33 ++++
4 files changed, 363 insertions(+)
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -347,6 +347,11 @@ struct ath12k_link_vif_pvt {
};
#endif
+struct ath12k_peer_ch_width_switch_data {
+ int count;
+ struct wmi_chan_width_peer_arg peer_arg[];
+};
+
struct ath12k_link_vif {
struct ath12k_base *ab;
struct ath12k_dp *dp;
@@ -410,6 +415,10 @@ struct ath12k_link_vif {
bool mvr_processing;
bool pending_csa_up;
int num_stations;
+
+ struct completion peer_ch_width_switch_send;
+ struct work_struct peer_ch_width_switch_work;
+ struct ath12k_peer_ch_width_switch_data *peer_ch_width_switch_data;
};
struct ath12k_vif {
@@ -1386,6 +1395,8 @@ struct ath12k_base {
int userpd_id;
+ u32 chwidth_num_peer_caps;
+
/* must be last */
u8 drv_priv[0] __aligned(sizeof(void *));
};
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -4877,6 +4877,9 @@ ath12k_mac_assign_link_vif( struct ath12
INIT_WORK(&arvif->update_bcn_template_work,
ath12k_update_bcn_template_work);
arvif->num_stations = 0;
+ init_completion(&arvif->peer_ch_width_switch_send);
+ INIT_WORK(&arvif->peer_ch_width_switch_work,
+ ath12k_wmi_peer_chan_width_switch_work);
}
}
@@ -6168,6 +6171,7 @@ static int ath12k_station_assoc(struct a
spin_lock_bh(&ar->data_lock);
+ arsta->bw = bandwidth;
arvif->num_stations++;
ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
"mac station %pM connected to vdev %u. num_stations=%u\n",
@@ -6281,6 +6285,73 @@ static int ath12k_station_unauthorize(st
return ret;
}
+static int ath12k_mac_set_peer_ch_switch_data(struct ath12k_link_vif *arvif,
+ struct ath12k_link_sta *arsta)
+{
+ struct ath12k *ar = arvif->ar;
+ struct ath12k_peer_ch_width_switch_data *peer_data;
+ struct wmi_chan_width_peer_arg *peer_arg;
+ struct ieee80211_link_sta *link_sta;
+ struct ieee80211_vif *vif = arvif->ahvif->vif;
+ struct cfg80211_chan_def def;
+ u16 ru_punct_bitmap;
+
+ lockdep_assert_held(&ar->conf_mutex);
+
+ if (!ar->ab->chwidth_num_peer_caps)
+ return -EOPNOTSUPP;
+
+ if (WARN_ON(ath12k_mac_vif_chan(vif, &def, arvif->link_id)))
+ return -EINVAL;
+
+ peer_data = arvif->peer_ch_width_switch_data;
+
+ if (!peer_data) {
+ peer_data = kzalloc(struct_size(peer_data, peer_arg,
+ arvif->num_stations),
+ GFP_KERNEL);
+ if (!peer_data)
+ return -ENOMEM;
+
+ peer_data->count = 0;
+ arvif->peer_ch_width_switch_data = peer_data;
+ }
+
+ peer_arg = &peer_data->peer_arg[peer_data->count++];
+
+
+ ru_punct_bitmap = 0;
+
+ rcu_read_lock();
+ link_sta = ath12k_get_link_sta(arsta);
+
+ if (link_sta) {
+ if (link_sta->he_cap.has_he && link_sta->eht_cap.has_eht)
+ ru_punct_bitmap = def.ru_punct_bitmap;
+
+ if (ieee80211_vif_is_mesh(vif) && link_sta->ru_punct_bitmap)
+ ru_punct_bitmap = link_sta->ru_punct_bitmap;
+ }
+
+ rcu_read_unlock();
+
+ spin_lock_bh(&ar->data_lock);
+ ether_addr_copy(peer_arg->mac_addr.addr, arsta->addr);
+ peer_arg->chan_width = arsta->bw;
+ peer_arg->puncture_20mhz_bitmap = ~ru_punct_bitmap;
+ spin_unlock_bh(&ar->data_lock);
+
+ if (peer_data->count == 1) {
+ reinit_completion(&arvif->peer_ch_width_switch_send);
+ ieee80211_queue_work(ar->ah->hw, &arvif->peer_ch_width_switch_work);
+ }
+
+ if (peer_data->count == arvif->num_stations)
+ complete(&arvif->peer_ch_width_switch_send);
+
+ return 0;
+}
+
static void ath12k_sta_rc_update_wk(struct work_struct *wk)
{
struct ath12k *ar;
@@ -6341,6 +6412,10 @@ static void ath12k_sta_rc_update_wk(stru
ath12k_dbg(ar->ab, ATH12K_DBG_PEER, "mac BW change for sta %pM new BW %d\n",
arsta->addr, bw);
+ err = ath12k_mac_set_peer_ch_switch_data(arvif, arsta);
+ if (!err || err == -EINVAL)
+ goto err_unlock;
+
err = ath12k_wmi_set_peer_param(ar, arsta->addr, arvif->vdev_id,
WMI_PEER_CHWIDTH, bw);
if (err)
@@ -10082,6 +10157,9 @@ static int ath12k_mac_op_add_interface(s
INIT_WORK(&ahvif->deflink.update_bcn_template_work,
ath12k_update_bcn_template_work);
ahvif->deflink.num_stations = 0;
+ init_completion(&ahvif->deflink.peer_ch_width_switch_send);
+ INIT_WORK(&ahvif->deflink.peer_ch_width_switch_work,
+ ath12k_wmi_peer_chan_width_switch_work);
ahvif->key_cipher = INVALID_CIPHER;
@@ -10136,6 +10214,7 @@ static void ath12k_mac_remove_link_inter
cancel_work_sync(&arvif->update_obss_color_notify_work);
cancel_work_sync(&arvif->update_bcn_template_work);
+ cancel_work_sync(&arvif->peer_ch_width_switch_work);
lockdep_assert_held(&ah->conf_mutex);
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -6201,6 +6201,7 @@ static int ath12k_wmi_tlv_svc_rdy_ext2_p
}
ab->max_msduq_per_tid = parse->param.max_msduq_per_tid;
ab->default_msduq_per_tid = parse->param.default_msduq_per_tid;
+ ab->chwidth_num_peer_caps = parse->param.chwidth_num_peer_caps;
break;
case WMI_TAG_ARRAY_STRUCT:
@@ -14374,3 +14375,187 @@ int ath12k_wmi_svc_send_disable(struct a
return ret;
}
+
+static void ath12k_wmi_put_peer_list(struct ath12k_base *ab,
+ struct wmi_chan_width_peer_list *peer_list,
+ struct wmi_chan_width_peer_arg *peer_arg,
+ u32 num_peers, int start_idx)
+{
+ struct wmi_chan_width_peer_list *itr;
+ struct wmi_chan_width_peer_arg *arg_itr;
+ int i;
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "wmi peer channel width switch command peer list\n");
+
+ for (i = 0; i < num_peers; i++) {
+ itr = &peer_list[i];
+ arg_itr = &peer_arg[start_idx + i];
+
+ itr->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_CHAN_WIDTH_PEER_LIST,
+ sizeof(*itr));
+ ether_addr_copy(itr->mac_addr.addr, arg_itr->mac_addr.addr);
+ itr->chan_width = cpu_to_le32(arg_itr->chan_width);
+ itr->puncture_20mhz_bitmap = cpu_to_le32(arg_itr->puncture_20mhz_bitmap);
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ " (%u) width %u addr %pM punct_bitmap 0x%x\n",
+ i + 1, arg_itr->chan_width, arg_itr->mac_addr.addr,
+ arg_itr->puncture_20mhz_bitmap);
+ }
+}
+
+static int ath12k_wmi_peer_chan_width_switch(struct ath12k *ar,
+ struct wmi_peer_chan_width_switch_arg *arg)
+{
+ struct ath12k_base *ab = ar->ab;
+ struct ath12k_pdev_wmi *wmi = ar->wmi;
+ struct wmi_peer_chan_width_switch_req_cmd *cmd;
+ struct wmi_chan_width_peer_list *peer_list;
+ struct wmi_tlv *tlv;
+ u32 num_peers;
+ size_t peer_list_len;
+ struct sk_buff *skb;
+ void *ptr;
+ int ret, len;
+
+ num_peers = arg->num_peers;
+
+ if (WARN_ON(num_peers > ab->chwidth_num_peer_caps))
+ return -EINVAL;
+
+ peer_list_len = num_peers * sizeof(*peer_list);
+
+ len = sizeof(*cmd) + TLV_HDR_SIZE + peer_list_len;
+
+ skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len);
+ if (!skb)
+ return -ENOMEM;
+
+ cmd = (struct wmi_peer_chan_width_switch_req_cmd *)skb->data;
+ cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_PEER_CHAN_WIDTH_SWITCH_CMD,
+ sizeof(*cmd));
+ cmd->num_peers = cpu_to_le32(num_peers);
+ cmd->vdev_var = cpu_to_le32(arg->vdev_var);
+
+ ptr = skb->data + sizeof(*cmd);
+ tlv = (struct wmi_tlv *)ptr;
+
+ tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, peer_list_len);
+ peer_list = (struct wmi_chan_width_peer_list *)tlv->value;
+
+ ath12k_wmi_put_peer_list(ab, peer_list, arg->peer_arg, num_peers,
+ arg->start_idx);
+
+ ptr += peer_list_len;
+
+ ret = ath12k_wmi_cmd_send(wmi, skb, WMI_PEER_CHAN_WIDTH_SWITCH_CMDID);
+ if (ret) {
+ ath12k_warn(ab, "wmi failed to send peer chan width switch command (%d)\n",
+ ret);
+ dev_kfree_skb(skb);
+ return ret;
+ }
+
+ ath12k_dbg(ab, ATH12K_DBG_WMI,
+ "wmi peer chan width switch cmd sent num_peers %d \n",
+ num_peers);
+
+ return ret;
+}
+
+void ath12k_wmi_set_peers_chan_width(struct ath12k_link_vif *arvif,
+ struct wmi_chan_width_peer_arg *peer_arg,
+ int num, u8 start_idx)
+{
+ struct ath12k *ar = arvif->ar;
+ struct wmi_chan_width_peer_arg *arg;
+ int i, err;
+
+ for (i = 0; i < num; i++) {
+ arg = &peer_arg[start_idx + i];
+
+ err = ath12k_wmi_set_peer_param(ar, arg->mac_addr.addr,
+ arvif->vdev_id, WMI_PEER_CHWIDTH,
+ arg->chan_width);
+ if (err) {
+ ath12k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n",
+ arg->mac_addr.addr, arg->chan_width, err);
+ continue;
+ }
+ }
+}
+
+void ath12k_wmi_peer_chan_width_switch_work(struct work_struct *work)
+{
+ struct ath12k_link_vif *arvif = container_of(work, struct ath12k_link_vif,
+ peer_ch_width_switch_work);
+ struct ath12k *ar = arvif->ar;
+ struct ath12k_peer_ch_width_switch_data *data;
+ struct wmi_peer_chan_width_switch_arg arg;
+ unsigned long time_left = 0;
+ int count_left, curr_count, max_count_per_cmd = ar->ab->chwidth_num_peer_caps;
+ int cmd_num = 0, ret;
+
+ mutex_lock(&ar->conf_mutex);
+
+ /* possible that the worker got scheduled after complete was triggered. In
+ * this case we don't wait for timeout */
+ if (arvif->peer_ch_width_switch_data->count == arvif->num_stations)
+ goto send_cmd;
+
+ mutex_unlock(&ar->conf_mutex);
+
+ time_left = wait_for_completion_timeout(&arvif->peer_ch_width_switch_send,
+ ATH12K_PEER_CH_WIDTH_SWITCH_TIMEOUT_HZ);
+ if (time_left == 0) {
+ /* Even though timeout occured, we would send the command for the peers
+ * for which we received sta rc update event, hence not returning */
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
+ "timed out waiting for all peers in peer channel width switch\n");
+ }
+
+ mutex_lock(&ar->conf_mutex);
+
+send_cmd:
+
+ data = arvif->peer_ch_width_switch_data;
+
+ spin_lock_bh(&ar->data_lock);
+ arg.vdev_var = arvif->vdev_id;
+ spin_unlock_bh(&ar->data_lock);
+
+ arg.vdev_var |= ATH12K_PEER_VALID_VDEV_ID | ATH12K_PEER_PUNCT_BITMAP_VALID;
+ arg.peer_arg = data->peer_arg;
+
+ count_left = data->count;
+
+ while (count_left > 0) {
+ if (count_left <= max_count_per_cmd)
+ curr_count = count_left;
+ else
+ curr_count = max_count_per_cmd;
+
+ count_left -= curr_count;
+
+ cmd_num++;
+
+ arg.num_peers = curr_count;
+ arg.start_idx = (cmd_num - 1) * max_count_per_cmd;
+
+ ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
+ "wmi peer channel width switch command num %u\n",
+ cmd_num);
+
+ ret = ath12k_wmi_peer_chan_width_switch(ar, &arg);
+ if (ret) {
+ /* fallback */
+ ath12k_wmi_set_peers_chan_width(arvif, arg.peer_arg, arg.num_peers,
+ arg.start_idx);
+ }
+ }
+
+ kfree(arvif->peer_ch_width_switch_data);
+ arvif->peer_ch_width_switch_data = NULL;
+ mutex_unlock(&ar->conf_mutex);
+}
--- a/drivers/net/wireless/ath/ath12k/wmi.h
+++ b/drivers/net/wireless/ath/ath12k/wmi.h
@@ -8242,6 +8242,36 @@ struct wmi_vdev_adfs_ocac_complete_event
u32 center_freq2;
} __packed;
+#define ATH12K_PEER_VALID_VDEV_ID (1 << 31)
+#define ATH12K_PEER_PUNCT_BITMAP_VALID (1 << 30)
+#define ATH12K_PEER_CH_WIDTH_SWITCH_TIMEOUT_HZ (5 * HZ)
+
+struct wmi_chan_width_peer_arg {
+ struct wmi_mac_addr mac_addr;
+ u32 chan_width;
+ u32 puncture_20mhz_bitmap;
+};
+
+struct wmi_peer_chan_width_switch_arg {
+ u32 num_peers;
+ u32 vdev_var;
+ u32 start_idx;
+ struct wmi_chan_width_peer_arg *peer_arg;
+};
+
+struct wmi_peer_chan_width_switch_req_cmd {
+ __le32 tlv_header;
+ __le32 num_peers;
+ __le32 vdev_var;
+} __packed;
+
+struct wmi_chan_width_peer_list {
+ __le32 tlv_header;
+ struct wmi_mac_addr mac_addr;
+ __le32 chan_width;
+ __le32 puncture_20mhz_bitmap;
+} __packed;
+
#define ATH12K_FW_STATS_BUF_SIZE (1024 * 1024)
void ath12k_wmi_init_qcn9274(struct ath12k_base *ab,
@@ -8450,4 +8480,5 @@ int ath12k_wmi_config_peer_ppeds_routing
u32 service_code, u32 priority_valid,
u32 src_info, bool ppe_routing_enable);
#endif
+void ath12k_wmi_peer_chan_width_switch_work(struct work_struct *work);
#endif