Skip to content

Commit b915c10

Browse files
sara-slucacoelho
authored andcommitted
iwlwifi: mvm: add reorder buffer per queue
Next hardware will direct packets to core based on the TCP/UDP streams. This logic can create holes in reorder buffer since packets that belong to other stream were directed to a different core. However, those are valid holes and the packets can be indicated in L3 order. The hardware will utilize a mechanism of informing the driver of the normalized ssn and the driver shall release all packets that SN is lower than the nssn. This enables managing the reorder across the queues without sharing any data between them. The reorder buffer is allocated and released directly in the RX path in order to avoid various races between control path and rx path. The code utilizes the internal messaging to notify rx queues of when to delete the reorder buffer. Signed-off-by: Sara Sharon <[email protected]> Signed-off-by: Luca Coelho <[email protected]>
1 parent 2dd4934 commit b915c10

File tree

5 files changed

+276
-9
lines changed

5 files changed

+276
-9
lines changed

drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,8 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm)
439439
ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS);
440440
ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU);
441441
ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR);
442+
if (iwl_mvm_has_new_rx_api(mvm))
443+
ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER);
442444

443445
if (mvm->trans->max_skb_frags)
444446
hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG;

drivers/net/wireless/intel/iwlwifi/mvm/mvm.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,28 @@ struct iwl_mvm_shared_mem_cfg {
613613
u32 internal_txfifo_size[TX_FIFO_INTERNAL_MAX_NUM];
614614
};
615615

616+
/**
617+
* struct iwl_mvm_reorder_buffer - per ra/tid/queue reorder buffer
618+
* @head_sn: reorder window head sn
619+
* @num_stored: number of mpdus stored in the buffer
620+
* @buf_size: the reorder buffer size as set by the last addba request
621+
* @sta_id: sta id of this reorder buffer
622+
* @queue: queue of this reorder buffer
623+
* @last_amsdu: track last ASMDU SN for duplication detection
624+
* @last_sub_index: track ASMDU sub frame index for duplication detection
625+
* @entries: list of skbs stored
626+
*/
627+
struct iwl_mvm_reorder_buffer {
628+
u16 head_sn;
629+
u16 num_stored;
630+
u8 buf_size;
631+
u8 sta_id;
632+
int queue;
633+
u16 last_amsdu;
634+
u8 last_sub_index;
635+
struct sk_buff_head entries[IEEE80211_MAX_AMPDU_BUF];
636+
} ____cacheline_aligned_in_smp;
637+
616638
/**
617639
* struct iwl_mvm_baid_data - BA session data
618640
* @sta_id: station id
@@ -622,6 +644,7 @@ struct iwl_mvm_shared_mem_cfg {
622644
* @last_rx: last rx jiffies, updated only if timeout passed from last update
623645
* @session_timer: timer to check if BA session expired, runs at 2 * timeout
624646
* @mvm: mvm pointer, needed for timer context
647+
* @reorder_buf: reorder buffer, allocated per queue
625648
*/
626649
struct iwl_mvm_baid_data {
627650
struct rcu_head rcu_head;
@@ -632,6 +655,7 @@ struct iwl_mvm_baid_data {
632655
unsigned long last_rx;
633656
struct timer_list session_timer;
634657
struct iwl_mvm *mvm;
658+
struct iwl_mvm_reorder_buffer reorder_buf[];
635659
};
636660

637661
struct iwl_mvm {

drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c

Lines changed: 180 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,67 @@ int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask,
395395
return ret;
396396
}
397397

398+
static void iwl_mvm_release_frames(struct iwl_mvm *mvm,
399+
struct ieee80211_sta *sta,
400+
struct napi_struct *napi,
401+
struct iwl_mvm_reorder_buffer *reorder_buf,
402+
u16 nssn)
403+
{
404+
u16 ssn = reorder_buf->head_sn;
405+
406+
while (ieee80211_sn_less(ssn, nssn)) {
407+
int index = ssn % reorder_buf->buf_size;
408+
struct sk_buff_head *skb_list = &reorder_buf->entries[index];
409+
struct sk_buff *skb;
410+
411+
ssn = ieee80211_sn_inc(ssn);
412+
413+
/* holes are valid since nssn indicates frames were received. */
414+
if (skb_queue_empty(skb_list) || !skb_peek_tail(skb_list))
415+
continue;
416+
/* Empty the list. Will have more than one frame for A-MSDU */
417+
while ((skb = __skb_dequeue(skb_list))) {
418+
iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb,
419+
reorder_buf->queue,
420+
sta);
421+
reorder_buf->num_stored--;
422+
}
423+
}
424+
reorder_buf->head_sn = nssn;
425+
}
426+
427+
static void iwl_mvm_del_ba(struct iwl_mvm *mvm, int queue,
428+
struct iwl_mvm_delba_data *data)
429+
{
430+
struct iwl_mvm_baid_data *ba_data;
431+
struct ieee80211_sta *sta;
432+
struct iwl_mvm_reorder_buffer *reorder_buf;
433+
u8 baid = data->baid;
434+
435+
if (WARN_ON_ONCE(baid >= IWL_RX_REORDER_DATA_INVALID_BAID))
436+
return;
437+
438+
rcu_read_lock();
439+
440+
ba_data = rcu_dereference(mvm->baid_map[baid]);
441+
if (WARN_ON_ONCE(!ba_data))
442+
goto out;
443+
444+
sta = rcu_dereference(mvm->fw_id_to_mac_id[ba_data->sta_id]);
445+
if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta)))
446+
goto out;
447+
448+
reorder_buf = &ba_data->reorder_buf[queue];
449+
450+
/* release all frames that are in the reorder buffer to the stack */
451+
iwl_mvm_release_frames(mvm, sta, NULL, reorder_buf,
452+
ieee80211_sn_add(reorder_buf->head_sn,
453+
reorder_buf->buf_size));
454+
455+
out:
456+
rcu_read_unlock();
457+
}
458+
398459
void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
399460
int queue)
400461
{
@@ -418,12 +479,129 @@ void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb,
418479
case IWL_MVM_RXQ_EMPTY:
419480
break;
420481
case IWL_MVM_RXQ_NOTIF_DEL_BA:
482+
iwl_mvm_del_ba(mvm, queue, (void *)internal_notif->data);
421483
break;
422484
default:
423485
WARN_ONCE(1, "Invalid identifier %d", internal_notif->type);
424486
}
425487
}
426488

489+
/*
490+
* Returns true if the MPDU was buffered\dropped, false if it should be passed
491+
* to upper layer.
492+
*/
493+
static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
494+
struct napi_struct *napi,
495+
int queue,
496+
struct ieee80211_sta *sta,
497+
struct sk_buff *skb,
498+
struct iwl_rx_mpdu_desc *desc)
499+
{
500+
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
501+
struct iwl_mvm_sta *mvm_sta = iwl_mvm_sta_from_mac80211(sta);
502+
struct iwl_mvm_baid_data *baid_data;
503+
struct iwl_mvm_reorder_buffer *buffer;
504+
struct sk_buff *tail;
505+
u32 reorder = le32_to_cpu(desc->reorder_data);
506+
bool amsdu = desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU;
507+
u8 tid = *ieee80211_get_qos_ctl(hdr) & IEEE80211_QOS_CTL_TID_MASK;
508+
u8 sub_frame_idx = desc->amsdu_info &
509+
IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK;
510+
int index;
511+
u16 nssn, sn;
512+
u8 baid;
513+
514+
baid = (reorder & IWL_RX_MPDU_REORDER_BAID_MASK) >>
515+
IWL_RX_MPDU_REORDER_BAID_SHIFT;
516+
517+
if (baid == IWL_RX_REORDER_DATA_INVALID_BAID)
518+
return false;
519+
520+
/* no sta yet */
521+
if (WARN_ON(IS_ERR_OR_NULL(sta)))
522+
return false;
523+
524+
/* not a data packet */
525+
if (!ieee80211_is_data_qos(hdr->frame_control) ||
526+
is_multicast_ether_addr(hdr->addr1))
527+
return false;
528+
529+
if (unlikely(!ieee80211_is_data_present(hdr->frame_control)))
530+
return false;
531+
532+
baid_data = rcu_dereference(mvm->baid_map[baid]);
533+
if (WARN(!baid_data,
534+
"Received baid %d, but no data exists for this BAID\n", baid))
535+
return false;
536+
if (WARN(tid != baid_data->tid || mvm_sta->sta_id != baid_data->sta_id,
537+
"baid 0x%x is mapped to sta:%d tid:%d, but was received for sta:%d tid:%d\n",
538+
baid, baid_data->sta_id, baid_data->tid, mvm_sta->sta_id,
539+
tid))
540+
return false;
541+
542+
nssn = reorder & IWL_RX_MPDU_REORDER_NSSN_MASK;
543+
sn = (reorder & IWL_RX_MPDU_REORDER_SN_MASK) >>
544+
IWL_RX_MPDU_REORDER_SN_SHIFT;
545+
546+
buffer = &baid_data->reorder_buf[queue];
547+
548+
/*
549+
* If there was a significant jump in the nssn - adjust.
550+
* If the SN is smaller than the NSSN it might need to first go into
551+
* the reorder buffer, in which case we just release up to it and the
552+
* rest of the function will take of storing it and releasing up to the
553+
* nssn
554+
*/
555+
if (!ieee80211_sn_less(nssn, buffer->head_sn + buffer->buf_size)) {
556+
u16 min_sn = ieee80211_sn_less(sn, nssn) ? sn : nssn;
557+
558+
iwl_mvm_release_frames(mvm, sta, napi, buffer, min_sn);
559+
}
560+
561+
/* drop any oudated packets */
562+
if (ieee80211_sn_less(sn, buffer->head_sn))
563+
goto drop;
564+
565+
/* release immediately if allowed by nssn and no stored frames */
566+
if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) {
567+
buffer->head_sn = nssn;
568+
/* No need to update AMSDU last SN - we are moving the head */
569+
return false;
570+
}
571+
572+
index = sn % buffer->buf_size;
573+
574+
/*
575+
* Check if we already stored this frame
576+
* As AMSDU is either received or not as whole, logic is simple:
577+
* If we have frames in that position in the buffer and the last frame
578+
* originated from AMSDU had a different SN then it is a retransmission.
579+
* If it is the same SN then if the subframe index is incrementing it
580+
* is the same AMSDU - otherwise it is a retransmission.
581+
*/
582+
tail = skb_peek_tail(&buffer->entries[index]);
583+
if (tail && !amsdu)
584+
goto drop;
585+
else if (tail && (sn != buffer->last_amsdu ||
586+
buffer->last_sub_index >= sub_frame_idx))
587+
goto drop;
588+
589+
/* put in reorder buffer */
590+
__skb_queue_tail(&buffer->entries[index], skb);
591+
buffer->num_stored++;
592+
if (amsdu) {
593+
buffer->last_amsdu = sn;
594+
buffer->last_sub_index = sub_frame_idx;
595+
}
596+
597+
iwl_mvm_release_frames(mvm, sta, napi, buffer, nssn);
598+
return true;
599+
600+
drop:
601+
kfree_skb(skb);
602+
return true;
603+
}
604+
427605
static void iwl_mvm_agg_rx_received(struct iwl_mvm *mvm, u8 baid)
428606
{
429607
unsigned long now = jiffies;
@@ -638,7 +816,8 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi,
638816
/* TODO: PHY info - gscan */
639817

640818
iwl_mvm_create_skb(skb, hdr, len, crypt_len, rxb);
641-
iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta);
819+
if (!iwl_mvm_reorder(mvm, napi, queue, sta, skb, desc))
820+
iwl_mvm_pass_packet_to_mac80211(mvm, napi, skb, queue, sta);
642821
rcu_read_unlock();
643822
}
644823

drivers/net/wireless/intel/iwlwifi/mvm/sta.c

Lines changed: 61 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1167,14 +1167,63 @@ int iwl_mvm_rm_bcast_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
11671167

11681168
#define IWL_MAX_RX_BA_SESSIONS 16
11691169

1170-
static void iwl_mvm_sync_rxq_del_ba(struct iwl_mvm *mvm)
1170+
static void iwl_mvm_sync_rxq_del_ba(struct iwl_mvm *mvm, u8 baid)
11711171
{
1172-
struct iwl_mvm_internal_rxq_notif data = {
1173-
.type = IWL_MVM_RXQ_EMPTY,
1174-
.sync = 1,
1172+
struct iwl_mvm_delba_notif notif = {
1173+
.metadata.type = IWL_MVM_RXQ_NOTIF_DEL_BA,
1174+
.metadata.sync = 1,
1175+
.delba.baid = baid,
11751176
};
1177+
iwl_mvm_sync_rx_queues_internal(mvm, (void *)&notif, sizeof(notif));
1178+
};
1179+
1180+
static void iwl_mvm_free_reorder(struct iwl_mvm *mvm,
1181+
struct iwl_mvm_baid_data *data)
1182+
{
1183+
int i;
1184+
1185+
iwl_mvm_sync_rxq_del_ba(mvm, data->baid);
1186+
1187+
for (i = 0; i < mvm->trans->num_rx_queues; i++) {
1188+
int j;
1189+
struct iwl_mvm_reorder_buffer *reorder_buf =
1190+
&data->reorder_buf[i];
11761191

1177-
iwl_mvm_sync_rx_queues_internal(mvm, &data, sizeof(data));
1192+
if (likely(!reorder_buf->num_stored))
1193+
continue;
1194+
1195+
/*
1196+
* This shouldn't happen in regular DELBA since the internal
1197+
* delBA notification should trigger a release of all frames in
1198+
* the reorder buffer.
1199+
*/
1200+
WARN_ON(1);
1201+
1202+
for (j = 0; j < reorder_buf->buf_size; j++)
1203+
__skb_queue_purge(&reorder_buf->entries[j]);
1204+
}
1205+
}
1206+
1207+
static void iwl_mvm_init_reorder_buffer(struct iwl_mvm *mvm,
1208+
u32 sta_id,
1209+
struct iwl_mvm_baid_data *data,
1210+
u16 ssn, u8 buf_size)
1211+
{
1212+
int i;
1213+
1214+
for (i = 0; i < mvm->trans->num_rx_queues; i++) {
1215+
struct iwl_mvm_reorder_buffer *reorder_buf =
1216+
&data->reorder_buf[i];
1217+
int j;
1218+
1219+
reorder_buf->num_stored = 0;
1220+
reorder_buf->head_sn = ssn;
1221+
reorder_buf->buf_size = buf_size;
1222+
reorder_buf->queue = i;
1223+
reorder_buf->sta_id = sta_id;
1224+
for (j = 0; j < reorder_buf->buf_size; j++)
1225+
__skb_queue_head_init(&reorder_buf->entries[j]);
1226+
}
11781227
}
11791228

11801229
int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
@@ -1198,7 +1247,10 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
11981247
* Allocate here so if allocation fails we can bail out early
11991248
* before starting the BA session in the firmware
12001249
*/
1201-
baid_data = kzalloc(sizeof(*baid_data), GFP_KERNEL);
1250+
baid_data = kzalloc(sizeof(*baid_data) +
1251+
mvm->trans->num_rx_queues *
1252+
sizeof(baid_data->reorder_buf[0]),
1253+
GFP_KERNEL);
12021254
if (!baid_data)
12031255
return -ENOMEM;
12041256
}
@@ -1273,6 +1325,8 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
12731325
mod_timer(&baid_data->session_timer,
12741326
TU_TO_EXP_TIME(timeout * 2));
12751327

1328+
iwl_mvm_init_reorder_buffer(mvm, mvm_sta->sta_id,
1329+
baid_data, ssn, buf_size);
12761330
/*
12771331
* protect the BA data with RCU to cover a case where our
12781332
* internal RX sync mechanism will timeout (not that it's
@@ -1297,9 +1351,8 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta,
12971351
return -EINVAL;
12981352

12991353
/* synchronize all rx queues so we can safely delete */
1300-
iwl_mvm_sync_rxq_del_ba(mvm);
1354+
iwl_mvm_free_reorder(mvm, baid_data);
13011355
del_timer_sync(&baid_data->session_timer);
1302-
13031356
RCU_INIT_POINTER(mvm->baid_map[baid], NULL);
13041357
kfree_rcu(baid_data, rcu_head);
13051358
}

drivers/net/wireless/intel/iwlwifi/mvm/sta.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,15 @@ struct iwl_mvm_key_pn {
348348
} ____cacheline_aligned_in_smp q[];
349349
};
350350

351+
struct iwl_mvm_delba_data {
352+
u32 baid;
353+
} __packed;
354+
355+
struct iwl_mvm_delba_notif {
356+
struct iwl_mvm_internal_rxq_notif metadata;
357+
struct iwl_mvm_delba_data delba;
358+
} __packed;
359+
351360
/**
352361
* struct iwl_mvm_rxq_dup_data - per station per rx queue data
353362
* @last_seq: last sequence per tid for duplicate packet detection

0 commit comments

Comments
 (0)