diff --git a/dp/inc/cdp_txrx_cmn_struct.h b/dp/inc/cdp_txrx_cmn_struct.h index 5a9128d958..2b117b155e 100644 --- a/dp/inc/cdp_txrx_cmn_struct.h +++ b/dp/inc/cdp_txrx_cmn_struct.h @@ -1827,4 +1827,75 @@ struct cdp_peer_cookie { uint8_t cookie; struct cdp_stats_cookie *ctx; }; + +/** + * cdp_flow_stats - Per-Flow (5-tuple) statistics + * @msdu_count: number of rx msdus matching this flow + * + * HW also includes msdu_byte_count and timestamp, which + * are not currently tracked in SW. + */ +struct cdp_flow_stats { + uint32_t msdu_count; +}; + +/** + * cdp_flow_fst_operation - RX FST operations allowed + */ +enum cdp_flow_fst_operation { + CDP_FLOW_FST_ENTRY_ADD, + CDP_FLOW_FST_ENTRY_DEL, + CDP_FLOW_FST_RX_BYPASS_ENABLE, + CDP_FLOW_FST_RX_BYPASS_DISABLE +}; + +/** + * cdp_flow_protocol_type - RX FST supported protocol types, mapped to HW spec + */ +enum cdp_flow_protocol_type { + CDP_FLOW_PROTOCOL_TYPE_TCP = 6, + CDP_FLOW_PROTOCOL_TYPE_UDP = 17, +}; + +/** + * cdp_rx_flow_tuple_info - RX flow tuple info used for addition/deletion + * @dest_ip_127_96: destination IP address bit fields 96-127 + * @dest_ip_95_64: destination IP address bit fields 64-95 + * @dest_ip_63_32: destination IP address bit fields 32-63 + * @dest_ip_31_0: destination IP address bit fields 0-31 + * @src_ip_127_96: source IP address bit fields 96-127 + * @src_ip_95_64: source IP address bit fields 64-95 + * @src_ip_63_32: source IP address bit fields 32-63 + * @src_ip_31_0: source IP address bit fields 0-31 + * @dest_port: destination port of flow + * @src_port: source port of flow + * @l4_protocol: protocol type in flow (TCP/UDP) + */ +struct cdp_rx_flow_tuple_info { + uint32_t dest_ip_127_96; + uint32_t dest_ip_95_64; + uint32_t dest_ip_63_32; + uint32_t dest_ip_31_0; + uint32_t src_ip_127_96; + uint32_t src_ip_95_64; + uint32_t src_ip_63_32; + uint32_t src_ip_31_0; + uint16_t dest_port; + uint16_t src_port; + uint16_t l4_protocol; +}; + +/** + * cdp_rx_flow_info - RX flow info used for addition/deletion + * @is_addr_ipv4: indicates whether given IP address is IPv4/IPv6 + * @op_code: add/delete/enable/disable operation requested + * @flow_tupe_info: structure containing tuple info + * @fse_metadata: metadata to be set in RX flow + */ +struct cdp_rx_flow_info { + bool is_addr_ipv4; + enum cdp_flow_fst_operation op_code; + struct cdp_rx_flow_tuple_info flow_tuple_info; + uint16_t fse_metadata; +}; #endif diff --git a/dp/inc/cdp_txrx_ctrl.h b/dp/inc/cdp_txrx_ctrl.h index 36090e797e..347c70ab97 100644 --- a/dp/inc/cdp_txrx_ctrl.h +++ b/dp/inc/cdp_txrx_ctrl.h @@ -844,4 +844,58 @@ static inline QDF_STATUS cdp_vdev_get_neighbour_rssi(ol_txrx_soc_handle soc, rssi); } #endif -#endif + +#ifdef WLAN_SUPPORT_RX_FLOW_TAG +/** + * cdp_set_rx_flow_tag() - wrapper function to set the flow + * tag in CDP layer from cfg layer + * @soc: SOC TXRX handle + * @pdev: CDP pdev pointer + * @flow_info: Flow 5-tuple, along with tag, if any, that needs to added/deleted + * + * Return: Success when add/del operation is successful, error otherwise + */ +static inline QDF_STATUS +cdp_set_rx_flow_tag(ol_txrx_soc_handle soc, struct cdp_pdev *pdev, + struct cdp_rx_flow_info *flow_info) +{ + if (!soc || !soc->ops) { + dp_err("Invalid SOC instance"); + QDF_BUG(0); + return QDF_STATUS_E_FAILURE; + } + + if (!soc->ops->ctrl_ops || + !soc->ops->ctrl_ops->txrx_set_rx_flow_tag) + return QDF_STATUS_E_FAILURE; + + return soc->ops->ctrl_ops->txrx_set_rx_flow_tag(pdev, flow_info); +} + +/** + * cdp_dump_rx_flow_tag_stats() - wrapper function to dump the flow + * tag statistics for given flow + * @soc: SOC TXRX handle + * @pdev: CDP pdev pointer + * @flow_info: Flow tuple for which we want to print the statistics + * + * Return: Success when flow is found and stats are printed, error otherwise + */ +static inline QDF_STATUS +cdp_dump_rx_flow_tag_stats(ol_txrx_soc_handle soc, struct cdp_pdev *pdev, + struct cdp_rx_flow_info *flow_info) +{ + if (!soc || !soc->ops) { + dp_err("Invalid SOC instance"); + QDF_BUG(0); + return QDF_STATUS_E_FAILURE; + } + + if (!soc->ops->ctrl_ops || + !soc->ops->ctrl_ops->txrx_dump_rx_flow_tag_stats) + return QDF_STATUS_E_FAILURE; + + return soc->ops->ctrl_ops->txrx_dump_rx_flow_tag_stats(pdev, flow_info); +} +#endif /* WLAN_SUPPORT_RX_FLOW_TAG */ +#endif /* _CDP_TXRX_CTRL_H_ */ diff --git a/dp/inc/cdp_txrx_ops.h b/dp/inc/cdp_txrx_ops.h index 11faeb0e25..b15c76dee0 100644 --- a/dp/inc/cdp_txrx_ops.h +++ b/dp/inc/cdp_txrx_ops.h @@ -668,6 +668,14 @@ struct cdp_ctrl_ops { uint16_t protocol_type); #endif /* WLAN_SUPPORT_RX_TAG_STATISTICS */ #endif /* WLAN_SUPPORT_RX_PROTOCOL_TYPE_TAG */ +#ifdef WLAN_SUPPORT_RX_FLOW_TAG + QDF_STATUS (*txrx_set_rx_flow_tag)( + struct cdp_pdev *txrx_pdev_handle, + struct cdp_rx_flow_info *flow_info); + QDF_STATUS (*txrx_dump_rx_flow_tag_stats)( + struct cdp_pdev *txrx_pdev_handle, + struct cdp_rx_flow_info *flow_info); +#endif /* WLAN_SUPPORT_RX_FLOW_TAG */ #ifdef QCA_MULTIPASS_SUPPORT void (*txrx_peer_set_vlan_id)(ol_txrx_soc_handle soc, struct cdp_vdev *vdev, uint8_t *peer_mac, diff --git a/dp/wifi3.0/dp_flow.c b/dp/wifi3.0/dp_flow.c new file mode 100644 index 0000000000..cdddd1822d --- /dev/null +++ b/dp/wifi3.0/dp_flow.c @@ -0,0 +1,723 @@ +/* + * Copyright (c) 2019 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. + */ + +#ifdef WLAN_SUPPORT_RX_FLOW_TAG +#include "dp_types.h" +#include "qdf_mem.h" +#include "qdf_nbuf.h" +#include "cfg_dp.h" +#include "wlan_cfg.h" +#include "dp_types.h" +#include "hal_rx_flow.h" +#include "dp_htt.h" + +/** + * In Hawkeye, a hardware bug disallows SW to only clear a single flow entry + * when added/deleted by upper layer. Workaround is to clear entire cache, + * which can have a performance impact. Flow additions/deletions + * are bundled together over 100ms to save HW cycles if upper layer + * adds/deletes multiple flows together. Use a longer timeout during setup + * stage since no flows are anticipated at this time. + */ +#define HW_RX_FSE_CACHE_INVALIDATE_BUNDLE_PERIOD_MS (100) +#define HW_RX_FSE_CACHE_INVALIDATE_DELAYED_FST_SETUP_MS (5000) + +/** + * dp_rx_flow_get_fse() - Obtain flow search entry from flow hash + * @fst: Rx FST Handle + * @flow_hash: Computed hash value of flow + * + * Return: Handle to flow search table entry + */ +static inline struct dp_rx_fse * +dp_rx_flow_get_fse(struct dp_rx_fst *fst, uint32_t flow_hash) +{ + struct dp_rx_fse *fse; + uint32_t idx = hal_rx_get_hal_hash(fst->hal_rx_fst, flow_hash); + + fse = (struct dp_rx_fse *)((uint8_t *)fst->base + (idx * + sizeof(struct dp_rx_fse))); + + return fse; +} + +/** + * dp_rx_flow_dump_flow_entry() - Print flow search entry from 5-tuple + * @fst: Rx FST Handle + * @flow_info: Flow 5-tuple + * + * Return: None + */ +void dp_rx_flow_dump_flow_entry(struct dp_rx_fst *fst, + struct cdp_rx_flow_info *flow_info) +{ + dp_info("Dest IP address %x:%x:%x:%x", + flow_info->flow_tuple_info.dest_ip_127_96, + flow_info->flow_tuple_info.dest_ip_95_64, + flow_info->flow_tuple_info.dest_ip_63_32, + flow_info->flow_tuple_info.dest_ip_31_0); + dp_info("Source IP address %x:%x:%x:%x", + flow_info->flow_tuple_info.src_ip_127_96, + flow_info->flow_tuple_info.src_ip_95_64, + flow_info->flow_tuple_info.src_ip_63_32, + flow_info->flow_tuple_info.src_ip_31_0); + dp_info("Dest port %u, Src Port %u, Protocol %u", + flow_info->flow_tuple_info.dest_port, + flow_info->flow_tuple_info.src_port, + flow_info->flow_tuple_info.l4_protocol); +} + +/** + * dp_rx_flow_compute_flow_hash() - Print flow search entry from 5-tuple + * @fst: Rx FST Handle + * @rx_flow_info: DP Rx Flow 5-tuple programmed by upper layer + * @flow: HAL (HW) flow entry + * + * Return: Computed Toeplitz hash + */ +uint32_t dp_rx_flow_compute_flow_hash(struct dp_rx_fst *fst, + struct cdp_rx_flow_info *rx_flow_info, + struct hal_rx_flow *flow) +{ + flow->tuple_info.dest_ip_127_96 = + rx_flow_info->flow_tuple_info.dest_ip_127_96; + flow->tuple_info.dest_ip_95_64 = + rx_flow_info->flow_tuple_info.dest_ip_95_64; + flow->tuple_info.dest_ip_63_32 = + rx_flow_info->flow_tuple_info.dest_ip_63_32; + flow->tuple_info.dest_ip_31_0 = + rx_flow_info->flow_tuple_info.dest_ip_31_0; + flow->tuple_info.src_ip_127_96 = + rx_flow_info->flow_tuple_info.src_ip_127_96; + flow->tuple_info.src_ip_95_64 = + rx_flow_info->flow_tuple_info.src_ip_95_64; + flow->tuple_info.src_ip_63_32 = + rx_flow_info->flow_tuple_info.src_ip_63_32; + flow->tuple_info.src_ip_31_0 = + rx_flow_info->flow_tuple_info.src_ip_31_0; + flow->tuple_info.dest_port = + rx_flow_info->flow_tuple_info.dest_port; + flow->tuple_info.src_port = + rx_flow_info->flow_tuple_info.src_port; + flow->tuple_info.l4_protocol = + rx_flow_info->flow_tuple_info.l4_protocol; + + return hal_flow_toeplitz_hash(fst->hal_rx_fst, flow); +} + +/** + * dp_rx_flow_alloc_entry() - Create DP and HAL flow entries in FST + * @fst: Rx FST Handle + * @rx_flow_info: DP Rx Flow 5-tuple to be added to DP FST + * @flow: HAL (HW) flow entry that is created + * + * Return: Computed Toeplitz hash + */ +struct dp_rx_fse *dp_rx_flow_alloc_entry(struct dp_rx_fst *fst, + struct cdp_rx_flow_info *rx_flow_info, + struct hal_rx_flow *flow) +{ + struct dp_rx_fse *fse = NULL; + uint32_t flow_hash; + uint32_t flow_idx; + QDF_STATUS status; + + flow_hash = dp_rx_flow_compute_flow_hash(fst, rx_flow_info, flow); + + status = hal_rx_insert_flow_entry(fst->hal_rx_fst, + flow_hash, + &rx_flow_info->flow_tuple_info, + &flow_idx); + if (status != QDF_STATUS_SUCCESS) { + dp_err("Add entry failed with status %d for tuple with hash %u", + status, flow_hash); + return NULL; + } + + fse = dp_rx_flow_get_fse(fst, flow_idx); + fse->is_ipv4_addr_entry = rx_flow_info->is_addr_ipv4; + fse->flow_hash = flow_hash; + fse->flow_id = flow_idx; + fse->stats.msdu_count = 0; + fse->is_valid = true; + + return fse; +} + +/** + * dp_rx_flow_find_entry_by_tuple() - Find the DP FSE matching a given 5-tuple + * @fst: Rx FST Handle + * @rx_flow_info: DP Rx Flow 5-tuple + * @flow: Pointer to the HAL (HW) flow entry + * + * Return: Pointer to the DP FSE entry + */ +struct dp_rx_fse * +dp_rx_flow_find_entry_by_tuple(struct dp_rx_fst *fst, + struct cdp_rx_flow_info *rx_flow_info, + struct hal_rx_flow *flow) +{ + uint32_t flow_hash; + uint32_t flow_idx; + QDF_STATUS status; + + flow_hash = dp_rx_flow_compute_flow_hash(fst, rx_flow_info, flow); + + status = hal_rx_find_flow_from_tuple(fst->hal_rx_fst, + flow_hash, + &rx_flow_info->flow_tuple_info, + &flow_idx); + if (status != QDF_STATUS_SUCCESS) { + dp_err("Could not find tuple with hash %u", flow_hash); + dp_rx_flow_dump_flow_entry(fst, rx_flow_info); + return NULL; + } + return dp_rx_flow_get_fse(fst, flow_idx); +} + +/** + * dp_rx_flow_find_entry_by_flowid() - Find DP FSE matching a given flow index + * @fst: Rx FST Handle + * @flow_id: Flow index of the requested flow + * + * Return: Pointer to the DP FSE entry + */ +struct dp_rx_fse * +dp_rx_flow_find_entry_by_flowid(struct dp_rx_fst *fst, + uint32_t flow_id) +{ + struct dp_rx_fse *fse = NULL; + + fse = dp_rx_flow_get_fse(fst, flow_id); + if (!fse->is_valid) + return NULL; + + dp_info("flow_idx= %d, flow_addr = %pK", flow_id, fse); + qdf_assert_always(fse->flow_id == flow_id); + + return fse; +} + +/** + * dp_rx_flow_send_htt_operation_cmd() - Send HTT FSE command to FW for flow + * addition/removal + * @pdev: Pdev instance + * @op: Add/delete operation + * @info: DP Flow parameters of the flow added/deleted + * + * Return: Success on sending HTT command to FW, error on failure + */ +QDF_STATUS dp_rx_flow_send_htt_operation_cmd(struct dp_pdev *pdev, + enum dp_htt_flow_fst_operation op, + struct cdp_rx_flow_info *info) +{ + struct dp_htt_rx_flow_fst_operation fst_op; + struct wlan_cfg_dp_soc_ctxt *cfg = pdev->soc->wlan_cfg_ctx; + + qdf_mem_set(&fst_op, 0, sizeof(struct dp_htt_rx_flow_fst_operation)); + + if (qdf_unlikely(wlan_cfg_is_rx_flow_search_table_per_pdev(cfg))) { + /* Firmware pdev ID starts from 1 */ + fst_op.pdev_id = DP_SW2HW_MACID(pdev->pdev_id); + } else { + fst_op.pdev_id = 0; + } + + fst_op.op_code = op; + fst_op.rx_flow = info; + + return dp_htt_rx_flow_fse_operation(pdev, &fst_op); +} + +/** + * dp_rx_flow_add_entry() - Add a flow entry to flow search table + * @pdev: DP pdev instance + * @rx_flow_info: DP flow paramaters + * + * Return: Success when flow is added, no-memory or already exists on error + */ +QDF_STATUS dp_rx_flow_add_entry(struct dp_pdev *pdev, + struct cdp_rx_flow_info *rx_flow_info) +{ + struct hal_rx_flow flow = { 0 }; + struct dp_rx_fse *fse; + struct dp_soc *soc = pdev->soc; + struct dp_rx_fst *fst; + + fst = pdev->rx_fst; + + /* Initialize unused bits in IPv6 address for IPv4 address */ + if (rx_flow_info->is_addr_ipv4) { + rx_flow_info->flow_tuple_info.dest_ip_63_32 = 0; + rx_flow_info->flow_tuple_info.dest_ip_95_64 = 0; + rx_flow_info->flow_tuple_info.dest_ip_127_96 = + HAL_IP_DA_SA_PREFIX_IPV4_COMPATIBLE_IPV6; + + rx_flow_info->flow_tuple_info.src_ip_63_32 = 0; + rx_flow_info->flow_tuple_info.src_ip_95_64 = 0; + rx_flow_info->flow_tuple_info.src_ip_127_96 = + HAL_IP_DA_SA_PREFIX_IPV4_COMPATIBLE_IPV6; + } + + /* Allocate entry in DP FST */ + fse = dp_rx_flow_alloc_entry(fst, rx_flow_info, &flow); + if (NULL == fse) { + dp_err("RX FSE alloc failed"); + dp_rx_flow_dump_flow_entry(fst, rx_flow_info); + return QDF_STATUS_E_NOMEM; + } + dp_info("flow_addr = %pK, flow_id = %u, valid = %d, v4 = %d\n", + fse, fse->flow_id, fse->is_valid, fse->is_ipv4_addr_entry); + + /* Initialize other parameters for HW flow & populate HW FSE entry */ + flow.reo_destination_indication = (fse->flow_hash & + HAL_REO_DEST_IND_HASH_MASK); + + /** + * Reo destination of each flow is mapped to match the same used + * by RX Hash algorithm. If RX Hash is disabled, then the REO + * destination below is directly got from pdev, rather than using + * dp_peer_setup_get_reo_hash since we do not have vdev handle here. + */ + if (wlan_cfg_is_rx_hash_enabled(soc->wlan_cfg_ctx)) { + flow.reo_destination_indication |= + HAL_REO_DEST_IND_START_OFFSET; + } else { + flow.reo_destination_indication = pdev->reo_dest; + } + + flow.reo_destination_handler = HAL_RX_FSE_REO_DEST_FT; + flow.fse_metadata = rx_flow_info->fse_metadata; + fse->hal_rx_fse = hal_rx_flow_setup_fse(fst->hal_rx_fst, + fse->flow_id, &flow); + if (qdf_unlikely(!fse->hal_rx_fse)) { + dp_err("Unable to alloc FSE entry"); + dp_rx_flow_dump_flow_entry(fst, rx_flow_info); + /* Free up the FSE entry as returning failure */ + fse->is_valid = false; + return QDF_STATUS_E_EXISTS; + } + + /* Increment number of valid entries in table */ + fst->num_entries++; + dp_info("FST num_entries = %d, reo_dest_ind = %d, reo_dest_hand = %u", + fst->num_entries, flow.reo_destination_indication, + flow.reo_destination_handler); + + if (soc->is_rx_fse_full_cache_invalidate_war_enabled) { + qdf_atomic_set(&fst->is_cache_update_pending, 1); + } else { + QDF_STATUS status; + /** + * Send HTT cache invalidation command to firmware to + * reflect the added flow + */ + status = dp_rx_flow_send_htt_operation_cmd( + pdev, + DP_HTT_FST_CACHE_INVALIDATE_ENTRY, + rx_flow_info); + + if (QDF_STATUS_SUCCESS != status) { + dp_err("Send cache invalidate entry to fw failed: %u", + status); + dp_rx_flow_dump_flow_entry(fst, rx_flow_info); + /* Free DP FSE and HAL FSE */ + hal_rx_flow_delete_entry(fst->hal_rx_fst, + fse->hal_rx_fse); + fse->is_valid = false; + return status; + } + } + + return QDF_STATUS_SUCCESS; +} + +/** + * dp_rx_flow_delete_entry() - Delete a flow entry from flow search table + * @pdev: pdev handle + * @rx_flow_info: DP flow parameters + * + * Return: Success when flow is deleted, error on failure + */ +QDF_STATUS dp_rx_flow_delete_entry(struct dp_pdev *pdev, + struct cdp_rx_flow_info *rx_flow_info) +{ + struct hal_rx_flow flow = { 0 }; + struct dp_rx_fse *fse; + struct dp_soc *soc = pdev->soc; + struct dp_rx_fst *fst; + QDF_STATUS status; + + fst = pdev->rx_fst; + + /* Find the given flow entry DP FST */ + fse = dp_rx_flow_find_entry_by_tuple(fst, rx_flow_info, &flow); + if (!fse) { + dp_err("RX flow delete entry failed"); + dp_rx_flow_dump_flow_entry(fst, rx_flow_info); + return QDF_STATUS_E_INVAL; + } + + /* Delete the FSE in HW FST */ + status = hal_rx_flow_delete_entry(fst->hal_rx_fst, fse->hal_rx_fse); + qdf_assert_always(status == QDF_STATUS_SUCCESS); + + /* Free the FSE in DP FST */ + fse->is_valid = false; + + /* Decrement number of valid entries in table */ + fst->num_entries--; + + if (soc->is_rx_fse_full_cache_invalidate_war_enabled) { + qdf_atomic_set(&fst->is_cache_update_pending, 1); + } else { + /** + * Send HTT cache invalidation command to firmware + * to reflect the deleted flow + */ + status = dp_rx_flow_send_htt_operation_cmd( + pdev, + DP_HTT_FST_CACHE_INVALIDATE_ENTRY, + rx_flow_info); + + if (QDF_STATUS_SUCCESS != status) { + dp_err("Send cache invalidate entry to fw failed: %u", + status); + dp_rx_flow_dump_flow_entry(fst, rx_flow_info); + /* Do not add entry back in DP FSE and HAL FSE */ + return status; + } + } + + return QDF_STATUS_SUCCESS; +} + +/* dp_rx_flow_update_fse_stats() - Update a flow's statistics + * @pdev: pdev handle + * @flow_id: flow index (truncated hash) in the Rx FST + * + * Return: Success when flow statistcs is updated, error on failure + */ +QDF_STATUS dp_rx_flow_update_fse_stats(struct dp_pdev *pdev, uint32_t flow_id) +{ + struct dp_rx_fse *fse; + + fse = dp_rx_flow_find_entry_by_flowid(pdev->rx_fst, flow_id); + + if (NULL == fse) { + dp_err("Flow not found, flow ID %u", flow_id); + return QDF_STATUS_E_NOENT; + } + + fse->stats.msdu_count += 1; + return QDF_STATUS_SUCCESS; +} + +/** + * dp_rx_flow_get_fse_stats() - Fetch a flow's stats based on DP flow parameter + * @pdev: pdev handle + * @rx_flow_info: Pointer to the DP flow struct of the requested flow + * @stats: Matching flow's stats returned to caller + * + * Return: Success when flow statistcs is updated, error on failure + */ +QDF_STATUS dp_rx_flow_get_fse_stats(struct dp_pdev *pdev, + struct cdp_rx_flow_info *rx_flow_info, + struct cdp_flow_stats *stats) +{ + struct dp_rx_fst *fst; + struct dp_rx_fse *fse; + struct hal_rx_flow flow; + + fst = pdev->rx_fst; + + /* Find the given flow entry DP FST */ + fse = dp_rx_flow_find_entry_by_tuple(fst, rx_flow_info, &flow); + if (!fse) { + dp_err("RX flow entry search failed"); + dp_rx_flow_dump_flow_entry(fst, rx_flow_info); + return QDF_STATUS_E_INVAL; + } + + stats->msdu_count = fse->stats.msdu_count; + return QDF_STATUS_SUCCESS; +} + +/** + * dp_rx_flow_cache_invalidate_timer_handler() - Timer handler used for bundling + * flows before invalidating entire cache + * @ctx: Pdev handle + * + * Return: None + */ +void dp_rx_flow_cache_invalidate_timer_handler(void *ctx) +{ + struct dp_pdev *pdev = (struct dp_pdev *)ctx; + struct dp_rx_fst *fst; + bool is_update_pending; + QDF_STATUS status; + + fst = pdev->rx_fst; + + qdf_assert_always(fst); + is_update_pending = qdf_atomic_read(&fst->is_cache_update_pending); + qdf_atomic_set(&fst->is_cache_update_pending, 0); + + if (is_update_pending) { + /* Send full cache invalidate command to firmware */ + status = dp_rx_flow_send_htt_operation_cmd( + pdev, + DP_HTT_FST_CACHE_INVALIDATE_FULL, + NULL); + + if (QDF_STATUS_SUCCESS != status) + dp_err("Send full cache inv to fw failed: %u", status); + } + + qdf_timer_start(&fst->cache_invalidate_timer, + HW_RX_FSE_CACHE_INVALIDATE_BUNDLE_PERIOD_MS); +} + +/** + * dp_rx_fst_attach() - Initialize Rx FST and setup necessary parameters + * @soc: SoC handle + * @pdev: Pdev handle + * + * Return: Handle to flow search table entry + */ +QDF_STATUS dp_rx_fst_attach(struct dp_soc *soc, struct dp_pdev *pdev) +{ + struct dp_rx_fst *fst; + uint8_t *hash_key; + struct wlan_cfg_dp_soc_ctxt *cfg = soc->wlan_cfg_ctx; + bool is_rx_flow_search_table_per_pdev = + wlan_cfg_is_rx_flow_search_table_per_pdev(cfg); + + if (qdf_unlikely(!wlan_cfg_is_rx_flow_tag_enabled(cfg))) { + dp_err("RX Flow tag feature disabled"); + return QDF_STATUS_E_NOSUPPORT; + } + + if (!wlan_psoc_nif_fw_ext_cap_get((void *)pdev->ctrl_pdev, + WLAN_SOC_CEXT_RX_FSE_SUPPORT)) { + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "rx fse disabled in FW\n"); + wlan_cfg_set_rx_flow_tag_enabled(cfg, false); + return QDF_STATUS_E_NOSUPPORT; + } + /** + * Func. is called for every pdev. If FST is per SOC, then return + * if it was already called once. + */ + if (!is_rx_flow_search_table_per_pdev && soc->rx_fst) { + pdev->rx_fst = soc->rx_fst; + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "RX FST for SoC is already initialized"); + return QDF_STATUS_SUCCESS; + } + + /** + * Func. is called for this pdev already. This is an error. + * Return failure + */ + if (is_rx_flow_search_table_per_pdev && pdev->rx_fst) { + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "RX FST for PDEV %u is already initialized", + pdev->pdev_id); + return QDF_STATUS_E_EXISTS; + } + + fst = qdf_mem_malloc(sizeof(struct dp_rx_fst)); + if (!fst) { + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "RX FST allocation failed\n"); + return QDF_STATUS_E_NOMEM; + } + + qdf_mem_set(fst, 0, sizeof(struct dp_rx_fst)); + + fst->max_skid_length = wlan_cfg_rx_fst_get_max_search(cfg); + fst->max_entries = wlan_cfg_get_rx_flow_search_table_size(cfg); + hash_key = wlan_cfg_rx_fst_get_hash_key(cfg); + + if (!(fst->max_entries && + (!(fst->max_entries & (fst->max_entries - 1))))) { + uint32_t next_power_of_2 = fst->max_entries - 1; + + next_power_of_2 |= (next_power_of_2 >> 1); + next_power_of_2 |= (next_power_of_2 >> 2); + next_power_of_2 |= (next_power_of_2 >> 4); + next_power_of_2 |= (next_power_of_2 >> 8); + next_power_of_2 |= (next_power_of_2 >> 16); + next_power_of_2++; + if (next_power_of_2 > WLAN_CFG_RX_FLOW_SEARCH_TABLE_SIZE_MAX) + next_power_of_2 = + WLAN_CFG_RX_FLOW_SEARCH_TABLE_SIZE_MAX; + dp_info("Num entries in cfg is not a ^2:%u, using next ^2:%u", + fst->max_entries, next_power_of_2); + fst->max_entries = next_power_of_2; + } + fst->hash_mask = fst->max_entries - 1; + fst->num_entries = 0; + + fst->base = (uint8_t *) qdf_mem_malloc(sizeof(struct dp_rx_fse) * + fst->max_entries); + + if (!fst->base) { + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "Rx fst->base allocation failed, #entries:%d\n", + fst->max_entries); + + qdf_mem_free(fst); + return QDF_STATUS_E_NOMEM; + } + + qdf_mem_set((uint8_t *)fst->base, 0, + (sizeof(struct dp_rx_fse) * fst->max_entries)); + + fst->hal_rx_fst = hal_rx_fst_attach( + soc->osdev, + &fst->hal_rx_fst_base_paddr, + fst->max_entries, + fst->max_skid_length, + hash_key); + + if (qdf_unlikely(!fst->hal_rx_fst)) { + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "Rx Hal fst allocation failed, #entries:%d\n", + fst->max_entries); + qdf_mem_free(fst->base); + qdf_mem_free(fst); + return QDF_STATUS_E_NOMEM; + } + if (!is_rx_flow_search_table_per_pdev) + soc->rx_fst = fst; + + pdev->rx_fst = fst; + + if (soc->is_rx_fse_full_cache_invalidate_war_enabled) { + QDF_STATUS status; + + status = qdf_timer_init( + soc->osdev, + &fst->cache_invalidate_timer, + dp_rx_flow_cache_invalidate_timer_handler, + (void *)pdev, + QDF_TIMER_TYPE_SW); + + qdf_assert_always(status == QDF_STATUS_SUCCESS); + + /* Start the timer */ + qdf_timer_start( + &fst->cache_invalidate_timer, + HW_RX_FSE_CACHE_INVALIDATE_DELAYED_FST_SETUP_MS); + + qdf_atomic_set(&fst->is_cache_update_pending, false); + } + + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_INFO, + "Rx FST attach successful, #entries:%d\n", + fst->max_entries); + return QDF_STATUS_SUCCESS; +} + +/** + * dp_rx_fst_detach() - De-initialize Rx FST + * @soc: SoC handle + * @pdev: Pdev handle + * + * Return: None + */ +void dp_rx_fst_detach(struct dp_soc *soc, struct dp_pdev *pdev) +{ + struct dp_rx_fst *dp_fst; + struct wlan_cfg_dp_soc_ctxt *cfg = soc->wlan_cfg_ctx; + + if (qdf_unlikely(wlan_cfg_is_rx_flow_search_table_per_pdev(cfg))) { + dp_fst = pdev->rx_fst; + pdev->rx_fst = NULL; + } else { + dp_fst = soc->rx_fst; + soc->rx_fst = NULL; + } + + if (qdf_likely(dp_fst)) { + hal_rx_fst_detach(dp_fst->hal_rx_fst, soc->osdev); + if (soc->is_rx_fse_full_cache_invalidate_war_enabled) { + qdf_timer_sync_cancel(&dp_fst->cache_invalidate_timer); + qdf_timer_stop(&dp_fst->cache_invalidate_timer); + qdf_timer_free(&dp_fst->cache_invalidate_timer); + } + qdf_mem_free(dp_fst->base); + qdf_mem_free(dp_fst); + } + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, + "Rx FST detached for pdev %u\n", pdev->pdev_id); +} + +/** + * dp_rx_flow_send_fst_fw_setup() - Program FST parameters in FW/HW post-attach + * @soc: SoC handle + * @pdev: Pdev handle + * + * Return: Success when fst parameters are programmed in FW, error otherwise + */ +QDF_STATUS dp_rx_flow_send_fst_fw_setup(struct dp_soc *soc, + struct dp_pdev *pdev) +{ + struct dp_htt_rx_flow_fst_setup fst_setup; + struct dp_rx_fst *fst; + QDF_STATUS status; + struct wlan_cfg_dp_soc_ctxt *cfg = soc->wlan_cfg_ctx; + + if (qdf_unlikely(!wlan_cfg_is_rx_flow_tag_enabled(cfg))) + return QDF_STATUS_SUCCESS; + + qdf_mem_set(&fst_setup, 0, sizeof(struct dp_htt_rx_flow_fst_setup)); + + if (qdf_unlikely(wlan_cfg_is_rx_flow_search_table_per_pdev(cfg))) { + /* Firmware pdev ID starts from 1 */ + fst_setup.pdev_id = DP_SW2HW_MACID(pdev->pdev_id); + fst = pdev->rx_fst; + } else { + fst_setup.pdev_id = 0; + fst = soc->rx_fst; + } + fst_setup.max_entries = fst->max_entries; + fst_setup.max_search = fst->max_skid_length; + fst_setup.base_addr_lo = (uint32_t)fst->hal_rx_fst_base_paddr; + fst_setup.base_addr_hi = + (uint32_t)((uint64_t)fst->hal_rx_fst_base_paddr >> 32); + fst_setup.ip_da_sa_prefix = + HAL_FST_IP_DA_SA_PFX_TYPE_IPV4_COMPATIBLE_IPV6; + fst_setup.hash_key = wlan_cfg_rx_fst_get_hash_key(cfg); + fst_setup.hash_key_len = HAL_FST_HASH_KEY_SIZE_BYTES; + + status = dp_htt_rx_flow_fst_setup(pdev, &fst_setup); + if (status == QDF_STATUS_SUCCESS) { + fst->fse_setup_done = true; + return status; + } + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "Failed to send Rx FSE Setup pdev%d status %d\n", + pdev->pdev_id, status); + /* Free all the memory allocations and data structures */ + dp_rx_fst_detach(pdev->soc, pdev); + return status; +} +#endif /* WLAN_SUPPORT_RX_FLOW_TAG */ diff --git a/dp/wifi3.0/dp_htt.c b/dp/wifi3.0/dp_htt.c index 7a9f8be150..c10607d673 100644 --- a/dp/wifi3.0/dp_htt.c +++ b/dp/wifi3.0/dp_htt.c @@ -4160,3 +4160,277 @@ dp_peer_update_inactive_time(struct dp_pdev *pdev, uint32_t tag_type, qdf_err("Invalid tag_type"); } } + +/** + * dp_htt_rx_flow_fst_setup(): Send HTT Rx FST setup message to FW + * @pdev: DP pdev handle + * @fse_setup_info: FST setup parameters + * + * Return: Success when HTT message is sent, error on failure + */ +QDF_STATUS +dp_htt_rx_flow_fst_setup(struct dp_pdev *pdev, + struct dp_htt_rx_flow_fst_setup *fse_setup_info) +{ + struct htt_soc *soc = pdev->soc->htt_handle; + struct dp_htt_htc_pkt *pkt; + qdf_nbuf_t msg; + u_int32_t *msg_word; + struct htt_h2t_msg_rx_fse_setup_t *fse_setup; + uint8_t *htt_logger_bufp; + u_int32_t *key; + + msg = qdf_nbuf_alloc( + soc->osdev, + HTT_MSG_BUF_SIZE(sizeof(struct htt_h2t_msg_rx_fse_setup_t)), + /* reserve room for the HTC header */ + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, TRUE); + + if (!msg) + return QDF_STATUS_E_NOMEM; + + /* + * Set the length of the message. + * The contribution from the HTC_HDR_ALIGNMENT_PADDING is added + * separately during the below call to qdf_nbuf_push_head. + * The contribution from the HTC header is added separately inside HTC. + */ + if (!qdf_nbuf_put_tail(msg, + sizeof(struct htt_h2t_msg_rx_fse_setup_t))) { + qdf_err("Failed to expand head for HTT RX_FSE_SETUP msg"); + return QDF_STATUS_E_FAILURE; + } + + /* fill in the message contents */ + msg_word = (u_int32_t *)qdf_nbuf_data(msg); + + memset(msg_word, 0, sizeof(struct htt_h2t_msg_rx_fse_setup_t)); + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + htt_logger_bufp = (uint8_t *)msg_word; + + *msg_word = 0; + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_RX_FSE_SETUP_CFG); + + fse_setup = (struct htt_h2t_msg_rx_fse_setup_t *)msg_word; + + HTT_RX_FSE_SETUP_PDEV_ID_SET(*msg_word, fse_setup_info->pdev_id); + + msg_word++; + HTT_RX_FSE_SETUP_NUM_REC_SET(*msg_word, fse_setup_info->max_entries); + HTT_RX_FSE_SETUP_MAX_SEARCH_SET(*msg_word, fse_setup_info->max_search); + HTT_RX_FSE_SETUP_IP_DA_SA_PREFIX_SET(*msg_word, + fse_setup_info->ip_da_sa_prefix); + + msg_word++; + HTT_RX_FSE_SETUP_BASE_ADDR_LO_SET(*msg_word, + fse_setup_info->base_addr_lo); + msg_word++; + HTT_RX_FSE_SETUP_BASE_ADDR_HI_SET(*msg_word, + fse_setup_info->base_addr_hi); + + key = (u_int32_t *)fse_setup_info->hash_key; + fse_setup->toeplitz31_0 = *key++; + fse_setup->toeplitz63_32 = *key++; + fse_setup->toeplitz95_64 = *key++; + fse_setup->toeplitz127_96 = *key++; + fse_setup->toeplitz159_128 = *key++; + fse_setup->toeplitz191_160 = *key++; + fse_setup->toeplitz223_192 = *key++; + fse_setup->toeplitz255_224 = *key++; + fse_setup->toeplitz287_256 = *key++; + fse_setup->toeplitz314_288 = *key; + + msg_word++; + HTT_RX_FSE_SETUP_HASH_VALUE_SET(*msg_word, fse_setup->toeplitz31_0); + msg_word++; + HTT_RX_FSE_SETUP_HASH_VALUE_SET(*msg_word, fse_setup->toeplitz63_32); + msg_word++; + HTT_RX_FSE_SETUP_HASH_VALUE_SET(*msg_word, fse_setup->toeplitz95_64); + msg_word++; + HTT_RX_FSE_SETUP_HASH_VALUE_SET(*msg_word, fse_setup->toeplitz127_96); + msg_word++; + HTT_RX_FSE_SETUP_HASH_VALUE_SET(*msg_word, fse_setup->toeplitz159_128); + msg_word++; + HTT_RX_FSE_SETUP_HASH_VALUE_SET(*msg_word, fse_setup->toeplitz191_160); + msg_word++; + HTT_RX_FSE_SETUP_HASH_VALUE_SET(*msg_word, fse_setup->toeplitz223_192); + msg_word++; + HTT_RX_FSE_SETUP_HASH_VALUE_SET(*msg_word, fse_setup->toeplitz255_224); + msg_word++; + HTT_RX_FSE_SETUP_HASH_VALUE_SET(*msg_word, fse_setup->toeplitz287_256); + msg_word++; + HTT_RX_FSE_SETUP_HASH_314_288_SET(*msg_word, + fse_setup->toeplitz314_288); + + pkt = htt_htc_pkt_alloc(soc); + if (!pkt) { + qdf_err("Fail to allocate dp_htt_htc_pkt buffer"); + qdf_assert(0); + qdf_nbuf_free(msg); + return QDF_STATUS_E_RESOURCES; /* failure */ + } + + pkt->soc_ctxt = NULL; /* not used during send-done callback */ + + SET_HTC_PACKET_INFO_TX( + &pkt->htc_pkt, + dp_htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + soc->htc_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + + DP_HTT_SEND_HTC_PKT(soc, pkt, HTT_H2T_MSG_TYPE_RX_FSE_SETUP_CFG, + htt_logger_bufp); + + qdf_info("HTT_H2T RX_FSE_SETUP sent to FW for pdev = %u", + fse_setup_info->pdev_id); + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_DEBUG, + (void *)fse_setup_info->hash_key, + fse_setup_info->hash_key_len); + + return QDF_STATUS_SUCCESS; +} + +/** + * dp_htt_rx_flow_fse_operation(): Send HTT Flow Search Entry msg to + * add/del a flow in HW + * @pdev: DP pdev handle + * @fse_op_info: Flow entry parameters + * + * Return: Success when HTT message is sent, error on failure + */ +QDF_STATUS +dp_htt_rx_flow_fse_operation(struct dp_pdev *pdev, + struct dp_htt_rx_flow_fst_operation *fse_op_info) +{ + struct htt_soc *soc = pdev->soc->htt_handle; + struct dp_htt_htc_pkt *pkt; + qdf_nbuf_t msg; + u_int32_t *msg_word; + struct htt_h2t_msg_rx_fse_operation_t *fse_operation; + uint8_t *htt_logger_bufp; + + msg = qdf_nbuf_alloc( + soc->osdev, + HTT_MSG_BUF_SIZE(sizeof(struct htt_h2t_msg_rx_fse_operation_t)), + /* reserve room for the HTC header */ + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, TRUE); + if (!msg) + return QDF_STATUS_E_NOMEM; + + /* + * Set the length of the message. + * The contribution from the HTC_HDR_ALIGNMENT_PADDING is added + * separately during the below call to qdf_nbuf_push_head. + * The contribution from the HTC header is added separately inside HTC. + */ + if (!qdf_nbuf_put_tail(msg, + sizeof(struct htt_h2t_msg_rx_fse_operation_t))) { + qdf_err("Failed to expand head for HTT_RX_FSE_OPERATION msg"); + return QDF_STATUS_E_FAILURE; + } + + /* fill in the message contents */ + msg_word = (u_int32_t *)qdf_nbuf_data(msg); + + memset(msg_word, 0, sizeof(struct htt_h2t_msg_rx_fse_operation_t)); + /* rewind beyond alignment pad to get to the HTC header reserved area */ + qdf_nbuf_push_head(msg, HTC_HDR_ALIGNMENT_PADDING); + htt_logger_bufp = (uint8_t *)msg_word; + + *msg_word = 0; + HTT_H2T_MSG_TYPE_SET(*msg_word, HTT_H2T_MSG_TYPE_RX_FSE_OPERATION_CFG); + + fse_operation = (struct htt_h2t_msg_rx_fse_operation_t *)msg_word; + + HTT_RX_FSE_OPERATION_PDEV_ID_SET(*msg_word, fse_op_info->pdev_id); + msg_word++; + HTT_RX_FSE_IPSEC_VALID_SET(*msg_word, false); + if (fse_op_info->op_code == DP_HTT_FST_CACHE_INVALIDATE_ENTRY) { + HTT_RX_FSE_OPERATION_SET(*msg_word, + HTT_RX_FSE_CACHE_INVALIDATE_ENTRY); + msg_word++; + HTT_RX_FSE_OPERATION_IP_ADDR_SET( + *msg_word, + qdf_htonl(fse_op_info->rx_flow->flow_tuple_info.src_ip_31_0)); + msg_word++; + HTT_RX_FSE_OPERATION_IP_ADDR_SET( + *msg_word, + qdf_htonl(fse_op_info->rx_flow->flow_tuple_info.src_ip_63_32)); + msg_word++; + HTT_RX_FSE_OPERATION_IP_ADDR_SET( + *msg_word, + qdf_htonl(fse_op_info->rx_flow->flow_tuple_info.src_ip_95_64)); + msg_word++; + HTT_RX_FSE_OPERATION_IP_ADDR_SET( + *msg_word, + qdf_htonl(fse_op_info->rx_flow->flow_tuple_info.src_ip_127_96)); + msg_word++; + HTT_RX_FSE_OPERATION_IP_ADDR_SET( + *msg_word, + qdf_htonl(fse_op_info->rx_flow->flow_tuple_info.dest_ip_31_0)); + msg_word++; + HTT_RX_FSE_OPERATION_IP_ADDR_SET( + *msg_word, + qdf_htonl(fse_op_info->rx_flow->flow_tuple_info.dest_ip_63_32)); + msg_word++; + HTT_RX_FSE_OPERATION_IP_ADDR_SET( + *msg_word, + qdf_htonl(fse_op_info->rx_flow->flow_tuple_info.dest_ip_95_64)); + msg_word++; + HTT_RX_FSE_OPERATION_IP_ADDR_SET( + *msg_word, + qdf_htonl( + fse_op_info->rx_flow->flow_tuple_info.dest_ip_127_96)); + msg_word++; + HTT_RX_FSE_SOURCEPORT_SET( + *msg_word, + fse_op_info->rx_flow->flow_tuple_info.src_port); + HTT_RX_FSE_DESTPORT_SET( + *msg_word, + fse_op_info->rx_flow->flow_tuple_info.dest_port); + msg_word++; + HTT_RX_FSE_L4_PROTO_SET( + *msg_word, + fse_op_info->rx_flow->flow_tuple_info.l4_protocol); + } else if (fse_op_info->op_code == DP_HTT_FST_CACHE_INVALIDATE_FULL) { + HTT_RX_FSE_OPERATION_SET(*msg_word, + HTT_RX_FSE_CACHE_INVALIDATE_FULL); + } else if (fse_op_info->op_code == DP_HTT_FST_DISABLE) { + HTT_RX_FSE_OPERATION_SET(*msg_word, HTT_RX_FSE_DISABLE); + } else if (fse_op_info->op_code == DP_HTT_FST_ENABLE) { + HTT_RX_FSE_OPERATION_SET(*msg_word, HTT_RX_FSE_ENABLE); + } + + pkt = htt_htc_pkt_alloc(soc); + if (!pkt) { + qdf_err("Fail to allocate dp_htt_htc_pkt buffer"); + qdf_assert(0); + qdf_nbuf_free(msg); + return QDF_STATUS_E_RESOURCES; /* failure */ + } + + pkt->soc_ctxt = NULL; /* not used during send-done callback */ + + SET_HTC_PACKET_INFO_TX( + &pkt->htc_pkt, + dp_htt_h2t_send_complete_free_netbuf, + qdf_nbuf_data(msg), + qdf_nbuf_len(msg), + soc->htc_endpoint, + 1); /* tag - not relevant here */ + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + + DP_HTT_SEND_HTC_PKT(soc, pkt, HTT_H2T_MSG_TYPE_RX_FSE_OPERATION_CFG, + htt_logger_bufp); + + qdf_info("HTT_H2T RX_FSE_OPERATION_CFG sent to FW for pdev = %u", + fse_op_info->pdev_id); + + return QDF_STATUS_SUCCESS; +} diff --git a/dp/wifi3.0/dp_htt.h b/dp/wifi3.0/dp_htt.h index aed9233103..9cb24f0a7e 100644 --- a/dp/wifi3.0/dp_htt.h +++ b/dp/wifi3.0/dp_htt.h @@ -207,6 +207,56 @@ struct htt_rx_ring_tlv_filter { uint16_t rx_attn_offset; }; +/** + * struct dp_htt_rx_flow_fst_setup - Rx FST setup message + * @pdev_id: DP Pdev identifier + * @max_entries: Size of Rx FST in number of entries + * @max_search: Number of collisions allowed + * @base_addr_lo: lower 32-bit physical address + * @base_addr_hi: upper 32-bit physical address + * @ip_da_sa_prefix: IPv4 prefix to map to IPv6 address scheme + * @hash_key_len: Rx FST hash key size + * @hash_key: Rx FST Toeplitz hash key + */ +struct dp_htt_rx_flow_fst_setup { + uint8_t pdev_id; + uint32_t max_entries; + uint32_t max_search; + uint32_t base_addr_lo; + uint32_t base_addr_hi; + uint32_t ip_da_sa_prefix; + uint32_t hash_key_len; + uint8_t *hash_key; +}; + +/** + * enum dp_htt_flow_fst_operation - FST related operations allowed + * @DP_HTT_FST_CACHE_OP_NONE: Cache no-op + * @DP_HTT_FST_CACHE_INVALIDATE_ENTRY: Invalidate single cache entry + * @DP_HTT_FST_CACHE_INVALIDATE_FULL: Invalidate entire cache + * @DP_HTT_FST_ENABLE: Bypass FST is enabled + * @DP_HTT_FST_DISABLE: Disable bypass FST + */ +enum dp_htt_flow_fst_operation { + DP_HTT_FST_CACHE_OP_NONE, + DP_HTT_FST_CACHE_INVALIDATE_ENTRY, + DP_HTT_FST_CACHE_INVALIDATE_FULL, + DP_HTT_FST_ENABLE, + DP_HTT_FST_DISABLE +}; + +/** + * struct dp_htt_rx_flow_fst_setup - Rx FST setup message + * @pdev_id: DP Pdev identifier + * @op_code: FST operation to be performed by FW/HW + * @rx_flow: Rx Flow information on which operation is to be performed + */ +struct dp_htt_rx_flow_fst_operation { + uint8_t pdev_id; + enum dp_htt_flow_fst_operation op_code; + struct cdp_rx_flow_info *rx_flow; +}; + /* * htt_soc_initialize() - SOC level HTT initialization * @htt_soc: Opaque htt SOC handle @@ -325,4 +375,26 @@ void dp_ppdu_desc_user_stats_update(struct dp_pdev *pdev, struct ppdu_info *ppdu_info); +/** + * dp_htt_rx_flow_fst_setup(): Send HTT Rx FST setup message to FW + * @pdev: DP pdev handle + * @fse_setup_info: FST setup parameters + * + * Return: Success when HTT message is sent, error on failure + */ +QDF_STATUS +dp_htt_rx_flow_fst_setup(struct dp_pdev *pdev, + struct dp_htt_rx_flow_fst_setup *setup_info); + +/** + * dp_htt_rx_flow_fse_operation(): Send HTT Flow Search Entry msg to + * add/del a flow in HW + * @pdev: DP pdev handle + * @fse_op_info: Flow entry parameters + * + * Return: Success when HTT message is sent, error on failure + */ +QDF_STATUS +dp_htt_rx_flow_fse_operation(struct dp_pdev *pdev, + struct dp_htt_rx_flow_fst_operation *op_info); #endif /* _DP_HTT_H_ */ diff --git a/dp/wifi3.0/dp_internal.h b/dp/wifi3.0/dp_internal.h index 14f8e675d9..c064589a37 100644 --- a/dp/wifi3.0/dp_internal.h +++ b/dp/wifi3.0/dp_internal.h @@ -1405,4 +1405,90 @@ struct cdp_soc_t *dp_soc_to_cdp_soc_t(struct dp_soc *psoc) { return (struct cdp_soc_t *)psoc; } + +#ifdef WLAN_SUPPORT_RX_FLOW_TAG +/** + * dp_rx_flow_update_fse_stats() - Update a flow's statistics + * @pdev: pdev handle + * @flow_id: flow index (truncated hash) in the Rx FST + * + * Return: Success when flow statistcs is updated, error on failure + */ +QDF_STATUS dp_rx_flow_get_fse_stats(struct dp_pdev *pdev, + struct cdp_rx_flow_info *rx_flow_info, + struct cdp_flow_stats *stats); + +/** + * dp_rx_flow_delete_entry() - Delete a flow entry from flow search table + * @pdev: pdev handle + * @rx_flow_info: DP flow parameters + * + * Return: Success when flow is deleted, error on failure + */ +QDF_STATUS dp_rx_flow_delete_entry(struct dp_pdev *pdev, + struct cdp_rx_flow_info *rx_flow_info); + +/** + * dp_rx_flow_add_entry() - Add a flow entry to flow search table + * @pdev: DP pdev instance + * @rx_flow_info: DP flow paramaters + * + * Return: Success when flow is added, no-memory or already exists on error + */ +QDF_STATUS dp_rx_flow_add_entry(struct dp_pdev *pdev, + struct cdp_rx_flow_info *rx_flow_info); + +/** + * dp_rx_fst_attach() - Initialize Rx FST and setup necessary parameters + * @soc: SoC handle + * @pdev: Pdev handle + * + * Return: Handle to flow search table entry + */ +QDF_STATUS dp_rx_fst_attach(struct dp_soc *soc, struct dp_pdev *pdev); + +/** + * dp_rx_fst_detach() - De-initialize Rx FST + * @soc: SoC handle + * @pdev: Pdev handle + * + * Return: None + */ +void dp_rx_fst_detach(struct dp_soc *soc, struct dp_pdev *pdev); + +/** + * dp_rx_flow_send_fst_fw_setup() - Program FST parameters in FW/HW post-attach + * @soc: SoC handle + * @pdev: Pdev handle + * + * Return: Success when fst parameters are programmed in FW, error otherwise + */ +QDF_STATUS dp_rx_flow_send_fst_fw_setup(struct dp_soc *soc, + struct dp_pdev *pdev); +#else +/** + * dp_rx_fst_attach() - Initialize Rx FST and setup necessary parameters + * @soc: SoC handle + * @pdev: Pdev handle + * + * Return: Handle to flow search table entry + */ +static inline +QDF_STATUS dp_rx_fst_attach(struct dp_soc *soc, struct dp_pdev *pdev) +{ + return QDF_STATUS_SUCCESS; +} + +/** + * dp_rx_fst_detach() - De-initialize Rx FST + * @soc: SoC handle + * @pdev: Pdev handle + * + * Return: None + */ +static inline +void dp_rx_fst_detach(struct dp_soc *soc, struct dp_pdev *pdev) +{ +} +#endif /* WLAN_SUPPORT_RX_FLOW_TAG */ #endif /* #ifndef _DP_INTERNAL_H_ */ diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c index a89c2f40a8..834a970588 100644 --- a/dp/wifi3.0/dp_main.c +++ b/dp/wifi3.0/dp_main.c @@ -3835,6 +3835,7 @@ static void dp_pdev_deinit(struct cdp_pdev *txrx_pdev, int force) dp_pktlogmod_exit(pdev); + dp_rx_fst_detach(soc, pdev); dp_rx_pdev_detach(pdev); dp_rx_pdev_mon_detach(pdev); dp_neighbour_peers_detach(pdev); @@ -4538,6 +4539,50 @@ dp_rxdma_ring_sel_cfg(struct dp_soc *soc) } #endif +/* + * dp_rx_target_fst_config() - configure the RXOLE Flow Search Engine + * + * This function is used to configure the FSE HW block in RX OLE on a + * per pdev basis. Here, we will be programming parameters related to + * the Flow Search Table. + * + * @soc: data path SoC handle + * + * Return: zero on success, non-zero on failure + */ +#ifdef WLAN_SUPPORT_RX_FLOW_TAG +static QDF_STATUS +dp_rx_target_fst_config(struct dp_soc *soc) +{ + int i; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + for (i = 0; i < MAX_PDEV_CNT; i++) { + struct dp_pdev *pdev = soc->pdev_list[i]; + + if (pdev) { + status = dp_rx_flow_send_fst_fw_setup(pdev->soc, pdev); + if (status != QDF_STATUS_SUCCESS) + break; + } + } + return status; +} +#else +/** + * dp_rx_target_fst_config() - Configure RX OLE FSE engine in HW + * @soc: SoC handle + * + * Return: Success + */ +static inline QDF_STATUS +dp_rx_target_fst_config(struct dp_soc *soc) +{ + return QDF_STATUS_SUCCESS; +} + +#endif /* WLAN_SUPPORT_RX_FLOW_TAG */ + /* * dp_soc_attach_target_wifi3() - SOC initialization in the target * @cdp_soc: Opaque Datapath SOC handle @@ -4564,6 +4609,12 @@ dp_soc_attach_target_wifi3(struct cdp_soc_t *cdp_soc) return status; } + status = dp_rx_target_fst_config(soc); + if (status != QDF_STATUS_SUCCESS) { + dp_err("Failed to send htt fst setup config message to target"); + return status; + } + DP_STATS_INIT(soc); /* initialize work queue for stats processing */ @@ -6277,10 +6328,13 @@ QDF_STATUS dp_pdev_configure_monitor_rings(struct dp_pdev *pdev) htt_tlv_filter.enable_mo = 0; } else if (pdev->rx_enh_capture_mode == CDP_RX_ENH_CAPTURE_MPDU_MSDU) { + bool is_rx_mon_proto_flow_tag_enabled = + wlan_cfg_is_rx_mon_protocol_flow_tag_enabled( + soc->wlan_cfg_ctx); htt_tlv_filter.header_per_msdu = 1; htt_tlv_filter.enable_mo = 0; - if (pdev->is_rx_protocol_tagging_enabled || - pdev->is_rx_enh_capture_trailer_enabled) + if (pdev->is_rx_enh_capture_trailer_enabled || + is_rx_mon_proto_flow_tag_enabled) htt_tlv_filter.msdu_end = 1; } } @@ -8626,6 +8680,97 @@ dp_update_pdev_rx_protocol_tag(struct cdp_pdev *pdev_handle, } #endif /* WLAN_SUPPORT_RX_PROTOCOL_TYPE_TAG */ +#ifdef WLAN_SUPPORT_RX_FLOW_TAG +/** + * dp_set_rx_flow_tag - add/delete a flow + * @pdev_handle: cdp_pdev handle + * @flow_info: flow tuple that is to be added to/deleted from flow search table + * + * Return: 0 for success, nonzero for failure + */ +static inline QDF_STATUS +dp_set_rx_flow_tag(struct cdp_pdev *pdev_handle, + struct cdp_rx_flow_info *flow_info) +{ + struct dp_pdev *pdev = (struct dp_pdev *)pdev_handle; + struct wlan_cfg_dp_soc_ctxt *cfg = pdev->soc->wlan_cfg_ctx; + + if (qdf_unlikely(!wlan_cfg_is_rx_flow_tag_enabled(cfg))) { + dp_err("RX Flow tag feature disabled"); + return QDF_STATUS_E_NOSUPPORT; + } + + if (flow_info->op_code == CDP_FLOW_FST_ENTRY_ADD) + return dp_rx_flow_add_entry(pdev, flow_info); + if (flow_info->op_code == CDP_FLOW_FST_ENTRY_DEL) + return dp_rx_flow_delete_entry(pdev, flow_info); + + return QDF_STATUS_E_INVAL; +} + +/** + * dp_dump_rx_flow_tag_stats - dump the number of packets tagged for + * given flow 5-tuple + * @pdev_handle: cdp_pdev handle + * @flow_info: flow 5-tuple for which stats should be displayed + * + * Return: 0 for success, nonzero for failure + */ +static inline QDF_STATUS +dp_dump_rx_flow_tag_stats(struct cdp_pdev *pdev_handle, + struct cdp_rx_flow_info *flow_info) +{ + QDF_STATUS status; + struct cdp_flow_stats stats; + struct dp_pdev *pdev = (struct dp_pdev *)pdev_handle; + struct wlan_cfg_dp_soc_ctxt *cfg = pdev->soc->wlan_cfg_ctx; + + if (qdf_unlikely(!wlan_cfg_is_rx_flow_tag_enabled(cfg))) { + dp_err("RX Flow tag feature disabled"); + return QDF_STATUS_E_NOSUPPORT; + } + + status = dp_rx_flow_get_fse_stats(pdev, flow_info, &stats); + + if (status != QDF_STATUS_SUCCESS) { + dp_err("Unable to get flow stats, error: %u", status); + return status; + } + + DP_PRINT_STATS("Dest IP address %x:%x:%x:%x", + flow_info->flow_tuple_info.dest_ip_127_96, + flow_info->flow_tuple_info.dest_ip_95_64, + flow_info->flow_tuple_info.dest_ip_63_32, + flow_info->flow_tuple_info.dest_ip_31_0); + DP_PRINT_STATS("Source IP address %x:%x:%x:%x", + flow_info->flow_tuple_info.src_ip_127_96, + flow_info->flow_tuple_info.src_ip_95_64, + flow_info->flow_tuple_info.src_ip_63_32, + flow_info->flow_tuple_info.src_ip_31_0); + DP_PRINT_STATS("Dest port %u, Src Port %u, Protocol %u", + flow_info->flow_tuple_info.dest_port, + flow_info->flow_tuple_info.src_port, + flow_info->flow_tuple_info.l4_protocol); + DP_PRINT_STATS("MSDU Count: %u", stats.msdu_count); + + return status; +} +#else +static inline QDF_STATUS +dp_set_rx_flow_tag(struct cdp_pdev *pdev, + struct cdp_rx_flow_info *flow_info) +{ + return QDF_STATUS_E_FAILURE; +} + +static inline QDF_STATUS +dp_dump_rx_flow_tag_stats(struct cdp_pdev *pdev, + struct cdp_rx_flow_info *flow_info) +{ + return QDF_STATUS_E_FAILURE; +} +#endif /* WLAN_SUPPORT_RX_FLOW_TAG */ + static QDF_STATUS dp_peer_map_attach_wifi3(struct cdp_soc_t *soc_hdl, uint32_t max_peers, uint32_t max_ast_index, @@ -9086,6 +9231,10 @@ static struct cdp_ctrl_ops dp_ops_ctrl = { dp_dump_pdev_rx_protocol_tag_stats, #endif /* WLAN_SUPPORT_RX_TAG_STATISTICS */ #endif /* WLAN_SUPPORT_RX_PROTOCOL_TYPE_TAG */ +#ifdef WLAN_SUPPORT_RX_FLOW_TAG + .txrx_set_rx_flow_tag = dp_set_rx_flow_tag, + .txrx_dump_rx_flow_tag_stats = dp_dump_rx_flow_tag_stats, +#endif /* WLAN_SUPPORT_RX_FLOW_TAG */ #ifdef QCA_MULTIPASS_SUPPORT .txrx_peer_set_vlan_id = dp_peer_set_vlan_id, #endif /*QCA_MULTIPASS_SUPPORT*/ @@ -9647,6 +9796,7 @@ void *dp_soc_init(void *dpsoc, HTC_HANDLE htc_handle, REO_DST_RING_SIZE_QCA8074); wlan_cfg_set_raw_mode_war(soc->wlan_cfg_ctx, true); soc->da_war_enabled = true; + soc->is_rx_fse_full_cache_invalidate_war_enabled = true; break; case TARGET_TYPE_QCA8074V2: case TARGET_TYPE_QCA6018: @@ -9658,6 +9808,7 @@ void *dp_soc_init(void *dpsoc, HTC_HANDLE htc_handle, soc->per_tid_basize_max_tid = 8; soc->num_hw_dscp_tid_map = HAL_MAX_HW_DSCP_TID_V2_MAPS; soc->da_war_enabled = false; + soc->is_rx_fse_full_cache_invalidate_war_enabled = true; break; default: qdf_print("%s: Unknown tgt type %d\n", __func__, target_type); diff --git a/dp/wifi3.0/dp_rx.c b/dp/wifi3.0/dp_rx.c index db468cb2ce..a31c5ea993 100644 --- a/dp/wifi3.0/dp_rx.c +++ b/dp/wifi3.0/dp_rx.c @@ -2064,6 +2064,9 @@ done: dp_rx_update_protocol_tag(soc, vdev, nbuf, rx_tlv_hdr, reo_ring_num, false, true); + /* Update the flow tag in SKB based on FSE metadata */ + dp_rx_update_flow_tag(soc, vdev, nbuf, rx_tlv_hdr, true); + dp_rx_msdu_stats_update(soc, nbuf, rx_tlv_hdr, peer, ring_id, tid_stats); @@ -2371,6 +2374,8 @@ dp_rx_pdev_attach(struct dp_pdev *pdev) uint32_t rx_sw_desc_weight; struct dp_srng *dp_rxdma_srng; struct rx_desc_pool *rx_desc_pool; + QDF_STATUS ret_val; + if (wlan_cfg_get_dp_pdev_nss_enabled(pdev->wlan_cfg_ctx)) { QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO, @@ -2394,6 +2399,15 @@ dp_rx_pdev_attach(struct dp_pdev *pdev) rx_desc_pool->owner = DP_WBM2SW_RBM; /* For Rx buffers, WBM release ring is SW RING 3,for all pdev's */ + ret_val = dp_rx_fst_attach(soc, pdev); + if ((ret_val != QDF_STATUS_SUCCESS) && + (ret_val != QDF_STATUS_E_NOSUPPORT)) { + QDF_TRACE(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_ERROR, + "RX Flow Search Table attach failed: pdev %d err %d", + pdev_id, ret_val); + return ret_val; + } + return dp_pdev_rx_buffers_attach(soc, pdev_id, dp_rxdma_srng, rx_desc_pool, rxdma_entries - 1); } diff --git a/dp/wifi3.0/dp_rx.h b/dp/wifi3.0/dp_rx.h index c98787ef14..6c9b2e84d1 100644 --- a/dp/wifi3.0/dp_rx.h +++ b/dp/wifi3.0/dp_rx.h @@ -901,6 +901,7 @@ void dp_rx_update_rx_err_protocol_tag_stats(struct dp_pdev *pdev, { } #endif /* WLAN_SUPPORT_RX_TAG_STATISTICS */ + /** * dp_rx_update_protocol_tag() - Reads CCE metadata from the RX MSDU end TLV * and set the corresponding tag in QDF packet @@ -977,7 +978,7 @@ dp_rx_update_protocol_tag(struct dp_soc *soc, struct dp_vdev *vdev, protocol_tag = pdev->rx_proto_tag_map[cce_metadata].tag; qdf_nbuf_set_rx_protocol_tag(nbuf, protocol_tag); QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_LOW, - "Seq:%u decap:%u CCE Match:%d ProtoID:%u Tag:%u US:%d", + "Seq:%u dcap:%u CCE Match:%u ProtoID:%u Tag:%u stats:%u", hal_rx_get_rx_sequence(rx_tlv_hdr), vdev->rx_decap_type, cce_match, cce_metadata, protocol_tag, is_update_stats); @@ -1007,23 +1008,122 @@ dp_rx_update_protocol_tag(struct dp_soc *soc, struct dp_vdev *vdev, #endif /* WLAN_SUPPORT_RX_PROTOCOL_TYPE_TAG */ /** - * dp_rx_mon_update_protocol_tag() - Performs necessary checks for monitor mode - * and then tags appropriate packets + * dp_rx_update_rx_flow_tag_stats() - Update stats for given flow index + * @pdev: TXRX pdev context for which stats should be incremented + * @flow_index: flow index for which the stats should be incremented + * + * Return: void + */ +#ifdef WLAN_SUPPORT_RX_FLOW_TAG +QDF_STATUS dp_rx_flow_update_fse_stats(struct dp_pdev *pdev, uint32_t flow_id); + +static inline void dp_rx_update_rx_flow_tag_stats(struct dp_pdev *pdev, + uint32_t flow_index) +{ + dp_rx_flow_update_fse_stats(pdev, flow_index); +} +#else +static inline void dp_rx_update_rx_flow_tag_stats(struct dp_pdev *pdev, + uint32_t flow_index) +{ +} +#endif /* WLAN_SUPPORT_RX_FLOW_TAG */ + +/** + * dp_rx_update_flow_tag() - Reads FSE metadata from the RX MSDU end TLV + * and set the corresponding tag in QDF packet + * @soc: core txrx main context + * @vdev: vdev on which the packet is received + * @nbuf: QDF pkt buffer on which the protocol tag should be set + * @rx_tlv_hdr: base address where the RX TLVs starts + * @is_update_stats: flag to indicate whether to update stats or not + * + * Return: void + */ +#ifdef WLAN_SUPPORT_RX_FLOW_TAG +static inline void +dp_rx_update_flow_tag(struct dp_soc *soc, struct dp_vdev *vdev, + qdf_nbuf_t nbuf, uint8_t *rx_tlv_hdr, bool update_stats) +{ + bool flow_idx_invalid, flow_idx_timeout; + uint32_t flow_idx, fse_metadata; + struct dp_pdev *pdev; + + if (qdf_unlikely(!vdev)) + return; + + pdev = vdev->pdev; + if (qdf_likely(!wlan_cfg_is_rx_flow_tag_enabled(soc->wlan_cfg_ctx))) + return; + + /** + * In case of raw frames, rx_msdu_end tlv may be stale or invalid. + * Do not tag such frames in normal REO path. + * Default decap_type is set to ethernet for monitor vdev currently, + * therefore, we will not check decap_type for monitor mode. + * We will call this only for eth frames from dp_rx_mon_dest.c. + */ + if (qdf_likely((vdev->rx_decap_type != htt_cmn_pkt_type_ethernet))) + return; + + flow_idx_invalid = hal_rx_msdu_flow_idx_invalid(rx_tlv_hdr); + hal_rx_msdu_get_flow_params(rx_tlv_hdr, &flow_idx_invalid, + &flow_idx_timeout, &flow_idx); + if (qdf_unlikely(flow_idx_invalid)) + return; + + if (qdf_unlikely(flow_idx_timeout)) + return; + + /** + * Limit FSE metadata to 16 bit as we have allocated only + * 16 bits for flow_tag field in skb->cb + */ + fse_metadata = hal_rx_msdu_fse_metadata_get(rx_tlv_hdr) & 0xFFFF; + + /* update the skb->cb with the user-specified tag/metadata */ + qdf_nbuf_set_rx_flow_tag(nbuf, fse_metadata); + + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_LOW, + "Seq:%u dcap:%u invalid:%u timeout:%u flow:%u tag:%u stat:%u", + hal_rx_get_rx_sequence(rx_tlv_hdr), + vdev->rx_decap_type, flow_idx_invalid, flow_idx_timeout, + flow_idx, fse_metadata, update_stats); + + if (qdf_likely(update_stats)) + dp_rx_update_rx_flow_tag_stats(pdev, flow_idx); +} +#else +static inline void +dp_rx_update_flow_tag(struct dp_soc *soc, struct dp_vdev *vdev, + qdf_nbuf_t nbuf, uint8_t *rx_tlv_hdr, bool update_stats) +{ +} +#endif /* WLAN_SUPPORT_RX_FLOW_TAG */ + +/** + * dp_rx_mon_update_protocol_flow_tag() - Performs necessary checks for monitor + * mode and then tags appropriate packets * @soc: core txrx main context * @vdev: pdev on which packet is received * @msdu: QDF packet buffer on which the protocol tag should be set * @rx_desc: base address where the RX TLVs start * Return: void */ -#ifdef WLAN_SUPPORT_RX_PROTOCOL_TYPE_TAG +#if defined(WLAN_SUPPORT_RX_PROTOCOL_TYPE_TAG) ||\ + defined(WLAN_SUPPORT_RX_FLOW_TAG) static inline -void dp_rx_mon_update_protocol_tag(struct dp_soc *soc, struct dp_pdev *dp_pdev, - qdf_nbuf_t msdu, void *rx_desc) +void dp_rx_mon_update_protocol_flow_tag(struct dp_soc *soc, + struct dp_pdev *dp_pdev, + qdf_nbuf_t msdu, void *rx_desc) { uint32_t msdu_ppdu_id = 0; struct mon_rx_status *mon_recv_status; - if (qdf_likely(!dp_pdev->is_rx_protocol_tagging_enabled)) + bool is_mon_protocol_flow_tag_enabled = + wlan_cfg_is_rx_mon_protocol_flow_tag_enabled(soc->wlan_cfg_ctx); + + if (qdf_likely(!is_mon_protocol_flow_tag_enabled)) return; if (qdf_likely(!dp_pdev->monitor_vdev)) @@ -1043,29 +1143,35 @@ void dp_rx_mon_update_protocol_tag(struct dp_soc *soc, struct dp_pdev *dp_pdev, return; } - /* - * Update the protocol tag in SKB for packets received on BSS. - * Do not update tag stats since it would double actual received count - */ mon_recv_status = &dp_pdev->ppdu_info.rx_status; if (mon_recv_status->frame_control_info_valid && ((mon_recv_status->frame_control & IEEE80211_FC0_TYPE_MASK) == IEEE80211_FC0_TYPE_DATA)) { + /* + * Update the protocol tag in SKB for packets received on BSS. + * Do not update tag stats since it would double actual + * received count. + */ dp_rx_update_protocol_tag(soc, dp_pdev->monitor_vdev, msdu, rx_desc, MAX_REO_DEST_RINGS, false, false); + /* Update the flow tag in SKB based on FSE metadata */ + dp_rx_update_flow_tag(soc, dp_pdev->monitor_vdev, + msdu, rx_desc, false); } } #else static inline -void dp_rx_mon_update_protocol_tag(struct dp_soc *soc, struct dp_pdev *dp_pdev, - qdf_nbuf_t msdu, void *rx_desc) +void dp_rx_mon_update_protocol_flow_tag(struct dp_soc *soc, + struct dp_pdev *dp_pdev, + qdf_nbuf_t msdu, void *rx_desc) { /* Stub API */ } -#endif /* WLAN_SUPPORT_RX_PROTOCOL_TYPE_TAG */ +#endif /* WLAN_SUPPORT_RX_PROTOCOL_TYPE_TAG || WLAN_SUPPORT_RX_FLOW_TAG */ + /* * dp_rx_buffers_replenish() - replenish rxdma ring with rx nbufs * called during dp rx initialization diff --git a/dp/wifi3.0/dp_rx_err.c b/dp/wifi3.0/dp_rx_err.c index c38d7327a9..bd69385fd5 100644 --- a/dp/wifi3.0/dp_rx_err.c +++ b/dp/wifi3.0/dp_rx_err.c @@ -819,6 +819,10 @@ dp_rx_null_q_desc_handle(struct dp_soc *soc, qdf_nbuf_t nbuf, EXCEPTION_DEST_RING_ID, true, true); + /* Update the flow tag in SKB based on FSE metadata */ + dp_rx_update_flow_tag(soc, vdev, nbuf, + rx_tlv_hdr, true); + if (qdf_unlikely(hal_rx_msdu_end_da_is_mcbc_get( rx_tlv_hdr) && (vdev->rx_decap_type == @@ -1000,6 +1004,8 @@ process_rx: /* Update the protocol tag in SKB based on CCE metadata */ dp_rx_update_protocol_tag(soc, vdev, nbuf, rx_tlv_hdr, EXCEPTION_DEST_RING_ID, true, true); + /* Update the flow tag in SKB based on FSE metadata */ + dp_rx_update_flow_tag(soc, vdev, nbuf, rx_tlv_hdr, true); DP_STATS_INC(peer, rx.to_stack.num, 1); vdev->osif_rx(vdev->osif_vdev, nbuf); } diff --git a/dp/wifi3.0/dp_rx_mon_dest.c b/dp/wifi3.0/dp_rx_mon_dest.c index 939a088c84..ca77f892bb 100644 --- a/dp/wifi3.0/dp_rx_mon_dest.c +++ b/dp/wifi3.0/dp_rx_mon_dest.c @@ -758,8 +758,9 @@ qdf_nbuf_t dp_rx_mon_restitch_mpdu_from_msdus(struct dp_soc *soc, is_first_frag = 0; } - /* Update protocol tag for MSDU */ - dp_rx_mon_update_protocol_tag(soc, dp_pdev, msdu_orig, rx_desc); + /* Update protocol and flow tag for MSDU */ + dp_rx_mon_update_protocol_flow_tag(soc, dp_pdev, + msdu_orig, rx_desc); dest = qdf_nbuf_put_tail(prev_buf, msdu_llc_len + amsdu_pad); diff --git a/dp/wifi3.0/dp_stats.c b/dp/wifi3.0/dp_stats.c index 4e558e18d8..2819489224 100644 --- a/dp/wifi3.0/dp_stats.c +++ b/dp/wifi3.0/dp_stats.c @@ -4525,6 +4525,12 @@ void dp_print_soc_cfg_params(struct dp_soc *soc) soc_cfg_ctx->tx_sw_internode_queue); DP_PRINT_STATS("RXDMA err dst ring: %u ", soc_cfg_ctx->rxdma_err_dst_ring); + DP_PRINT_STATS("RX Flow Tag Enabled: %u ", + soc_cfg_ctx->is_rx_flow_tag_enabled); + DP_PRINT_STATS("RX Flow Search Table Size (# of entries): %u ", + soc_cfg_ctx->rx_flow_search_table_size); + DP_PRINT_STATS("RX Flow Search Table Per PDev : %u ", + soc_cfg_ctx->is_rx_flow_search_table_per_pdev); } void diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h index 2796253638..3f51331f3f 100644 --- a/dp/wifi3.0/dp_types.h +++ b/dp/wifi3.0/dp_types.h @@ -43,6 +43,7 @@ #include #include #include "hal_rx.h" +//#include "hal_rx_flow.h" #define MAX_BW 7 #define MAX_RETRIES 4 @@ -116,6 +117,7 @@ struct dp_soc; union dp_rx_desc_list_elem_t; struct cdp_peer_rate_stats_ctx; struct cdp_soc_rate_stats_ctx; +struct dp_rx_fst; #define DP_PDEV_ITERATE_VDEV_LIST(_pdev, _vdev) \ TAILQ_FOREACH((_vdev), &(_pdev)->vdev_list, vdev_list_elem) @@ -1128,6 +1130,19 @@ struct dp_soc { qdf_atomic_t num_tx_outstanding; /* Num Tx allowed */ uint32_t num_tx_allowed; + + /** + * Flag to indicate whether WAR to address single cache entry + * invalidation bug is enabled or not + */ + bool is_rx_fse_full_cache_invalidate_war_enabled; +#ifdef WLAN_SUPPORT_RX_FLOW_TAG + /** + * Pointer to DP RX Flow FST at SOC level if + * is_rx_flow_search_table_per_pdev is false + */ + struct dp_rx_fst *rx_fst; +#endif /* WLAN_SUPPORT_RX_FLOW_TAG */ }; #ifdef IPA_OFFLOAD @@ -1292,13 +1307,13 @@ struct dp_peer_tx_capture { * at end of each MSDU in monitor-lite mode * @reserved1: reserved for future use * @reserved2: reserved for future use - * @reserved3: reserved for future use + * @flow_tag: flow tag value read from skb->cb * @protocol_tag: protocol tag value read from skb->cb */ struct dp_rx_mon_enh_trailer_data { uint16_t reserved1; uint16_t reserved2; - uint16_t reserved3; + uint16_t flow_tag; uint16_t protocol_tag; }; #endif /* WLAN_RX_PKT_CAPTURE_ENH */ @@ -1647,6 +1662,13 @@ struct dp_pdev { * belonging to one ppdu */ qdf_nbuf_queue_t rx_ppdu_buf_q; +#ifdef WLAN_SUPPORT_RX_FLOW_TAG + /** + * Pointer to DP Flow FST at SOC level if + * is_rx_flow_search_table_per_pdev is true + */ + struct dp_rx_fst *rx_fst; +#endif /* WLAN_SUPPORT_RX_FLOW_TAG */ }; struct dp_peer; @@ -1998,4 +2020,49 @@ struct dp_tx_me_buf_t { uint8_t data[QDF_MAC_ADDR_SIZE]; }; +#ifdef WLAN_SUPPORT_RX_FLOW_TAG +struct hal_rx_fst; + +struct dp_rx_fse { + /* HAL Rx Flow Search Entry which matches HW definition */ + void *hal_rx_fse; + /* Toeplitz hash value */ + uint32_t flow_hash; + /* Flow index, equivalent to hash value truncated to FST size */ + uint32_t flow_id; + /* Stats tracking for this flow */ + struct cdp_flow_stats stats; + /* Flag indicating whether flow is IPv4 address tuple */ + bool is_ipv4_addr_entry; + /* Flag indicating whether flow is valid */ + bool is_valid; +}; + +struct dp_rx_fst { + /* Software (DP) FST */ + uint8_t *base; + /* Pointer to HAL FST */ + struct hal_rx_fst *hal_rx_fst; + /* Base physical address of HAL RX HW FST */ + uint64_t hal_rx_fst_base_paddr; + /* Maximum number of flows FSE supports */ + uint16_t max_entries; + /* Num entries in flow table */ + uint16_t num_entries; + /* SKID Length */ + uint16_t max_skid_length; + /* Hash mask to obtain legitimate hash entry */ + uint32_t hash_mask; + /* Timer for bundling of flows */ + qdf_timer_t cache_invalidate_timer; + /** + * Flag which tracks whether cache update + * is needed on timer expiry + */ + qdf_atomic_t is_cache_update_pending; + /* Flag to indicate completion of FSE setup in HW/FW */ + bool fse_setup_done; +}; +#endif /* WLAN_SUPPORT_RX_FLOW_TAG */ + #endif /* _DP_TYPES_H_ */ diff --git a/hal/wifi3.0/hal_api_mon.h b/hal/wifi3.0/hal_api_mon.h index 12572fd87f..14cd1620fe 100644 --- a/hal/wifi3.0/hal_api_mon.h +++ b/hal/wifi3.0/hal_api_mon.h @@ -469,10 +469,18 @@ struct hal_rx_nac_info { /** * struct hal_rx_ppdu_msdu_info - struct for msdu info from HW TLVs - * @cce_metadata: cached metadata value received in the MSDU_END TLV + * @cce_metadata: cached CCE metadata value received in the MSDU_END TLV + * @is_flow_idx_timeout: flag to indicate if flow search timeout occurred + * @is_flow_idx_invalid: flag to indicate if flow idx is valid or not + * @fse_metadata: cached FSE metadata value received in the MSDU END TLV + * @flow_idx: flow idx matched in FSE received in the MSDU END TLV */ struct hal_rx_ppdu_msdu_info { uint16_t cce_metadata; + bool is_flow_idx_timeout; + bool is_flow_idx_invalid; + uint32_t fse_metadata; + uint32_t flow_idx; }; struct hal_rx_ppdu_info { diff --git a/hal/wifi3.0/hal_flow.h b/hal/wifi3.0/hal_flow.h new file mode 100644 index 0000000000..b89146d10a --- /dev/null +++ b/hal/wifi3.0/hal_flow.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2019 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. + */ +#ifndef __HAL_FLOW_H +#define __HAL_FLOW_H + +#define HAL_SET_FLD_SM(block, field, value) \ + (((value) << (block ## _ ## field ## _LSB)) & \ + (block ## _ ## field ## _MASK)) + +#define HAL_SET_FLD_MS(block, field, value) \ + (((value) & (block ## _ ## field ## _MASK)) >> \ + (block ## _ ## field ## _LSB)) + +#define HAL_CLR_FLD(desc, block, field) \ +do { \ + uint32_t val; \ + typeof(desc) desc_ = desc; \ + val = *((uint32_t *)((uint8_t *)(desc_) + \ + HAL_OFFSET(block, field))); \ + val &= ~(block ## _ ## field ## _MASK); \ + HAL_SET_FLD(desc_, block, field) = val; \ +} while (0) + +#define HAL_GET_FLD(desc, block, field) \ + ((*((uint32_t *)((uint8_t *)(desc) + HAL_OFFSET(block, field))) & \ + (block ## _ ## field ## _MASK)) >> (block ## _ ## field ## _LSB)) + +/** + * struct hal_flow_tuple_info - Hal Flow 5-tuple + * @dest_ip_127_96: Destination IP address bits 96-127 + * @dest_ip_95_64: Destination IP address bits 64-95 + * @dest_ip_63_32: Destination IP address bits 32-63 + * @dest_ip_31_0: Destination IP address bits 0-31 + * @src_ip_127_96: Source IP address bits 96-127 + * @src_ip_95_64: Source IP address bits 64-95 + * @src_ip_63_32: Source IP address bits 32-63 + * @src_ip_31_0: Source IP address bits 0-31 + * @dest_port: Destination Port + * @src_port: Source Port + * @l4_protocol: Layer-4 protocol type (TCP/UDP) + */ +struct hal_flow_tuple_info { + uint32_t dest_ip_127_96; + uint32_t dest_ip_95_64; + uint32_t dest_ip_63_32; + uint32_t dest_ip_31_0; + uint32_t src_ip_127_96; + uint32_t src_ip_95_64; + uint32_t src_ip_63_32; + uint32_t src_ip_31_0; + uint16_t dest_port; + uint16_t src_port; + uint16_t l4_protocol; +}; + +/** + * key_bitwise_shift_left() - Bitwise left shift (in place) an array of bytes + * @key: Pointer to array to key bytes + * @len: size of array (number of key bytes) + * @shift: number of shift operations to be performed + * + * Return: + */ +static inline void +key_bitwise_shift_left(uint8_t *key, int len, int shift) +{ + int i; + int next; + + while (shift--) { + for (i = len - 1; i >= 0 ; i--) { + if (i > 0) + next = (key[i - 1] & 0x80 ? 1 : 0); + else + next = 0; + key[i] = (key[i] << 1) | next; + } + } +} + +/** + * key_reverse() - Reverse the key buffer from MSB to LSB + * @dest: pointer to the destination key + * @src: pointer to the source key which should be shifted + * @len: size of key in bytes + * + * Return: + */ +static inline void +key_reverse(uint8_t *dest, uint8_t *src, int len) +{ + int i, j; + + for (i = 0, j = len - 1; i < len; i++, j--) + dest[i] = src[j]; +} +#endif /* HAL_FLOW_H */ diff --git a/hal/wifi3.0/hal_generic_api.h b/hal/wifi3.0/hal_generic_api.h index 4baccfc0b3..df22e81fe3 100644 --- a/hal/wifi3.0/hal_generic_api.h +++ b/hal/wifi3.0/hal_generic_api.h @@ -1403,6 +1403,14 @@ hal_rx_status_get_tlv_info_generic(void *rx_tlv_hdr, void *ppduinfo, if (user_id < HAL_MAX_UL_MU_USERS) { ppdu_info->rx_msdu_info[user_id].cce_metadata = HAL_RX_MSDU_END_CCE_METADATA_GET(rx_tlv); + ppdu_info->rx_msdu_info[user_id].fse_metadata = + HAL_RX_MSDU_END_FSE_METADATA_GET(rx_tlv); + ppdu_info->rx_msdu_info[user_id].is_flow_idx_timeout = + HAL_RX_MSDU_END_FLOW_IDX_TIMEOUT_GET(rx_tlv); + ppdu_info->rx_msdu_info[user_id].is_flow_idx_invalid = + HAL_RX_MSDU_END_FLOW_IDX_INVALID_GET(rx_tlv); + ppdu_info->rx_msdu_info[user_id].flow_idx = + HAL_RX_MSDU_END_FLOW_IDX_GET(rx_tlv); } return HAL_TLV_STATUS_MSDU_END; case 0: diff --git a/hal/wifi3.0/hal_rx.h b/hal/wifi3.0/hal_rx.h index 9fff9cf711..0e7478df75 100644 --- a/hal/wifi3.0/hal_rx.h +++ b/hal/wifi3.0/hal_rx.h @@ -1926,13 +1926,13 @@ hal_rx_msdu_end_last_msdu_get(uint8_t *buf) #define HAL_RX_MSDU_END_CCE_METADATA_GET(_rx_msdu_end) \ (_HAL_MS((*_OFFSET_TO_WORD_PTR(_rx_msdu_end, \ RX_MSDU_END_16_CCE_METADATA_OFFSET)), \ - RX_MSDU_END_16_CCE_METADATA_MASK, \ + RX_MSDU_END_16_CCE_METADATA_MASK, \ RX_MSDU_END_16_CCE_METADATA_LSB)) /** * hal_rx_msdu_cce_metadata_get: API to get CCE metadata * from rx_msdu_end TLV - * @ buf: pointer to the start of RX PKT TLV headers + * @buf: pointer to the start of RX PKT TLV headers * Return: last_msdu */ @@ -3443,4 +3443,118 @@ bool HAL_IS_DECAP_FORMAT_RAW(uint8_t *rx_tlv_hdr) return true; } #endif + +#define HAL_RX_MSDU_END_FSE_METADATA_GET(_rx_msdu_end) \ + (_HAL_MS((*_OFFSET_TO_WORD_PTR(_rx_msdu_end, \ + RX_MSDU_END_15_FSE_METADATA_OFFSET)), \ + RX_MSDU_END_15_FSE_METADATA_MASK, \ + RX_MSDU_END_15_FSE_METADATA_LSB)) + +/** + * hal_rx_msdu_fse_metadata_get: API to get FSE metadata + * from rx_msdu_end TLV + * @buf: pointer to the start of RX PKT TLV headers + * + * Return: fse metadata value from MSDU END TLV + */ +static inline uint32_t hal_rx_msdu_fse_metadata_get(uint8_t *buf) +{ + struct rx_pkt_tlvs *pkt_tlvs = (struct rx_pkt_tlvs *)buf; + struct rx_msdu_end *msdu_end = &pkt_tlvs->msdu_end_tlv.rx_msdu_end; + uint32_t fse_metadata; + + fse_metadata = HAL_RX_MSDU_END_FSE_METADATA_GET(msdu_end); + return fse_metadata; +} + +#define HAL_RX_MSDU_END_FLOW_IDX_GET(_rx_msdu_end) \ + (_HAL_MS((*_OFFSET_TO_WORD_PTR(_rx_msdu_end, \ + RX_MSDU_END_14_FLOW_IDX_OFFSET)), \ + RX_MSDU_END_14_FLOW_IDX_MASK, \ + RX_MSDU_END_14_FLOW_IDX_LSB)) + +/** + * hal_rx_msdu_flow_idx_get: API to get flow index + * from rx_msdu_end TLV + * @buf: pointer to the start of RX PKT TLV headers + * + * Return: flow index value from MSDU END TLV + */ +static inline uint32_t hal_rx_msdu_flow_idx_get(uint8_t *buf) +{ + struct rx_pkt_tlvs *pkt_tlvs = (struct rx_pkt_tlvs *)buf; + struct rx_msdu_end *msdu_end = &pkt_tlvs->msdu_end_tlv.rx_msdu_end; + uint32_t flow_idx; + + flow_idx = HAL_RX_MSDU_END_FLOW_IDX_GET(msdu_end); + return flow_idx; +} + +#define HAL_RX_MSDU_END_FLOW_IDX_TIMEOUT_GET(_rx_msdu_end) \ + (_HAL_MS((*_OFFSET_TO_WORD_PTR(_rx_msdu_end, \ + RX_MSDU_END_5_FLOW_IDX_TIMEOUT_OFFSET)), \ + RX_MSDU_END_5_FLOW_IDX_TIMEOUT_MASK, \ + RX_MSDU_END_5_FLOW_IDX_TIMEOUT_LSB)) + +/** + * hal_rx_msdu_flow_idx_timeout: API to get flow index timeout + * from rx_msdu_end TLV + * @buf: pointer to the start of RX PKT TLV headers + * + * Return: flow index timeout value from MSDU END TLV + */ +static inline bool hal_rx_msdu_flow_idx_timeout(uint8_t *buf) +{ + struct rx_pkt_tlvs *pkt_tlvs = (struct rx_pkt_tlvs *)buf; + struct rx_msdu_end *msdu_end = &pkt_tlvs->msdu_end_tlv.rx_msdu_end; + bool timeout; + + timeout = HAL_RX_MSDU_END_FLOW_IDX_TIMEOUT_GET(msdu_end); + return timeout; +} + +#define HAL_RX_MSDU_END_FLOW_IDX_INVALID_GET(_rx_msdu_end) \ + (_HAL_MS((*_OFFSET_TO_WORD_PTR(_rx_msdu_end, \ + RX_MSDU_END_5_FLOW_IDX_INVALID_OFFSET)), \ + RX_MSDU_END_5_FLOW_IDX_INVALID_MASK, \ + RX_MSDU_END_5_FLOW_IDX_INVALID_LSB)) +/** + * hal_rx_msdu_flow_idx_invalid: API to get flow index invalid + * from rx_msdu_end TLV + * @buf: pointer to the start of RX PKT TLV headers + * + * Return: flow index invalid value from MSDU END TLV + */ +static inline bool hal_rx_msdu_flow_idx_invalid(uint8_t *buf) +{ + struct rx_pkt_tlvs *pkt_tlvs = (struct rx_pkt_tlvs *)buf; + struct rx_msdu_end *msdu_end = &pkt_tlvs->msdu_end_tlv.rx_msdu_end; + bool invalid; + + invalid = HAL_RX_MSDU_END_FLOW_IDX_INVALID_GET(msdu_end); + return invalid; +} + +/** + * hal_rx_msdu_get_flow_params: API to get flow index, flow index invalid + * and flow index timeout from rx_msdu_end TLV + * @buf: pointer to the start of RX PKT TLV headers + * @flow_invalid: pointer to return value of flow_idx_valid + * @flow_timeout: pointer to return value of flow_idx_timeout + * @flow_index: pointer to return value of flow_idx + * + * Return: none + */ +static inline void hal_rx_msdu_get_flow_params(uint8_t *buf, + bool *flow_invalid, + bool *flow_timeout, + uint32_t *flow_index) +{ + struct rx_pkt_tlvs *pkt_tlvs = (struct rx_pkt_tlvs *)buf; + struct rx_msdu_end *msdu_end = &pkt_tlvs->msdu_end_tlv.rx_msdu_end; + + *flow_invalid = HAL_RX_MSDU_END_FLOW_IDX_INVALID_GET(msdu_end); + *flow_timeout = HAL_RX_MSDU_END_FLOW_IDX_TIMEOUT_GET(msdu_end); + *flow_index = HAL_RX_MSDU_END_FLOW_IDX_GET(msdu_end); +} #endif /* _HAL_RX_H */ diff --git a/hal/wifi3.0/hal_rx_flow.h b/hal/wifi3.0/hal_rx_flow.h new file mode 100644 index 0000000000..353f9e6f7d --- /dev/null +++ b/hal/wifi3.0/hal_rx_flow.h @@ -0,0 +1,628 @@ +/* + * Copyright (c) 2019 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. + */ +#ifndef __HAL_RX_FLOW_H +#define __HAL_RX_FLOW_H + +#include "hal_flow.h" +#include "wlan_cfg.h" +#include "hal_api.h" +#include "qdf_mem.h" +#include "rx_flow_search_entry.h" + +#define HAL_FST_HASH_KEY_SIZE_BITS 315 +#define HAL_FST_HASH_KEY_SIZE_BYTES 40 +#define HAL_FST_HASH_KEY_SIZE_WORDS 10 +#define HAL_FST_HASH_DATA_SIZE 37 +#define HAL_FST_HASH_MASK 0x7ffff +#define HAL_RX_FST_ENTRY_SIZE (NUM_OF_DWORDS_RX_FLOW_SEARCH_ENTRY * 4) + +/** + * Four possible options for IP SA/DA prefix, currently use 0x0 which + * maps to type 2 in HW spec + */ +#define HAL_FST_IP_DA_SA_PFX_TYPE_IPV4_COMPATIBLE_IPV6 2 + +#define HAL_IP_DA_SA_PREFIX_IPV4_COMPATIBLE_IPV6 0x0 + +/** + * REO destination indication is a lower 4-bits of hash value + * This should match the REO destination used in Rx hash based routing. + */ +#define HAL_REO_DEST_IND_HASH_MASK 0xF + +/** + * REO destinations are valid from 16-31 for Hawkeye + * and 0-15 are not setup for SW + */ +#define HAL_REO_DEST_IND_START_OFFSET 0x10 + +/** + * struct hal_rx_flow - Rx Flow parameters to be sent to HW + * @tuple_info: Rx Flow 5-tuple (src & dest IP, src & dest ports, L4 protocol) + * @reo_destination_handler: REO destination for this flow + * @reo_destination_indication: REO indication for this flow + * @fse_metadata: Flow metadata or tag passed to HW for marking packets + */ +struct hal_rx_flow { + struct hal_flow_tuple_info tuple_info; + uint8_t reo_destination_handler; + uint8_t reo_destination_indication; + uint32_t fse_metadata; +}; + +/** + * enum hal_rx_fse_reo_destination_handler + * @HAL_RX_FSE_REO_DEST_FT: Use this entry's destination indication + * @HAL_RX_FSE_REO_DEST_ASPT: Use Address Search + Peer Table's entry + * @HAL_RX_FSE_REO_DEST_FT2: Use FT2's destination indication + * @HAL_RX_FSE_REO_DEST_CCE: Use CCE's destination indication for this entry + */ +enum hal_rx_fse_reo_destination_handler { + HAL_RX_FSE_REO_DEST_FT = 0, + HAL_RX_FSE_REO_DEST_ASPT = 1, + HAL_RX_FSE_REO_DEST_FT2 = 2, + HAL_RX_FSE_REO_DEST_CCE = 3, +}; + +/** + * struct hal_rx_fst - HAL RX Flow search table context + * @base_vaddr: Virtual Base address of HW FST + * @base_paddr: Physical Base address of HW FST + * @key: Pointer to 320-bit Key read from cfg + * @shifted_key: Pointer to left-shifted 320-bit Key used for Toeplitz Hash + * @max_entries : Max number of entries in flow searchh table + * @max_skid_length : Max search length if there is hash collision + * @hash_mask: Hash mask to apply to index into FST + * @key_cache: Toepliz Key Cache configured key + */ +struct hal_rx_fst { + uint8_t *base_vaddr; + qdf_dma_addr_t base_paddr; + uint8_t *key; + uint8_t shifted_key[HAL_FST_HASH_KEY_SIZE_BYTES]; + uint16_t max_entries; + uint16_t max_skid_length; + uint16_t hash_mask; + uint32_t key_cache[HAL_FST_HASH_KEY_SIZE_BYTES][1 << 8]; +}; + +/** + * hal_rx_flow_setup_fse() - Setup a flow search entry in HW FST + * @fst: Pointer to the Rx Flow Search Table + * @table_offset: offset into the table where the flow is to be setup + * @flow: Flow Parameters + * + * Return: Success/Failure + */ +static void * +hal_rx_flow_setup_fse(struct hal_rx_fst *fst, uint32_t table_offset, + struct hal_rx_flow *flow) +{ + uint8_t *fse; + bool fse_valid; + + if (table_offset >= fst->max_entries) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + "HAL FSE table offset %u exceeds max entries %u", + table_offset, fst->max_entries); + return NULL; + } + + fse = (uint8_t *)fst->base_vaddr + + (table_offset * HAL_RX_FST_ENTRY_SIZE); + + fse_valid = HAL_GET_FLD(fse, RX_FLOW_SEARCH_ENTRY_9, VALID); + + if (fse_valid) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "HAL FSE %pK already valid", fse); + return NULL; + } + + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_0, SRC_IP_127_96) = + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_0, SRC_IP_127_96, + qdf_htonl(flow->tuple_info.src_ip_127_96)); + + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_1, SRC_IP_95_64) = + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_1, SRC_IP_95_64, + qdf_htonl(flow->tuple_info.src_ip_95_64)); + + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_2, SRC_IP_63_32) = + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_2, SRC_IP_63_32, + qdf_htonl(flow->tuple_info.src_ip_63_32)); + + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_3, SRC_IP_31_0) = + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_3, SRC_IP_31_0, + qdf_htonl(flow->tuple_info.src_ip_31_0)); + + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_4, DEST_IP_127_96) = + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_4, DEST_IP_127_96, + qdf_htonl(flow->tuple_info.dest_ip_127_96)); + + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_5, DEST_IP_95_64) = + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_5, DEST_IP_95_64, + qdf_htonl(flow->tuple_info.dest_ip_95_64)); + + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_6, DEST_IP_63_32) = + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_6, DEST_IP_63_32, + qdf_htonl(flow->tuple_info.dest_ip_63_32)); + + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_7, DEST_IP_31_0) = + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_7, DEST_IP_31_0, + qdf_htonl(flow->tuple_info.dest_ip_31_0)); + + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_8, DEST_PORT); + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_8, DEST_PORT) |= + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_8, DEST_PORT, + (flow->tuple_info.dest_port)); + + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_8, SRC_PORT); + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_8, SRC_PORT) |= + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_8, SRC_PORT, + (flow->tuple_info.src_port)); + + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_9, L4_PROTOCOL); + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_9, L4_PROTOCOL) |= + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_9, L4_PROTOCOL, + flow->tuple_info.l4_protocol); + + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_9, REO_DESTINATION_HANDLER); + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_9, REO_DESTINATION_HANDLER) |= + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_9, REO_DESTINATION_HANDLER, + flow->reo_destination_handler); + + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_9, VALID); + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_9, VALID) |= + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_9, VALID, 1); + + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_10, METADATA); + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_10, METADATA) = + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_10, METADATA, + flow->fse_metadata); + + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_11, REO_DESTINATION_INDICATION); + HAL_SET_FLD(fse, RX_FLOW_SEARCH_ENTRY_11, REO_DESTINATION_INDICATION) |= + HAL_SET_FLD_SM(RX_FLOW_SEARCH_ENTRY_11, + REO_DESTINATION_INDICATION, + flow->reo_destination_indication); + + /* Reset all the other fields in FSE */ + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_9, RESERVED_9); + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_11, MSDU_DROP); + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_11, RESERVED_11); + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_11, MSDU_COUNT); + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_12, MSDU_BYTE_COUNT); + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_13, TIMESTAMP); + + return fse; +} + +/** + * hal_rx_flow_delete_entry() - Delete a flow from the Rx Flow Search Table + * @fst: Pointer to the Rx Flow Search Table + * @hal_rx_fse: Pointer to the Rx Flow that is to be deleted from the FST + * + * Return: Success/Failure + */ +static inline QDF_STATUS +hal_rx_flow_delete_entry(struct hal_rx_fst *fst, void *hal_rx_fse) +{ + uint8_t *fse = (uint8_t *)hal_rx_fse; + + if (!HAL_GET_FLD(fse, RX_FLOW_SEARCH_ENTRY_9, VALID)) + return QDF_STATUS_E_NOENT; + + HAL_CLR_FLD(fse, RX_FLOW_SEARCH_ENTRY_9, VALID); + + return QDF_STATUS_SUCCESS; +} + +/** + * hal_rx_fst_key_configure() - Configure the Toeplitz key in the FST + * @fst: Pointer to the Rx Flow Search Table + * + * Return: Success/Failure + */ +static void hal_rx_fst_key_configure(struct hal_rx_fst *fst) +{ + uint8_t key_bytes[HAL_FST_HASH_KEY_SIZE_BYTES]; + + qdf_mem_copy(key_bytes, fst->key, HAL_FST_HASH_KEY_SIZE_BYTES); + + /** + * The Toeplitz algorithm as per the Microsoft spec works in a + * “big-endian” manner, using the MSBs of the key to hash the + * initial bytes of the input going on to use up the lower order bits + * of the key to hash further bytes of the input until the LSBs of the + * key are used finally. + * + * So first, rightshift 320-bit input key 5 times to get 315 MS bits + */ + key_bitwise_shift_left(key_bytes, HAL_FST_HASH_KEY_SIZE_BYTES, 5); + key_reverse(fst->shifted_key, key_bytes, HAL_FST_HASH_KEY_SIZE_BYTES); +} + +/** + * hal_rx_fst_get_base() - Retrieve the virtual base address of the Rx FST + * @fst: Pointer to the Rx Flow Search Table + * + * Return: Success/Failure + */ +static inline void *hal_rx_fst_get_base(struct hal_rx_fst *fst) +{ + return fst->base_vaddr; +} + +/** + * hal_rx_fst_get_fse_size() - Retrieve the size of each entry(flow) in Rx FST + * + * Return: size of each entry/flow in Rx FST + */ +static inline uint32_t hal_rx_fst_get_fse_size(void) +{ + return HAL_RX_FST_ENTRY_SIZE; +} + +/** + * hal_rx_flow_get_tuple_info() - Retrieve the 5-tuple flow info for an entry + * @hal_fse: Pointer to the Flow in Rx FST + * @tuple_info: 5-tuple info of the flow returned to the caller + * + * Return: Success/Failure + */ +QDF_STATUS hal_rx_flow_get_tuple_info(void *hal_fse, + struct hal_flow_tuple_info *tuple_info) +{ + if (!hal_fse || !tuple_info) + return QDF_STATUS_E_INVAL; + + if (!HAL_GET_FLD(hal_fse, RX_FLOW_SEARCH_ENTRY_9, VALID)) + return QDF_STATUS_E_NOENT; + + tuple_info->src_ip_127_96 = qdf_ntohl(HAL_GET_FLD(hal_fse, + RX_FLOW_SEARCH_ENTRY_0, SRC_IP_127_96)); + tuple_info->src_ip_95_64 = qdf_ntohl(HAL_GET_FLD(hal_fse, + RX_FLOW_SEARCH_ENTRY_1, SRC_IP_95_64)); + tuple_info->src_ip_63_32 = qdf_ntohl(HAL_GET_FLD(hal_fse, + RX_FLOW_SEARCH_ENTRY_2, SRC_IP_63_32)); + tuple_info->src_ip_31_0 = qdf_ntohl(HAL_GET_FLD(hal_fse, + RX_FLOW_SEARCH_ENTRY_3, SRC_IP_31_0)); + tuple_info->dest_ip_127_96 = + qdf_ntohl(HAL_GET_FLD(hal_fse, + RX_FLOW_SEARCH_ENTRY_4, DEST_IP_127_96)); + tuple_info->dest_ip_95_64 = qdf_ntohl(HAL_GET_FLD(hal_fse, + RX_FLOW_SEARCH_ENTRY_5, DEST_IP_95_64)); + tuple_info->dest_ip_63_32 = qdf_ntohl(HAL_GET_FLD(hal_fse, + RX_FLOW_SEARCH_ENTRY_6, DEST_IP_63_32)); + tuple_info->dest_ip_31_0 = qdf_ntohl(HAL_GET_FLD(hal_fse, + RX_FLOW_SEARCH_ENTRY_7, DEST_IP_31_0)); + tuple_info->dest_port = (HAL_GET_FLD(hal_fse, + RX_FLOW_SEARCH_ENTRY_8, DEST_PORT)); + tuple_info->src_port = (HAL_GET_FLD(hal_fse, + RX_FLOW_SEARCH_ENTRY_8, SRC_PORT)); + tuple_info->l4_protocol = HAL_GET_FLD(hal_fse, + RX_FLOW_SEARCH_ENTRY_9, L4_PROTOCOL); + + return QDF_STATUS_SUCCESS; +} + +/** + * hal_flow_toeplitz_create_cache() - Calculate hashes for each possible + * byte value with the key taken as is + * + * @fst: FST Handle + * @key: Hash Key + * + * Return: Success/Failure + */ +void hal_flow_toeplitz_create_cache(struct hal_rx_fst *fst) +{ + int bit; + int val; + int i; + uint8_t *key = fst->shifted_key; + + /* + * Initialise to first 32 bits of the key; shift in further key material + * through the loop + */ + uint32_t cur_key = (key[0] << 24) | (key[1] << 16) | (key[2] << 8) | + key[3]; + + for (i = 0; i < HAL_FST_HASH_KEY_SIZE_BYTES; i++) { + uint8_t new_key_byte; + uint32_t shifted_key[8]; + + if (i + 4 < HAL_FST_HASH_KEY_SIZE_BYTES) + new_key_byte = key[i + 4]; + else + new_key_byte = 0; + + shifted_key[0] = cur_key; + + for (bit = 1; bit < 8; bit++) { + /* + * For each iteration, shift out one more bit of the + * current key and shift in one more bit of the new key + * material + */ + shifted_key[bit] = cur_key << bit | + new_key_byte >> (8 - bit); + } + + for (val = 0; val < (1 << 8); val++) { + uint32_t hash = 0; + int mask; + + /* + * For each bit set in the input, XOR in + * the appropriately shifted key + */ + for (bit = 0, mask = 1 << 7; bit < 8; bit++, mask >>= 1) + if ((val & mask)) + hash ^= shifted_key[bit]; + + fst->key_cache[i][val] = hash; + } + + cur_key = cur_key << 8 | new_key_byte; + } +} + +/** + * hal_rx_fst_attach() - Initialize Rx flow search table in HW FST + * + * @qdf_dev: QDF device handle + * @hal_fst_base_paddr: Pointer to the physical base address of the Rx FST + * @max_entries: Max number of flows allowed in the FST + * @max_search: Number of collisions allowed in the hash-based FST + * @hash_key: Toeplitz key used for the hash FST + * + * Return: + */ +static struct hal_rx_fst * +hal_rx_fst_attach(qdf_device_t qdf_dev, + uint64_t *hal_fst_base_paddr, uint16_t max_entries, + uint16_t max_search, uint8_t *hash_key) +{ + struct hal_rx_fst *fst = qdf_mem_malloc(sizeof(struct hal_rx_fst)); + + if (!fst) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + FL("hal fst allocation failed,")); + return NULL; + } + + qdf_mem_set(fst, 0, sizeof(struct hal_rx_fst)); + + fst->key = hash_key; + fst->max_skid_length = max_search; + fst->max_entries = max_entries; + fst->hash_mask = max_entries - 1; + + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG, + "HAL FST allocation %x %d * %d\n", fst, + fst->max_entries, HAL_RX_FST_ENTRY_SIZE); + + fst->base_vaddr = (uint8_t *)qdf_mem_alloc_consistent(qdf_dev, + qdf_dev->dev, + (fst->max_entries * HAL_RX_FST_ENTRY_SIZE), + &fst->base_paddr); + + if (!fst->base_vaddr) { + QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, + FL("hal fst->base_vaddr allocation failed")); + qdf_mem_free(fst); + return NULL; + } + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_ANY, QDF_TRACE_LEVEL_DEBUG, + (void *)fst->key, HAL_FST_HASH_KEY_SIZE_BYTES); + + qdf_mem_set((uint8_t *)fst->base_vaddr, 0, + (fst->max_entries * HAL_RX_FST_ENTRY_SIZE)); + + hal_rx_fst_key_configure(fst); + hal_flow_toeplitz_create_cache(fst); + *hal_fst_base_paddr = (uint64_t)fst->base_paddr; + return fst; +} + +/** + * hal_rx_fst_detach() - De-init the Rx flow search table from HW + * + * @rx_fst: Pointer to the Rx FST + * @qdf_dev: QDF device handle + * + * Return: + */ +void hal_rx_fst_detach(struct hal_rx_fst *rx_fst, + qdf_device_t qdf_dev) +{ + if (!rx_fst || !qdf_dev) + return; + + qdf_mem_free_consistent(qdf_dev, qdf_dev->dev, + rx_fst->max_entries * HAL_RX_FST_ENTRY_SIZE, + rx_fst->base_vaddr, rx_fst->base_paddr, 0); + + qdf_mem_free(rx_fst); +} + +/** + * hal_flow_toeplitz_hash() - Calculate Toeplitz hash by using the cached key + * + * @hal_fst: FST Handle + * @flow: Flow Parameters + * + * Return: Success/Failure + */ +static inline uint32_t +hal_flow_toeplitz_hash(void *hal_fst, struct hal_rx_flow *flow) +{ + int i, j; + uint32_t hash = 0; + struct hal_rx_fst *fst = (struct hal_rx_fst *)hal_fst; + uint32_t input[HAL_FST_HASH_KEY_SIZE_WORDS]; + uint8_t *tuple; + + qdf_mem_zero(input, HAL_FST_HASH_KEY_SIZE_BYTES); + *(uint32_t *)&input[0] = qdf_htonl(flow->tuple_info.src_ip_127_96); + *(uint32_t *)&input[1] = qdf_htonl(flow->tuple_info.src_ip_95_64); + *(uint32_t *)&input[2] = qdf_htonl(flow->tuple_info.src_ip_63_32); + *(uint32_t *)&input[3] = qdf_htonl(flow->tuple_info.src_ip_31_0); + *(uint32_t *)&input[4] = qdf_htonl(flow->tuple_info.dest_ip_127_96); + *(uint32_t *)&input[5] = qdf_htonl(flow->tuple_info.dest_ip_95_64); + *(uint32_t *)&input[6] = qdf_htonl(flow->tuple_info.dest_ip_63_32); + *(uint32_t *)&input[7] = qdf_htonl(flow->tuple_info.dest_ip_31_0); + *(uint32_t *)&input[8] = (flow->tuple_info.dest_port << 16) | + (flow->tuple_info.src_port); + *(uint32_t *)&input[9] = flow->tuple_info.l4_protocol; + + tuple = (uint8_t *)input; + QDF_TRACE_HEX_DUMP(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_DEBUG, + tuple, sizeof(input)); + for (i = 0, j = HAL_FST_HASH_DATA_SIZE - 1; + i < HAL_FST_HASH_KEY_SIZE_BYTES && j >= 0; i++, j--) { + hash ^= fst->key_cache[i][tuple[j]]; + } + + QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO_LOW, + "Hash value %u %u truncated hash %u\n", hash, + (hash >> 12), (hash >> 12) % (fst->max_entries)); + + hash >>= 12; + hash &= (fst->max_entries - 1); + + return hash; +} + +/** + * hal_rx_get_hal_hash() - Retrieve hash index of a flow in the FST table + * + * @hal_fst: HAL Rx FST Handle + * @flow_hash: Flow hash computed from flow tuple + * + * Return: hash index truncated to the size of the hash table + */ +inline +uint32_t hal_rx_get_hal_hash(struct hal_rx_fst *hal_fst, uint32_t flow_hash) +{ + uint32_t trunc_hash = flow_hash; + + /* Take care of hash wrap around scenario */ + if (flow_hash >= hal_fst->max_entries) + trunc_hash &= hal_fst->hash_mask; + return trunc_hash; +} + +/** + * hal_rx_insert_flow_entry() - Add a flow into the FST table + * + * @hal_fst: HAL Rx FST Handle + * @flow_hash: Flow hash computed from flow tuple + * @flow_tuple_info: Flow tuple used to compute the hash + * @flow_index: Hash index of the flow in the table when inserted successfully + * + * Return: Success if flow is inserted into the table, error otherwise + */ +QDF_STATUS +hal_rx_insert_flow_entry(struct hal_rx_fst *fst, uint32_t flow_hash, + void *flow_tuple_info, uint32_t *flow_idx) { + int i; + void *hal_fse; + uint32_t hal_hash; + struct hal_flow_tuple_info hal_tuple_info = { 0 }; + QDF_STATUS status; + + for (i = 0; i < fst->max_skid_length; i++) { + hal_hash = hal_rx_get_hal_hash(fst, (flow_hash + i)); + hal_fse = (uint8_t *)fst->base_vaddr + + (hal_hash * HAL_RX_FST_ENTRY_SIZE); + status = hal_rx_flow_get_tuple_info(hal_fse, &hal_tuple_info); + if (QDF_STATUS_E_NOENT == status) + break; + + /* Find the matching flow entry in HW FST */ + if (!qdf_mem_cmp(&hal_tuple_info, + flow_tuple_info, + sizeof(struct hal_flow_tuple_info))) { + dp_err("Duplicate flow entry in FST %u at skid %u ", + hal_hash, i); + return QDF_STATUS_E_EXISTS; + } + } + if (i == fst->max_skid_length) { + dp_err("Max skid length reached for hash %u", flow_hash); + return QDF_STATUS_E_RANGE; + } + *flow_idx = hal_hash; + dp_info("flow_hash = %u, skid_entry = %d, flow_addr = %pK flow_idx = %d", + flow_hash, i, hal_fse, *flow_idx); + + return QDF_STATUS_SUCCESS; +} + +/** + * hal_rx_find_flow_from_tuple() - Find a flow in the FST table + * + * @fst: HAL Rx FST Handle + * @flow_hash: Flow hash computed from flow tuple + * @flow_tuple_info: Flow tuple used to compute the hash + * @flow_index: Hash index of the flow in the table when found + * + * Return: Success if matching flow is found in the table, error otherwise + */ +QDF_STATUS +hal_rx_find_flow_from_tuple(struct hal_rx_fst *fst, uint32_t flow_hash, + void *flow_tuple_info, uint32_t *flow_idx) +{ + int i; + void *hal_fse; + uint32_t hal_hash; + struct hal_flow_tuple_info hal_tuple_info = { 0 }; + QDF_STATUS status; + + for (i = 0; i < fst->max_skid_length; i++) { + hal_hash = hal_rx_get_hal_hash(fst, (flow_hash + i)); + hal_fse = (uint8_t *)fst->base_vaddr + + (hal_hash * HAL_RX_FST_ENTRY_SIZE); + status = hal_rx_flow_get_tuple_info(hal_fse, &hal_tuple_info); + if (QDF_STATUS_SUCCESS != status) + continue; + + /* Find the matching flow entry in HW FST */ + if (!qdf_mem_cmp(&hal_tuple_info, + flow_tuple_info, + sizeof(struct hal_flow_tuple_info))) { + break; + } + } + + if (i == fst->max_skid_length) { + dp_err("Max skid length reached for hash %u", flow_hash); + return QDF_STATUS_E_RANGE; + } + + *flow_idx = hal_hash; + dp_info("flow_hash = %u, skid_entry = %d, flow_addr = %pK flow_idx = %d", + flow_hash, i, hal_fse, *flow_idx); + + return QDF_STATUS_SUCCESS; +} + +#endif /* HAL_RX_FLOW_H */