diff --git a/dp/inc/cdp_txrx_cmn_struct.h b/dp/inc/cdp_txrx_cmn_struct.h index 0b683ef603..576f645a52 100644 --- a/dp/inc/cdp_txrx_cmn_struct.h +++ b/dp/inc/cdp_txrx_cmn_struct.h @@ -204,6 +204,7 @@ enum htt_cmn_dbg_stats_type { * @TXRX_SOC_CFG_PARAMS: Print soc cfg params info * @TXRX_PDEV_CFG_PARAMS: Print pdev cfg params info * @TXRX_NAPI_STATS: Print NAPI scheduling statistics + * @TXRX_SOC_INTERRUPT_STATS: Print soc interrupt stats */ enum cdp_host_txrx_stats { TXRX_HOST_STATS_INVALID = -1, @@ -219,6 +220,7 @@ enum cdp_host_txrx_stats { TXRX_SOC_CFG_PARAMS = 9, TXRX_PDEV_CFG_PARAMS = 10, TXRX_NAPI_STATS = 11, + TXRX_SOC_INTERRUPT_STATS = 12, TXRX_HOST_STATS_MAX, }; @@ -1042,6 +1044,7 @@ typedef void (*data_stall_detect_cb)(struct data_stall_event_info *); * @CDP_TXRX_STATS_28: Host Peer entry stats * @CDP_TXRX_STATS_29: Host Soc config params info * @CDP_TXRX_STATS_30: Host Pdev config params info + * @CDP_TXRX_STATS_31: Host DP Interrupt Stats */ enum cdp_stats { CDP_TXRX_STATS_0 = 0, @@ -1075,6 +1078,7 @@ enum cdp_stats { CDP_TXRX_STATS_28, CDP_TXRX_STATS_29, CDP_TXRX_STATS_30, + CDP_TXRX_STATS_31, CDP_TXRX_STATS_HTT_MAX = 256, CDP_TXRX_MAX_STATS = 265, }; @@ -1536,6 +1540,9 @@ struct cdp_rx_indication_msdu { * @tx_flow_stop_queue_threshold: Value to Pause tx queues * @tx_flow_start_queue_offset: Available Tx descriptors to unpause * tx queue + * @tx_comp_loop_pkt_limit: Max # of packets to be processed in 1 tx comp loop + * @rx_reap_loop_pkt_limit: Max # of packets to be processed in 1 rx reap loop + * @rx_hp_oos_update_limit: Max # of HP OOS (out of sync) updates */ struct cdp_config_params { unsigned int tso_enable:1; @@ -1548,6 +1555,9 @@ struct cdp_config_params { /* Set when QCA_LL_TX_FLOW_CONTROL_V2 is enabled */ uint8_t tx_flow_stop_queue_threshold; uint8_t tx_flow_start_queue_offset; + uint32_t tx_comp_loop_pkt_limit; + uint32_t rx_reap_loop_pkt_limit; + uint32_t rx_hp_oos_update_limit; }; diff --git a/dp/wifi3.0/dp_internal.h b/dp/wifi3.0/dp_internal.h index af68f8bd6c..8c6eff0307 100644 --- a/dp/wifi3.0/dp_internal.h +++ b/dp/wifi3.0/dp_internal.h @@ -816,7 +816,14 @@ extern QDF_STATUS dp_reo_send_cmd(struct dp_soc *soc, void (*callback_fn), void *data); extern void dp_reo_cmdlist_destroy(struct dp_soc *soc); -extern void dp_reo_status_ring_handler(struct dp_soc *soc); + +/** + * dp_reo_status_ring_handler - Handler for REO Status ring + * @soc: DP Soc handle + * + * Returns: Number of descriptors reaped + */ +uint32_t dp_reo_status_ring_handler(struct dp_soc *soc); void dp_aggregate_vdev_stats(struct dp_vdev *vdev, struct cdp_vdev_stats *vdev_stats); void dp_rx_tid_stats_cb(struct dp_soc *soc, void *cb_ctxt, diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c index 0c95402eac..1bb352b2ba 100644 --- a/dp/wifi3.0/dp_main.c +++ b/dp/wifi3.0/dp_main.c @@ -285,6 +285,7 @@ const int dp_stats_mapping_table[][STATS_TYPE_MAX] = { {TXRX_FW_STATS_INVALID, TXRX_REO_QUEUE_STATS}, {TXRX_FW_STATS_INVALID, TXRX_SOC_CFG_PARAMS}, {TXRX_FW_STATS_INVALID, TXRX_PDEV_CFG_PARAMS}, + {TXRX_FW_STATS_INVALID, TXRX_SOC_INTERRUPT_STATS}, }; /* MCL specific functions */ @@ -1273,6 +1274,7 @@ void *hif_get_hal_handle(void *hif_handle); static uint32_t dp_service_srngs(void *dp_ctx, uint32_t dp_budget) { struct dp_intr *int_ctx = (struct dp_intr *)dp_ctx; + struct dp_intr_stats *intr_stats = &int_ctx->intr_stats; struct dp_soc *soc = int_ctx->soc; int ring = 0; uint32_t work_done = 0; @@ -1286,15 +1288,27 @@ static uint32_t dp_service_srngs(void *dp_ctx, uint32_t dp_budget) struct dp_pdev *pdev = NULL; int mac_id; + dp_verbose_debug("tx %x rx %x rx_err %x rx_wbm_rel %x reo_status %x rx_mon_ring %x host2rxdma %x rxdma2host %x\n", + tx_mask, rx_mask, rx_err_mask, rx_wbm_rel_mask, + reo_status_mask, + int_ctx->rx_mon_ring_mask, + int_ctx->host2rxdma_ring_mask, + int_ctx->rxdma2host_ring_mask); + /* Process Tx completion interrupts first to return back buffers */ while (tx_mask) { if (tx_mask & 0x1) { - work_done = dp_tx_comp_handler(soc, - soc->tx_comp_ring[ring].hal_srng, - remaining_quota); + work_done = dp_tx_comp_handler(int_ctx, + soc, + soc->tx_comp_ring[ring].hal_srng, + remaining_quota); - dp_verbose_debug("tx mask 0x%x ring %d, budget %d, work_done %d", - tx_mask, ring, budget, work_done); + if (work_done) { + intr_stats->num_tx_ring_masks[ring]++; + dp_verbose_debug("tx mask 0x%x ring %d, budget %d, work_done %d", + tx_mask, ring, budget, + work_done); + } budget -= work_done; if (budget <= 0) @@ -1306,15 +1320,17 @@ static uint32_t dp_service_srngs(void *dp_ctx, uint32_t dp_budget) ring++; } - /* Process REO Exception ring interrupt */ if (rx_err_mask) { work_done = dp_rx_err_process(soc, soc->reo_exception_ring.hal_srng, remaining_quota); - dp_verbose_debug("REO Exception Ring: work_done %d budget %d", - work_done, budget); + if (work_done) { + intr_stats->num_rx_err_ring_masks++; + dp_verbose_debug("REO Exception Ring: work_done %d budget %d", + work_done, budget); + } budget -= work_done; if (budget <= 0) { @@ -1328,8 +1344,11 @@ static uint32_t dp_service_srngs(void *dp_ctx, uint32_t dp_budget) work_done = dp_rx_wbm_err_process(soc, soc->rx_rel_ring.hal_srng, remaining_quota); - dp_verbose_debug("WBM Release Ring: work_done %d budget %d", - work_done, budget); + if (work_done) { + intr_stats->num_rx_wbm_rel_ring_masks++; + dp_verbose_debug("WBM Release Ring: work_done %d budget %d", + work_done, budget); + } budget -= work_done; if (budget <= 0) { @@ -1341,16 +1360,17 @@ static uint32_t dp_service_srngs(void *dp_ctx, uint32_t dp_budget) /* Process Rx interrupts */ if (rx_mask) { for (ring = 0; ring < soc->num_reo_dest_rings; ring++) { - if (rx_mask & (1 << ring)) { - work_done = dp_rx_process(int_ctx, - soc->reo_dest_ring[ring].hal_srng, - ring, - remaining_quota); - + if (!(rx_mask & (1 << ring))) + continue; + work_done = dp_rx_process(int_ctx, + soc->reo_dest_ring[ring].hal_srng, + ring, + remaining_quota); + if (work_done) { + intr_stats->num_rx_ring_masks[ring]++; dp_verbose_debug("rx mask 0x%x ring %d, work_done %d budget %d", rx_mask, ring, work_done, budget); - budget -= work_done; if (budget <= 0) goto budget_done; @@ -1359,8 +1379,10 @@ static uint32_t dp_service_srngs(void *dp_ctx, uint32_t dp_budget) } } - if (reo_status_mask) - dp_reo_status_ring_handler(soc); + if (reo_status_mask) { + if (dp_reo_status_ring_handler(soc)) + int_ctx->intr_stats.num_reo_status_ring_masks++; + } /* Process LMAC interrupts */ for (ring = 0 ; ring < MAX_PDEV_CNT; ring++) { @@ -1372,7 +1394,9 @@ static uint32_t dp_service_srngs(void *dp_ctx, uint32_t dp_budget) pdev->pdev_id); if (int_ctx->rx_mon_ring_mask & (1 << mac_for_pdev)) { work_done = dp_mon_process(soc, mac_for_pdev, - remaining_quota); + remaining_quota); + if (work_done) + intr_stats->num_rx_mon_ring_masks++; budget -= work_done; if (budget <= 0) goto budget_done; @@ -1382,8 +1406,10 @@ static uint32_t dp_service_srngs(void *dp_ctx, uint32_t dp_budget) if (int_ctx->rxdma2host_ring_mask & (1 << mac_for_pdev)) { work_done = dp_rxdma_err_process(soc, - mac_for_pdev, - remaining_quota); + mac_for_pdev, + remaining_quota); + if (work_done) + intr_stats->num_rxdma2host_ring_masks++; budget -= work_done; if (budget <= 0) goto budget_done; @@ -1397,17 +1423,19 @@ static uint32_t dp_service_srngs(void *dp_ctx, uint32_t dp_budget) struct dp_srng *rx_refill_buf_ring = &pdev->rx_refill_buf_ring; + intr_stats->num_host2rxdma_ring_masks++; DP_STATS_INC(pdev, replenish.low_thresh_intrs, 1); dp_rx_buffers_replenish(soc, mac_for_pdev, - rx_refill_buf_ring, - &soc->rx_desc_buf[mac_for_pdev], 0, - &desc_list, &tail); + rx_refill_buf_ring, + &soc->rx_desc_buf[mac_for_pdev], + 0, &desc_list, &tail); } } } qdf_lro_flush(int_ctx->lro_ctx); + intr_stats->num_masks++; budget_done: return dp_budget - budget; @@ -6920,7 +6948,44 @@ dp_print_soc_tx_stats(struct dp_soc *soc) soc->stats.tx.tcl_ring_full[2]); DP_PRINT_STATS("Tx invalid completion release = %d", soc->stats.tx.invalid_release_source); + DP_PRINT_STATS("Tx comp loop pkt limit hit = %d", + soc->stats.tx.tx_comp_loop_pkt_limit_hit); + DP_PRINT_STATS("Tx comp HP out of sync2 = %d", + soc->stats.tx.hp_oos2); } + +/** + * dp_print_soc_interrupt_stats() - Print interrupt stats for the soc + * @soc: dp_soc handle + * + * Return: None + */ +static void dp_print_soc_interrupt_stats(struct dp_soc *soc) +{ + int i = 0; + struct dp_intr_stats *intr_stats; + + DP_PRINT_STATS("INT: Total |txComps|reo[0] |reo[1] |reo[2] |reo[3] |mon |rx_err | wbm |reo_sta|rxdm2hst|hst2rxdm|"); + for (i = 0; i < WLAN_CFG_INT_NUM_CONTEXTS; i++) { + intr_stats = &soc->intr_ctx[i].intr_stats; + DP_PRINT_STATS("%3u[%d]: %7u %7u %7u %7u %7u %7u %7u %7u %7u %7u %8u %8u", + i, + hif_get_int_ctx_irq_num(soc->hif_handle, i), + intr_stats->num_masks, + intr_stats->num_tx_ring_masks[0], + intr_stats->num_rx_ring_masks[0], + intr_stats->num_rx_ring_masks[1], + intr_stats->num_rx_ring_masks[2], + intr_stats->num_rx_ring_masks[3], + intr_stats->num_rx_mon_ring_masks, + intr_stats->num_rx_err_ring_masks, + intr_stats->num_rx_wbm_rel_ring_masks, + intr_stats->num_reo_status_ring_masks, + intr_stats->num_rxdma2host_ring_masks, + intr_stats->num_host2rxdma_ring_masks); + } +} + /** * dp_print_soc_rx_stats: Print SOC level Rx stats * @soc: DP_SOC Handle @@ -6962,7 +7027,11 @@ dp_print_soc_rx_stats(struct dp_soc *soc) DP_PRINT_STATS("RX frags: %d", soc->stats.rx.rx_frags); DP_PRINT_STATS("RX frag wait: %d", soc->stats.rx.rx_frag_wait); DP_PRINT_STATS("RX frag err: %d", soc->stats.rx.rx_frag_err); - DP_PRINT_STATS("RX HP out_of_sync: %d", soc->stats.rx.hp_oos); + + DP_PRINT_STATS("RX HP out_of_sync: %d %d", soc->stats.rx.hp_oos, + soc->stats.rx.hp_oos2); + DP_PRINT_STATS("RX Reap Loop Pkt Limit Hit: %d", + soc->stats.rx.reap_loop_pkt_limit_hit); DP_PRINT_STATS("RX DESC invalid magic: %u", soc->stats.rx.err.rx_desc_invalid_magic); DP_PRINT_STATS("RX DUP DESC: %d", @@ -7211,6 +7280,8 @@ dp_print_host_stats(struct cdp_vdev *vdev_handle, break; case TXRX_NAPI_STATS: dp_print_napi_stats(pdev->soc); + case TXRX_SOC_INTERRUPT_STATS: + dp_print_soc_interrupt_stats(pdev->soc); break; default: dp_info("Wrong Input For TxRx Host Stats"); @@ -8148,6 +8219,7 @@ static QDF_STATUS dp_txrx_dump_stats(void *psoc, uint16_t value, switch (value) { case CDP_TXRX_PATH_STATS: dp_txrx_path_stats(soc); + dp_print_soc_interrupt_stats(soc); break; case CDP_RX_RING_STATS: @@ -8205,6 +8277,50 @@ void dp_update_flow_control_parameters(struct dp_soc *soc, } #endif +#ifdef WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT +/* Max packet limit for TX Comp packet loop (dp_tx_comp_handler) */ +#define DP_TX_COMP_LOOP_PKT_LIMIT_MAX 1024 + +/* Max packet limit for RX REAP Loop (dp_rx_process) */ +#define DP_RX_REAP_LOOP_PKT_LIMIT_MAX 1024 + +static +void dp_update_rx_soft_irq_limit_params(struct dp_soc *soc, + struct cdp_config_params *params) +{ + soc->wlan_cfg_ctx->tx_comp_loop_pkt_limit = + params->tx_comp_loop_pkt_limit; + + if (params->tx_comp_loop_pkt_limit < DP_TX_COMP_LOOP_PKT_LIMIT_MAX) + soc->wlan_cfg_ctx->tx_comp_enable_eol_data_check = true; + else + soc->wlan_cfg_ctx->tx_comp_enable_eol_data_check = false; + + soc->wlan_cfg_ctx->rx_reap_loop_pkt_limit = + params->rx_reap_loop_pkt_limit; + + if (params->rx_reap_loop_pkt_limit < DP_RX_REAP_LOOP_PKT_LIMIT_MAX) + soc->wlan_cfg_ctx->rx_enable_eol_data_check = true; + else + soc->wlan_cfg_ctx->rx_enable_eol_data_check = false; + + soc->wlan_cfg_ctx->rx_hp_oos_update_limit = + params->rx_hp_oos_update_limit; + + dp_info("tx_comp_loop_pkt_limit %u tx_comp_enable_eol_data_check %u rx_reap_loop_pkt_limit %u rx_enable_eol_data_check %u rx_hp_oos_update_limit %u", + soc->wlan_cfg_ctx->tx_comp_loop_pkt_limit, + soc->wlan_cfg_ctx->tx_comp_enable_eol_data_check, + soc->wlan_cfg_ctx->rx_reap_loop_pkt_limit, + soc->wlan_cfg_ctx->rx_enable_eol_data_check, + soc->wlan_cfg_ctx->rx_hp_oos_update_limit); +} +#else +static inline +void dp_update_rx_soft_irq_limit_params(struct dp_soc *soc, + struct cdp_config_params *params) +{ } +#endif /* WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT */ + /** * dp_update_config_parameters() - API to store datapath * config parameters @@ -8234,6 +8350,7 @@ QDF_STATUS dp_update_config_parameters(struct cdp_soc *psoc, soc->wlan_cfg_ctx->ipa_enabled = params->ipa_enable; soc->wlan_cfg_ctx->gro_enabled = params->gro_enable; + dp_update_rx_soft_irq_limit_params(soc, params); dp_update_flow_control_parameters(soc, params); return QDF_STATUS_SUCCESS; diff --git a/dp/wifi3.0/dp_reo.c b/dp/wifi3.0/dp_reo.c index d0670be32b..0225eb19d7 100644 --- a/dp/wifi3.0/dp_reo.c +++ b/dp/wifi3.0/dp_reo.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2018 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-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 @@ -85,22 +85,24 @@ QDF_STATUS dp_reo_send_cmd(struct dp_soc *soc, enum hal_reo_cmd_type type, return QDF_STATUS_SUCCESS; } -void dp_reo_status_ring_handler(struct dp_soc *soc) +uint32_t dp_reo_status_ring_handler(struct dp_soc *soc) { uint32_t *reo_desc; struct dp_reo_cmd_info *reo_cmd = NULL; union hal_reo_status reo_status; int num; + int processed_count = 0; if (hal_srng_access_start(soc->hal_soc, soc->reo_status_ring.hal_srng)) { - return; + return processed_count; } reo_desc = hal_srng_dst_get_next(soc->hal_soc, soc->reo_status_ring.hal_srng); while (reo_desc) { uint16_t tlv = HAL_GET_TLV(reo_desc); + processed_count++; switch (tlv) { case HAL_REO_QUEUE_STATS_STATUS_TLV: @@ -173,6 +175,7 @@ next: } /* while */ hal_srng_access_end(soc->hal_soc, soc->reo_status_ring.hal_srng); + return processed_count; } /** diff --git a/dp/wifi3.0/dp_rx.c b/dp/wifi3.0/dp_rx.c index 4e320060df..9957568b5e 100644 --- a/dp/wifi3.0/dp_rx.c +++ b/dp/wifi3.0/dp_rx.c @@ -1619,6 +1619,57 @@ static inline void dp_rx_desc_nbuf_sanity_check(void *ring_desc, } #endif +#ifdef WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT +static inline +bool dp_rx_reap_loop_pkt_limit_hit(struct dp_soc *soc, int num_reaped) +{ + bool limit_hit = false; + struct wlan_cfg_dp_soc_ctxt *cfg = soc->wlan_cfg_ctx; + + limit_hit = + (num_reaped >= cfg->rx_reap_loop_pkt_limit) ? true : false; + + if (limit_hit) + DP_STATS_INC(soc, rx.reap_loop_pkt_limit_hit, 1) + + return limit_hit; +} + +static inline +bool dp_rx_hp_oos_update_limit_hit(struct dp_soc *soc, int hp_oos_updates) +{ + bool limit_hit = false; + struct wlan_cfg_dp_soc_ctxt *cfg = soc->wlan_cfg_ctx; + + limit_hit = + (hp_oos_updates >= cfg->rx_hp_oos_update_limit) ? true : false; + return limit_hit; +} + +static inline bool dp_rx_enable_eol_data_check(struct dp_soc *soc) +{ + return soc->wlan_cfg_ctx->rx_enable_eol_data_check; +} + +#else +static inline +bool dp_rx_reap_loop_pkt_limit_hit(struct dp_soc *soc, int num_reaped) +{ + return false; +} + +static inline +bool dp_rx_hp_oos_update_limit_hit(struct dp_soc *soc, int hp_oos_updates) +{ + return false; +} + +static inline bool dp_rx_enable_eol_data_check(struct dp_soc *soc) +{ + return false; +} + +#endif /* WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT */ /** * dp_rx_process() - Brain of the Rx processing functionality * Called from the bottom half (tasklet/NET_RX_SOFTIRQ) @@ -1639,21 +1690,21 @@ uint32_t dp_rx_process(struct dp_intr *int_ctx, void *hal_ring, void *ring_desc; struct dp_rx_desc *rx_desc = NULL; qdf_nbuf_t nbuf, next; - union dp_rx_desc_list_elem_t *head[MAX_PDEV_CNT] = { NULL }; - union dp_rx_desc_list_elem_t *tail[MAX_PDEV_CNT] = { NULL }; + union dp_rx_desc_list_elem_t *head[MAX_PDEV_CNT]; + union dp_rx_desc_list_elem_t *tail[MAX_PDEV_CNT]; uint32_t rx_bufs_used = 0, rx_buf_cookie; uint32_t l2_hdr_offset = 0; uint16_t msdu_len = 0; uint16_t peer_id; - struct dp_peer *peer = NULL; - struct dp_vdev *vdev = NULL; + struct dp_peer *peer; + struct dp_vdev *vdev; uint32_t pkt_len = 0; - struct hal_rx_mpdu_desc_info mpdu_desc_info = { 0 }; - struct hal_rx_msdu_desc_info msdu_desc_info = { 0 }; + struct hal_rx_mpdu_desc_info mpdu_desc_info; + struct hal_rx_msdu_desc_info msdu_desc_info; enum hal_reo_error_status error; uint32_t peer_mdata; uint8_t *rx_tlv_hdr; - uint32_t rx_bufs_reaped[MAX_PDEV_CNT] = { 0 }; + uint32_t rx_bufs_reaped[MAX_PDEV_CNT]; uint8_t mac_id = 0; struct dp_pdev *pdev; struct dp_pdev *rx_pdev; @@ -1662,24 +1713,41 @@ uint32_t dp_rx_process(struct dp_intr *int_ctx, void *hal_ring, struct dp_soc *soc = int_ctx->soc; uint8_t ring_id = 0; uint8_t core_id = 0; - qdf_nbuf_t nbuf_head = NULL; - qdf_nbuf_t nbuf_tail = NULL; - qdf_nbuf_t deliver_list_head = NULL; - qdf_nbuf_t deliver_list_tail = NULL; - int32_t tid = 0; - uint32_t dst_num_valid = 0; struct cdp_tid_rx_stats *tid_stats; - + qdf_nbuf_t nbuf_head; + qdf_nbuf_t nbuf_tail; + qdf_nbuf_t deliver_list_head; + qdf_nbuf_t deliver_list_tail; + uint32_t num_rx_bufs_reaped = 0; + uint32_t intr_id; + struct hif_opaque_softc *scn; + uint32_t hp_oos_updates = 0; + int32_t tid = 0; DP_HIST_INIT(); - /* Debug -- Remove later */ - qdf_assert(soc && hal_ring); + qdf_assert_always(soc && hal_ring); hal_soc = soc->hal_soc; - - /* Debug -- Remove later */ - qdf_assert(hal_soc); + qdf_assert_always(hal_soc); hif_pm_runtime_mark_last_busy(soc->osdev->dev); + scn = soc->hif_handle; + intr_id = int_ctx->dp_intr_id; + +more_data: + /* reset local variables here to be re-used in the function */ + nbuf_head = NULL; + nbuf_tail = NULL; + deliver_list_head = NULL; + deliver_list_tail = NULL; + peer = NULL; + vdev = NULL; + num_rx_bufs_reaped = 0; + + qdf_mem_zero(rx_bufs_reaped, sizeof(rx_bufs_reaped)); + qdf_mem_zero(&mpdu_desc_info, sizeof(mpdu_desc_info)); + qdf_mem_zero(&msdu_desc_info, sizeof(msdu_desc_info)); + qdf_mem_zero(head, sizeof(head)); + qdf_mem_zero(tail, sizeof(tail)); if (qdf_unlikely(hal_srng_access_start(hal_soc, hal_ring))) { @@ -1699,6 +1767,7 @@ uint32_t dp_rx_process(struct dp_intr *int_ctx, void *hal_ring, * them in per vdev queue. * Process the received pkts in a different per vdev loop. */ + hp_oos_updates = 0; while (qdf_likely(quota)) { ring_desc = hal_srng_dst_get_next(hal_soc, hal_ring); @@ -1716,10 +1785,12 @@ uint32_t dp_rx_process(struct dp_intr *int_ctx, void *hal_ring, * complete mpdu in one reap. */ if (qdf_unlikely(!ring_desc)) { - dst_num_valid = hal_srng_dst_num_valid(hal_soc, - hal_ring, - true); - if (dst_num_valid) { + if (dp_rx_hp_oos_update_limit_hit(soc, + hp_oos_updates)) { + break; + } + hp_oos_updates++; + if (hal_srng_dst_peek_sync(hal_soc, hal_ring)) { DP_STATS_INC(soc, rx.hp_oos, 1); hal_srng_access_end_unlocked(hal_soc, hal_ring); @@ -1834,6 +1905,10 @@ uint32_t dp_rx_process(struct dp_intr *int_ctx, void *hal_ring, dp_rx_add_to_free_desc_list(&head[rx_desc->pool_id], &tail[rx_desc->pool_id], rx_desc); + + num_rx_bufs_reaped++; + if (dp_rx_reap_loop_pkt_limit_hit(soc, num_rx_bufs_reaped)) + break; } done: hal_srng_access_end(hal_soc, hal_ring); @@ -1858,6 +1933,7 @@ done: &head[mac_id], &tail[mac_id]); } + dp_verbose_debug("replenished %u\n", rx_bufs_reaped[0]); /* Peer can be NULL is case of LFR */ if (qdf_likely(peer)) vdev = NULL; @@ -2069,16 +2145,6 @@ done: dp_rx_fill_mesh_stats(vdev, nbuf, rx_tlv_hdr, peer); } -#ifdef QCA_WIFI_NAPIER_EMULATION_DBG /* Debug code, remove later */ - QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, - "p_id %d msdu_len %d hdr_off %d", - peer_id, msdu_len, l2_hdr_offset); - - print_hex_dump(KERN_ERR, - "\t Pkt Data:", DUMP_PREFIX_NONE, 32, 4, - qdf_nbuf_data(nbuf), 128, false); -#endif /* NAPIER_EMULATION */ - if (qdf_likely(vdev->rx_decap_type == htt_cmn_pkt_type_ethernet) && qdf_likely(!vdev->mesh_vdev)) { @@ -2133,13 +2199,21 @@ done: dp_peer_unref_del_find_by_id(peer); } - /* Update histogram statistics by looping through pdev's */ - DP_RX_HIST_STATS_PER_PDEV(); - if (deliver_list_head) dp_rx_deliver_to_stack(vdev, peer, deliver_list_head, deliver_list_tail); + if (dp_rx_enable_eol_data_check(soc)) { + if (quota && + hal_srng_dst_peek_sync_locked(soc, hal_ring)) { + DP_STATS_INC(soc, rx.hp_oos2, 1); + if (!hif_exec_should_yield(scn, intr_id)) + goto more_data; + } + } + /* Update histogram statistics by looping through pdev's */ + DP_RX_HIST_STATS_PER_PDEV(); + return rx_bufs_used; /* Assume no scale factor for now */ } diff --git a/dp/wifi3.0/dp_tx.c b/dp/wifi3.0/dp_tx.c index d1adcbc045..952a31974d 100644 --- a/dp/wifi3.0/dp_tx.c +++ b/dp/wifi3.0/dp_tx.c @@ -3205,7 +3205,6 @@ dp_tx_comp_process_desc_list(struct dp_soc *soc, struct dp_peer *peer; qdf_nbuf_t netbuf; - DP_HIST_INIT(); desc = comp_head; while (desc) { @@ -3223,15 +3222,12 @@ dp_tx_comp_process_desc_list(struct dp_soc *soc, if (peer) dp_peer_unref_del_find_by_id(peer); - DP_HIST_PACKET_COUNT_INC(desc->pdev->pdev_id); - next = desc->next; dp_tx_desc_release(desc, desc->pool_id); desc = next; } - DP_TX_HIST_STATS_PER_PDEV(); } /** @@ -3342,19 +3338,41 @@ void dp_tx_process_htt_completion(struct dp_tx_desc_s *tx_desc, uint8_t *status) } } -/** - * dp_tx_comp_handler() - Tx completion handler - * @soc: core txrx main context - * @ring_id: completion ring id - * @quota: No. of packets/descriptors that can be serviced in one loop - * - * This function will collect hardware release ring element contents and - * handle descriptor contents. Based on contents, free packet or handle error - * conditions - * - * Return: none - */ -uint32_t dp_tx_comp_handler(struct dp_soc *soc, void *hal_srng, uint32_t quota) +#ifdef WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT +static inline +bool dp_tx_comp_loop_pkt_limit_hit(struct dp_soc *soc, int num_reaped) +{ + bool limit_hit = false; + struct wlan_cfg_dp_soc_ctxt *cfg = soc->wlan_cfg_ctx; + + limit_hit = + (num_reaped >= cfg->tx_comp_loop_pkt_limit) ? true : false; + + if (limit_hit) + DP_STATS_INC(soc, tx.tx_comp_loop_pkt_limit_hit, 1); + + return limit_hit; +} + +static inline bool dp_tx_comp_enable_eol_data_check(struct dp_soc *soc) +{ + return soc->wlan_cfg_ctx->tx_comp_enable_eol_data_check; +} +#else +static inline +bool dp_tx_comp_loop_pkt_limit_hit(struct dp_soc *soc, int num_reaped) +{ + return false; +} + +static inline bool dp_tx_comp_enable_eol_data_check(struct dp_soc *soc) +{ + return false; +} +#endif + +uint32_t dp_tx_comp_handler(struct dp_intr *int_ctx, struct dp_soc *soc, + void *hal_srng, uint32_t quota) { void *tx_comp_hal_desc; uint8_t buffer_src; @@ -3363,9 +3381,16 @@ uint32_t dp_tx_comp_handler(struct dp_soc *soc, void *hal_srng, uint32_t quota) struct dp_tx_desc_s *tx_desc = NULL; struct dp_tx_desc_s *head_desc = NULL; struct dp_tx_desc_s *tail_desc = NULL; - uint32_t num_processed; - uint32_t count; + uint32_t num_processed = 0; + uint32_t count = 0; + bool force_break = false; + DP_HIST_INIT(); + +more_data: + /* Re-initialize local variables to be re-used */ + head_desc = NULL; + tail_desc = NULL; if (qdf_unlikely(hal_srng_access_start(soc->hal_soc, hal_srng))) { QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR, "%s %d : HAL RING Access Failed -- %pK", @@ -3373,9 +3398,6 @@ uint32_t dp_tx_comp_handler(struct dp_soc *soc, void *hal_srng, uint32_t quota) return 0; } - num_processed = 0; - count = 0; - /* Find head descriptor from completion ring */ while (qdf_likely(tx_comp_hal_desc = hal_srng_dst_get_next(soc->hal_soc, hal_srng))) { @@ -3466,6 +3488,8 @@ uint32_t dp_tx_comp_handler(struct dp_soc *soc, void *hal_srng, uint32_t quota) tx_desc->next = NULL; tail_desc = tx_desc; + DP_HIST_PACKET_COUNT_INC(tx_desc->pdev->pdev_id); + /* Collect hw completion contents */ hal_tx_comp_desc_sync(tx_comp_hal_desc, &tx_desc->comp, 1); @@ -3478,10 +3502,15 @@ uint32_t dp_tx_comp_handler(struct dp_soc *soc, void *hal_srng, uint32_t quota) * Processed packet count is more than given quota * stop to processing */ - if ((num_processed >= quota)) + if (num_processed >= quota) { + force_break = true; break; + } count++; + + if (dp_tx_comp_loop_pkt_limit_hit(soc, count)) + break; } hal_srng_access_end(soc->hal_soc, hal_srng); @@ -3490,6 +3519,17 @@ uint32_t dp_tx_comp_handler(struct dp_soc *soc, void *hal_srng, uint32_t quota) if (head_desc) dp_tx_comp_process_desc_list(soc, head_desc); + if (dp_tx_comp_enable_eol_data_check(soc)) { + if (!force_break && + hal_srng_dst_peek_sync_locked(soc, hal_srng)) { + DP_STATS_INC(soc, tx.hp_oos2, 1); + if (!hif_exec_should_yield(soc->hif_handle, + int_ctx->dp_intr_id)) + goto more_data; + } + } + DP_TX_HIST_STATS_PER_PDEV(); + return num_processed; } diff --git a/dp/wifi3.0/dp_tx.h b/dp/wifi3.0/dp_tx.h index fc5484018b..4b63d243c1 100644 --- a/dp/wifi3.0/dp_tx.h +++ b/dp/wifi3.0/dp_tx.h @@ -177,7 +177,21 @@ qdf_nbuf_t dp_tx_non_std(struct cdp_vdev *vdev_handle, enum ol_tx_spec tx_spec, qdf_nbuf_t msdu_list); #endif -uint32_t dp_tx_comp_handler(struct dp_soc *soc, void *hal_srng, uint32_t quota); +/** + * dp_tx_comp_handler() - Tx completion handler + * @int_ctx: pointer to DP interrupt context + * @soc: core txrx main context + * @ring_id: completion ring id + * @quota: No. of packets/descriptors that can be serviced in one loop + * + * This function will collect hardware release ring element contents and + * handle descriptor contents. Based on contents, free packet or handle error + * conditions + * + * Return: Number of TX completions processed + */ +uint32_t dp_tx_comp_handler(struct dp_intr *int_ctx, struct dp_soc *soc, + void *hal_srng, uint32_t quota); QDF_STATUS dp_tx_prepare_send_me(struct dp_vdev *vdev, qdf_nbuf_t nbuf); diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h index da292be027..82ab9c6254 100644 --- a/dp/wifi3.0/dp_types.h +++ b/dp/wifi3.0/dp_types.h @@ -576,6 +576,34 @@ struct dp_rx_tid { }; +/** + * struct dp_intr_stats - DP Interrupt Stats for an interrupt context + * @num_tx_ring_masks: interrupts with tx_ring_mask set + * @num_rx_ring_masks: interrupts with rx_ring_mask set + * @num_rx_mon_ring_masks: interrupts with rx_mon_ring_mask set + * @num_rx_err_ring_masks: interrupts with rx_err_ring_mask set + * @num_rx_wbm_rel_ring_masks: interrupts with rx_wbm_rel_ring_mask set + * @num_reo_status_ring_masks: interrupts with reo_status_ring_mask set + * @num_rxdma2host_ring_masks: interrupts with rxdma2host_ring_mask set + * @num_host2rxdma_ring_masks: interrupts with host2rxdma_ring_mask set + * @num_host2rxdma_ring_masks: interrupts with host2rxdma_ring_mask set + * @num_masks: total number of times the interrupt was received + * + * Counter for individual masks are incremented only if there are any packets + * on that ring. + */ +struct dp_intr_stats { + uint32_t num_tx_ring_masks[MAX_TCL_DATA_RINGS]; + uint32_t num_rx_ring_masks[MAX_REO_DEST_RINGS]; + uint32_t num_rx_mon_ring_masks; + uint32_t num_rx_err_ring_masks; + uint32_t num_rx_wbm_rel_ring_masks; + uint32_t num_reo_status_ring_masks; + uint32_t num_rxdma2host_ring_masks; + uint32_t num_host2rxdma_ring_masks; + uint32_t num_masks; +}; + /* per interrupt context */ struct dp_intr { uint8_t tx_ring_mask; /* WBM Tx completion rings (0-2) @@ -594,6 +622,9 @@ struct dp_intr { to get DMA ring handles */ qdf_lro_ctx_t lro_ctx; uint8_t dp_intr_id; + + /* Interrupt Stats for individual masks */ + struct dp_intr_stats intr_stats; }; #define REO_DESC_FREELIST_SIZE 64 @@ -624,6 +655,10 @@ struct dp_soc_stats { uint32_t dropped_fw_removed; /* tx completion release_src != TQM or FW */ uint32_t invalid_release_source; + /* TX Comp loop packet limit hit */ + uint32_t tx_comp_loop_pkt_limit_hit; + /* Head pointer Out of sync at the end of dp_tx_comp_handler */ + uint32_t hp_oos2; } tx; /* SOC level RX stats */ @@ -639,8 +674,13 @@ struct dp_soc_stats { uint32_t rx_frag_err; /* No of reinjected packets */ uint32_t reo_reinject; - /* Head pointer Out of sync */ + + /* Reap loop packet limit hit */ + uint32_t reap_loop_pkt_limit_hit; + /* Head pointer Out of sync during reap loop*/ uint32_t hp_oos; + /* Head pointer Out of sync at the end of dp_rx_process */ + uint32_t hp_oos2; struct { /* Invalid RBM error count */ uint32_t invalid_rbm; diff --git a/hal/wifi3.0/hal_api.h b/hal/wifi3.0/hal_api.h index a4cb6ccf5d..005922d38f 100644 --- a/hal/wifi3.0/hal_api.h +++ b/hal/wifi3.0/hal_api.h @@ -628,26 +628,79 @@ static inline void *hal_srng_dst_get_next_hp(void *hal_soc, void *hal_ring) } /** - * hal_srng_dst_peek - Get next entry from a ring without moving tail pointer. - * hal_srng_dst_get_next should be called subsequently to move the tail pointer - * TODO: See if we need an optimized version of get_next that doesn't check for - * loop_cnt - * + * hal_srng_dst_peek - Check if there are any entries in the ring (peek) * @hal_soc: Opaque HAL SOC handle * @hal_ring: Destination ring pointer * + * Caller takes responsibility for any locking needs. + * * Return: Opaque pointer for next ring entry; NULL on failire */ -static inline void *hal_srng_dst_peek(void *hal_soc, void *hal_ring) +static inline +void *hal_srng_dst_peek(void *hal_soc, void *hal_ring) { struct hal_srng *srng = (struct hal_srng *)hal_ring; + if (srng->u.dst_ring.tp != srng->u.dst_ring.cached_hp) + return (void *)(&srng->ring_base_vaddr[srng->u.dst_ring.tp]); + + return NULL; +} + +/** + * hal_srng_dst_peek_sync - Check if there are any entries in the ring (peek) + * @hal_soc: Opaque HAL SOC handle + * @hal_ring: Destination ring pointer + * + * Sync cached head pointer with HW. + * Caller takes responsibility for any locking needs. + * + * Return: Opaque pointer for next ring entry; NULL on failire + */ +static inline +void *hal_srng_dst_peek_sync(void *hal_soc, void *hal_ring) +{ + struct hal_srng *srng = (struct hal_srng *)hal_ring; + + srng->u.dst_ring.cached_hp = + *(volatile uint32_t *)(srng->u.dst_ring.hp_addr); + if (srng->u.dst_ring.tp != srng->u.dst_ring.cached_hp) return (void *)(&(srng->ring_base_vaddr[srng->u.dst_ring.tp])); return NULL; } +/** + * hal_srng_dst_peek_sync_locked - Peek for any entries in the ring + * @hal_soc: Opaque HAL SOC handle + * @hal_ring: Destination ring pointer + * + * Sync cached head pointer with HW. + * This function takes up SRNG_LOCK. Should not be called with SRNG lock held. + * + * Return: Opaque pointer for next ring entry; NULL on failire + */ +static inline +void *hal_srng_dst_peek_sync_locked(void *hal_soc, void *hal_ring) +{ + struct hal_srng *srng = (struct hal_srng *)hal_ring; + void *ring_desc_ptr = NULL; + + if (qdf_unlikely(!hal_ring)) { + qdf_print("Error: Invalid hal_ring\n"); + return NULL; + } + + SRNG_LOCK(&srng->lock); + + ring_desc_ptr = hal_srng_dst_peek_sync(hal_soc, hal_ring); + + SRNG_UNLOCK(&srng->lock); + + return ring_desc_ptr; +} + /** * hal_srng_dst_num_valid - Returns number of valid entries (to be processed * by SW) in destination ring diff --git a/hif/inc/hif.h b/hif/inc/hif.h index dfa8f9a436..a59d70f27e 100644 --- a/hif/inc/hif.h +++ b/hif/inc/hif.h @@ -147,6 +147,7 @@ struct CE_state; * but this does not change the number of buckets */ #define QCA_NAPI_NUM_BUCKETS 4 + /** * qca_napi_stat - stats structure for execution contexts * @napi_schedules - number of times the schedule function is called @@ -158,8 +159,8 @@ struct CE_state; * @napi_budget_uses - histogram of work done per execution run * @time_limit_reache - count of yields due to time limit threshholds * @rxpkt_thresh_reached - count of yields due to a work limit + * @poll_time_buckets - histogram of poll times for the napi * - * needs to be renamed */ struct qca_napi_stat { uint32_t napi_schedules; @@ -171,6 +172,9 @@ struct qca_napi_stat { uint32_t time_limit_reached; uint32_t rxpkt_thresh_reached; unsigned long long napi_max_poll_time; +#ifdef WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT + uint32_t poll_time_buckets[QCA_NAPI_NUM_BUCKETS]; +#endif }; @@ -275,10 +279,13 @@ struct qca_napi_data { }; /** - * struct hif_config_info - Place Holder for hif confiruation + * struct hif_config_info - Place Holder for HIF configuration * @enable_self_recovery: Self Recovery + * @enable_runtime_pm: Enable Runtime PM + * @runtime_pm_delay: Runtime PM Delay + * @rx_softirq_max_yield_duration_ns: Max Yield time duration for RX Softirq * - * Structure for holding hif ini parameters. + * Structure for holding HIF ini parameters. */ struct hif_config_info { bool enable_self_recovery; @@ -286,6 +293,7 @@ struct hif_config_info { bool enable_runtime_pm; u_int32_t runtime_pm_delay; #endif + uint64_t rx_softirq_max_yield_duration_ns; }; /** @@ -697,6 +705,27 @@ void hif_offld_flush_cb_register(struct hif_opaque_softc *scn, void hif_offld_flush_cb_deregister(struct hif_opaque_softc *scn); #endif +#ifdef WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT +/** + * hif_exec_should_yield() - Check if hif napi context should yield + * @hif_ctx - HIF opaque context + * @grp_id - grp_id of the napi for which check needs to be done + * + * The function uses grp_id to look for NAPI and checks if NAPI needs to + * yield. HIF_EXT_GROUP_MAX_YIELD_DURATION_NS is the duration used for + * yield decision. + * + * Return: true if NAPI needs to yield, else false + */ +bool hif_exec_should_yield(struct hif_opaque_softc *hif_ctx, uint grp_id); +#else +static inline bool hif_exec_should_yield(struct hif_opaque_softc *hif_ctx, + uint grp_id) +{ + return false; +} +#endif + void hif_disable_isr(struct hif_opaque_softc *hif_ctx); void hif_reset_soc(struct hif_opaque_softc *hif_ctx); void hif_save_htc_htt_config_endpoint(struct hif_opaque_softc *hif_ctx, @@ -915,6 +944,18 @@ enum hif_exec_type { }; typedef uint32_t (*ext_intr_handler)(void *, uint32_t); + +/** + * hif_get_int_ctx_irq_num() - retrieve an irq num for an interrupt context id + * @softc: hif opaque context owning the exec context + * @id: the id of the interrupt context + * + * Return: IRQ number of the first (zero'th) IRQ within the interrupt context ID + * 'id' registered with the OS + */ +int32_t hif_get_int_ctx_irq_num(struct hif_opaque_softc *softc, + uint8_t id); + uint32_t hif_configure_ext_group_interrupts(struct hif_opaque_softc *hif_ctx); uint32_t hif_register_ext_group(struct hif_opaque_softc *hif_ctx, uint32_t numirq, uint32_t irq[], ext_intr_handler handler, @@ -928,6 +969,12 @@ void hif_update_pipe_callback(struct hif_opaque_softc *osc, u_int8_t pipeid, struct hif_msg_callbacks *callbacks); +/** + * hif_print_napi_stats() - Display HIF NAPI stats + * @hif_ctx - HIF opaque context + * + * Return: None + */ void hif_print_napi_stats(struct hif_opaque_softc *hif_ctx); /* hif_clear_napi_stats() - function clears the stats of the diff --git a/hif/src/hif_exec.c b/hif/src/hif_exec.c index 4051240ac3..abf15fdc6e 100644 --- a/hif/src/hif_exec.c +++ b/hif/src/hif_exec.c @@ -21,7 +21,6 @@ #include #include "qdf_module.h" #include "qdf_net_if.h" - /* mapping NAPI budget 0 to internal budget 0 * NAPI budget 1 to internal budget [1,scaler -1] * NAPI budget 2 to internal budget [scaler, 2 * scaler - 1], etc @@ -33,32 +32,6 @@ static struct hif_exec_context *hif_exec_tasklet_create(void); -/** - * hif_clear_napi_stats() - reset NAPI stats - * @hif_ctx: hif context - * - * return: void - */ -void hif_clear_napi_stats(struct hif_opaque_softc *hif_ctx) -{ - struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx); - struct hif_exec_context *hif_ext_group; - size_t i; - - for (i = 0; i < hif_state->hif_num_extgroup; i++) { - hif_ext_group = hif_state->hif_ext_group[i]; - - if (!hif_ext_group) - return; - - qdf_mem_set(hif_ext_group->sched_latency_stats, - sizeof(hif_ext_group->sched_latency_stats), - 0x0); - } -} - -qdf_export_symbol(hif_clear_napi_stats); - /** * hif_print_napi_latency_stats() - print NAPI scheduling latency stats * @hif_state: hif context @@ -127,11 +100,209 @@ static void hif_print_napi_latency_stats(struct HIF_CE_state *hif_state) #endif /** - * hif_print_napi_stats() - print NAPI stats + * hif_clear_napi_stats() - reset NAPI stats * @hif_ctx: hif context * * return: void */ +void hif_clear_napi_stats(struct hif_opaque_softc *hif_ctx) +{ + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx); + struct hif_exec_context *hif_ext_group; + size_t i; + + for (i = 0; i < hif_state->hif_num_extgroup; i++) { + hif_ext_group = hif_state->hif_ext_group[i]; + + if (!hif_ext_group) + return; + + qdf_mem_set(hif_ext_group->sched_latency_stats, + sizeof(hif_ext_group->sched_latency_stats), + 0x0); + } +} + +qdf_export_symbol(hif_clear_napi_stats); + +#ifdef WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT +/** + * hif_get_poll_times_hist_str() - Get HIF poll times histogram string + * @stats: NAPI stats to get poll time buckets + * @buf: buffer to fill histogram string + * @buf_len: length of the buffer + * + * Return: void + */ +static void hif_get_poll_times_hist_str(struct qca_napi_stat *stats, char *buf, + uint8_t buf_len) +{ + int i; + int str_index = 0; + + for (i = 0; i < QCA_NAPI_NUM_BUCKETS; i++) + str_index += qdf_scnprintf(buf + str_index, buf_len - str_index, + "%u|", stats->poll_time_buckets[i]); +} + +/** + * hif_exec_fill_poll_time_histogram() - fills poll time histogram for a NAPI + * @hif_ext_group: hif_ext_group of type NAPI + * + * The function is called at the end of a NAPI poll to calculate poll time + * buckets. + * + * Return: void + */ +static +void hif_exec_fill_poll_time_histogram(struct hif_exec_context *hif_ext_group) +{ + struct qca_napi_stat *napi_stat; + unsigned long long poll_time_ns; + uint32_t poll_time_us; + uint32_t bucket_size_us = 500; + uint32_t bucket; + uint32_t cpu_id = qdf_get_cpu(); + + poll_time_ns = sched_clock() - hif_ext_group->poll_start_time; + poll_time_us = poll_time_ns / 1000; + + napi_stat = &hif_ext_group->stats[cpu_id]; + if (poll_time_ns > hif_ext_group->stats[cpu_id].napi_max_poll_time) + hif_ext_group->stats[cpu_id].napi_max_poll_time = poll_time_ns; + + bucket = poll_time_us / bucket_size_us; + if (bucket >= QCA_NAPI_NUM_BUCKETS) + bucket = QCA_NAPI_NUM_BUCKETS - 1; + ++napi_stat->poll_time_buckets[bucket]; +} + +/** + * hif_exec_poll_should_yield() - Local function deciding if NAPI should yield + * @hif_ext_group: hif_ext_group of type NAPI + * + * Return: true if NAPI needs to yield, else false + */ +static bool hif_exec_poll_should_yield(struct hif_exec_context *hif_ext_group) +{ + bool time_limit_reached = false; + unsigned long long poll_time_ns; + int cpu_id = qdf_get_cpu(); + struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif); + struct hif_config_info *cfg = &scn->hif_config; + + poll_time_ns = sched_clock() - hif_ext_group->poll_start_time; + time_limit_reached = + poll_time_ns > cfg->rx_softirq_max_yield_duration_ns ? 1 : 0; + + if (time_limit_reached) { + hif_ext_group->stats[cpu_id].time_limit_reached++; + hif_ext_group->force_break = true; + } + + return time_limit_reached; +} + +bool hif_exec_should_yield(struct hif_opaque_softc *hif_ctx, uint grp_id) +{ + struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx); + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(scn); + struct hif_exec_context *hif_ext_group; + bool ret_val = false; + + if (!(grp_id < hif_state->hif_num_extgroup) || + !(grp_id < HIF_MAX_GROUP)) + return false; + + hif_ext_group = hif_state->hif_ext_group[grp_id]; + + if (hif_ext_group->type == HIF_EXEC_NAPI_TYPE) + ret_val = hif_exec_poll_should_yield(hif_ext_group); + + return ret_val; +} + +/** + * hif_exec_update_service_start_time() - Update NAPI poll start time + * @hif_ext_group: hif_ext_group of type NAPI + * + * The function is called at the beginning of a NAPI poll to record the poll + * start time. + * + * Return: None + */ +static inline +void hif_exec_update_service_start_time(struct hif_exec_context *hif_ext_group) +{ + hif_ext_group->poll_start_time = sched_clock(); +} + +void hif_print_napi_stats(struct hif_opaque_softc *hif_ctx) +{ + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx); + struct hif_exec_context *hif_ext_group; + struct qca_napi_stat *napi_stats; + int i, j; + + /* + * Max value of uint_32 (poll_time_bucket) = 4294967295 + * Thus we need 10 chars + 1 space =11 chars for each bucket value. + * +1 space for '\0'. + */ + char hist_str[(QCA_NAPI_NUM_BUCKETS * 11) + 1] = {'\0'}; + + QDF_TRACE(QDF_MODULE_ID_HIF, QDF_TRACE_LEVEL_ERROR, + "NAPI[#]CPU[#] |scheds |polls |comps |dones |t-lim |max(us)|hist(500us buckets)"); + + for (i = 0; + (i < hif_state->hif_num_extgroup && hif_state->hif_ext_group[i]); + i++) { + hif_ext_group = hif_state->hif_ext_group[i]; + for (j = 0; j < num_possible_cpus(); j++) { + napi_stats = &hif_ext_group->stats[j]; + if (!napi_stats->napi_schedules) + continue; + + hif_get_poll_times_hist_str(napi_stats, + hist_str, + sizeof(hist_str)); + QDF_TRACE(QDF_MODULE_ID_HIF, + QDF_TRACE_LEVEL_ERROR, + "NAPI[%d]CPU[%d]: %7u %7u %7u %7u %7u %7llu %s", + i, j, + napi_stats->napi_schedules, + napi_stats->napi_polls, + napi_stats->napi_completes, + napi_stats->napi_workdone, + napi_stats->time_limit_reached, + (napi_stats->napi_max_poll_time / 1000), + hist_str); + } + } + + hif_print_napi_latency_stats(hif_state); +} + +qdf_export_symbol(hif_print_napi_stats); + +#else + +static inline +void hif_get_poll_times_hist_str(struct qca_napi_stat *stats, char *buf, + uint8_t buf_len) +{ +} + +static inline +void hif_exec_update_service_start_time(struct hif_exec_context *hif_ext_group) +{ +} + +static inline +void hif_exec_fill_poll_time_histogram(struct hif_exec_context *hif_ext_group) +{ +} + void hif_print_napi_stats(struct hif_opaque_softc *hif_ctx) { struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(hif_ctx); @@ -164,6 +335,7 @@ void hif_print_napi_stats(struct hif_opaque_softc *hif_ctx) hif_print_napi_latency_stats(hif_state); } qdf_export_symbol(hif_print_napi_stats); +#endif /* WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT */ static void hif_exec_tasklet_schedule(struct hif_exec_context *ctx) { @@ -258,23 +430,27 @@ static void hif_latency_profile_start(struct hif_exec_context *hif_ext_group) #endif /** - * hif_exec_poll() - napi pool + * hif_exec_poll() - napi poll * napi: napi struct * budget: budget for napi * - * return: mapping of internal budget to napi + * Return: mapping of internal budget to napi */ static int hif_exec_poll(struct napi_struct *napi, int budget) { - struct hif_napi_exec_context *exec_ctx = + struct hif_napi_exec_context *napi_exec_ctx = qdf_container_of(napi, struct hif_napi_exec_context, napi); - struct hif_exec_context *hif_ext_group = &exec_ctx->exec_ctx; + struct hif_exec_context *hif_ext_group = &napi_exec_ctx->exec_ctx; struct hif_softc *scn = HIF_GET_SOFTC(hif_ext_group->hif); int work_done; int normalized_budget = 0; + int actual_dones; int shift = hif_ext_group->scale_bin_shift; int cpu = smp_processor_id(); + hif_ext_group->force_break = false; + hif_exec_update_service_start_time(hif_ext_group); + if (budget) normalized_budget = NAPI_BUDGET_TO_INTERNAL_BUDGET(budget, shift); @@ -283,7 +459,9 @@ static int hif_exec_poll(struct napi_struct *napi, int budget) work_done = hif_ext_group->handler(hif_ext_group->context, normalized_budget); - if (work_done < normalized_budget) { + actual_dones = work_done; + + if (!hif_ext_group->force_break && work_done < normalized_budget) { napi_complete(napi); qdf_atomic_dec(&scn->active_grp_tasklet_cnt); hif_ext_group->irq_enable(hif_ext_group); @@ -295,12 +473,14 @@ static int hif_exec_poll(struct napi_struct *napi, int budget) } hif_ext_group->stats[cpu].napi_polls++; - hif_ext_group->stats[cpu].napi_workdone += work_done; + hif_ext_group->stats[cpu].napi_workdone += actual_dones; /* map internal budget to NAPI budget */ if (work_done) work_done = INTERNAL_BUDGET_TO_NAPI_BUDGET(work_done, shift); + hif_exec_fill_poll_time_histogram(hif_ext_group); + return work_done; } @@ -436,13 +616,18 @@ struct hif_exec_context *hif_exec_get_ctx(struct hif_opaque_softc *softc, return NULL; } -/** - * hif_configure_ext_group_interrupts() - API to configure external group - * interrpts - * @hif_ctx : HIF Context - * - * Return: status - */ +int32_t hif_get_int_ctx_irq_num(struct hif_opaque_softc *softc, + uint8_t id) +{ + struct HIF_CE_state *hif_state = HIF_GET_CE_STATE(softc); + + if (id < hif_state->hif_num_extgroup) + return hif_state->hif_ext_group[id]->os_irq[0]; + return -EINVAL; +} + +qdf_export_symbol(hif_get_int_ctx_irq_num); + uint32_t hif_configure_ext_group_interrupts(struct hif_opaque_softc *hif_ctx) { struct hif_softc *scn = HIF_GET_SOFTC(hif_ctx); @@ -474,6 +659,7 @@ uint32_t hif_configure_ext_group_interrupts(struct hif_opaque_softc *hif_ctx) return QDF_STATUS_SUCCESS; } + qdf_export_symbol(hif_configure_ext_group_interrupts); #ifdef WLAN_SUSPEND_RESUME_TEST @@ -610,6 +796,7 @@ uint32_t hif_register_ext_group(struct hif_opaque_softc *hif_ctx, hif_ext_group->grp_id = hif_state->hif_num_extgroup; hif_ext_group->hif = hif_ctx; hif_ext_group->context_name = context_name; + hif_ext_group->type = type; hif_state->hif_num_extgroup++; return QDF_STATUS_SUCCESS; diff --git a/hif/src/hif_exec.h b/hif/src/hif_exec.h index 5447c29530..a8d514b4c0 100644 --- a/hif/src/hif_exec.h +++ b/hif/src/hif_exec.h @@ -68,6 +68,12 @@ struct hif_execution_ops { * @irq_disable: called before scheduling the context. * @irq_enable: called when the context leaves polling mode * @irq_name: pointer to function to return irq name/string mapped to irq number + * @irq_lock: spinlock used while enabling/disabling IRQs + * @type: type of execution context + * @poll_start_time: hif napi poll start time in nanoseconds + * @force_break: flag to indicate if HIF execution context was forced to return + * to HIF. This means there is more work to be done. Hence do not + * call napi_complete. */ struct hif_exec_context { struct hif_execution_ops *sched_ops; @@ -96,6 +102,9 @@ struct hif_exec_context { bool irq_requested; bool irq_enabled; qdf_spinlock_t irq_lock; + enum hif_exec_type type; + unsigned long long poll_start_time; + bool force_break; }; /** @@ -109,7 +118,7 @@ struct hif_tasklet_exec_context { }; /** - * struct hif_napi_exec_context - exec_context for tasklets + * struct hif_napi_exec_context - exec_context for NAPI * @exec_ctx: inherited data type * @netdev: dummy net device associated with the napi context * @napi: napi structure used in scheduling diff --git a/wlan_cfg/wlan_cfg.h b/wlan_cfg/wlan_cfg.h index dc6ca70a79..fae40b684c 100644 --- a/wlan_cfg/wlan_cfg.h +++ b/wlan_cfg/wlan_cfg.h @@ -137,6 +137,16 @@ struct wlan_cfg_dp_pdev_ctxt; * @rxdma_refill_ring: rxdma refill ring size * @rxdma_err_dst_ring: rxdma error detination ring size * @raw_mode_war: enable/disable raw mode war + * @enable_data_stall_detection: flag to enable data stall detection + * @disable_intra_bss_fwd: flag to disable intra bss forwarding + * @rxdma1_enable: flag to indicate if rxdma1 is enabled + * @tx_comp_loop_pkt_limit: Max # of packets to be processed in 1 tx comp loop + * @rx_reap_loop_pkt_limit: Max # of packets to be processed in 1 rx reap loop + * @rx_hp_oos_update_limit: Max # of HP OOS (out of sync) updates + * @rx_enable_eol_data_check: flag to enable check for more ring data at end of + * dp_rx_process loop + * tx_comp_enable_eol_data_check: flag to enable/disable checking for more data + * at end of tx_comp_handler loop. */ struct wlan_cfg_dp_soc_ctxt { int num_int_ctxts; @@ -211,6 +221,13 @@ struct wlan_cfg_dp_soc_ctxt { bool disable_intra_bss_fwd; bool rxdma1_enable; int max_ast_idx; +#ifdef WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT + uint32_t tx_comp_loop_pkt_limit; + uint32_t rx_reap_loop_pkt_limit; + uint32_t rx_hp_oos_update_limit; + bool rx_enable_eol_data_check; + bool tx_comp_enable_eol_data_check; +#endif /* WLAN_FEATURE_RX_SOFTIRQ_TIME_LIMIT */ }; /** @@ -1001,4 +1018,5 @@ int wlan_cfg_get_tx_flow_start_queue_offset(struct wlan_cfg_dp_soc_ctxt *cfg); int wlan_cfg_get_rx_defrag_min_timeout(struct wlan_cfg_dp_soc_ctxt *cfg); int wlan_cfg_get_defrag_timeout_check(struct wlan_cfg_dp_soc_ctxt *cfg); + #endif