1
0
Ficheiros
android_kernel_samsung_sm86…/dp/wifi3.0/rh/dp_rh_rx.c
Karthik Kantamneni 2edb0c0388 qcacmn: Add RX handling for RHINE architecture
RHINE is soft UMAC based architecture which is not having
REO block, all the REO functionality will be implemented
in F.W and host level. Host will get the RX packets in
CE-RX rings in HTT format, to reap RX packets new HTT
messages will be extracted and parsed.

So implement RX handling based on new softumac architecture for RHINE.

Change-Id: If430dd017309e2b2a3eb5e27e1d8b58696abceb4
CRs-Fixed: 3382920
2023-03-14 01:41:05 -07:00

1127 linhas
32 KiB
C

/*
* Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
#include "cdp_txrx_cmn_struct.h"
#include "hal_hw_headers.h"
#include "dp_types.h"
#include "dp_rx.h"
#include "dp_tx.h"
#include "dp_rx_defrag.h"
#include "dp_rh_rx.h"
#include "dp_rh_htt.h"
#include "dp_peer.h"
#include "hal_rx.h"
#include "hal_rh_rx.h"
#include "hal_api.h"
#include "hal_rh_api.h"
#include "qdf_nbuf.h"
#include "dp_internal.h"
#ifdef WIFI_MONITOR_SUPPORT
#include <dp_mon.h>
#endif
#ifdef FEATURE_WDS
#include "dp_txrx_wds.h"
#endif
#include "dp_hist.h"
#include "dp_rx_buffer_pool.h"
#include "dp_rh.h"
static inline uint8_t dp_rx_get_ctx_id_frm_napiid(uint8_t napi_id)
{
/*
* This is NAPI to CE then to rx context id mapping
* example: CE1 is assigned with napi id 3(ce_id+1)
* CE1 maps to RX context id 0, so napi id 2 maps to
* RX context id 0, this need to optimized further.
*/
switch (napi_id) {
case 2:
return 0;
case 11:
return 1;
case 12:
return 2;
default:
dp_err("Invalid napi id, this should not happen");
qdf_assert_always(0);
break;
}
return 0;
}
void
dp_rx_data_flush(void *data)
{
struct qca_napi_info *napi_info = (struct qca_napi_info *)data;
uint8_t rx_ctx_id = dp_rx_get_ctx_id_frm_napiid(napi_info->id);
struct dp_soc *soc = cds_get_context(QDF_MODULE_ID_SOC);
struct dp_vdev *vdev;
int i;
if (rx_ctx_id == 0 && soc->rx.flags.defrag_timeout_check) {
uint32_t now_ms =
qdf_system_ticks_to_msecs(qdf_system_ticks());
if (now_ms >= soc->rx.defrag.next_flush_ms)
dp_rx_defrag_waitlist_flush(soc);
}
/*Get first available vdev to flush all RX packets across soc*/
for (i = 0; i < MAX_VDEV_CNT; i++) {
vdev = dp_vdev_get_ref_by_id(soc, i, DP_MOD_ID_RX);
if (vdev && vdev->osif_fisa_flush)
vdev->osif_fisa_flush(soc, rx_ctx_id);
if (vdev && vdev->osif_gro_flush) {
vdev->osif_gro_flush(vdev->osif_vdev,
rx_ctx_id);
dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_RX);
return;
}
if (vdev)
dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_RX);
}
}
static inline
bool is_sa_da_idx_valid(uint32_t max_ast,
qdf_nbuf_t nbuf, struct hal_rx_msdu_metadata msdu_info)
{
if ((qdf_nbuf_is_sa_valid(nbuf) && (msdu_info.sa_idx > max_ast)) ||
(!qdf_nbuf_is_da_mcbc(nbuf) && qdf_nbuf_is_da_valid(nbuf) &&
(msdu_info.da_idx > max_ast)))
return false;
return true;
}
#if defined(FEATURE_MCL_REPEATER) && defined(FEATURE_MEC)
/**
* dp_rx_mec_check_wrapper() - wrapper to dp_rx_mcast_echo_check
* @soc: core DP main context
* @txrx_peer: dp peer handler
* @rx_tlv_hdr: start of the rx TLV header
* @nbuf: pkt buffer
*
* Return: bool (true if it is a looped back pkt else false)
*/
static inline bool dp_rx_mec_check_wrapper(struct dp_soc *soc,
struct dp_txrx_peer *txrx_peer,
uint8_t *rx_tlv_hdr,
qdf_nbuf_t nbuf)
{
return dp_rx_mcast_echo_check(soc, txrx_peer, rx_tlv_hdr, nbuf);
}
#else
static inline bool dp_rx_mec_check_wrapper(struct dp_soc *soc,
struct dp_txrx_peer *txrx_peer,
uint8_t *rx_tlv_hdr,
qdf_nbuf_t nbuf)
{
return false;
}
#endif
static bool
dp_rx_intrabss_ucast_check_rh(struct dp_soc *soc, qdf_nbuf_t nbuf,
struct dp_txrx_peer *ta_txrx_peer,
struct hal_rx_msdu_metadata *msdu_metadata,
uint8_t *p_tx_vdev_id)
{
uint16_t da_peer_id;
struct dp_txrx_peer *da_peer;
struct dp_ast_entry *ast_entry;
dp_txrx_ref_handle txrx_ref_handle = NULL;
if (!qdf_nbuf_is_da_valid(nbuf) || qdf_nbuf_is_da_mcbc(nbuf))
return false;
ast_entry = soc->ast_table[msdu_metadata->da_idx];
if (!ast_entry)
return false;
if (ast_entry->type == CDP_TXRX_AST_TYPE_DA) {
ast_entry->is_active = TRUE;
return false;
}
da_peer_id = ast_entry->peer_id;
/* TA peer cannot be same as peer(DA) on which AST is present
* this indicates a change in topology and that AST entries
* are yet to be updated.
*/
if (da_peer_id == ta_txrx_peer->peer_id ||
da_peer_id == HTT_INVALID_PEER)
return false;
da_peer = dp_txrx_peer_get_ref_by_id(soc, da_peer_id,
&txrx_ref_handle, DP_MOD_ID_RX);
if (!da_peer)
return false;
*p_tx_vdev_id = da_peer->vdev->vdev_id;
/* If the source or destination peer in the isolation
* list then dont forward instead push to bridge stack.
*/
if (dp_get_peer_isolation(ta_txrx_peer) ||
dp_get_peer_isolation(da_peer) ||
da_peer->vdev->vdev_id != ta_txrx_peer->vdev->vdev_id) {
dp_txrx_peer_unref_delete(txrx_ref_handle, DP_MOD_ID_RX);
return false;
}
if (da_peer->bss_peer) {
dp_txrx_peer_unref_delete(txrx_ref_handle, DP_MOD_ID_RX);
return false;
}
dp_txrx_peer_unref_delete(txrx_ref_handle, DP_MOD_ID_RX);
return true;
}
/*
* dp_rx_intrabss_fwd_rh() - Implements the Intra-BSS forwarding logic
*
* @soc: core txrx main context
* @ta_txrx_peer : source peer entry
* @rx_tlv_hdr : start address of rx tlvs
* @nbuf : nbuf that has to be intrabss forwarded
*
* Return: bool: true if it is forwarded else false
*/
static bool
dp_rx_intrabss_fwd_rh(struct dp_soc *soc,
struct dp_txrx_peer *ta_txrx_peer,
uint8_t *rx_tlv_hdr,
qdf_nbuf_t nbuf,
struct hal_rx_msdu_metadata msdu_metadata,
struct cdp_tid_rx_stats *tid_stats)
{
uint8_t tx_vdev_id;
/* if it is a broadcast pkt (eg: ARP) and it is not its own
* source, then clone the pkt and send the cloned pkt for
* intra BSS forwarding and original pkt up the network stack
* Note: how do we handle multicast pkts. do we forward
* all multicast pkts as is or let a higher layer module
* like igmpsnoop decide whether to forward or not with
* Mcast enhancement.
*/
if (qdf_nbuf_is_da_mcbc(nbuf) && !ta_txrx_peer->bss_peer)
return dp_rx_intrabss_mcbc_fwd(soc, ta_txrx_peer, rx_tlv_hdr,
nbuf, tid_stats);
if (dp_rx_intrabss_eapol_drop_check(soc, ta_txrx_peer, rx_tlv_hdr,
nbuf))
return true;
if (dp_rx_intrabss_ucast_check_rh(soc, nbuf, ta_txrx_peer,
&msdu_metadata, &tx_vdev_id))
return dp_rx_intrabss_ucast_fwd(soc, ta_txrx_peer, tx_vdev_id,
rx_tlv_hdr, nbuf, tid_stats);
return false;
}
void
dp_rx_data_indication_handler(struct dp_soc *soc, qdf_nbuf_t data_ind,
uint16_t vdev_id, uint16_t peer_id,
uint16_t msdu_count)
{
uint8_t *data_ind_msg;
uint32_t *msg_word;
uint32_t rx_ctx_id;
hal_soc_handle_t hal_soc;
struct dp_rx_desc *rx_desc = NULL;
qdf_nbuf_t nbuf, next;
union dp_rx_desc_list_elem_t *head[MAX_PDEV_CNT];
union dp_rx_desc_list_elem_t *tail[MAX_PDEV_CNT];
uint32_t num_pending = msdu_count;
uint32_t rx_buf_cookie;
uint16_t msdu_len = 0;
struct dp_txrx_peer *txrx_peer;
dp_txrx_ref_handle txrx_ref_handle = NULL;
struct dp_vdev *vdev;
uint32_t pkt_len = 0;
uint8_t *rx_tlv_hdr;
uint32_t rx_bufs_reaped[MAX_PDEV_CNT];
uint8_t mac_id = 0;
struct dp_pdev *rx_pdev;
struct dp_srng *dp_rxdma_srng;
struct rx_desc_pool *rx_desc_pool;
struct cdp_tid_rx_stats *tid_stats;
qdf_nbuf_t nbuf_head;
qdf_nbuf_t nbuf_tail;
qdf_nbuf_t deliver_list_head;
qdf_nbuf_t deliver_list_tail;
uint32_t num_rx_bufs_reaped = 0;
struct hif_opaque_softc *scn;
int32_t tid = 0;
bool is_prev_msdu_last = true;
uint32_t rx_ol_pkt_cnt = 0;
struct hal_rx_msdu_metadata msdu_metadata;
qdf_nbuf_t ebuf_head;
qdf_nbuf_t ebuf_tail;
uint8_t pkt_capture_offload = 0;
uint32_t old_tid;
uint32_t peer_ext_stats;
uint32_t dsf;
uint32_t max_ast;
uint64_t current_time = 0;
uint32_t error;
DP_HIST_INIT();
/* TODO implement dp_rx_dump_info_and_assert equivalent in RHINE */
qdf_assert_always(soc && msdu_count);
hal_soc = soc->hal_soc;
qdf_assert_always(hal_soc);
scn = soc->hif_handle;
dp_runtime_pm_mark_last_busy(soc);
/* reset local variables here to be re-used in the function */
nbuf_head = NULL;
nbuf_tail = NULL;
deliver_list_head = NULL;
deliver_list_tail = NULL;
txrx_peer = NULL;
vdev = NULL;
num_rx_bufs_reaped = 0;
ebuf_head = NULL;
ebuf_tail = NULL;
qdf_mem_zero(rx_bufs_reaped, sizeof(rx_bufs_reaped));
qdf_mem_zero(head, sizeof(head));
qdf_mem_zero(tail, sizeof(tail));
old_tid = 0xff;
dsf = 0;
peer_ext_stats = 0;
max_ast = 0;
rx_pdev = NULL;
tid_stats = NULL;
dp_pkt_get_timestamp(&current_time);
peer_ext_stats = wlan_cfg_is_peer_ext_stats_enabled(soc->wlan_cfg_ctx);
max_ast = wlan_cfg_get_max_ast_idx(soc->wlan_cfg_ctx);
data_ind_msg = qdf_nbuf_data(data_ind);
msg_word =
(uint32_t *)(data_ind_msg + HTT_RX_DATA_IND_HDR_SIZE);
rx_ctx_id =
dp_rx_get_ctx_id_frm_napiid(QDF_NBUF_CB_RX_CTX_ID(data_ind));
while (qdf_likely(num_pending)) {
error = HTT_RX_DATA_MSDU_INFO_ERROR_VALID_GET(*(msg_word + 3));
if (qdf_unlikely(error)) {
dp_rx_err("MSDU RX error encountered error:%u", error);
/* TODO handle error MSDU gracefully */
qdf_assert_always(0);
}
rx_buf_cookie =
HTT_RX_DATA_MSDU_INFO_SW_BUFFER_COOKIE_GET(*(msg_word + 1));
rx_desc = dp_rx_cookie_2_va_rxdma_buf(soc, rx_buf_cookie);
if (qdf_unlikely(!rx_desc && !rx_desc->nbuf &&
!rx_desc->in_use)) {
dp_rx_err("Invalid RX descriptor");
qdf_assert_always(0);
/* TODO handle this if its valid case */
}
if (qdf_unlikely(!dp_rx_desc_check_magic(rx_desc))) {
dp_err("Invalid rx_desc cookie=%d", rx_buf_cookie);
DP_STATS_INC(soc, rx.err.rx_desc_invalid_magic, 1);
qdf_assert(0);
}
msdu_len =
HTT_RX_DATA_MSDU_INFO_MSDU_LENGTH_GET(*(msg_word + 2));
if (qdf_unlikely(
HTT_RX_DATA_MSDU_INFO_MSDU_CONTINUATION_GET(*(msg_word + 2)))) {
/* previous msdu has end bit set, so current one is
* the new MPDU
*/
if (is_prev_msdu_last) {
/* For new MPDU check if we can read complete
* MPDU by comparing the number of buffers
* available and number of buffers needed to
* reap this MPDU
*/
if ((msdu_len /
(RX_DATA_BUFFER_SIZE -
soc->rx_pkt_tlv_size) + 1) >
num_pending) {
DP_STATS_INC(soc,
rx.msdu_scatter_wait_break,
1);
/* This is not expected host cannot deal
* with partial frame in single DATA
* indication, F.W has to submit full
* frame in single DATA indication
*/
qdf_assert_always(0);
}
is_prev_msdu_last = false;
}
}
if (HTT_RX_DATA_MSDU_INFO_MPDU_RETRY_BIT_GET(*(msg_word + 2)))
qdf_nbuf_set_rx_retry_flag(rx_desc->nbuf, 1);
if (HTT_RX_DATA_MSDU_INFO_RAW_MPDU_FRAME_GET(*(msg_word + 2)))
qdf_nbuf_set_raw_frame(rx_desc->nbuf, 1);
if (!is_prev_msdu_last &&
HTT_RX_DATA_MSDU_INFO_LAST_MSDU_IN_MPDU_GET(*(msg_word + 2)))
is_prev_msdu_last = true;
rx_bufs_reaped[rx_desc->pool_id]++;
QDF_NBUF_CB_RX_PEER_ID(rx_desc->nbuf) = peer_id;
QDF_NBUF_CB_RX_VDEV_ID(rx_desc->nbuf) = vdev_id;
/*
* save msdu flags first, last and continuation msdu in
* nbuf->cb, also save mcbc, is_da_valid, is_sa_valid and
* length to nbuf->cb. This ensures the info required for
* per pkt processing is always in the same cache line.
* This helps in improving throughput for smaller pkt
* sizes.
*/
if (HTT_RX_DATA_MSDU_INFO_FIRST_MSDU_IN_MPDU_GET(*(msg_word + 2)))
qdf_nbuf_set_rx_chfrag_start(rx_desc->nbuf, 1);
if (HTT_RX_DATA_MSDU_INFO_MSDU_CONTINUATION_GET(*(msg_word + 2)))
qdf_nbuf_set_rx_chfrag_cont(rx_desc->nbuf, 1);
if (HTT_RX_DATA_MSDU_INFO_LAST_MSDU_IN_MPDU_GET(*(msg_word + 2)))
qdf_nbuf_set_rx_chfrag_end(rx_desc->nbuf, 1);
if (HTT_RX_DATA_MSDU_INFO_DA_IS_MCBC_GET(*(msg_word + 2)))
qdf_nbuf_set_da_mcbc(rx_desc->nbuf, 1);
if (HTT_RX_DATA_MSDU_INFO_DA_IS_VALID_GET(*(msg_word + 2)))
qdf_nbuf_set_da_valid(rx_desc->nbuf, 1);
if (HTT_RX_DATA_MSDU_INFO_SA_IS_VALID_GET(*(msg_word + 2)))
qdf_nbuf_set_sa_valid(rx_desc->nbuf, 1);
qdf_nbuf_set_tid_val(rx_desc->nbuf,
HTT_RX_DATA_MSDU_INFO_TID_INFO_GET(*(msg_word + 2)));
/*
* TODO add REO destination indication value in HTT
* set reo dest indication
* qdf_nbuf_set_rx_reo_dest_ind_or_sw_excpt(
* rx_desc->nbuf,
* HAL_RX_REO_MSDU_REO_DST_IND_GET(ring_desc));
*/
QDF_NBUF_CB_RX_PKT_LEN(rx_desc->nbuf) = msdu_len;
QDF_NBUF_CB_RX_CTX_ID(rx_desc->nbuf) = rx_ctx_id;
/*
* TODO move unmap after scattered msdu waiting break logic
* in case double skb unmap happened.
*/
dp_rx_nbuf_unmap(soc, rx_desc, rx_ctx_id);
rx_desc->unmapped = 1;
DP_RX_PROCESS_NBUF(soc, nbuf_head, nbuf_tail, ebuf_head,
ebuf_tail, rx_desc);
num_pending -= 1;
dp_rx_add_to_free_desc_list(&head[rx_desc->pool_id],
&tail[rx_desc->pool_id], rx_desc);
num_rx_bufs_reaped++;
msg_word += HTT_RX_DATA_MSDU_INFO_SIZE >> 2;
}
dp_rx_per_core_stats_update(soc, rx_ctx_id, num_rx_bufs_reaped);
for (mac_id = 0; mac_id < MAX_PDEV_CNT; mac_id++) {
/*
* continue with next mac_id if no pkts were reaped
* from that pool
*/
if (!rx_bufs_reaped[mac_id])
continue;
dp_rxdma_srng = &soc->rx_refill_buf_ring[mac_id];
rx_desc_pool = &soc->rx_desc_buf[mac_id];
dp_rx_buffers_replenish_simple(soc, mac_id, dp_rxdma_srng,
rx_desc_pool,
rx_bufs_reaped[mac_id],
&head[mac_id], &tail[mac_id]);
}
dp_verbose_debug("replenished %u\n", rx_bufs_reaped[0]);
/* Peer can be NULL is case of LFR */
if (qdf_likely(txrx_peer))
vdev = NULL;
/*
* BIG loop where each nbuf is dequeued from global queue,
* processed and queued back on a per vdev basis. These nbufs
* are sent to stack as and when we run out of nbufs
* or a new nbuf dequeued from global queue has a different
* vdev when compared to previous nbuf.
*/
nbuf = nbuf_head;
while (nbuf) {
next = nbuf->next;
if (qdf_unlikely(dp_rx_is_raw_frame_dropped(nbuf))) {
nbuf = next;
DP_STATS_INC(soc, rx.err.raw_frm_drop, 1);
continue;
}
rx_tlv_hdr = qdf_nbuf_data(nbuf);
vdev_id = QDF_NBUF_CB_RX_VDEV_ID(nbuf);
peer_id = QDF_NBUF_CB_RX_PEER_ID(nbuf);
/* Get TID from struct cb->tid_val, save to tid */
if (qdf_nbuf_is_rx_chfrag_start(nbuf)) {
tid = qdf_nbuf_get_tid_val(nbuf);
if (tid >= CDP_MAX_DATA_TIDS) {
DP_STATS_INC(soc, rx.err.rx_invalid_tid_err, 1);
dp_rx_nbuf_free(nbuf);
nbuf = next;
continue;
}
}
if (qdf_unlikely(!txrx_peer)) {
txrx_peer =
dp_rx_get_txrx_peer_and_vdev(soc, nbuf, peer_id,
&txrx_ref_handle,
pkt_capture_offload,
&vdev,
&rx_pdev, &dsf,
&old_tid);
if (qdf_unlikely(!txrx_peer) || qdf_unlikely(!vdev)) {
nbuf = next;
continue;
}
} else if (txrx_peer && txrx_peer->peer_id != peer_id) {
dp_txrx_peer_unref_delete(txrx_ref_handle,
DP_MOD_ID_RX);
txrx_peer =
dp_rx_get_txrx_peer_and_vdev(soc, nbuf, peer_id,
&txrx_ref_handle,
pkt_capture_offload,
&vdev,
&rx_pdev, &dsf,
&old_tid);
if (qdf_unlikely(!txrx_peer) || qdf_unlikely(!vdev)) {
nbuf = next;
continue;
}
}
if (txrx_peer) {
QDF_NBUF_CB_DP_TRACE_PRINT(nbuf) = false;
qdf_dp_trace_set_track(nbuf, QDF_RX);
QDF_NBUF_CB_RX_DP_TRACE(nbuf) = 1;
QDF_NBUF_CB_RX_PACKET_TRACK(nbuf) =
QDF_NBUF_RX_PKT_DATA_TRACK;
}
/* when hlos tid override is enabled, save tid in
* skb->priority
*/
if (qdf_unlikely(vdev->skip_sw_tid_classification &
DP_TXRX_HLOS_TID_OVERRIDE_ENABLED))
qdf_nbuf_set_priority(nbuf, tid);
DP_RX_TID_SAVE(nbuf, tid);
if (qdf_unlikely(dsf) || qdf_unlikely(peer_ext_stats) ||
dp_rx_pkt_tracepoints_enabled())
qdf_nbuf_set_timestamp(nbuf);
if (qdf_likely(old_tid != tid)) {
tid_stats =
&rx_pdev->stats.tid_stats.tid_rx_stats[rx_ctx_id][tid];
old_tid = tid;
}
/*
* Check if DMA completed -- msdu_done is the last bit
* to be written
*/
if (qdf_likely(!qdf_nbuf_is_rx_chfrag_cont(nbuf))) {
if (qdf_unlikely(!hal_rx_attn_msdu_done_get_rh(
rx_tlv_hdr))) {
dp_err_rl("MSDU DONE failure");
DP_STATS_INC(soc, rx.err.msdu_done_fail, 1);
hal_rx_dump_pkt_tlvs(hal_soc, rx_tlv_hdr,
QDF_TRACE_LEVEL_INFO);
tid_stats->fail_cnt[MSDU_DONE_FAILURE]++;
qdf_assert(0);
dp_rx_nbuf_free(nbuf);
nbuf = next;
continue;
} else if (qdf_unlikely(hal_rx_attn_msdu_len_err_get_rh(
rx_tlv_hdr))) {
DP_STATS_INC(soc, rx.err.msdu_len_err, 1);
dp_rx_nbuf_free(nbuf);
nbuf = next;
continue;
}
}
DP_HIST_PACKET_COUNT_INC(vdev->pdev->pdev_id);
/*
* First IF condition:
* This condition is valid when 802.11 fragemented
* pkts reinjected back, even though this case is
* not valid for Rhine keeping it for sanity, verify
* and remove this first if condition based on test.
* Second IF condition:
* The below condition happens when an MSDU is spread
* across multiple buffers. This can happen in two cases
* 1. The nbuf size is smaller then the received msdu.
* ex: we have set the nbuf size to 2048 during
* nbuf_alloc. but we received an msdu which is
* 2304 bytes in size then this msdu is spread
* across 2 nbufs.
*
* 2. AMSDUs when RAW mode is enabled.
* ex: 1st MSDU is in 1st nbuf and 2nd MSDU is spread
* across 1st nbuf and 2nd nbuf and last MSDU is
* spread across 2nd nbuf and 3rd nbuf.
*
* for these scenarios let us create a skb frag_list and
* append these buffers till the last MSDU of the AMSDU
* Third condition:
* This is the most likely case, we receive 802.3 pkts
* decapsulated by HW, here we need to set the pkt length.
*/
hal_rx_msdu_metadata_get(hal_soc, rx_tlv_hdr, &msdu_metadata);
if (qdf_unlikely(qdf_nbuf_is_frag(nbuf))) {
bool is_mcbc, is_sa_vld, is_da_vld;
is_mcbc = hal_rx_msdu_end_da_is_mcbc_get(soc->hal_soc,
rx_tlv_hdr);
is_sa_vld =
hal_rx_msdu_end_sa_is_valid_get(soc->hal_soc,
rx_tlv_hdr);
is_da_vld =
hal_rx_msdu_end_da_is_valid_get(soc->hal_soc,
rx_tlv_hdr);
qdf_nbuf_set_da_mcbc(nbuf, is_mcbc);
qdf_nbuf_set_da_valid(nbuf, is_da_vld);
qdf_nbuf_set_sa_valid(nbuf, is_sa_vld);
qdf_nbuf_pull_head(nbuf, soc->rx_pkt_tlv_size);
} else if (qdf_nbuf_is_rx_chfrag_cont(nbuf)) {
msdu_len = QDF_NBUF_CB_RX_PKT_LEN(nbuf);
nbuf = dp_rx_sg_create(soc, nbuf);
next = nbuf->next;
if (qdf_nbuf_is_raw_frame(nbuf)) {
DP_STATS_INC(vdev->pdev, rx_raw_pkts, 1);
DP_PEER_PER_PKT_STATS_INC_PKT(txrx_peer,
rx.raw, 1,
msdu_len);
} else {
dp_rx_nbuf_free(nbuf);
DP_STATS_INC(soc, rx.err.scatter_msdu, 1);
dp_info_rl("scatter msdu len %d, dropped",
msdu_len);
nbuf = next;
continue;
}
} else {
msdu_len = QDF_NBUF_CB_RX_PKT_LEN(nbuf);
pkt_len = msdu_len +
msdu_metadata.l3_hdr_pad +
soc->rx_pkt_tlv_size;
qdf_nbuf_set_pktlen(nbuf, pkt_len);
dp_rx_skip_tlvs(soc, nbuf, msdu_metadata.l3_hdr_pad);
}
dp_rx_send_pktlog(soc, rx_pdev, nbuf, QDF_TX_RX_STATUS_OK);
if (!dp_wds_rx_policy_check(rx_tlv_hdr, vdev, txrx_peer)) {
dp_rx_err("%pK: Policy Check Drop pkt", soc);
DP_PEER_PER_PKT_STATS_INC(txrx_peer,
rx.policy_check_drop, 1);
tid_stats->fail_cnt[POLICY_CHECK_DROP]++;
/* Drop & free packet */
dp_rx_nbuf_free(nbuf);
/* Statistics */
nbuf = next;
continue;
}
/*
* Drop non-EAPOL frames from unauthorized peer.
*/
if (qdf_likely(txrx_peer) &&
qdf_unlikely(!txrx_peer->authorize) &&
!qdf_nbuf_is_raw_frame(nbuf)) {
bool is_eapol = qdf_nbuf_is_ipv4_eapol_pkt(nbuf) ||
qdf_nbuf_is_ipv4_wapi_pkt(nbuf);
if (!is_eapol) {
DP_PEER_PER_PKT_STATS_INC(txrx_peer,
rx.peer_unauth_rx_pkt_drop,
1);
dp_rx_nbuf_free(nbuf);
nbuf = next;
continue;
}
}
if (soc->process_rx_status)
dp_rx_cksum_offload(vdev->pdev, nbuf, rx_tlv_hdr);
dp_rx_msdu_stats_update(soc, nbuf, rx_tlv_hdr, txrx_peer,
rx_ctx_id, tid_stats);
if (qdf_likely(vdev->rx_decap_type ==
htt_cmn_pkt_type_ethernet)) {
/* Due to HW issue, sometimes we see that the sa_idx
* and da_idx are invalid with sa_valid and da_valid
* bits set
*
* in this case we also see that value of
* sa_sw_peer_id is set as 0
*
* Drop the packet if sa_idx and da_idx OOB or
* sa_sw_peerid is 0
*/
if (!is_sa_da_idx_valid(max_ast, nbuf,
msdu_metadata)) {
dp_rx_nbuf_free(nbuf);
nbuf = next;
DP_STATS_INC(soc, rx.err.invalid_sa_da_idx, 1);
continue;
}
if (qdf_unlikely(dp_rx_mec_check_wrapper(soc,
txrx_peer,
rx_tlv_hdr,
nbuf))) {
/* this is a looped back MCBC pkt,drop it */
DP_PEER_PER_PKT_STATS_INC_PKT(txrx_peer,
rx.mec_drop, 1,
QDF_NBUF_CB_RX_PKT_LEN(nbuf));
dp_rx_nbuf_free(nbuf);
nbuf = next;
continue;
}
/* WDS Source Port Learning */
if (qdf_likely(vdev->wds_enabled))
dp_rx_wds_srcport_learn(soc,
rx_tlv_hdr,
txrx_peer,
nbuf,
msdu_metadata);
/* Intrabss-fwd */
if (dp_rx_check_ap_bridge(vdev))
if (dp_rx_intrabss_fwd_rh(soc, txrx_peer,
rx_tlv_hdr,
nbuf,
msdu_metadata,
tid_stats)) {
nbuf = next;
tid_stats->intrabss_cnt++;
continue; /* Get next desc */
}
}
dp_rx_fill_gro_info(soc, rx_tlv_hdr, nbuf, &rx_ol_pkt_cnt);
/*
* TODO mark first packet after wow get this from HTT desc
* dp_rx_mark_first_packet_after_wow_wakeup(vdev->pdev,
* rx_tlv_hdr, nbuf);
*/
dp_rx_update_stats(soc, nbuf);
dp_pkt_add_timestamp(txrx_peer->vdev, QDF_PKT_RX_DRIVER_ENTRY,
current_time, nbuf);
DP_RX_LIST_APPEND(deliver_list_head,
deliver_list_tail,
nbuf);
DP_PEER_STATS_FLAT_INC_PKT(txrx_peer, to_stack, 1,
QDF_NBUF_CB_RX_PKT_LEN(nbuf));
if (qdf_unlikely(txrx_peer->in_twt))
DP_PEER_PER_PKT_STATS_INC_PKT(txrx_peer,
rx.to_stack_twt, 1,
QDF_NBUF_CB_RX_PKT_LEN(nbuf));
tid_stats->delivered_to_stack++;
nbuf = next;
}
DP_RX_DELIVER_TO_STACK(soc, vdev, txrx_peer, peer_id,
pkt_capture_offload,
deliver_list_head,
deliver_list_tail);
if (qdf_likely(txrx_peer))
dp_txrx_peer_unref_delete(txrx_ref_handle, DP_MOD_ID_RX);
if (vdev && vdev->osif_fisa_flush)
vdev->osif_fisa_flush(soc, rx_ctx_id);
if (vdev && vdev->osif_gro_flush && rx_ol_pkt_cnt) {
vdev->osif_gro_flush(vdev->osif_vdev,
rx_ctx_id);
}
/* Update histogram statistics by looping through pdev's */
DP_RX_HIST_STATS_PER_PDEV();
}
/*
* dp_rx_defrag_deliver_rh(): Deliver defrag packet to stack
* @peer: Pointer to the peer
* @tid: Transmit Identifier
* @head: Nbuf to be delivered
*
* Returns: None
*/
static inline void dp_rx_defrag_deliver_rh(struct dp_txrx_peer *txrx_peer,
unsigned int tid,
qdf_nbuf_t head)
{
struct dp_vdev *vdev = txrx_peer->vdev;
struct dp_soc *soc = vdev->pdev->soc;
qdf_nbuf_t deliver_list_head = NULL;
qdf_nbuf_t deliver_list_tail = NULL;
uint8_t *rx_tlv_hdr;
rx_tlv_hdr = qdf_nbuf_data(head);
QDF_NBUF_CB_RX_VDEV_ID(head) = vdev->vdev_id;
qdf_nbuf_set_tid_val(head, tid);
qdf_nbuf_pull_head(head, soc->rx_pkt_tlv_size);
DP_RX_LIST_APPEND(deliver_list_head, deliver_list_tail,
head);
dp_rx_deliver_to_stack(soc, vdev, txrx_peer, deliver_list_head,
deliver_list_tail);
}
static
QDF_STATUS dp_rx_defrag_store_fragment_rh(struct dp_soc *soc, qdf_nbuf_t frag)
{
struct dp_rx_reorder_array_elem *rx_reorder_array_elem;
struct dp_pdev *pdev;
struct dp_txrx_peer *txrx_peer = NULL;
dp_txrx_ref_handle txrx_ref_handle = NULL;
uint16_t peer_id, tid;
uint8_t fragno, more_frag, all_frag_present = 0;
uint16_t rxseq;
QDF_STATUS status;
struct dp_rx_tid_defrag *rx_tid;
uint8_t mpdu_sequence_control_valid;
uint8_t mpdu_frame_control_valid;
uint8_t *rx_buf_start = qdf_nbuf_data(frag);
uint32_t msdu_len;
if (qdf_nbuf_len(frag) > 0) {
dp_rx_info("Dropping unexpected packet with skb_len: %d, data len: %d",
(uint32_t)qdf_nbuf_len(frag), frag->data_len);
DP_STATS_INC(soc, rx.rx_frag_err_len_error, 1);
goto discard_frag;
}
msdu_len = QDF_NBUF_CB_RX_PKT_LEN(frag);
qdf_nbuf_set_pktlen(frag, (msdu_len + soc->rx_pkt_tlv_size));
qdf_nbuf_append_ext_list(frag, NULL, 0);
/* Check if the packet is from a valid peer */
peer_id = QDF_NBUF_CB_RX_PEER_ID(frag);
txrx_peer = dp_txrx_peer_get_ref_by_id(soc, peer_id, &txrx_ref_handle,
DP_MOD_ID_RX);
if (!txrx_peer) {
/* We should not receive anything from unknown peer
* however, that might happen while we are in the monitor mode.
* We don't need to handle that here
*/
dp_rx_info_rl("Unknown peer with peer_id %d, dropping fragment",
peer_id);
DP_STATS_INC(soc, rx.rx_frag_err_no_peer, 1);
goto discard_frag;
}
tid = qdf_nbuf_get_tid_val(frag);
if (tid >= DP_MAX_TIDS) {
dp_rx_info("TID out of bounds: %d", tid);
qdf_assert_always(0);
goto discard_frag;
}
mpdu_sequence_control_valid =
hal_rx_get_mpdu_sequence_control_valid(soc->hal_soc,
rx_buf_start);
/* Invalid MPDU sequence control field, MPDU is of no use */
if (!mpdu_sequence_control_valid) {
dp_rx_err("Invalid MPDU seq control field, dropping MPDU");
qdf_assert(0);
goto discard_frag;
}
mpdu_frame_control_valid =
hal_rx_get_mpdu_frame_control_valid(soc->hal_soc,
rx_buf_start);
/* Invalid frame control field */
if (!mpdu_frame_control_valid) {
dp_rx_err("Invalid frame control field, dropping MPDU");
qdf_assert(0);
goto discard_frag;
}
/* Current mpdu sequence */
more_frag = dp_rx_frag_get_more_frag_bit(soc, rx_buf_start);
/* HW does not populate the fragment number as of now
* need to get from the 802.11 header
*/
fragno = dp_rx_frag_get_mpdu_frag_number(soc, rx_buf_start);
rxseq = dp_rx_frag_get_mpdu_seq_number(soc, rx_buf_start);
pdev = txrx_peer->vdev->pdev;
rx_tid = &txrx_peer->rx_tid[tid];
qdf_spin_lock_bh(&rx_tid->defrag_tid_lock);
rx_reorder_array_elem = txrx_peer->rx_tid[tid].array;
if (!rx_reorder_array_elem) {
dp_err_rl("Rcvd Fragmented pkt before tid setup for peer %pK",
txrx_peer);
qdf_spin_unlock_bh(&rx_tid->defrag_tid_lock);
goto discard_frag;
}
/*
* !more_frag: no more fragments to be delivered
* !frag_no: packet is not fragmented
* !rx_reorder_array_elem->head: no saved fragments so far
*/
if (!more_frag && !fragno && !rx_reorder_array_elem->head) {
/* We should not get into this situation here.
* It means an unfragmented packet with fragment flag
* is delivered over frag indication.
* Typically it follows normal rx path.
*/
dp_rx_err("Rcvd unfragmented pkt on fragmented path, dropping");
qdf_spin_unlock_bh(&rx_tid->defrag_tid_lock);
qdf_assert(0);
goto discard_frag;
}
/* Check if the fragment is for the same sequence or a different one */
dp_rx_debug("rx_tid %d", tid);
if (rx_reorder_array_elem->head) {
dp_rx_debug("rxseq %d\n", rxseq);
if (rxseq != rx_tid->curr_seq_num) {
dp_rx_debug("mismatch cur_seq %d rxseq %d\n",
rx_tid->curr_seq_num, rxseq);
/* Drop stored fragments if out of sequence
* fragment is received
*/
dp_rx_reorder_flush_frag(txrx_peer, tid);
DP_STATS_INC(soc, rx.rx_frag_oor, 1);
dp_rx_debug("cur rxseq %d\n", rxseq);
/*
* The sequence number for this fragment becomes the
* new sequence number to be processed
*/
rx_tid->curr_seq_num = rxseq;
}
} else {
/* Check if we are processing first fragment if it is
* not first fragment discard fragment.
*/
if (fragno) {
qdf_spin_unlock_bh(&rx_tid->defrag_tid_lock);
goto discard_frag;
}
dp_rx_debug("cur rxseq %d\n", rxseq);
/* Start of a new sequence */
dp_rx_defrag_cleanup(txrx_peer, tid);
rx_tid->curr_seq_num = rxseq;
}
/*
* If the earlier sequence was dropped, this will be the fresh start.
* Else, continue with next fragment in a given sequence
*/
status = dp_rx_defrag_fraglist_insert(txrx_peer, tid,
&rx_reorder_array_elem->head,
&rx_reorder_array_elem->tail,
frag, &all_frag_present);
if (pdev->soc->rx.flags.defrag_timeout_check)
dp_rx_defrag_waitlist_remove(txrx_peer, tid);
/* Yet to receive more fragments for this sequence number */
if (!all_frag_present) {
uint32_t now_ms =
qdf_system_ticks_to_msecs(qdf_system_ticks());
txrx_peer->rx_tid[tid].defrag_timeout_ms =
now_ms + pdev->soc->rx.defrag.timeout_ms;
if (pdev->soc->rx.flags.defrag_timeout_check)
dp_rx_defrag_waitlist_add(txrx_peer, tid);
dp_txrx_peer_unref_delete(txrx_ref_handle, DP_MOD_ID_RX_ERR);
qdf_spin_unlock_bh(&rx_tid->defrag_tid_lock);
return QDF_STATUS_SUCCESS;
}
dp_rx_debug("All fragments received for sequence: %d", rxseq);
/* Process the fragments */
status = dp_rx_defrag(txrx_peer, tid, rx_reorder_array_elem->head,
rx_reorder_array_elem->tail);
if (QDF_IS_STATUS_ERROR(status)) {
dp_rx_err("Fragment processing failed");
dp_rx_defrag_cleanup(txrx_peer, tid);
qdf_spin_unlock_bh(&rx_tid->defrag_tid_lock);
goto end;
}
dp_rx_defrag_deliver_rh(txrx_peer, tid, rx_reorder_array_elem->head);
dp_rx_debug("Fragmented sequence successfully reinjected");
dp_rx_defrag_cleanup(txrx_peer, tid);
qdf_spin_unlock_bh(&rx_tid->defrag_tid_lock);
dp_txrx_peer_unref_delete(txrx_ref_handle, DP_MOD_ID_RX_ERR);
return QDF_STATUS_SUCCESS;
discard_frag:
dp_rx_nbuf_free(frag);
end:
if (txrx_peer)
dp_txrx_peer_unref_delete(txrx_ref_handle, DP_MOD_ID_RX_ERR);
DP_STATS_INC(soc, rx.rx_frag_err, 1);
return QDF_STATUS_E_DEFRAG_ERROR;
}
void
dp_rx_frag_indication_handler(struct dp_soc *soc, qdf_nbuf_t data_ind,
uint16_t vdev_id, uint16_t peer_id)
{
uint8_t *data_ind_msg;
uint32_t *msg_word;
uint32_t rx_ctx_id;
qdf_nbuf_t nbuf;
union dp_rx_desc_list_elem_t *head = NULL;
union dp_rx_desc_list_elem_t *tail = NULL;
QDF_STATUS status = QDF_STATUS_SUCCESS;
uint32_t rx_buf_cookie;
struct dp_rx_desc *rx_desc;
uint8_t mac_id = 0;
qdf_assert(soc);
data_ind_msg = qdf_nbuf_data(data_ind);
msg_word =
(uint32_t *)(data_ind_msg + HTT_RX_DATA_IND_HDR_SIZE);
rx_ctx_id =
dp_rx_get_ctx_id_frm_napiid(QDF_NBUF_CB_RX_CTX_ID(data_ind));
rx_buf_cookie =
HTT_RX_DATA_MSDU_INFO_SW_BUFFER_COOKIE_GET(*(msg_word + 1));
rx_desc = dp_rx_cookie_2_va_rxdma_buf(soc, rx_buf_cookie);
if (qdf_unlikely(!rx_desc && !rx_desc->nbuf &&
!rx_desc->in_use)) {
dp_rx_err("Invalid RX descriptor");
qdf_assert_always(0);
/* TODO handle this if its valid case */
}
if (qdf_unlikely(!dp_rx_desc_check_magic(rx_desc))) {
dp_err("Invalid rx_desc cookie=%d", rx_buf_cookie);
DP_STATS_INC(soc, rx.err.rx_desc_invalid_magic, 1);
qdf_assert(0);
}
nbuf = rx_desc->nbuf;
QDF_NBUF_CB_RX_PKT_LEN(nbuf) =
HTT_RX_DATA_MSDU_INFO_MSDU_LENGTH_GET(*(msg_word + 2));
qdf_nbuf_set_tid_val(nbuf, HTT_RX_DATA_MSDU_INFO_TID_INFO_GET(*(msg_word + 2)));
QDF_NBUF_CB_RX_PEER_ID(nbuf) = peer_id;
QDF_NBUF_CB_RX_VDEV_ID(nbuf) = vdev_id;
QDF_NBUF_CB_RX_CTX_ID(nbuf) = rx_ctx_id;
dp_rx_nbuf_unmap(soc, rx_desc, rx_ctx_id);
rx_desc->unmapped = 1;
dp_rx_add_to_free_desc_list(&head, &tail, rx_desc);
dp_rx_buffers_replenish_simple(soc, rx_desc->pool_id,
&soc->rx_refill_buf_ring[mac_id],
&soc->rx_desc_buf[rx_desc->pool_id],
1, &head, &tail);
if (dp_rx_buffer_pool_refill(soc, nbuf, rx_desc->pool_id))
/* fragment queued back to the pool no frag to handle*/
return;
/* Process fragment-by-fragment */
status = dp_rx_defrag_store_fragment_rh(soc, nbuf);
if (QDF_IS_STATUS_ERROR(status))
dp_rx_err("Unable to handle frag ret:%u", status);
}
QDF_STATUS dp_rx_desc_pool_init_rh(struct dp_soc *soc,
struct rx_desc_pool *rx_desc_pool,
uint32_t pool_id)
{
return dp_rx_desc_pool_init_generic(soc, rx_desc_pool, pool_id);
}
void dp_rx_desc_pool_deinit_rh(struct dp_soc *soc,
struct rx_desc_pool *rx_desc_pool,
uint32_t pool_id)
{
}