diff options
Diffstat (limited to 'drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c')
-rw-r--r-- | drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 185 |
1 files changed, 134 insertions, 51 deletions
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 64f950501287..854edd7d7103 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -463,20 +463,22 @@ static bool iwl_mvm_is_dup(struct ieee80211_sta *sta, int queue, } int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask, - const u8 *data, u32 count) + const u8 *data, u32 count, bool async) { - struct iwl_rxq_sync_cmd *cmd; + u8 buf[sizeof(struct iwl_rxq_sync_cmd) + + sizeof(struct iwl_mvm_rss_sync_notif)]; + struct iwl_rxq_sync_cmd *cmd = (void *)buf; u32 data_size = sizeof(*cmd) + count; int ret; - /* should be DWORD aligned */ - if (WARN_ON(count & 3 || count > IWL_MULTI_QUEUE_SYNC_MSG_MAX_SIZE)) + /* + * size must be a multiple of DWORD + * Ensure we don't overflow buf + */ + if (WARN_ON(count & 3 || + count > sizeof(struct iwl_mvm_rss_sync_notif))) return -EINVAL; - cmd = kzalloc(data_size, GFP_KERNEL); - if (!cmd) - return -ENOMEM; - cmd->rxq_mask = cpu_to_le32(rxq_mask); cmd->count = cpu_to_le32(count); cmd->flags = 0; @@ -485,9 +487,8 @@ int iwl_mvm_notify_rx_queue(struct iwl_mvm *mvm, u32 rxq_mask, ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(DATA_PATH_GROUP, TRIGGER_RX_QUEUES_NOTIF_CMD), - 0, data_size, cmd); + async ? CMD_ASYNC : 0, data_size, cmd); - kfree(cmd); return ret; } @@ -503,14 +504,31 @@ static bool iwl_mvm_is_sn_less(u16 sn1, u16 sn2, u16 buffer_size) !ieee80211_sn_less(sn1, sn2 - buffer_size); } +static void iwl_mvm_sync_nssn(struct iwl_mvm *mvm, u8 baid, u16 nssn) +{ + struct iwl_mvm_rss_sync_notif notif = { + .metadata.type = IWL_MVM_RXQ_NSSN_SYNC, + .metadata.sync = 0, + .nssn_sync.baid = baid, + .nssn_sync.nssn = nssn, + }; + + iwl_mvm_sync_rx_queues_internal(mvm, (void *)¬if, sizeof(notif)); +} + #define RX_REORDER_BUF_TIMEOUT_MQ (HZ / 10) +enum iwl_mvm_release_flags { + IWL_MVM_RELEASE_SEND_RSS_SYNC = BIT(0), + IWL_MVM_RELEASE_FROM_RSS_SYNC = BIT(1), +}; + static void iwl_mvm_release_frames(struct iwl_mvm *mvm, struct ieee80211_sta *sta, struct napi_struct *napi, struct iwl_mvm_baid_data *baid_data, struct iwl_mvm_reorder_buffer *reorder_buf, - u16 nssn) + u16 nssn, u32 flags) { struct iwl_mvm_reorder_buf_entry *entries = &baid_data->entries[reorder_buf->queue * @@ -519,6 +537,18 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm, lockdep_assert_held(&reorder_buf->lock); + /* + * We keep the NSSN not too far behind, if we are sync'ing it and it + * is more than 2048 ahead of us, it must be behind us. Discard it. + * This can happen if the queue that hit the 0 / 2048 seqno was lagging + * behind and this queue already processed packets. The next if + * would have caught cases where this queue would have processed less + * than 64 packets, but it may have processed more than 64 packets. + */ + if ((flags & IWL_MVM_RELEASE_FROM_RSS_SYNC) && + ieee80211_sn_less(nssn, ssn)) + goto set_timer; + /* ignore nssn smaller than head sn - this can happen due to timeout */ if (iwl_mvm_is_sn_less(nssn, ssn, reorder_buf->buf_size)) goto set_timer; @@ -529,6 +559,9 @@ static void iwl_mvm_release_frames(struct iwl_mvm *mvm, struct sk_buff *skb; ssn = ieee80211_sn_inc(ssn); + if ((flags & IWL_MVM_RELEASE_SEND_RSS_SYNC) && + (ssn == 2048 || ssn == 0)) + iwl_mvm_sync_nssn(mvm, baid_data->baid, ssn); /* * Empty the list. Will have more than one frame for A-MSDU. @@ -615,7 +648,8 @@ void iwl_mvm_reorder_timer_expired(struct timer_list *t) sta_id, sn); iwl_mvm_event_frame_timeout_callback(buf->mvm, mvmsta->vif, sta, baid_data->tid); - iwl_mvm_release_frames(buf->mvm, sta, NULL, baid_data, buf, sn); + iwl_mvm_release_frames(buf->mvm, sta, NULL, baid_data, + buf, sn, IWL_MVM_RELEASE_SEND_RSS_SYNC); rcu_read_unlock(); } else { /* @@ -657,7 +691,8 @@ static void iwl_mvm_del_ba(struct iwl_mvm *mvm, int queue, spin_lock_bh(&reorder_buf->lock); iwl_mvm_release_frames(mvm, sta, NULL, ba_data, reorder_buf, ieee80211_sn_add(reorder_buf->head_sn, - reorder_buf->buf_size)); + reorder_buf->buf_size), + 0); spin_unlock_bh(&reorder_buf->lock); del_timer_sync(&reorder_buf->reorder_timer); @@ -665,8 +700,54 @@ out: rcu_read_unlock(); } -void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, - int queue) +static void iwl_mvm_release_frames_from_notif(struct iwl_mvm *mvm, + struct napi_struct *napi, + u8 baid, u16 nssn, int queue, + u32 flags) +{ + struct ieee80211_sta *sta; + struct iwl_mvm_reorder_buffer *reorder_buf; + struct iwl_mvm_baid_data *ba_data; + + IWL_DEBUG_HT(mvm, "Frame release notification for BAID %u, NSSN %d\n", + baid, nssn); + + if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID || + baid >= ARRAY_SIZE(mvm->baid_map))) + return; + + rcu_read_lock(); + + ba_data = rcu_dereference(mvm->baid_map[baid]); + if (WARN_ON_ONCE(!ba_data)) + goto out; + + sta = rcu_dereference(mvm->fw_id_to_mac_id[ba_data->sta_id]); + if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) + goto out; + + reorder_buf = &ba_data->reorder_buf[queue]; + + spin_lock_bh(&reorder_buf->lock); + iwl_mvm_release_frames(mvm, sta, napi, ba_data, + reorder_buf, nssn, flags); + spin_unlock_bh(&reorder_buf->lock); + +out: + rcu_read_unlock(); +} + +static void iwl_mvm_nssn_sync(struct iwl_mvm *mvm, + struct napi_struct *napi, int queue, + const struct iwl_mvm_nssn_sync_data *data) +{ + iwl_mvm_release_frames_from_notif(mvm, napi, data->baid, + data->nssn, queue, + IWL_MVM_RELEASE_FROM_RSS_SYNC); +} + +void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, int queue) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_rxq_sync_notification *notif; @@ -687,6 +768,10 @@ void iwl_mvm_rx_queue_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb, case IWL_MVM_RXQ_NOTIF_DEL_BA: iwl_mvm_del_ba(mvm, queue, (void *)internal_notif->data); break; + case IWL_MVM_RXQ_NSSN_SYNC: + iwl_mvm_nssn_sync(mvm, napi, queue, + (void *)internal_notif->data); + break; default: WARN_ONCE(1, "Invalid identifier %d", internal_notif->type); } @@ -785,7 +870,8 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, } if (ieee80211_is_back_req(hdr->frame_control)) { - iwl_mvm_release_frames(mvm, sta, napi, baid_data, buffer, nssn); + iwl_mvm_release_frames(mvm, sta, napi, baid_data, + buffer, nssn, 0); goto drop; } @@ -794,7 +880,10 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, * If the SN is smaller than the NSSN it might need to first go into * the reorder buffer, in which case we just release up to it and the * rest of the function will take care of storing it and releasing up to - * the nssn + * the nssn. + * This should not happen. This queue has been lagging and it should + * have been updated by a IWL_MVM_RXQ_NSSN_SYNC notification. Be nice + * and update the other queues. */ if (!iwl_mvm_is_sn_less(nssn, buffer->head_sn + buffer->buf_size, buffer->buf_size) || @@ -802,7 +891,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, u16 min_sn = ieee80211_sn_less(sn, nssn) ? sn : nssn; iwl_mvm_release_frames(mvm, sta, napi, baid_data, buffer, - min_sn); + min_sn, IWL_MVM_RELEASE_SEND_RSS_SYNC); } /* drop any oudated packets */ @@ -813,8 +902,23 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) { if (iwl_mvm_is_sn_less(buffer->head_sn, nssn, buffer->buf_size) && - (!amsdu || last_subframe)) + (!amsdu || last_subframe)) { + /* + * If we crossed the 2048 or 0 SN, notify all the + * queues. This is done in order to avoid having a + * head_sn that lags behind for too long. When that + * happens, we can get to a situation where the head_sn + * is within the interval [nssn - buf_size : nssn] + * which will make us think that the nssn is a packet + * that we already freed because of the reordering + * buffer and we will ignore it. So maintain the + * head_sn somewhat updated across all the queues: + * when it crosses 0 and 2048. + */ + if (sn == 2048 || sn == 0) + iwl_mvm_sync_nssn(mvm, baid, sn); buffer->head_sn = nssn; + } /* No need to update AMSDU last SN - we are moving the head */ spin_unlock_bh(&buffer->lock); return false; @@ -829,8 +933,11 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, * while technically there is no hole and we can move forward. */ if (!buffer->num_stored && sn == buffer->head_sn) { - if (!amsdu || last_subframe) + if (!amsdu || last_subframe) { + if (sn == 2048 || sn == 0) + iwl_mvm_sync_nssn(mvm, baid, sn); buffer->head_sn = ieee80211_sn_inc(buffer->head_sn); + } /* No need to update AMSDU last SN - we are moving the head */ spin_unlock_bh(&buffer->lock); return false; @@ -875,7 +982,9 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, * release notification with up to date NSSN. */ if (!amsdu || last_subframe) - iwl_mvm_release_frames(mvm, sta, napi, baid_data, buffer, nssn); + iwl_mvm_release_frames(mvm, sta, napi, baid_data, + buffer, nssn, + IWL_MVM_RELEASE_SEND_RSS_SYNC); spin_unlock_bh(&buffer->lock); return true; @@ -1840,40 +1949,14 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, out: rcu_read_unlock(); } + void iwl_mvm_rx_frame_release(struct iwl_mvm *mvm, struct napi_struct *napi, struct iwl_rx_cmd_buffer *rxb, int queue) { struct iwl_rx_packet *pkt = rxb_addr(rxb); struct iwl_frame_release *release = (void *)pkt->data; - struct ieee80211_sta *sta; - struct iwl_mvm_reorder_buffer *reorder_buf; - struct iwl_mvm_baid_data *ba_data; - - int baid = release->baid; - - IWL_DEBUG_HT(mvm, "Frame release notification for BAID %u, NSSN %d\n", - release->baid, le16_to_cpu(release->nssn)); - if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID)) - return; - - rcu_read_lock(); - - ba_data = rcu_dereference(mvm->baid_map[baid]); - if (WARN_ON_ONCE(!ba_data)) - goto out; - - sta = rcu_dereference(mvm->fw_id_to_mac_id[ba_data->sta_id]); - if (WARN_ON_ONCE(IS_ERR_OR_NULL(sta))) - goto out; - - reorder_buf = &ba_data->reorder_buf[queue]; - - spin_lock_bh(&reorder_buf->lock); - iwl_mvm_release_frames(mvm, sta, napi, ba_data, reorder_buf, - le16_to_cpu(release->nssn)); - spin_unlock_bh(&reorder_buf->lock); - -out: - rcu_read_unlock(); + iwl_mvm_release_frames_from_notif(mvm, napi, release->baid, + le16_to_cpu(release->nssn), + queue, 0); } |