Path: blob/main/sys/contrib/dev/iwlwifi/mld/agg.c
106751 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/*2* Copyright (C) 2024-2025 Intel Corporation3*/4#include "agg.h"5#include "sta.h"6#include "hcmd.h"7#if defined(__FreeBSD__)8#include <linux/cache.h>9#endif1011static void12iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta,13struct napi_struct *napi,14struct iwl_mld_baid_data *baid_data,15struct iwl_mld_reorder_buffer *reorder_buf,16u16 nssn)17{18struct iwl_mld_reorder_buf_entry *entries =19&baid_data->entries[reorder_buf->queue *20baid_data->entries_per_queue];21u16 ssn = reorder_buf->head_sn;2223while (ieee80211_sn_less(ssn, nssn)) {24int index = ssn % baid_data->buf_size;25struct sk_buff_head *skb_list = &entries[index].frames;26struct sk_buff *skb;2728ssn = ieee80211_sn_inc(ssn);2930/* Empty the list. Will have more than one frame for A-MSDU.31* Empty list is valid as well since nssn indicates frames were32* received.33*/34while ((skb = __skb_dequeue(skb_list))) {35iwl_mld_pass_packet_to_mac80211(mld, napi, skb,36reorder_buf->queue,37sta);38reorder_buf->num_stored--;39}40}41reorder_buf->head_sn = nssn;42}4344static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld,45struct napi_struct *napi,46u8 baid, u16 nssn, int queue)47{48struct iwl_mld_reorder_buffer *reorder_buf;49struct iwl_mld_baid_data *ba_data;50struct ieee80211_link_sta *link_sta;51u32 sta_id;5253IWL_DEBUG_HT(mld, "Frame release notification for BAID %u, NSSN %d\n",54baid, nssn);5556if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID ||57baid >= ARRAY_SIZE(mld->fw_id_to_ba)))58return;5960rcu_read_lock();6162ba_data = rcu_dereference(mld->fw_id_to_ba[baid]);63if (!ba_data) {64IWL_DEBUG_HT(mld, "BAID %d not found in map\n", baid);65goto out_unlock;66}6768/* pick any STA ID to find the pointer */69sta_id = ffs(ba_data->sta_mask) - 1;70link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]);71if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta))72goto out_unlock;7374reorder_buf = &ba_data->reorder_buf[queue];7576iwl_mld_reorder_release_frames(mld, link_sta->sta, napi, ba_data,77reorder_buf, nssn);78out_unlock:79rcu_read_unlock();80}8182void iwl_mld_handle_frame_release_notif(struct iwl_mld *mld,83struct napi_struct *napi,84struct iwl_rx_packet *pkt, int queue)85{86struct iwl_frame_release *release = (void *)pkt->data;87u32 pkt_len = iwl_rx_packet_payload_len(pkt);8889if (IWL_FW_CHECK(mld, pkt_len < sizeof(*release),90"Unexpected frame release notif size %u (expected %zu)\n",91pkt_len, sizeof(*release)))92return;9394iwl_mld_release_frames_from_notif(mld, napi, release->baid,95le16_to_cpu(release->nssn),96queue);97}9899void iwl_mld_handle_bar_frame_release_notif(struct iwl_mld *mld,100struct napi_struct *napi,101struct iwl_rx_packet *pkt,102int queue)103{104struct iwl_bar_frame_release *release = (void *)pkt->data;105struct iwl_mld_baid_data *baid_data;106unsigned int baid, nssn, sta_id, tid;107u32 pkt_len = iwl_rx_packet_payload_len(pkt);108109if (IWL_FW_CHECK(mld, pkt_len < sizeof(*release),110"Unexpected frame release notif size %u (expected %zu)\n",111pkt_len, sizeof(*release)))112return;113114baid = le32_get_bits(release->ba_info,115IWL_BAR_FRAME_RELEASE_BAID_MASK);116nssn = le32_get_bits(release->ba_info,117IWL_BAR_FRAME_RELEASE_NSSN_MASK);118sta_id = le32_get_bits(release->sta_tid,119IWL_BAR_FRAME_RELEASE_STA_MASK);120tid = le32_get_bits(release->sta_tid,121IWL_BAR_FRAME_RELEASE_TID_MASK);122123if (IWL_FW_CHECK(mld, baid >= ARRAY_SIZE(mld->fw_id_to_ba),124"BAR release: invalid BAID (%x)\n", baid))125return;126127rcu_read_lock();128baid_data = rcu_dereference(mld->fw_id_to_ba[baid]);129if (!baid_data) {130IWL_DEBUG_HT(mld,131"Got valid BAID %d but not allocated\n",132baid);133goto out_unlock;134}135136if (IWL_FW_CHECK(mld, tid != baid_data->tid ||137sta_id > mld->fw->ucode_capa.num_stations ||138!(baid_data->sta_mask & BIT(sta_id)),139"BAID 0x%x is mapped to sta_mask:0x%x tid:%d, but BAR release received for sta:%d tid:%d\n",140baid, baid_data->sta_mask, baid_data->tid, sta_id,141tid))142goto out_unlock;143144IWL_DEBUG_DROP(mld, "Received a BAR, expect packet loss: nssn %d\n",145nssn);146147iwl_mld_release_frames_from_notif(mld, napi, baid, nssn, queue);148out_unlock:149rcu_read_unlock();150}151152void iwl_mld_del_ba(struct iwl_mld *mld, int queue,153struct iwl_mld_delba_data *data)154{155struct iwl_mld_baid_data *ba_data;156struct iwl_mld_reorder_buffer *reorder_buf;157struct ieee80211_link_sta *link_sta;158u8 baid = data->baid;159u32 sta_id;160161if (WARN_ONCE(baid >= IWL_MAX_BAID, "invalid BAID: %x\n", baid))162return;163164rcu_read_lock();165166ba_data = rcu_dereference(mld->fw_id_to_ba[baid]);167if (WARN_ON_ONCE(!ba_data))168goto out_unlock;169170/* pick any STA ID to find the pointer */171sta_id = ffs(ba_data->sta_mask) - 1;172link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]);173if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta))174goto out_unlock;175176reorder_buf = &ba_data->reorder_buf[queue];177178/* release all frames that are in the reorder buffer to the stack */179iwl_mld_reorder_release_frames(mld, link_sta->sta, NULL,180ba_data, reorder_buf,181ieee80211_sn_add(reorder_buf->head_sn,182ba_data->buf_size));183out_unlock:184rcu_read_unlock();185}186187/* Returns true if the MPDU was buffered\dropped, false if it should be passed188* to upper layer.189*/190enum iwl_mld_reorder_result191iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,192int queue, struct ieee80211_sta *sta,193struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc)194{195struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb);196struct iwl_mld_baid_data *baid_data;197struct iwl_mld_reorder_buffer *buffer;198struct iwl_mld_reorder_buf_entry *entries;199struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);200struct iwl_mld_link_sta *mld_link_sta;201u32 reorder = le32_to_cpu(desc->reorder_data);202bool amsdu, last_subframe, is_old_sn, is_dup;203#if defined(__linux__)204u8 tid = ieee80211_get_tid(hdr);205#elif defined(__FreeBSD__)206u8 tid;207#endif208u8 baid;209u16 nssn, sn;210u32 sta_mask = 0;211int index;212u8 link_id;213214baid = u32_get_bits(reorder, IWL_RX_MPDU_REORDER_BAID_MASK);215216/* This also covers the case of receiving a Block Ack Request217* outside a BA session; we'll pass it to mac80211 and that218* then sends a delBA action frame.219* This also covers pure monitor mode, in which case we won't220* have any BA sessions.221*/222if (baid == IWL_RX_REORDER_DATA_INVALID_BAID)223return IWL_MLD_PASS_SKB;224225/* no sta yet */226if (WARN_ONCE(!sta,227"Got valid BAID without a valid station assigned\n"))228return IWL_MLD_PASS_SKB;229230/* not a data packet */231if (!ieee80211_is_data_qos(hdr->frame_control) ||232is_multicast_ether_addr(hdr->addr1))233return IWL_MLD_PASS_SKB;234235if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))236return IWL_MLD_PASS_SKB;237238baid_data = rcu_dereference(mld->fw_id_to_ba[baid]);239if (!baid_data) {240IWL_DEBUG_HT(mld,241"Got valid BAID but no baid allocated, bypass re-ordering (BAID=%d reorder=0x%x)\n",242baid, reorder);243return IWL_MLD_PASS_SKB;244}245246for_each_mld_link_sta(mld_sta, mld_link_sta, link_id)247sta_mask |= BIT(mld_link_sta->fw_id);248249#if defined(__FreeBSD__)250tid = ieee80211_get_tid(hdr);251#endif252253/* verify the BAID is correctly mapped to the sta and tid */254if (IWL_FW_CHECK(mld,255tid != baid_data->tid ||256!(sta_mask & baid_data->sta_mask),257"BAID 0x%x is mapped to sta_mask:0x%x tid:%d, but was received for sta_mask:0x%x tid:%d\n",258baid, baid_data->sta_mask, baid_data->tid,259sta_mask, tid))260return IWL_MLD_PASS_SKB;261262buffer = &baid_data->reorder_buf[queue];263entries = &baid_data->entries[queue * baid_data->entries_per_queue];264265is_old_sn = !!(reorder & IWL_RX_MPDU_REORDER_BA_OLD_SN);266267if (!buffer->valid && is_old_sn)268return IWL_MLD_PASS_SKB;269270buffer->valid = true;271272is_dup = !!(desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_DUPLICATE));273274/* drop any duplicated or outdated packets */275if (is_dup || is_old_sn)276return IWL_MLD_DROP_SKB;277278sn = u32_get_bits(reorder, IWL_RX_MPDU_REORDER_SN_MASK);279nssn = u32_get_bits(reorder, IWL_RX_MPDU_REORDER_NSSN_MASK);280amsdu = desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU;281last_subframe = desc->amsdu_info & IWL_RX_MPDU_AMSDU_LAST_SUBFRAME;282283/* release immediately if allowed by nssn and no stored frames */284if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) {285if (!amsdu || last_subframe)286buffer->head_sn = nssn;287return IWL_MLD_PASS_SKB;288}289290/* release immediately if there are no stored frames, and the sn is291* equal to the head.292* This can happen due to reorder timer, where NSSN is behind head_sn.293* When we released everything, and we got the next frame in the294* sequence, according to the NSSN we can't release immediately,295* while technically there is no hole and we can move forward.296*/297if (!buffer->num_stored && sn == buffer->head_sn) {298if (!amsdu || last_subframe)299buffer->head_sn = ieee80211_sn_inc(buffer->head_sn);300return IWL_MLD_PASS_SKB;301}302303/* put in reorder buffer */304index = sn % baid_data->buf_size;305__skb_queue_tail(&entries[index].frames, skb);306buffer->num_stored++;307308/* We cannot trust NSSN for AMSDU sub-frames that are not the last. The309* reason is that NSSN advances on the first sub-frame, and may cause310* the reorder buffer to advance before all the sub-frames arrive.311*312* Example: reorder buffer contains SN 0 & 2, and we receive AMSDU with313* SN 1. NSSN for first sub frame will be 3 with the result of driver314* releasing SN 0,1, 2. When sub-frame 1 arrives - reorder buffer is315* already ahead and it will be dropped.316* If the last sub-frame is not on this queue - we will get frame317* release notification with up to date NSSN.318* If this is the first frame that is stored in the buffer, the head_sn319* may be outdated. Update it based on the last NSSN to make sure it320* will be released when the frame release notification arrives.321*/322if (!amsdu || last_subframe)323iwl_mld_reorder_release_frames(mld, sta, napi, baid_data,324buffer, nssn);325else if (buffer->num_stored == 1)326buffer->head_sn = nssn;327328return IWL_MLD_BUFFERED_SKB;329}330EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_reorder);331332static void iwl_mld_rx_agg_session_expired(struct timer_list *t)333{334struct iwl_mld_baid_data *data =335timer_container_of(data, t, session_timer);336struct iwl_mld_baid_data __rcu **rcu_ptr = data->rcu_ptr;337struct iwl_mld_baid_data *ba_data;338struct ieee80211_link_sta *link_sta;339struct iwl_mld_sta *mld_sta;340unsigned long timeout;341unsigned int sta_id;342343rcu_read_lock();344345ba_data = rcu_dereference(*rcu_ptr);346if (WARN_ON(!ba_data))347goto unlock;348349if (WARN_ON(!ba_data->timeout))350goto unlock;351352timeout = ba_data->last_rx_timestamp +353TU_TO_JIFFIES(ba_data->timeout * 2);354if (time_is_after_jiffies(timeout)) {355mod_timer(&ba_data->session_timer, timeout);356goto unlock;357}358359/* timer expired, pick any STA ID to find the pointer */360sta_id = ffs(ba_data->sta_mask) - 1;361link_sta = rcu_dereference(ba_data->mld->fw_id_to_link_sta[sta_id]);362363/* sta should be valid unless the following happens:364* The firmware asserts which triggers a reconfig flow, but365* the reconfig fails before we set the pointer to sta into366* the fw_id_to_link_sta pointer table. mac80211 can't stop367* A-MPDU and hence the timer continues to run. Then, the368* timer expires and sta is NULL.369*/370if (IS_ERR_OR_NULL(link_sta) || WARN_ON(!link_sta->sta))371goto unlock;372373mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);374ieee80211_rx_ba_timer_expired(mld_sta->vif, link_sta->sta->addr,375ba_data->tid);376unlock:377rcu_read_unlock();378}379380static int381iwl_mld_stop_ba_in_fw(struct iwl_mld *mld, struct ieee80211_sta *sta, int tid)382{383struct iwl_rx_baid_cfg_cmd cmd = {384.action = cpu_to_le32(IWL_RX_BAID_ACTION_REMOVE),385.remove.sta_id_mask =386cpu_to_le32(iwl_mld_fw_sta_id_mask(mld, sta)),387.remove.tid = cpu_to_le32(tid),388389};390int ret;391392ret = iwl_mld_send_cmd_pdu(mld,393WIDE_ID(DATA_PATH_GROUP,394RX_BAID_ALLOCATION_CONFIG_CMD),395&cmd);396if (ret)397return ret;398399IWL_DEBUG_HT(mld, "RX BA Session stopped in fw\n");400401return ret;402}403404static int405iwl_mld_start_ba_in_fw(struct iwl_mld *mld, struct ieee80211_sta *sta,406int tid, u16 ssn, u16 buf_size)407{408struct iwl_rx_baid_cfg_cmd cmd = {409.action = cpu_to_le32(IWL_RX_BAID_ACTION_ADD),410.alloc.sta_id_mask =411cpu_to_le32(iwl_mld_fw_sta_id_mask(mld, sta)),412.alloc.tid = tid,413.alloc.ssn = cpu_to_le16(ssn),414.alloc.win_size = cpu_to_le16(buf_size),415};416struct iwl_host_cmd hcmd = {417.id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD),418.flags = CMD_WANT_SKB,419.len[0] = sizeof(cmd),420.data[0] = &cmd,421};422struct iwl_rx_baid_cfg_resp *resp;423struct iwl_rx_packet *pkt;424u32 resp_len;425int ret, baid;426427BUILD_BUG_ON(sizeof(*resp) != sizeof(baid));428429ret = iwl_mld_send_cmd(mld, &hcmd);430if (ret)431return ret;432433pkt = hcmd.resp_pkt;434435resp_len = iwl_rx_packet_payload_len(pkt);436if (IWL_FW_CHECK(mld, resp_len != sizeof(*resp),437"BAID_ALLOC_CMD: unexpected response length %d\n",438resp_len)) {439ret = -EIO;440goto out;441}442443IWL_DEBUG_HT(mld, "RX BA Session started in fw\n");444445resp = (void *)pkt->data;446baid = le32_to_cpu(resp->baid);447448if (IWL_FW_CHECK(mld, baid < 0 || baid >= ARRAY_SIZE(mld->fw_id_to_ba),449"BAID_ALLOC_CMD: invalid BAID response %d\n", baid)) {450ret = -EINVAL;451goto out;452}453454ret = baid;455out:456iwl_free_resp(&hcmd);457return ret;458}459460static void iwl_mld_init_reorder_buffer(struct iwl_mld *mld,461struct iwl_mld_baid_data *data,462u16 ssn)463{464for (int i = 0; i < mld->trans->info.num_rxqs; i++) {465struct iwl_mld_reorder_buffer *reorder_buf =466&data->reorder_buf[i];467struct iwl_mld_reorder_buf_entry *entries =468&data->entries[i * data->entries_per_queue];469470reorder_buf->head_sn = ssn;471reorder_buf->queue = i;472473for (int j = 0; j < data->buf_size; j++)474__skb_queue_head_init(&entries[j].frames);475}476}477478static void iwl_mld_free_reorder_buffer(struct iwl_mld *mld,479struct iwl_mld_baid_data *data)480{481struct iwl_mld_delba_data delba_data = {482.baid = data->baid,483};484485iwl_mld_sync_rx_queues(mld, IWL_MLD_RXQ_NOTIF_DEL_BA,486&delba_data, sizeof(delba_data));487488for (int i = 0; i < mld->trans->info.num_rxqs; i++) {489struct iwl_mld_reorder_buffer *reorder_buf =490&data->reorder_buf[i];491struct iwl_mld_reorder_buf_entry *entries =492&data->entries[i * data->entries_per_queue];493494if (likely(!reorder_buf->num_stored))495continue;496497/* This shouldn't happen in regular DELBA since the RX queues498* sync internal DELBA notification should trigger a release499* of all frames in the reorder buffer.500*/501WARN_ON(1);502503for (int j = 0; j < data->buf_size; j++)504__skb_queue_purge(&entries[j].frames);505}506}507508int iwl_mld_ampdu_rx_start(struct iwl_mld *mld, struct ieee80211_sta *sta,509int tid, u16 ssn, u16 buf_size, u16 timeout)510{511struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);512struct iwl_mld_baid_data *baid_data = NULL;513u32 reorder_buf_size = buf_size * sizeof(baid_data->entries[0]);514int ret, baid;515u32 sta_mask;516517lockdep_assert_wiphy(mld->wiphy);518519if (mld->num_rx_ba_sessions >= IWL_MAX_BAID) {520IWL_DEBUG_HT(mld,521"Max num of RX BA sessions reached; blocking new session\n");522return -ENOSPC;523}524525sta_mask = iwl_mld_fw_sta_id_mask(mld, sta);526if (WARN_ON(!sta_mask))527return -EINVAL;528529/* sparse doesn't like the __align() so don't check */530#ifndef __CHECKER__531/* The division below will be OK if either the cache line size532* can be divided by the entry size (ALIGN will round up) or if533* the entry size can be divided by the cache line size, in which534* case the ALIGN() will do nothing.535*/536BUILD_BUG_ON(SMP_CACHE_BYTES % sizeof(baid_data->entries[0]) &&537sizeof(baid_data->entries[0]) % SMP_CACHE_BYTES);538#endif539540/* Upward align the reorder buffer size to fill an entire cache541* line for each queue, to avoid sharing cache lines between542* different queues.543*/544reorder_buf_size = ALIGN(reorder_buf_size, SMP_CACHE_BYTES);545546/* Allocate here so if allocation fails we can bail out early547* before starting the BA session in the firmware548*/549baid_data = kzalloc(sizeof(*baid_data) +550mld->trans->info.num_rxqs * reorder_buf_size,551GFP_KERNEL);552if (!baid_data)553return -ENOMEM;554555/* This division is why we need the above BUILD_BUG_ON(),556* if that doesn't hold then this will not be right.557*/558baid_data->entries_per_queue =559reorder_buf_size / sizeof(baid_data->entries[0]);560561baid = iwl_mld_start_ba_in_fw(mld, sta, tid, ssn, buf_size);562if (baid < 0) {563ret = baid;564goto out_free;565}566567mld->num_rx_ba_sessions++;568mld_sta->tid_to_baid[tid] = baid;569570baid_data->baid = baid;571baid_data->mld = mld;572baid_data->tid = tid;573baid_data->buf_size = buf_size;574baid_data->sta_mask = sta_mask;575baid_data->timeout = timeout;576baid_data->last_rx_timestamp = jiffies;577baid_data->rcu_ptr = &mld->fw_id_to_ba[baid];578579iwl_mld_init_reorder_buffer(mld, baid_data, ssn);580581timer_setup(&baid_data->session_timer, iwl_mld_rx_agg_session_expired,5820);583if (timeout)584mod_timer(&baid_data->session_timer,585TU_TO_EXP_TIME(timeout * 2));586587IWL_DEBUG_HT(mld, "STA mask=0x%x (tid=%d) is assigned to BAID %d\n",588baid_data->sta_mask, tid, baid);589590/* protect the BA data with RCU to cover a case where our591* internal RX sync mechanism will timeout (not that it's592* supposed to happen) and we will free the session data while593* RX is being processed in parallel594*/595WARN_ON(rcu_access_pointer(mld->fw_id_to_ba[baid]));596rcu_assign_pointer(mld->fw_id_to_ba[baid], baid_data);597598return 0;599600out_free:601kfree(baid_data);602return ret;603}604605int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta,606int tid)607{608struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);609int baid = mld_sta->tid_to_baid[tid];610struct iwl_mld_baid_data *baid_data;611int ret;612613lockdep_assert_wiphy(mld->wiphy);614615/* during firmware restart, do not send the command as the firmware no616* longer recognizes the session. instead, only clear the driver BA617* session data.618*/619if (!mld->fw_status.in_hw_restart) {620ret = iwl_mld_stop_ba_in_fw(mld, sta, tid);621if (ret)622return ret;623}624625if (!WARN_ON(mld->num_rx_ba_sessions == 0))626mld->num_rx_ba_sessions--;627628baid_data = wiphy_dereference(mld->wiphy, mld->fw_id_to_ba[baid]);629if (WARN_ON(!baid_data))630return -EINVAL;631632if (timer_pending(&baid_data->session_timer))633timer_shutdown_sync(&baid_data->session_timer);634635iwl_mld_free_reorder_buffer(mld, baid_data);636637RCU_INIT_POINTER(mld->fw_id_to_ba[baid], NULL);638kfree_rcu(baid_data, rcu_head);639640IWL_DEBUG_HT(mld, "BAID %d is free\n", baid);641642return 0;643}644645int iwl_mld_update_sta_baids(struct iwl_mld *mld,646u32 old_sta_mask,647u32 new_sta_mask)648{649struct iwl_rx_baid_cfg_cmd cmd = {650.action = cpu_to_le32(IWL_RX_BAID_ACTION_MODIFY),651.modify.old_sta_id_mask = cpu_to_le32(old_sta_mask),652.modify.new_sta_id_mask = cpu_to_le32(new_sta_mask),653};654u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD);655int baid;656657/* mac80211 will remove sessions later, but we ignore all that */658if (mld->fw_status.in_hw_restart)659return 0;660661BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid));662663for (baid = 0; baid < ARRAY_SIZE(mld->fw_id_to_ba); baid++) {664struct iwl_mld_baid_data *data;665int ret;666667data = wiphy_dereference(mld->wiphy, mld->fw_id_to_ba[baid]);668if (!data)669continue;670671if (!(data->sta_mask & old_sta_mask))672continue;673674WARN_ONCE(data->sta_mask != old_sta_mask,675"BAID data for %d corrupted - expected 0x%x found 0x%x\n",676baid, old_sta_mask, data->sta_mask);677678cmd.modify.tid = cpu_to_le32(data->tid);679680ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd);681if (ret)682return ret;683data->sta_mask = new_sta_mask;684}685686return 0;687}688689690