Skip to content

Commit 3c514bf

Browse files
egrumbachjmberg-intel
authored andcommitted
iwlwifi: mvm: add a loose synchronization of the NSSN across Rx queues
In order to support MSI-X efficiently, we want to avoid communication across Rx queues. Each Rx queue should have all the data it needs to process a packet. The reordering buffer is a challenge in the MSI-X world since we can have a single BA session whose packets are directed to different queues. This is why each queue has its own reordering buffer. The hardware is able to hint the driver whether we have a hole or not, which allows the driver to know whether it can release a packet or not. This indication is called NSSN. Roughly, if the packet's SN is lower than the NSSN, we can release the packet to the stack. The NSSN is the SN of the newest packet received without any holes + 1. This is working as long as we don't have packets that we release because of a timeout. When that happens, we could have taken the decision to release a packet after we have been waiting for its predecessor for too long. If this predecessor comes later, we have to drop it because we can't release packets out of order. In that case, the hardware will give us an indication that we can we release the packet (SN < NSSN), but the packet still needs to be dropped. This is why we sometimes need to ignore the NSSN and we track the head_sn in software. Here is a specific example of this: 1) Rx queue 1 got packets: 480, 482, 483 2) We release 480 to to the stack and wait for 481 3) NSSN is now 481 4) The timeout expires 5) We release 482 and 483, NSSN is still 480 6) 481 arrives its NSSN is 484. We need to drop 481 even if 481 < 484. This is why we'll update the head_sn to 484 at step 2. The flow now is: 1) Rx queue 1 got packets: 480, 482, 483 2) We release 480 to to the stack and wait for 481 3) NSSN is now 481 / head_sn is 481 4) The timeout expires 5) We release 482 and 483, NSSN is still 480 but head_sn is 484. 6) 481 arrives its NSSN is 484, but head_sn is 484 and we drop it. This code introduces another problem in case all the traffic goes well (no hole, no timeout): Rx queue 1: 0 -> 483 (head_sn = 484) Rx queue 2: 501 -> 4095 (head_sn = 0) Rx queue 2: 0 -> 480 (head_sn = 481) Rx queue 1: 481 but head_sn = 484 and we drop it. At this point, the SN of queue 1 is far behind: more than 4040 packets behind. Queue 1 will consider 481 "old" because 481 is in [501-64:501] whereas it is a very new packet. In order to fix that, send an Rx notification from time to time (twice across the full set of 4096 packets) to make sure no Rx queue is lagging too far behind. What will happen then is: Rx queue 1: 0 -> 483 (head_sn = 484) Rx queue 2: 501 -> 2047 (head_sn = 2048) Rx queue 1: Sync nofication (head_sn = 2048) Rx queue 2: 2048 -> 4095 (head_sn = 0) Rx queue 1: Sync notification (head_sn = 0) Rx queue 2: 1 -> 481 (head_sn = 482) Rx queue 1: 481 and head_sn = 0. In queue 1's data, head_sn is now 0, the packet coming in is 481, it'll understand that the new packet is new and it won't be dropped. Signed-off-by: Emmanuel Grumbach <[email protected]> Signed-off-by: Luca Coelho <[email protected]> Signed-off-by: Johannes Berg <[email protected]>
1 parent 521dc6c commit 3c514bf

File tree

3 files changed

+56
-15
lines changed

3 files changed

+56
-15
lines changed

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5053,7 +5053,6 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
50535053
u32 qmask = BIT(mvm->trans->num_rx_queues) - 1;
50545054
int ret;
50555055

5056-
lockdep_assert_held(&mvm->mutex);
50575056

50585057
if (!iwl_mvm_has_new_rx_api(mvm))
50595058
return;
@@ -5064,13 +5063,15 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm,
50645063
atomic_set(&mvm->queue_sync_counter,
50655064
mvm->trans->num_rx_queues);
50665065

5067-
ret = iwl_mvm_notify_rx_queue(mvm, qmask, (u8 *)notif, size);
5066+
ret = iwl_mvm_notify_rx_queue(mvm, qmask, (u8 *)notif,
5067+
size, !notif->sync);
50685068
if (ret) {
50695069
IWL_ERR(mvm, "Failed to trigger RX queues sync (%d)\n", ret);
50705070
goto out;
50715071
}
50725072

50735073
if (notif->sync) {
5074+
lockdep_assert_held(&mvm->mutex);
50745075
ret = wait_event_timeout(mvm->rx_sync_waitq,
50755076
atomic_read(&mvm->queue_sync_counter) == 0 ||
50765077
iwl_mvm_is_radio_killed(mvm),

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1664,7 +1664,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,
16641664
void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi,
16651665
struct iwl_rx_cmd_buffer *rxb, int queue);
16661666
int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask,
1667-
const u8 *data, u32 count);
1667+
const u8 *data, u32 count, bool async);
16681668
void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct napi_struct *napi,
16691669
struct iwl_rx_cmd_buffer *rxb, int queue);
16701670
void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb);

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

Lines changed: 52 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -463,7 +463,7 @@ static bool iwl_mvm_is_dup(struct ieee80211_sta *sta, int queue,
463463
}
464464

465465
int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask,
466-
const u8 *data, u32 count)
466+
const u8 *data, u32 count, bool async)
467467
{
468468
u8 buf[sizeof(struct iwl_rxq_sync_cmd) +
469469
sizeof(struct iwl_mvm_rss_sync_notif)];
@@ -487,7 +487,7 @@ int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask,
487487
ret = iwl_mvm_send_cmd_pdu(mvm,
488488
WIDE_ID(DATA_PATH_GROUP,
489489
TRIGGER_RX_QUEUES_NOTIF_CMD),
490-
0, data_size, cmd);
490+
async ? CMD_ASYNC : 0, data_size, cmd);
491491

492492
return ret;
493493
}
@@ -504,14 +504,26 @@ static bool iwl_mvm_is_sn_less(u16 sn1, u16 sn2, u16 buffer_size)
504504
!ieee80211_sn_less(sn1, sn2 - buffer_size);
505505
}
506506

507+
static void iwl_mvm_sync_nssn(struct iwl_mvm *mvm, u8 baid, u16 nssn)
508+
{
509+
struct iwl_mvm_rss_sync_notif notif = {
510+
.metadata.type = IWL_MVM_RXQ_NSSN_SYNC,
511+
.metadata.sync = 0,
512+
.nssn_sync.baid = baid,
513+
.nssn_sync.nssn = nssn,
514+
};
515+
516+
iwl_mvm_sync_rx_queues_internal(mvm, (void *)&notif, sizeof(notif));
517+
}
518+
507519
#define RX_REORDER_BUF_TIMEOUT_MQ (HZ / 10)
508520

509521
static void iwl_mvm_release_frames(struct iwl_mvm *mvm,
510522
struct ieee80211_sta *sta,
511523
struct napi_struct *napi,
512524
struct iwl_mvm_baid_data *baid_data,
513525
struct iwl_mvm_reorder_buffer *reorder_buf,
514-
u16 nssn)
526+
u16 nssn, bool sync_rss)
515527
{
516528
struct iwl_mvm_reorder_buf_entry *entries =
517529
&baid_data->entries[reorder_buf->queue *
@@ -530,6 +542,8 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm,
530542
struct sk_buff *skb;
531543

532544
ssn = ieee80211_sn_inc(ssn);
545+
if (sync_rss && (ssn == 2048 || ssn == 0))
546+
iwl_mvm_sync_nssn(mvm, baid_data->baid, ssn);
533547

534548
/*
535549
* Empty the list. Will have more than one frame for A-MSDU.
@@ -616,7 +630,8 @@ void iwl_mvm_reorder_timer_expired(struct timer_list *t)
616630
sta_id, sn);
617631
iwl_mvm_event_frame_timeout_callback(buf->mvm, mvmsta->vif,
618632
sta, baid_data->tid);
619-
iwl_mvm_release_frames(buf->mvm, sta, NULL, baid_data, buf, sn);
633+
iwl_mvm_release_frames(buf->mvm, sta, NULL, baid_data,
634+
buf, sn, true);
620635
rcu_read_unlock();
621636
} else {
622637
/*
@@ -658,7 +673,8 @@ static void iwl_mvm_del_ba(struct iwl_mvm *mvm, int queue,
658673
spin_lock_bh(&reorder_buf->lock);
659674
iwl_mvm_release_frames(mvm, sta, NULL, ba_data, reorder_buf,
660675
ieee80211_sn_add(reorder_buf->head_sn,
661-
reorder_buf->buf_size));
676+
reorder_buf->buf_size),
677+
false);
662678
spin_unlock_bh(&reorder_buf->lock);
663679
del_timer_sync(&reorder_buf->reorder_timer);
664680

@@ -694,7 +710,8 @@ static void iwl_mvm_release_frames_from_notif(struct iwl_mvm *mvm,
694710
reorder_buf = &ba_data->reorder_buf[queue];
695711

696712
spin_lock_bh(&reorder_buf->lock);
697-
iwl_mvm_release_frames(mvm, sta, napi, ba_data, reorder_buf, nssn);
713+
iwl_mvm_release_frames(mvm, sta, napi, ba_data,
714+
reorder_buf, nssn, false);
698715
spin_unlock_bh(&reorder_buf->lock);
699716

700717
out:
@@ -833,7 +850,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
833850
}
834851

835852
if (ieee80211_is_back_req(hdr->frame_control)) {
836-
iwl_mvm_release_frames(mvm, sta, napi, baid_data, buffer, nssn);
853+
iwl_mvm_release_frames(mvm, sta, napi, baid_data,
854+
buffer, nssn, false);
837855
goto drop;
838856
}
839857

@@ -842,15 +860,18 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
842860
* If the SN is smaller than the NSSN it might need to first go into
843861
* the reorder buffer, in which case we just release up to it and the
844862
* rest of the function will take care of storing it and releasing up to
845-
* the nssn
863+
* the nssn.
864+
* This should not happen. This queue has been lagging and it should
865+
* have been updated by a IWL_MVM_RXQ_NSSN_SYNC notification. Be nice
866+
* and update the other queues.
846867
*/
847868
if (!iwl_mvm_is_sn_less(nssn, buffer->head_sn + buffer->buf_size,
848869
buffer->buf_size) ||
849870
!ieee80211_sn_less(sn, buffer->head_sn + buffer->buf_size)) {
850871
u16 min_sn = ieee80211_sn_less(sn, nssn) ? sn : nssn;
851872

852873
iwl_mvm_release_frames(mvm, sta, napi, baid_data, buffer,
853-
min_sn);
874+
min_sn, true);
854875
}
855876

856877
/* drop any oudated packets */
@@ -861,8 +882,23 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
861882
if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) {
862883
if (iwl_mvm_is_sn_less(buffer->head_sn, nssn,
863884
buffer->buf_size) &&
864-
(!amsdu || last_subframe))
885+
(!amsdu || last_subframe)) {
886+
/*
887+
* If we crossed the 2048 or 0 SN, notify all the
888+
* queues. This is done in order to avoid having a
889+
* head_sn that lags behind for too long. When that
890+
* happens, we can get to a situation where the head_sn
891+
* is within the interval [nssn - buf_size : nssn]
892+
* which will make us think that the nssn is a packet
893+
* that we already freed because of the reordering
894+
* buffer and we will ignore it. So maintain the
895+
* head_sn somewhat updated across all the queues:
896+
* when it crosses 0 and 2048.
897+
*/
898+
if (sn == 2048 || sn == 0)
899+
iwl_mvm_sync_nssn(mvm, baid, sn);
865900
buffer->head_sn = nssn;
901+
}
866902
/* No need to update AMSDU last SN - we are moving the head */
867903
spin_unlock_bh(&buffer->lock);
868904
return false;
@@ -877,8 +913,11 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
877913
* while technically there is no hole and we can move forward.
878914
*/
879915
if (!buffer->num_stored && sn == buffer->head_sn) {
880-
if (!amsdu || last_subframe)
916+
if (!amsdu || last_subframe) {
917+
if (sn == 2048 || sn == 0)
918+
iwl_mvm_sync_nssn(mvm, baid, sn);
881919
buffer->head_sn = ieee80211_sn_inc(buffer->head_sn);
920+
}
882921
/* No need to update AMSDU last SN - we are moving the head */
883922
spin_unlock_bh(&buffer->lock);
884923
return false;
@@ -923,7 +962,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm,
923962
* release notification with up to date NSSN.
924963
*/
925964
if (!amsdu || last_subframe)
926-
iwl_mvm_release_frames(mvm, sta, napi, baid_data, buffer, nssn);
965+
iwl_mvm_release_frames(mvm, sta, napi, baid_data,
966+
buffer, nssn, true);
927967

928968
spin_unlock_bh(&buffer->lock);
929969
return true;

0 commit comments

Comments
 (0)