while (ieee80211_sn_less(ssn, nssn)) { int index = ssn % baid_data->buf_size; struct sk_buff_head *skb_list = &entries[index].frames; struct sk_buff *skb;
ssn = ieee80211_sn_inc(ssn);
/* Empty the list. Will have more than one frame for A-MSDU. * Empty list is valid as well since nssn indicates frames were * received.
*/ while ((skb = __skb_dequeue(skb_list))) {
iwl_mld_pass_packet_to_mac80211(mld, napi, skb,
reorder_buf->queue,
sta);
reorder_buf->num_stored--;
}
}
reorder_buf->head_sn = nssn;
}
if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID ||
baid >= ARRAY_SIZE(mld->fw_id_to_ba))) return;
rcu_read_lock();
ba_data = rcu_dereference(mld->fw_id_to_ba[baid]); if (!ba_data) {
IWL_DEBUG_HT(mld, "BAID %d not found in map\n", baid); goto out_unlock;
}
/* pick any STA ID to find the pointer */
sta_id = ffs(ba_data->sta_mask) - 1;
link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta)) goto out_unlock;
rcu_read_lock();
baid_data = rcu_dereference(mld->fw_id_to_ba[baid]); if (!baid_data) {
IWL_DEBUG_HT(mld, "Got valid BAID %d but not allocated\n",
baid); goto out_unlock;
}
if (IWL_FW_CHECK(mld, tid != baid_data->tid ||
sta_id > mld->fw->ucode_capa.num_stations ||
!(baid_data->sta_mask & BIT(sta_id)), "BAID 0x%x is mapped to sta_mask:0x%x tid:%d, but BAR release received for sta:%d tid:%d\n",
baid, baid_data->sta_mask, baid_data->tid, sta_id,
tid)) goto out_unlock;
IWL_DEBUG_DROP(mld, "Received a BAR, expect packet loss: nssn %d\n",
nssn);
if (WARN_ONCE(baid >= IWL_MAX_BAID, "invalid BAID: %x\n", baid)) return;
rcu_read_lock();
ba_data = rcu_dereference(mld->fw_id_to_ba[baid]); if (WARN_ON_ONCE(!ba_data)) goto out_unlock;
/* pick any STA ID to find the pointer */
sta_id = ffs(ba_data->sta_mask) - 1;
link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta)) goto out_unlock;
reorder_buf = &ba_data->reorder_buf[queue];
/* release all frames that are in the reorder buffer to the stack */
iwl_mld_reorder_release_frames(mld, link_sta->sta, NULL,
ba_data, reorder_buf,
ieee80211_sn_add(reorder_buf->head_sn,
ba_data->buf_size));
out_unlock:
rcu_read_unlock();
}
/* Returns true if the MPDU was buffered\dropped, false if it should be passed * to upper layer.
*/ enum iwl_mld_reorder_result
iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, int queue, struct ieee80211_sta *sta, struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc)
{ struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb); struct iwl_mld_baid_data *baid_data; struct iwl_mld_reorder_buffer *buffer; struct iwl_mld_reorder_buf_entry *entries; struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); struct iwl_mld_link_sta *mld_link_sta;
u32 reorder = le32_to_cpu(desc->reorder_data); bool amsdu, last_subframe, is_old_sn, is_dup;
u8 tid = ieee80211_get_tid(hdr);
u8 baid;
u16 nssn, sn;
u32 sta_mask = 0; int index;
u8 link_id;
/* This also covers the case of receiving a Block Ack Request * outside a BA session; we'll pass it to mac80211 and that * then sends a delBA action frame. * This also covers pure monitor mode, in which case we won't * have any BA sessions.
*/ if (baid == IWL_RX_REORDER_DATA_INVALID_BAID) return IWL_MLD_PASS_SKB;
/* no sta yet */ if (WARN_ONCE(!sta, "Got valid BAID without a valid station assigned\n")) return IWL_MLD_PASS_SKB;
/* not a data packet */ if (!ieee80211_is_data_qos(hdr->frame_control) ||
is_multicast_ether_addr(hdr->addr1)) return IWL_MLD_PASS_SKB;
if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) return IWL_MLD_PASS_SKB;
baid_data = rcu_dereference(mld->fw_id_to_ba[baid]); if (!baid_data) {
IWL_DEBUG_HT(mld, "Got valid BAID but no baid allocated, bypass re-ordering (BAID=%d reorder=0x%x)\n",
baid, reorder); return IWL_MLD_PASS_SKB;
}
/* verify the BAID is correctly mapped to the sta and tid */ if (IWL_FW_CHECK(mld,
tid != baid_data->tid ||
!(sta_mask & baid_data->sta_mask), "BAID 0x%x is mapped to sta_mask:0x%x tid:%d, but was received for sta_mask:0x%x tid:%d\n",
baid, baid_data->sta_mask, baid_data->tid,
sta_mask, tid)) return IWL_MLD_PASS_SKB;
/* release immediately if allowed by nssn and no stored frames */ if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) { if (!amsdu || last_subframe)
buffer->head_sn = nssn; return IWL_MLD_PASS_SKB;
}
/* release immediately if there are no stored frames, and the sn is * equal to the head. * This can happen due to reorder timer, where NSSN is behind head_sn. * When we released everything, and we got the next frame in the * sequence, according to the NSSN we can't release immediately, * while technically there is no hole and we can move forward.
*/ if (!buffer->num_stored && sn == buffer->head_sn) { if (!amsdu || last_subframe)
buffer->head_sn = ieee80211_sn_inc(buffer->head_sn); return IWL_MLD_PASS_SKB;
}
/* put in reorder buffer */
index = sn % baid_data->buf_size;
__skb_queue_tail(&entries[index].frames, skb);
buffer->num_stored++;
/* We cannot trust NSSN for AMSDU sub-frames that are not the last. The * reason is that NSSN advances on the first sub-frame, and may cause * the reorder buffer to advance before all the sub-frames arrive. * * Example: reorder buffer contains SN 0 & 2, and we receive AMSDU with * SN 1. NSSN for first sub frame will be 3 with the result of driver * releasing SN 0,1, 2. When sub-frame 1 arrives - reorder buffer is * already ahead and it will be dropped. * If the last sub-frame is not on this queue - we will get frame * release notification with up to date NSSN. * If this is the first frame that is stored in the buffer, the head_sn * may be outdated. Update it based on the last NSSN to make sure it * will be released when the frame release notification arrives.
*/ if (!amsdu || last_subframe)
iwl_mld_reorder_release_frames(mld, sta, napi, baid_data,
buffer, nssn); elseif (buffer->num_stored == 1)
buffer->head_sn = nssn;
/* timer expired, pick any STA ID to find the pointer */
sta_id = ffs(ba_data->sta_mask) - 1;
link_sta = rcu_dereference(ba_data->mld->fw_id_to_link_sta[sta_id]);
/* sta should be valid unless the following happens: * The firmware asserts which triggers a reconfig flow, but * the reconfig fails before we set the pointer to sta into * the fw_id_to_link_sta pointer table. mac80211 can't stop * A-MPDU and hence the timer continues to run. Then, the * timer expires and sta is NULL.
*/ if (IS_ERR_OR_NULL(link_sta) || WARN_ON(!link_sta->sta)) goto unlock;
for (int i = 0; i < mld->trans->info.num_rxqs; i++) { struct iwl_mld_reorder_buffer *reorder_buf =
&data->reorder_buf[i]; struct iwl_mld_reorder_buf_entry *entries =
&data->entries[i * data->entries_per_queue];
if (likely(!reorder_buf->num_stored)) continue;
/* This shouldn't happen in regular DELBA since the RX queues * sync internal DELBA notification should trigger a release * of all frames in the reorder buffer.
*/
WARN_ON(1);
if (mld->num_rx_ba_sessions >= IWL_MAX_BAID) {
IWL_DEBUG_HT(mld, "Max num of RX BA sessions reached; blocking new session\n"); return -ENOSPC;
}
sta_mask = iwl_mld_fw_sta_id_mask(mld, sta); if (WARN_ON(!sta_mask)) return -EINVAL;
/* sparse doesn't like the __align() so don't check */ #ifndef __CHECKER__ /* The division below will be OK if either the cache line size * can be divided by the entry size (ALIGN will round up) or if * the entry size can be divided by the cache line size, in which * case the ALIGN() will do nothing.
*/
BUILD_BUG_ON(SMP_CACHE_BYTES % sizeof(baid_data->entries[0]) && sizeof(baid_data->entries[0]) % SMP_CACHE_BYTES); #endif
/* Upward align the reorder buffer size to fill an entire cache * line for each queue, to avoid sharing cache lines between * different queues.
*/
reorder_buf_size = ALIGN(reorder_buf_size, SMP_CACHE_BYTES);
/* Allocate here so if allocation fails we can bail out early * before starting the BA session in the firmware
*/
baid_data = kzalloc(sizeof(*baid_data) +
mld->trans->info.num_rxqs * reorder_buf_size,
GFP_KERNEL); if (!baid_data) return -ENOMEM;
/* This division is why we need the above BUILD_BUG_ON(), * if that doesn't hold then this will not be right.
*/
baid_data->entries_per_queue =
reorder_buf_size / sizeof(baid_data->entries[0]);
baid = iwl_mld_start_ba_in_fw(mld, sta, tid, ssn, buf_size); if (baid < 0) {
ret = baid; goto out_free;
}
timer_setup(&baid_data->session_timer, iwl_mld_rx_agg_session_expired,
0); if (timeout)
mod_timer(&baid_data->session_timer,
TU_TO_EXP_TIME(timeout * 2));
IWL_DEBUG_HT(mld, "STA mask=0x%x (tid=%d) is assigned to BAID %d\n",
baid_data->sta_mask, tid, baid);
/* protect the BA data with RCU to cover a case where our * internal RX sync mechanism will timeout (not that it's * supposed to happen) and we will free the session data while * RX is being processed in parallel
*/
WARN_ON(rcu_access_pointer(mld->fw_id_to_ba[baid]));
rcu_assign_pointer(mld->fw_id_to_ba[baid], baid_data);
return 0;
out_free:
kfree(baid_data); return ret;
}
int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta, int tid)
{ struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); int baid = mld_sta->tid_to_baid[tid]; struct iwl_mld_baid_data *baid_data; int ret;
lockdep_assert_wiphy(mld->wiphy);
/* during firmware restart, do not send the command as the firmware no * longer recognizes the session. instead, only clear the driver BA * session data.
*/ if (!mld->fw_status.in_hw_restart) {
ret = iwl_mld_stop_ba_in_fw(mld, sta, tid); if (ret) return ret;
}
if (!WARN_ON(mld->num_rx_ba_sessions == 0))
mld->num_rx_ba_sessions--;
baid_data = wiphy_dereference(mld->wiphy, mld->fw_id_to_ba[baid]); if (WARN_ON(!baid_data)) return -EINVAL;
if (timer_pending(&baid_data->session_timer))
timer_shutdown_sync(&baid_data->session_timer);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.