diff --git a/Kbuild b/Kbuild index cb306e07f840..2ca06e7f1083 100644 --- a/Kbuild +++ b/Kbuild @@ -1392,6 +1392,12 @@ TXRX3.0_DIR := core/dp/txrx3.0 TXRX3.0_INC := -I$(WLAN_ROOT)/$(TXRX3.0_DIR) TXRX3.0_OBJS := $(TXRX3.0_DIR)/dp_txrx.o \ $(TXRX3.0_DIR)/dp_rx_thread.o + +ifeq ($(CONFIG_RX_FISA), y) +TXRX3.0_OBJS += $(TXRX3.0_DIR)/dp_fisa_rx.o +TXRX3.0_OBJS += $(TXRX3.0_DIR)/dp_rx_fst.o +endif + ifeq ($(CONFIG_LITHIUM), y) ############ DP 3.0 ############ DP_INC := -I$(WLAN_COMMON_INC)/dp/inc \ diff --git a/core/dp/txrx3.0/dp_fisa_rx.c b/core/dp/txrx3.0/dp_fisa_rx.c new file mode 100644 index 000000000000..66e390f9cd4f --- /dev/null +++ b/core/dp/txrx3.0/dp_fisa_rx.c @@ -0,0 +1,660 @@ +/* + * Copyright (c) 2020, The Linux Foundation. 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 +#include +#include "hal_rx_flow.h" +#include "dp_htt.h" +#include "dp_internal.h" +#include +#include + +#if defined(FISA_DEBUG_ENABLE) +/** + * hex_dump_skb_data() - Helper function to dump skb while debugging + * @nbuf: Nbuf to be dumped + * @dump: dump enable/disable dumping + * + * Return: NONE + */ +static void hex_dump_skb_data(qdf_nbuf_t nbuf, bool dump) +{ + qdf_nbuf_t next_nbuf; + int i = 0; + + if (!dump) + return; + + if (!nbuf) + return; + + dp_fisa_debug("%ps: skb: %pk skb->next:%pk frag_list %pk skb->data:%pk len %d data_len%d", + (void *)_RET_IP_, nbuf, qdf_nbuf_next(nbuf), + skb_shinfo(nbuf)->frag_list, qdf_nbuf_data(nbuf), nbuf->len, + nbuf->data_len); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, nbuf->data, + 64); + + next_nbuf = skb_shinfo(nbuf)->frag_list; + while (next_nbuf) { + dp_fisa_debug("%d nbuf:%pk nbuf->next:%pK nbuf->data:%pk len %d", i, + next_nbuf, qdf_nbuf_next(next_nbuf), + qdf_nbuf_data(next_nbuf), qdf_nbuf_len(next_nbuf)); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, + qdf_nbuf_data(next_nbuf), 64); + next_nbuf = qdf_nbuf_next(next_nbuf); + i++; + } +} + +/** + * dump_tlvs() - Helper function to dump TLVs of msdu + * @hal_soc_hdl: Handle to TLV functions + * @buf: Pointer to TLV header + * @dbg_level: level control output of TLV dump + * + * Return: NONE + */ +static void dump_tlvs(hal_soc_handle_t hal_soc_hdl, uint8_t *buf, + uint8_t dbg_level) +{ + uint32_t fisa_aggr_count, fisa_timeout, cumulat_l4_csum, cumulat_ip_len; + int flow_aggr_cont; + + hal_rx_dump_pkt_tlvs(hal_soc_hdl, buf, dbg_level); + + flow_aggr_cont = hal_rx_get_fisa_flow_agg_continuation(hal_soc_hdl, + buf); + fisa_aggr_count = hal_rx_get_fisa_flow_agg_count(hal_soc_hdl, buf); + fisa_timeout = hal_rx_get_fisa_timeout(hal_soc_hdl, buf); + cumulat_l4_csum = hal_rx_get_fisa_cumulative_l4_checksum(hal_soc_hdl, + buf); + cumulat_ip_len = hal_rx_get_fisa_cumulative_ip_length(hal_soc_hdl, buf); + + dp_fisa_debug("flow_aggr_cont %d, fisa_timeout %d, fisa_aggr_count %d, cumulat_l4_csum %d, cumulat_ip_len %d", + flow_aggr_cont, fisa_timeout, fisa_aggr_count, cumulat_l4_csum, + cumulat_ip_len); +} +#else +static void hex_dump_skb_data(qdf_nbuf_t nbuf, bool dump) +{ +} + +static void dump_tlvs(hal_soc_handle_t hal_soc_hdl, uint8_t *buf, + uint8_t dbg_level) +{ +} +#endif + +/** + * nbuf_skip_rx_pkt_tlv() - Function to skip the TLVs and mac header from msdu + * @hal_soc_hdl: Handle to hal_soc to get the TLV info + * @nbuf: msdu for which TLVs has to be skipped + * + * Return: None + */ +static void nbuf_skip_rx_pkt_tlv(hal_soc_handle_t hal_soc_hdl, qdf_nbuf_t nbuf) +{ + uint8_t *rx_tlv_hdr; + uint32_t l2_hdr_offset; + + rx_tlv_hdr = qdf_nbuf_data(nbuf); + l2_hdr_offset = hal_rx_msdu_end_l3_hdr_padding_get(hal_soc_hdl, + rx_tlv_hdr); + qdf_nbuf_pull_head(nbuf, RX_PKT_TLVS_LEN + l2_hdr_offset); +} + +/** + * print_flow_tuple() - Debug function to dump flow tuple + * @flow_tuple: flow tuple containing tuple info + * + * Return: NONE + */ +static void print_flow_tuple(struct cdp_rx_flow_tuple_info *flow_tuple) +{ + dp_info("dest_ip_127_96 0x%x", flow_tuple->dest_ip_127_96); + dp_info("dest_ip_95_64 0x%x", flow_tuple->dest_ip_95_64); + dp_info("dest_ip_63_32 0x%x", flow_tuple->dest_ip_63_32); + dp_info("dest_ip_31_0 0x%x", flow_tuple->dest_ip_31_0); + dp_info("src_ip_127_96 0x%x", flow_tuple->src_ip_127_96); + dp_info("src_ip_95_64 0x%x", flow_tuple->src_ip_95_64); + dp_info("src_ip_63_32 0x%x", flow_tuple->src_ip_63_32); + dp_info("src_ip_31_0 0x%x", flow_tuple->src_ip_31_0); + dp_info("dest_port 0x%x", flow_tuple->dest_port); + dp_info("src_port 0x%x", flow_tuple->src_port); + dp_info("l4_protocol 0x%x", flow_tuple->l4_protocol); +} + +/** + * get_flow_tuple_from_nbuf() - Get the flow tuple from msdu + * @hal_soc_hdl: Handle to hal soc + * @flow_tuple_info: return argument where the flow is populated + * @nbuf: msdu from which flow tuple is extracted. + * @rx_tlv_hdr: Pointer to msdu TLVs + * + * Return: None + */ +static void +get_flow_tuple_from_nbuf(hal_soc_handle_t hal_soc_hdl, + struct cdp_rx_flow_tuple_info *flow_tuple_info, + qdf_nbuf_t nbuf, uint8_t *rx_tlv_hdr) +{ + struct iphdr *iph; + struct tcphdr *tcph; + uint32_t ip_hdr_offset = HAL_RX_TLV_GET_IP_OFFSET(rx_tlv_hdr); + uint32_t tcp_hdr_offset = HAL_RX_TLV_GET_TCP_OFFSET(rx_tlv_hdr); + uint32_t l2_hdr_offset = hal_rx_msdu_end_l3_hdr_padding_get(hal_soc_hdl, + rx_tlv_hdr); + + flow_tuple_info->tuple_populated = true; + + qdf_nbuf_pull_head(nbuf, RX_PKT_TLVS_LEN + l2_hdr_offset); + + iph = (struct iphdr *)(qdf_nbuf_data(nbuf) + ip_hdr_offset); + tcph = (struct tcphdr *)(qdf_nbuf_data(nbuf) + ip_hdr_offset + + tcp_hdr_offset); + + flow_tuple_info->dest_ip_31_0 = qdf_ntohl(iph->daddr); + flow_tuple_info->dest_ip_63_32 = 0; + flow_tuple_info->dest_ip_95_64 = 0; + flow_tuple_info->dest_ip_127_96 = + HAL_IP_DA_SA_PREFIX_IPV4_COMPATIBLE_IPV6; + + flow_tuple_info->src_ip_31_0 = qdf_ntohl(iph->saddr); + flow_tuple_info->src_ip_63_32 = 0; + flow_tuple_info->src_ip_95_64 = 0; + flow_tuple_info->src_ip_127_96 = + HAL_IP_DA_SA_PREFIX_IPV4_COMPATIBLE_IPV6; + + flow_tuple_info->dest_port = qdf_ntohs(tcph->dest); + flow_tuple_info->src_port = qdf_ntohs(tcph->source); + flow_tuple_info->l4_protocol = iph->protocol; + dp_fisa_debug("l4_protocol %d", flow_tuple_info->l4_protocol); + + qdf_nbuf_push_head(nbuf, RX_PKT_TLVS_LEN + l2_hdr_offset); + + dp_fisa_debug("head_skb: %pk head_skb->next:%pk head_skb->data:%pk len %d data_len", + nbuf, qdf_nbuf_next(nbuf), qdf_nbuf_data(nbuf), nbuf->len, + nbuf->data_len); +} + +/** + * dp_rx_fisa_setup_hw_fse() - Populate flow so as to update DDR flow table + * @fisa_hdl: Handle fisa context + * @hashed_flow_idx: Index to flow table + * @rx_flow_info: tuple to be populated in flow table + * @flow_steer_info: REO index to which flow to be steered + * + * Return: Pointer to DDR flow table entry + */ +static void * +dp_rx_fisa_setup_hw_fse(struct dp_rx_fst *fisa_hdl, + uint32_t hashed_flow_idx, + struct cdp_rx_flow_tuple_info *rx_flow_info, + uint32_t flow_steer_info) +{ + struct hal_rx_flow flow; + void *hw_fse; + + /* REO destination index starts from 1 */ + flow.reo_destination_indication = flow_steer_info + 1; + flow.fse_metadata = 0xDEADBEEF; + flow.tuple_info.dest_ip_127_96 = rx_flow_info->dest_ip_127_96; + flow.tuple_info.dest_ip_95_64 = rx_flow_info->dest_ip_95_64; + flow.tuple_info.dest_ip_63_32 = rx_flow_info->dest_ip_63_32; + flow.tuple_info.dest_ip_31_0 = rx_flow_info->dest_ip_31_0; + flow.tuple_info.src_ip_127_96 = rx_flow_info->src_ip_127_96; + flow.tuple_info.src_ip_95_64 = rx_flow_info->src_ip_95_64; + flow.tuple_info.src_ip_63_32 = rx_flow_info->src_ip_63_32; + flow.tuple_info.src_ip_31_0 = rx_flow_info->src_ip_31_0; + flow.tuple_info.dest_port = rx_flow_info->dest_port; + flow.tuple_info.src_port = rx_flow_info->src_port; + flow.tuple_info.l4_protocol = rx_flow_info->l4_protocol; + flow.reo_destination_handler = HAL_RX_FSE_REO_DEST_FT; + hw_fse = hal_rx_flow_setup_fse(fisa_hdl->hal_rx_fst, hashed_flow_idx, + &flow); + dp_rx_dump_fisa_table(fisa_hdl->soc_hdl); + + return hw_fse; +} + +/** + * dp_rx_fisa_update_sw_ft_entry() - Helper function to update few SW FT entry + * @sw_ft_entry: Pointer to softerware flow tabel entry + * @flow_hash: flow_hash for the flow + * @vdev: Saving dp_vdev in FT later used in the flushing the flow + * @flow_id: Flow ID of the flow + * + * Return: NONE + */ +static void dp_rx_fisa_update_sw_ft_entry(struct dp_fisa_rx_sw_ft *sw_ft_entry, + uint32_t flow_hash, + struct dp_vdev *vdev, + uint32_t flow_id) +{ + sw_ft_entry->flow_hash = flow_hash; + sw_ft_entry->flow_id = flow_id; + sw_ft_entry->vdev = vdev; +} + +/** + * is_same_flow() - Function to compare flow tuple to decide if they match + * @tuple1: flow tuple 1 + * @tuple2: flow tuple 2 + * + * Return: true if they match, false if they differ + */ +static bool is_same_flow(struct cdp_rx_flow_tuple_info *tuple1, + struct cdp_rx_flow_tuple_info *tuple2) +{ + if ((tuple1->src_port ^ tuple2->src_port) | + (tuple1->dest_port ^ tuple2->dest_port) | + (tuple1->src_ip_31_0 ^ tuple2->src_ip_31_0) | + (tuple1->src_ip_63_32 ^ tuple2->src_ip_63_32) | + (tuple1->src_ip_95_64 ^ tuple2->src_ip_95_64) | + (tuple1->src_ip_127_96 ^ tuple2->src_ip_127_96) | + (tuple1->dest_ip_31_0 ^ tuple2->dest_ip_31_0) | + /* DST IP check not required? */ + (tuple1->dest_ip_63_32 ^ tuple2->dest_ip_63_32) | + (tuple1->dest_ip_95_64 ^ tuple2->dest_ip_95_64) | + (tuple1->dest_ip_127_96 ^ tuple2->dest_ip_127_96) | + (tuple1->l4_protocol ^ tuple2->l4_protocol)) + return false; + else + return true; +} + +/** + * dp_rx_flow_send_htt_operation_cmd() - Invalidate FSE cache on FT change + * @pdev: handle to DP pdev + * @fse_op: Cache operation code + * @rx_flow_tuple: flow tuple whose entry has to be invalidated + * + * Return: Success if we successfully send FW HTT command + */ +static QDF_STATUS +dp_rx_flow_send_htt_operation_cmd(struct dp_pdev *pdev, + enum dp_htt_flow_fst_operation fse_op, + struct cdp_rx_flow_tuple_info *rx_flow_tuple) +{ + struct dp_htt_rx_flow_fst_operation fse_op_cmd; + struct cdp_rx_flow_info rx_flow_info; + + rx_flow_info.is_addr_ipv4 = true; + rx_flow_info.op_code = CDP_FLOW_FST_ENTRY_ADD; + qdf_mem_copy(&rx_flow_info.flow_tuple_info, rx_flow_tuple, + sizeof(struct cdp_rx_flow_tuple_info)); + rx_flow_info.fse_metadata = 0xDADA; + fse_op_cmd.pdev_id = pdev->pdev_id; + fse_op_cmd.op_code = fse_op; + fse_op_cmd.rx_flow = &rx_flow_info; + + return dp_htt_rx_flow_fse_operation(pdev, &fse_op_cmd); +} + +/** + * dp_rx_fisa_add_ft_entry() - Add new flow to HW and SW FT if it is not added + * @fisa_hdl: handle to FISA context + * @flow_idx_hash: Hashed flow index + * @nbuf: nbuf belonging to new flow + * @vdev: Handle DP vdev to save in SW flow table + * @rx_tlv_hdr: Pointer to TLV header + * + * Return: pointer to sw FT entry on success, NULL otherwise + */ +static struct dp_fisa_rx_sw_ft * +dp_rx_fisa_add_ft_entry(struct dp_rx_fst *fisa_hdl, + uint32_t flow_idx_hash, + qdf_nbuf_t nbuf, struct dp_vdev *vdev, + uint8_t *rx_tlv_hdr) +{ + struct dp_fisa_rx_sw_ft *sw_ft_entry; + uint32_t flow_hash; + uint32_t hashed_flow_idx; + uint32_t skid_count = 0, max_skid_length; + struct cdp_rx_flow_tuple_info rx_flow_tuple_info; + QDF_STATUS status; + bool is_fst_updated = false; + bool is_flow_tcp, is_flow_udp, is_flow_ipv6; + hal_soc_handle_t hal_soc_hdl = fisa_hdl->soc_hdl->hal_soc; + uint32_t reo_id = QDF_NBUF_CB_RX_CTX_ID(nbuf); + + is_flow_tcp = HAL_RX_TLV_GET_TCP_PROTO(rx_tlv_hdr); + is_flow_udp = HAL_RX_TLV_GET_UDP_PROTO(rx_tlv_hdr); + is_flow_ipv6 = HAL_RX_TLV_GET_IPV6(rx_tlv_hdr); + + if (is_flow_ipv6 || !(is_flow_tcp || is_flow_udp)) { + dp_fisa_debug("Not UDP or TCP IPV4 flow"); + return NULL; + } + + /* Get the hash from TLV + * FSE FT Toeplitz hash is same Common parser hash available in TLV + * common parser toeplitz hash is same as FSE toeplitz hash as + * toeplitz key is same. + */ + rx_flow_tuple_info.tuple_populated = false; + flow_hash = flow_idx_hash; + hashed_flow_idx = flow_hash & fisa_hdl->hash_mask; + max_skid_length = fisa_hdl->max_skid_length; + + dp_fisa_debug("flow_hash 0x%x hashed_flow_idx 0x%x", flow_hash, + hashed_flow_idx); + dp_fisa_debug("max_skid_length 0x%x", max_skid_length); + qdf_spin_lock_bh(&fisa_hdl->dp_rx_fst_lock); + do { + sw_ft_entry = &(((struct dp_fisa_rx_sw_ft *) + fisa_hdl->base)[hashed_flow_idx]); + if (!sw_ft_entry->is_populated) { + /* Add SW FT entry */ + dp_rx_fisa_update_sw_ft_entry(sw_ft_entry, + flow_hash, vdev, + hashed_flow_idx); + if (!rx_flow_tuple_info.tuple_populated) + get_flow_tuple_from_nbuf(hal_soc_hdl, + &rx_flow_tuple_info, + nbuf, rx_tlv_hdr); + + /* Add HW FT entry */ + sw_ft_entry->hw_fse = + dp_rx_fisa_setup_hw_fse(fisa_hdl, + hashed_flow_idx, + &rx_flow_tuple_info, + reo_id); + sw_ft_entry->is_populated = true; + sw_ft_entry->napi_id = reo_id; + qdf_mem_copy(&sw_ft_entry->rx_flow_tuple_info, + &rx_flow_tuple_info, + sizeof(struct cdp_rx_flow_tuple_info)); + + sw_ft_entry->is_flow_tcp = is_flow_tcp; + sw_ft_entry->is_flow_udp = is_flow_udp; + + is_fst_updated = true; + fisa_hdl->add_flow_count++; + break; + } + /* else */ + if (!rx_flow_tuple_info.tuple_populated) + get_flow_tuple_from_nbuf(hal_soc_hdl, + &rx_flow_tuple_info, + nbuf, rx_tlv_hdr); + + if (is_same_flow(&sw_ft_entry->rx_flow_tuple_info, + &rx_flow_tuple_info)) { + dp_fisa_debug("It is same flow fse entry idx %d", + hashed_flow_idx); + /* Incoming flow tuple matches with existing + * entry. This is subsequent skbs of the same + * flow. Earlier entry made is not reflected + * yet in FSE cache + */ + break; + } + /* else */ + /* hash collision move to the next FT entry */ + dp_fisa_debug("Hash collision %d", fisa_hdl->hash_collision_cnt); + fisa_hdl->hash_collision_cnt++; +#ifdef NOT_YET /* assist Flow eviction algorithm */ + /* uint32_t lru_ft_entry_time = 0xffffffff, lru_ft_entry_idx = 0; */ + if (fisa_hdl->hw_ft_entry->timestamp < lru_ft_entry_time) { + lru_ft_entry_time = fisa_hdl->hw_ft_entry->timestamp; + lru_ft_entry_idx = hashed_flow_idx; + } +#endif + skid_count++; + hashed_flow_idx++; + hashed_flow_idx &= fisa_hdl->hash_mask; + } while (skid_count <= max_skid_length); + + /* + * fisa_hdl->flow_eviction_cnt++; + * if (skid_count > max_skid_length) + * Remove LRU flow from HW FT + * Remove LRU flow from SW FT + */ + qdf_spin_unlock_bh(&fisa_hdl->dp_rx_fst_lock); + + if (skid_count > max_skid_length) { + dp_fisa_debug("Max skid length reached flow cannot be added, evict exiting flow"); + return NULL; + } + + /** + * Send HTT cache invalidation command to firmware to + * reflect the flow update + */ + if (is_fst_updated) { + status = dp_rx_flow_send_htt_operation_cmd(vdev->pdev, + DP_HTT_FST_CACHE_INVALIDATE_FULL, + &rx_flow_tuple_info); + if (QDF_STATUS_SUCCESS != status) { + dp_err("Failed to send the cache invalidation\n"); + /* TBD: remove flow from SW and HW flow table + * Not big impact cache entry gets updated later + */ + } + } + dp_fisa_debug("sw_ft_entry %pk", sw_ft_entry); + return sw_ft_entry; +} + +/** + * is_flow_idx_valid() - Function to decide if flow_idx TLV is valid + * @flow_invalid: flow invalid TLV value + * @flow_timeout: flow timeout TLV value, set when FSE timedout flow search + * + * Return: True if flow_idx value is valid + */ +static bool is_flow_idx_valid(bool flow_invalid, bool flow_timeout) +{ + if (!flow_invalid && !flow_timeout) + return true; + else + return false; +} + +/** + * dp_rx_get_fisa_flow() - Get FT entry corresponding to incoming nbuf + * @fisa_hdl: handle to FISA context + * @vdev: handle to DP vdev + * @nbuf: incoming msdu + * + * Return: handle SW FT entry for nbuf flow + */ +static struct dp_fisa_rx_sw_ft * +dp_rx_get_fisa_flow(struct dp_rx_fst *fisa_hdl, struct dp_vdev *vdev, + qdf_nbuf_t nbuf) +{ + uint8_t *rx_tlv_hdr; + uint32_t flow_idx; + bool flow_invalid, flow_timeout, flow_idx_valid; + struct dp_fisa_rx_sw_ft *sw_ft_entry = NULL; + struct dp_fisa_rx_sw_ft *sw_ft_base = (struct dp_fisa_rx_sw_ft *) + fisa_hdl->base; + hal_soc_handle_t hal_soc_hdl = fisa_hdl->soc_hdl->hal_soc; + + if (QDF_NBUF_CB_RX_TCP_PROTO(nbuf)) + return sw_ft_entry; + + rx_tlv_hdr = qdf_nbuf_data(nbuf); + hal_rx_msdu_get_flow_params(hal_soc_hdl, rx_tlv_hdr, &flow_invalid, + &flow_timeout, &flow_idx); + + dp_fisa_debug("nbuf %pk fl_idx %d fl_inv %d fl_timeout %d", + nbuf, flow_idx, flow_invalid, flow_timeout); + + flow_idx_valid = is_flow_idx_valid(flow_invalid, flow_timeout); + if (flow_idx_valid) { + qdf_assert_always(flow_idx < fisa_hdl->max_entries); + dp_fisa_debug("flow_idx is valid 0x%x", flow_idx); + return &sw_ft_base[flow_idx]; + } + + /* else new flow, add entry to FT */ + sw_ft_entry = dp_rx_fisa_add_ft_entry(fisa_hdl, flow_idx, nbuf, vdev, + rx_tlv_hdr); + + return sw_ft_entry; +} + +/** + * dp_add_nbuf_to_fisa_flow() - Aggregate incoming nbuf + * @fisa_hdl: handle to fisa context + * @vdev: handle DP vdev + * @nbuf: Incoming nbuf + * @fisa_flow: Handle SW flow entry + * + * Return: Success on aggregation + */ +static int dp_add_nbuf_to_fisa_flow(struct dp_rx_fst *fisa_hdl, + struct dp_vdev *vdev, qdf_nbuf_t nbuf, + struct dp_fisa_rx_sw_ft *fisa_flow) +{ + return FISA_AGGR_NOT_ELIGIBLE; +} + +/** + * dp_fisa_rx() - Entry function to FISA to handle aggregation + * @soc: core txrx main context + * @vdev: Handle DP vdev + * @nbuf_list: List nbufs to be aggregated + * + * Return: Success on aggregation + */ +QDF_STATUS dp_fisa_rx(struct dp_soc *soc, struct dp_vdev *vdev, + qdf_nbuf_t nbuf_list) +{ + struct dp_rx_fst *dp_fisa_rx_hdl = soc->rx_fst; + qdf_nbuf_t head_nbuf; + qdf_nbuf_t next_nbuf; + struct dp_fisa_rx_sw_ft *fisa_flow; + int fisa_ret; + + head_nbuf = nbuf_list; + + while (head_nbuf) { + next_nbuf = head_nbuf->next; + qdf_nbuf_set_next(head_nbuf, NULL); + + /* Add new flow if the there is no ongoing flow */ + fisa_flow = dp_rx_get_fisa_flow(dp_fisa_rx_hdl, vdev, + head_nbuf); + + /* Fragmented skb do not handle via fisa + * get that flow and deliver that flow to rx_thread + */ + if (qdf_unlikely(qdf_nbuf_get_ext_list(head_nbuf))) { + dp_fisa_debug("Fragmented skb, will not be FISAed"); + if (fisa_flow) + dp_rx_fisa_flush_flow(vdev, fisa_flow); + goto deliver_nbuf; + } + + if (!fisa_flow) + goto pull_nbuf; + + fisa_ret = dp_add_nbuf_to_fisa_flow(dp_fisa_rx_hdl, vdev, + head_nbuf, fisa_flow); + if (fisa_ret == FISA_AGGR_DONE) + goto next_msdu; + else + qdf_assert(0); + +pull_nbuf: + nbuf_skip_rx_pkt_tlv(dp_fisa_rx_hdl->soc_hdl->hal_soc, + head_nbuf); + +deliver_nbuf: /* Deliver without FISA */ + qdf_nbuf_set_next(head_nbuf, NULL); + hex_dump_skb_data(head_nbuf, false); + vdev->osif_rx(vdev->osif_vdev, head_nbuf); +next_msdu: + head_nbuf = next_nbuf; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * dp_rx_fisa_flush() - Flush function to end of context flushing of aggregates + * @soc: core txrx main context + * @napi_id: REO number to flush the flow Rxed on the REO + * + * Return: Success on flushing the flows for the REO + */ +QDF_STATUS dp_rx_fisa_flush(struct dp_soc *soc, int napi_id) +{ + struct dp_rx_fst *fisa_hdl = soc->rx_fst; + struct dp_fisa_rx_sw_ft *sw_ft_entry = + (struct dp_fisa_rx_sw_ft *)fisa_hdl->base; + int ft_size = fisa_hdl->max_entries; + int i; + + for (i = 0; i < ft_size; i++) { + if (sw_ft_entry[i].napi_id == napi_id && + sw_ft_entry[i].is_populated) { + dp_fisa_debug("flushing %d %pk napi_id %d\n", i, + &sw_ft_entry[i], napi_id); + /* Save the ip_len and checksum as hardware assist is + * always based on his start of aggregation + */ + sw_ft_entry[i].napi_flush_cumulative_l4_checksum = + sw_ft_entry[i].cumulative_l4_checksum; + sw_ft_entry[i].napi_flush_cumulative_ip_length = + sw_ft_entry[i].hal_cumultive_ip_len; + dp_fisa_debug("napi_flush_cumulative_ip_length 0x%x", + sw_ft_entry[i].napi_flush_cumulative_ip_length); + + dp_rx_fisa_flush_flow(sw_ft_entry[i].vdev, + &sw_ft_entry[i]); + sw_ft_entry[i].cur_aggr = 0; + } + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS dp_rx_dump_fisa_stats(struct dp_soc *soc) +{ + struct dp_rx_fst *rx_fst = soc->rx_fst; + struct dp_fisa_rx_sw_ft *sw_ft_entry = + &((struct dp_fisa_rx_sw_ft *)rx_fst->base)[0]; + int ft_size = rx_fst->max_entries; + int i; + + dp_info("Num of flows programmed %d", rx_fst->add_flow_count); + dp_info("Num of flows evicted %d", rx_fst->del_flow_count); + dp_info("Hash collision count %d", rx_fst->hash_collision_cnt); + + for (i = 0; i < ft_size; i++, sw_ft_entry++) { + if (!sw_ft_entry->is_populated) + continue; + + dp_info("FLOw ID %d is %s on napi/ring %d", + sw_ft_entry->flow_id, + sw_ft_entry->is_flow_udp ? "udp" : "tcp", + sw_ft_entry->napi_id); + dp_info("num msdu aggr %d", sw_ft_entry->aggr_count); + dp_info("flush count %d", sw_ft_entry->flush_count); + dp_info("bytes_aggregated %d", sw_ft_entry->bytes_aggregated); + dp_info("avg aggregation %d", + sw_ft_entry->bytes_aggregated / sw_ft_entry->flush_count + ); + print_flow_tuple(&sw_ft_entry->rx_flow_tuple_info); + } + return QDF_STATUS_SUCCESS; +} diff --git a/core/dp/txrx3.0/dp_fisa_rx.h b/core/dp/txrx3.0/dp_fisa_rx.h new file mode 100644 index 000000000000..2c61f6c1a6b9 --- /dev/null +++ b/core/dp/txrx3.0/dp_fisa_rx.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2020, The Linux Foundation. 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 +#include + +//#define FISA_DEBUG_ENABLE + +#ifdef FISA_DEBUG_ENABLE +#define dp_fisa_debug dp_info +#else +#define dp_fisa_debug dp_debug +#endif + +#if defined(WLAN_SUPPORT_RX_FISA) +/** + * dp_rx_dump_fisa_stats() - Dump fisa stats + * @soc: core txrx main context + * + * Return: QDF_STATUS + */ +QDF_STATUS dp_rx_dump_fisa_stats(struct dp_soc *soc); + +/** + * dp_fisa_rx() - FISA Rx packet delivery entry function + * @soc: core txrx main context + * @vdev: core txrx vdev + * @nbuf_list: Delivery list of nbufs + * + * Return: QDF_STATUS + */ +QDF_STATUS dp_fisa_rx(struct dp_soc *dp_fisa_rx_hdl, struct dp_vdev *vdev, + qdf_nbuf_t nbuf_list); + +/** + * dp_rx_fisa_flush() - FISA Rx flush function to flush aggregation at end of + * NAPI + * @soc: core txrx main context + * @napi_id: Flows which are rxed on the NAPI ID to be flushed + * + * Return: QDF_STATUS + */ +QDF_STATUS dp_rx_fisa_flush(struct dp_soc *soc, int napi_id); + +#else +static QDF_STATUS dp_rx_dump_fisa_stats(struct dp_soc *soc) +{ + return QDF_STATUS_SUCCESS; +} + +void dp_rx_dump_fisa_table(struct dp_soc *soc) +{ +} +#endif