Johannes Berg says:

====================
Much quieter, thankfully:
 - a set of ath12k fixes, including a recent
   MLO regression for WCN7850/QCC2072
 - iwlegacy gets rid of a BUG_ON that triggered
 - a couple more robustness/security fixes

* tag 'wireless-2026-07-29' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless:
  wifi: iwlegacy: replace BUG_ON() with WARN_ON() on num_stations check
  wifi: mac80211: validate individual TWT params before driver setup
  wifi: cfg80211: publish PMSR request before starting the driver
  wifi: mwifiex: use the subframe length when parsing A-MSDU TDLS frames
  wifi: cfg80211: validate IEs in cfg80211_wext_siwgenie()
  wifi: mac80211: fix tid_tx use-after-free on BA session stop
  wifi: ath12k: resolve PENDING ML peer ID from MLO_PEER_MAP HTT event
  wifi: ath12k: defer dp_peer registration when firmware allocates MLD peer ID
  wifi: ath12k: do not advertise MLD peer ID for firmware-allocate devices
  wifi: ath12k: introduce host_alloc_ml_id hardware parameter
  wifi: ath12k: add support for HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP
  wifi: ath12k: keep ATH12K_PEER_ML_ID_VALID set in ath12k_sta::ml_peer_id
  wifi: ath12k: factor out peer assoc send-and-wait into a helper
  wifi: ath12k: fix out-of-bounds clear_bit in ath12k_mac_dp_peer_cleanup()
====================

Link: https://patch.msgid.link/20260729071954.45655-3-johannes@sipsolutions.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
Jakub Kicinski
2026-07-29 17:07:49 -07:00
18 changed files with 333 additions and 71 deletions

View File

@@ -1544,6 +1544,8 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
}
wiphy_unlock(ah->hw->wiphy);
complete(&ah->peer_ml_id_done);
}
wake_up(&ab->wmi_ab.tx_credits_wq);

View File

@@ -72,6 +72,7 @@
#define ATH12K_MAX_MLO_PEERS 256
#define ATH12K_MLO_PEER_ID_INVALID 0xFFFF
#define ATH12K_MLO_PEER_ID_PENDING 0xFFFE
#define ATH12K_INVALID_RSSI_FULL -1
#define ATH12K_INVALID_RSSI_EMPTY -128
@@ -793,6 +794,8 @@ struct ath12k_hw {
enum ath12k_hw_state state;
bool regd_updated;
bool use_6ghz_regd;
bool host_alloc_ml_id;
struct completion peer_ml_id_done;
u8 num_radio;

View File

@@ -6,6 +6,7 @@
#include "core.h"
#include "peer.h"
#include "dp_peer.h"
#include "htc.h"
#include "dp_htt.h"
#include "debugfs_htt_stats.h"
@@ -575,6 +576,51 @@ static void ath12k_htt_mlo_offset_event_handler(struct ath12k_base *ab,
rcu_read_unlock();
}
static void ath12k_dp_htt_mlo_peer_map_handler(struct ath12k_base *ab,
struct sk_buff *skb)
{
struct htt_resp_msg *resp = (struct htt_resp_msg *)skb->data;
struct htt_t2h_mlo_peer_map_event *ev = &resp->mlo_peer_map_ev;
u16 raw_peer_id, peer_id, addr_h16;
u8 peer_addr[ETH_ALEN];
int ret;
if (skb->len < sizeof(*ev)) {
ath12k_warn(ab, "unexpected htt mlo peer map event len %u\n",
skb->len);
return;
}
raw_peer_id = le32_get_bits(ev->info0,
HTT_T2H_MLO_PEER_MAP_INFO0_MLO_PEER_ID);
peer_id = raw_peer_id | ATH12K_PEER_ML_ID_VALID;
addr_h16 = le32_get_bits(ev->info1,
HTT_T2H_MLO_PEER_MAP_INFO1_MAC_ADDR_H16);
ath12k_dp_get_mac_addr(le32_to_cpu(ev->mac_addr_l32), addr_h16,
peer_addr);
ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "htt mlo peer map peer %pM id %u\n",
peer_addr, peer_id);
/*
* Fix up the dp_peer entry created with ATH12K_MLO_PEER_ID_PENDING
* earlier; on chips with host_alloc_ml_id == false this is the only
* point at which the host learns the firmware-assigned ID. Chips
* that allocate the ID on the host also receive this event but the
* firmware-reported ID matches the host-allocated one, so there is
* nothing to fix up.
*/
if (!ab->hw_params->host_alloc_ml_id) {
ret = ath12k_dp_peer_fixup_peer_id(ab, peer_addr,
peer_id);
if (ret)
ath12k_warn(ab,
"failed to fix up peer id %u for dp peer %pM: %d\n",
peer_id, peer_addr, ret);
}
}
void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
struct sk_buff *skb)
{
@@ -659,6 +705,9 @@ void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
case HTT_T2H_MSG_TYPE_MLO_TIMESTAMP_OFFSET_IND:
ath12k_htt_mlo_offset_event_handler(ab, skb);
break;
case HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP:
ath12k_dp_htt_mlo_peer_map_handler(ab, skb);
break;
default:
ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "dp_htt event %d not handled\n",
type);

View File

@@ -930,6 +930,7 @@ enum htt_t2h_msg_type {
HTT_T2H_MSG_TYPE_EXT_STATS_CONF = 0x1c,
HTT_T2H_MSG_TYPE_BKPRESSURE_EVENT_IND = 0x24,
HTT_T2H_MSG_TYPE_MLO_TIMESTAMP_OFFSET_IND = 0x28,
HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP = 0x29,
HTT_T2H_MSG_TYPE_PEER_MAP3 = 0x2b,
HTT_T2H_MSG_TYPE_VDEV_TXRX_STATS_PERIODIC_IND = 0x2c,
};
@@ -974,11 +975,22 @@ struct htt_t2h_peer_unmap_event {
__le32 info1;
} __packed;
#define HTT_T2H_MLO_PEER_MAP_INFO0_MLO_PEER_ID GENMASK(23, 8)
#define HTT_T2H_MLO_PEER_MAP_INFO1_MAC_ADDR_H16 GENMASK(15, 0)
struct htt_t2h_mlo_peer_map_event {
__le32 info0;
__le32 mac_addr_l32;
__le32 info1;
__le32 reserved[5];
} __packed;
struct htt_resp_msg {
union {
struct htt_t2h_version_conf_msg version_msg;
struct htt_t2h_peer_map_event peer_map_ev;
struct htt_t2h_peer_unmap_event peer_unmap_ev;
struct htt_t2h_mlo_peer_map_event mlo_peer_map_ev;
};
} __packed;

View File

@@ -475,7 +475,9 @@ int ath12k_dp_peer_create(struct ath12k_dp_hw *dp_hw, u8 *addr,
dp_peer->is_mlo = params->is_mlo;
/*
* For MLO client, the host assigns the ML peer ID, so set peer_id in dp_peer
* For MLO client, the ML peer ID, either known or PENDING, needs to be
* initialized here since the following logic depends on it.
*
* For non-MLO client, host gets link peer ID from firmware and will be
* assigned at the time of link peer creation
*/
@@ -491,13 +493,17 @@ int ath12k_dp_peer_create(struct ath12k_dp_hw *dp_hw, u8 *addr,
list_add(&dp_peer->list, &dp_hw->dp_peers_list);
/*
* For MLO client, the peer_id for ath12k_dp_peer is allocated by host
* and that peer_id is known at this point, and hence this ath12k_dp_peer
* can be added to the RCU table using the peer_id.
* For non-MLO client, this addition to RCU table shall be done at the
* time of assignment of ath12k_dp_link_peer to ath12k_dp_peer.
* For an MLO client whose ML peer ID is allocated by the host, the
* peer_id is known here and the dp_peer can be added to the RCU
* table using it. For an MLO client on chips where the firmware
* allocates the ID, peer_id is ATH12K_MLO_PEER_ID_PENDING and the
* RCU table publish is deferred to the
* HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP handler. For a non-MLO client
* the publish happens later, at the time of assignment of
* ath12k_dp_link_peer to ath12k_dp_peer.
*/
if (dp_peer->is_mlo)
if (dp_peer->is_mlo &&
dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING)
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], dp_peer);
spin_unlock_bh(&dp_hw->peer_lock);
@@ -518,7 +524,8 @@ void ath12k_dp_peer_delete(struct ath12k_dp_hw *dp_hw, u8 *addr,
return;
}
if (dp_peer->is_mlo)
if (dp_peer->is_mlo &&
dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING)
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL);
list_del(&dp_peer->list);
@@ -695,3 +702,55 @@ void ath12k_dp_link_peer_reset_rx_stats(struct ath12k_dp *dp, const u8 *addr)
if (rx_stats)
memset(rx_stats, 0, sizeof(*rx_stats));
}
int ath12k_dp_peer_fixup_peer_id(struct ath12k_base *ab,
const u8 *peer_addr, u16 peer_id)
{
struct ath12k_dp_link_peer *link_peer;
struct ath12k_dp_peer *dp_peer = NULL;
struct ath12k_hw_group *ag = ab->ag;
struct ath12k_dp_hw *dp_hw = NULL;
struct ath12k_hw *ah;
int i;
if (peer_id >= (ATH12K_PEER_ML_ID_VALID | ATH12K_MAX_MLO_PEERS))
return -EINVAL;
for (i = 0; i < ag->num_hw; i++) {
ah = ag->ah[i];
if (!ah)
continue;
spin_lock_bh(&ah->dp_hw.peer_lock);
dp_peer = ath12k_dp_peer_find_by_addr(&ah->dp_hw,
(u8 *)peer_addr);
if (dp_peer) {
dp_hw = &ah->dp_hw;
break;
}
spin_unlock_bh(&ah->dp_hw.peer_lock);
}
if (!dp_peer)
return -ENOENT;
/* dp_hw->peer_lock is held */
dp_peer->peer_id = peer_id;
rcu_assign_pointer(dp_hw->dp_peers[peer_id], dp_peer);
for (i = 0; i < ATH12K_NUM_MAX_LINKS; i++) {
link_peer = rcu_dereference_protected(dp_peer->link_peers[i],
lockdep_is_held(&dp_hw->peer_lock));
if (link_peer)
link_peer->ml_id = peer_id;
}
ath12k_sta_to_ahsta(dp_peer->sta)->ml_peer_id = peer_id;
spin_unlock_bh(&dp_hw->peer_lock);
complete(&ah->peer_ml_id_done);
return 0;
}

View File

@@ -181,4 +181,6 @@ struct ath12k_dp_peer *ath12k_dp_peer_find_by_peerid(struct ath12k_pdev_dp *dp_p
struct ath12k_dp_link_peer *
ath12k_dp_link_peer_find_by_peerid(struct ath12k_pdev_dp *dp_pdev, u16 peer_id);
void ath12k_dp_link_peer_free(struct ath12k_dp_link_peer *peer);
int ath12k_dp_peer_fixup_peer_id(struct ath12k_base *ab, const u8 *peer_addr,
u16 peer_id);
#endif

View File

@@ -236,6 +236,8 @@ struct ath12k_hw_params {
u32 max_client_dbs;
u32 max_client_dbs_sbs;
} client;
bool host_alloc_ml_id;
};
struct ath12k_hw_ops {

View File

@@ -1282,13 +1282,17 @@ void ath12k_mac_dp_peer_cleanup(struct ath12k_hw *ah)
struct ath12k_dp_peer *dp_peer, *tmp;
struct ath12k_dp_hw *dp_hw = &ah->dp_hw;
lockdep_assert_wiphy(ah->hw->wiphy);
INIT_LIST_HEAD(&peers);
spin_lock_bh(&dp_hw->peer_lock);
list_for_each_entry_safe(dp_peer, tmp, &dp_hw->dp_peers_list, list) {
if (dp_peer->is_mlo) {
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id], NULL);
clear_bit(dp_peer->peer_id, ah->free_ml_peer_id_map);
if (dp_peer->peer_id != ATH12K_MLO_PEER_ID_PENDING)
rcu_assign_pointer(dp_hw->dp_peers[dp_peer->peer_id],
NULL);
ath12k_peer_ml_free(ah, ath12k_sta_to_ahsta(dp_peer->sta));
}
list_move(&dp_peer->list, &peers);
@@ -3531,11 +3535,16 @@ static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta,
struct ath12k_sta *ahsta = arsta->ahsta;
struct ath12k_link_sta *arsta_p;
struct ath12k_link_vif *arvif;
struct ath12k_hw *ah = arsta->arvif->ar->ah;
unsigned long links;
u8 link_id;
int i;
if (!sta->mlo || ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID)
if (!sta->mlo)
return;
if (ah->host_alloc_ml_id &&
ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID)
return;
ml->enabled = true;
@@ -3543,12 +3552,25 @@ static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta,
/* For now considering the primary umac based on assoc link */
ml->primary_umac = arsta->is_assoc_link;
ml->peer_id_valid = true;
/*
* Only chips that allocate the MLD peer ID on the host send a valid
* ml_peer_id in WMI_PEER_ASSOC_CMDID. For chips where the firmware
* picks the ID, leave peer_id_valid false to avoid unexpected issues.
*/
ml->peer_id_valid = ah->host_alloc_ml_id;
ml->logical_link_idx_valid = true;
ether_addr_copy(ml->mld_addr, sta->addr);
ml->logical_link_idx = arsta->link_idx;
ml->ml_peer_id = ahsta->ml_peer_id;
/*
* WMI_MLO_PEER_ASSOC_PARAMS expects the raw ML peer ID without
* the host-side ATH12K_PEER_ML_ID_VALID bookkeeping bit. For chips
* where the firmware allocates the ID, the field is unused (the
* firmware always allocates regardless of the value here); send 0
* to make that intent explicit.
*/
ml->ml_peer_id = ah->host_alloc_ml_id ?
(ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID) : 0;
ml->ieee_link_id = arsta->link_id;
ml->num_partner_links = 0;
ml->eml_cap = sta->eml_cap;
@@ -3595,8 +3617,6 @@ static void ath12k_peer_assoc_prepare(struct ath12k *ar,
memset(arg, 0, sizeof(*arg));
reinit_completion(&ar->peer_assoc_done);
arg->peer_new_assoc = !reassoc;
ath12k_peer_assoc_h_basic(ar, arvif, arsta, arg);
ath12k_peer_assoc_h_crypto(ar, arvif, arsta, arg);
@@ -3836,6 +3856,52 @@ static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar,
return bw;
}
static int ath12k_mac_peer_assoc(struct ath12k *ar,
struct ath12k_wmi_peer_assoc_arg *peer_arg)
{
struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
int ret;
reinit_completion(&ar->peer_assoc_done);
reinit_completion(&ah->peer_ml_id_done);
ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
if (ret) {
ath12k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n",
peer_arg->peer_mac, peer_arg->vdev_id, ret);
return ret;
}
if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) {
ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
peer_arg->peer_mac, peer_arg->vdev_id);
return -ETIMEDOUT;
}
/*
* For devices where the firmware allocates the MLD peer ID, the host
* learns the real ID only from the MLO_RX_PEER_MAP HTT event, which is
* handled in a softirq (BH workqueue) context that cannot take the
* wiphy lock. Block here, while still holding the wiphy lock, until
* that event has fixed up the ID. This serialises the fixup against
* all other wiphy-locked ml_peer_id accesses.
*
* The firmware sends the event only once, in response to the assoc-link
* peer assoc, so block only for that link.
*/
if (!ah->host_alloc_ml_id &&
peer_arg->is_assoc &&
peer_arg->ml.enabled &&
peer_arg->ml.assoc_link &&
!wait_for_completion_timeout(&ah->peer_ml_id_done, 1 * HZ)) {
ath12k_warn(ar->ab, "failed to get MLO peer map event for %pM vdev %i\n",
peer_arg->peer_mac, peer_arg->vdev_id);
return -ETIMEDOUT;
}
return 0;
}
static void ath12k_bss_assoc(struct ath12k *ar,
struct ath12k_link_vif *arvif,
struct ieee80211_bss_conf *bss_conf)
@@ -3916,18 +3982,10 @@ static void ath12k_bss_assoc(struct ath12k *ar,
}
peer_arg->is_assoc = true;
ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
if (ret) {
ath12k_warn(ar->ab, "failed to run peer assoc for %pM vdev %i: %d\n",
bss_conf->bssid, arvif->vdev_id, ret);
return;
}
if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) {
ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
bss_conf->bssid, arvif->vdev_id);
ret = ath12k_mac_peer_assoc(ar, peer_arg);
if (ret)
return;
}
ret = ath12k_setup_peer_smps(ar, arvif, bss_conf->bssid,
&link_sta->ht_cap, &link_sta->he_6ghz_capa);
@@ -6481,18 +6539,10 @@ static int ath12k_mac_station_assoc(struct ath12k *ar,
}
peer_arg->is_assoc = true;
ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
if (ret) {
ath12k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n",
arsta->addr, arvif->vdev_id, ret);
return ret;
}
if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) {
ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
arsta->addr, arvif->vdev_id);
return -ETIMEDOUT;
}
ret = ath12k_mac_peer_assoc(ar, peer_arg);
if (ret)
return ret;
num_vht_rates = ath12k_mac_bitrate_mask_num_vht_rates(ar, band, mask);
num_he_rates = ath12k_mac_bitrate_mask_num_he_rates(ar, band, mask);
@@ -6841,14 +6891,8 @@ static void ath12k_sta_rc_update_wk(struct wiphy *wiphy, struct wiphy_work *wk)
peer_arg, true);
peer_arg->is_assoc = false;
err = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
if (err)
ath12k_warn(ar->ab, "failed to run peer assoc for STA %pM vdev %i: %d\n",
arsta->addr, arvif->vdev_id, err);
if (!wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ))
ath12k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n",
arsta->addr, arvif->vdev_id);
ath12k_mac_peer_assoc(ar, peer_arg);
}
}
}
@@ -7266,10 +7310,8 @@ static void ath12k_mac_ml_station_remove(struct ath12k_vif *ahvif,
ath12k_mac_free_unassign_link_sta(ah, ahsta, link_id);
}
if (sta->mlo) {
clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map);
ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
}
if (sta->mlo)
ath12k_peer_ml_free(ah, ahsta);
}
static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw,
@@ -7733,15 +7775,23 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
/* ML sta */
if (sta->mlo && !ahsta->links_map &&
(hweight16(sta->valid_links) == 1)) {
ahsta->ml_peer_id = ath12k_peer_ml_alloc(ah);
if (ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) {
ath12k_hw_warn(ah, "unable to allocate ML peer id for sta %pM",
sta->addr);
goto exit;
if (ah->host_alloc_ml_id) {
ahsta->ml_peer_id = ath12k_peer_ml_alloc(ah);
if (ahsta->ml_peer_id == ATH12K_MLO_PEER_ID_INVALID) {
ath12k_hw_warn(ah, "unable to allocate ML peer id for sta %pM",
sta->addr);
goto exit;
}
} else {
/*
* firmware allocates the ML peer ID and notifies
* the host via HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP
*/
ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_PENDING;
}
dp_params.is_mlo = true;
dp_params.peer_id = ahsta->ml_peer_id | ATH12K_PEER_ML_ID_VALID;
dp_params.peer_id = ahsta->ml_peer_id;
}
dp_params.sta = sta;
@@ -7878,10 +7928,8 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
peer_delete:
ath12k_dp_peer_delete(&ah->dp_hw, sta->addr, sta);
ml_peer_id_clear:
if (sta->mlo) {
clear_bit(ahsta->ml_peer_id, ah->free_ml_peer_id_map);
ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
}
if (sta->mlo)
ath12k_peer_ml_free(ah, ahsta);
exit:
/* update the state if everything went well */
if (!ret)
@@ -15310,6 +15358,7 @@ static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_hw_group *ag,
ah->num_radio = num_pdev_map;
mutex_init(&ah->hw_mutex);
init_completion(&ah->peer_ml_id_done);
spin_lock_init(&ah->dp_hw.peer_lock);
INIT_LIST_HEAD(&ah->dp_hw.dp_peers_list);
@@ -15384,8 +15433,9 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag)
int mac_id, device_id, total_radio, num_hw;
struct ath12k_base *ab;
struct ath12k_hw *ah;
int ret, i, j;
bool conf = false;
u8 radio_per_hw;
int ret, i, j;
total_radio = 0;
for (i = 0; i < ag->num_devices; i++) {
@@ -15425,6 +15475,20 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag)
}
ab = ag->ab[device_id];
/*
* the assumption is all devices within an ah
* share the same host_alloc_ml_id configuration
*/
if (j == 0) {
conf = ab->hw_params->host_alloc_ml_id;
} else if (conf != ab->hw_params->host_alloc_ml_id) {
ath12k_warn(ab, "inconsistent ML ID config within ah, device 0 uses %s allocated ID, while device %u doesn't\n",
conf ? "host" : "firmware", device_id);
ret = -EINVAL;
goto err;
}
pdev_map[j].ab = ab;
pdev_map[j].pdev_idx = mac_id;
mac_id++;
@@ -15449,6 +15513,7 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag)
}
ah->dev = ab->dev;
ah->host_alloc_ml_id = conf;
ag->ah[i] = ah;
ag->num_hw++;

View File

@@ -230,7 +230,16 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif,
/* Fill ML info into created peer */
if (sta->mlo) {
ml_peer_id = ahsta->ml_peer_id;
peer->ml_id = ml_peer_id | ATH12K_PEER_ML_ID_VALID;
/*
* For chips where firmware allocates the ML peer ID,
* ml_peer_id is ATH12K_MLO_PEER_ID_PENDING here. The
* MLO_RX_PEER_MAP HTT event handler fixes up
* peer->ml_id once the ID is known.
*/
if (ml_peer_id == ATH12K_MLO_PEER_ID_PENDING)
peer->ml_id = ATH12K_MLO_PEER_ID_INVALID;
else
peer->ml_id = ml_peer_id;
ether_addr_copy(peer->ml_addr, sta->addr);
/* the assoc link is considered primary for now */
@@ -276,9 +285,25 @@ u16 ath12k_peer_ml_alloc(struct ath12k_hw *ah)
}
if (ml_peer_id == ATH12K_MAX_MLO_PEERS)
ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
return ATH12K_MLO_PEER_ID_INVALID;
return ml_peer_id;
return ml_peer_id | ATH12K_PEER_ML_ID_VALID;
}
void ath12k_peer_ml_free(struct ath12k_hw *ah, struct ath12k_sta *ahsta)
{
lockdep_assert_wiphy(ah->hw->wiphy);
/*
* Only devices that allocate the ID on the host own a slot in
* free_ml_peer_id_map.
*/
if (ah->host_alloc_ml_id &&
(ahsta->ml_peer_id <
(ATH12K_MAX_MLO_PEERS | ATH12K_PEER_ML_ID_VALID)))
clear_bit(ahsta->ml_peer_id & ~ATH12K_PEER_ML_ID_VALID,
ah->free_ml_peer_id_map);
ahsta->ml_peer_id = ATH12K_MLO_PEER_ID_INVALID;
}
int ath12k_peer_mlo_link_peers_delete(struct ath12k_vif *ahvif, struct ath12k_sta *ahsta)

View File

@@ -26,4 +26,5 @@ int ath12k_link_sta_rhash_add(struct ath12k_base *ab, struct ath12k_link_sta *ar
struct ath12k_link_sta *ath12k_link_sta_find_by_addr(struct ath12k_base *ab,
const u8 *addr);
u16 ath12k_peer_ml_alloc(struct ath12k_hw *ah);
void ath12k_peer_ml_free(struct ath12k_hw *ah, struct ath12k_sta *ahsta);
#endif /* _PEER_H_ */

View File

@@ -442,6 +442,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
.max_client_dbs = 128,
.max_client_dbs_sbs = 128,
},
.host_alloc_ml_id = true,
},
{
.name = "wcn7850 hw2.0",
@@ -533,6 +535,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
.max_client_dbs = 128,
.max_client_dbs_sbs = 128,
},
.host_alloc_ml_id = false,
},
{
.name = "qcn9274 hw2.0",
@@ -620,6 +624,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
.max_client_dbs = 128,
.max_client_dbs_sbs = 128,
},
.host_alloc_ml_id = true,
},
{
.name = "ipq5332 hw1.0",
@@ -700,6 +706,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
.max_client_dbs = 128,
.max_client_dbs_sbs = 128,
},
.host_alloc_ml_id = true,
},
{
.name = "qcc2072 hw1.0",
@@ -792,6 +800,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
.max_client_dbs = 128,
.max_client_dbs_sbs = 128,
},
.host_alloc_ml_id = false,
},
{
.name = "ipq5424 hw1.0",
@@ -876,6 +886,8 @@ static const struct ath12k_hw_params ath12k_wifi7_hw_params[] = {
.max_client_dbs = 128,
.max_client_dbs_sbs = 128,
},
.host_alloc_ml_id = true,
},
};

View File

@@ -2179,8 +2179,8 @@ il_remove_station(struct il_priv *il, const u8 sta_id, const u8 * addr)
il->stations[sta_id].used &= ~IL_STA_DRIVER_ACTIVE;
il->num_stations--;
BUG_ON(il->num_stations < 0);
if (WARN_ON(il->num_stations < 0))
il->num_stations = 0;
spin_unlock_irqrestore(&il->sta_lock, flags);
@@ -2328,7 +2328,8 @@ il_dealloc_bcast_stations(struct il_priv *il)
il->stations[i].used &= ~IL_STA_UCODE_ACTIVE;
il->num_stations--;
BUG_ON(il->num_stations < 0);
if (WARN_ON(il->num_stations < 0))
il->num_stations = 0;
kfree(il->stations[i].lq);
il->stations[i].lq = NULL;
}

View File

@@ -396,7 +396,7 @@ il_dbgfs_stations_read(struct file *file, char __user *user_buf, size_t count,
int i, j, pos = 0;
ssize_t ret;
/* Add 30 for initial string */
const size_t bufsz = 30 + sizeof(char) * 500 * (il->num_stations);
const size_t bufsz = 30 + sizeof(char) * 500 * max_sta;
buf = kmalloc(bufsz, GFP_KERNEL);
if (!buf)

View File

@@ -44,7 +44,7 @@ static int mwifiex_11n_dispatch_amsdu_pkt(struct mwifiex_private *priv,
ntohs(rx_hdr->eth803_hdr.h_proto) == ETH_P_TDLS) {
mwifiex_process_tdls_action_frame(priv,
(u8 *)rx_hdr,
skb->len);
rx_skb->len);
}
if (priv->bss_role == MWIFIEX_BSS_ROLE_UAP)

View File

@@ -915,6 +915,7 @@ void ieee80211_stop_tx_ba_cb(struct sta_info *sta, int tid,
struct tid_ampdu_tx *tid_tx)
{
struct ieee80211_sub_if_data *sdata = sta->sdata;
bool ndp = ndp = tid_tx->ndp;
bool send_delba = false;
bool start_txq = false;
@@ -934,6 +935,7 @@ void ieee80211_stop_tx_ba_cb(struct sta_info *sta, int tid,
send_delba = true;
ieee80211_remove_tid_tx(sta, tid);
/* tid_tx is now invalid since ieee80211_remove_tid_tx() frees it */
start_txq = true;
unlock_sta:
@@ -946,7 +948,7 @@ void ieee80211_stop_tx_ba_cb(struct sta_info *sta, int tid,
ieee80211_send_delba(sdata, sta->sta.addr, tid,
WLAN_BACK_INITIATOR,
WLAN_REASON_QSTA_NOT_USE,
tid_tx->ndp);
ndp);
}
void ieee80211_stop_tx_ba_cb_irqsafe(struct ieee80211_vif *vif,

View File

@@ -101,6 +101,10 @@ ieee80211_s1g_rx_twt_setup(struct ieee80211_sub_if_data *sdata,
struct ieee80211_twt_setup *twt = (void *)mgmt->u.action.s1g.variable;
struct ieee80211_twt_params *twt_agrt = (void *)twt->params;
if (!(twt->control & IEEE80211_TWT_CONTROL_NEG_TYPE_BROADCAST) &&
twt->length < sizeof(twt->control) + sizeof(*twt_agrt))
return;
twt_agrt->req_type &= cpu_to_le16(~IEEE80211_TWT_REQTYPE_REQUEST);
/* broadcast TWT not supported yet */

View File

@@ -420,6 +420,7 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
const struct cfg80211_pmsr_capabilities *capa;
struct cfg80211_pmsr_request *req;
struct nlattr *peers, *peer;
u64 cookie;
capa = rdev->wiphy.pmsr_capa;
@@ -521,14 +522,27 @@ int nl80211_pmsr_start(struct sk_buff *skb, struct genl_info *info)
}
req->cookie = cfg80211_assign_cookie(rdev);
req->nl_portid = info->snd_portid;
cookie = req->cookie;
/*
* Add to the list before the driver call; under races or broken
* drivers, completion may free the request before rdev_start_pmsr()
* returns. Use the saved cookie below.
*/
spin_lock_bh(&wdev->pmsr_lock);
list_add_tail(&req->list, &wdev->pmsr_list);
spin_unlock_bh(&wdev->pmsr_lock);
err = rdev_start_pmsr(rdev, wdev, req);
if (err)
if (err) {
/* An error return leaves the request owned by this path. */
spin_lock_bh(&wdev->pmsr_lock);
list_del(&req->list);
spin_unlock_bh(&wdev->pmsr_lock);
goto out_err;
}
list_add_tail(&req->list, &wdev->pmsr_list);
nl_set_extack_cookie_u64(info->extack, req->cookie);
nl_set_extack_cookie_u64(info->extack, cookie);
return 0;
out_err:
kfree(req);

View File

@@ -319,6 +319,15 @@ int cfg80211_wext_siwgenie(struct net_device *dev,
return 0;
if (ie_len) {
const struct element *elem;
for_each_element(elem, extra, ie_len) {
/* nothing */
}
if (!for_each_element_completed(elem, extra, ie_len))
return -EINVAL;
ie = kmemdup(extra, ie_len, GFP_KERNEL);
if (!ie)
return -ENOMEM;