diff --git a/dp/inc/cdp_txrx_stats_struct.h b/dp/inc/cdp_txrx_stats_struct.h index 8329ee1073..7ea9c50923 100644 --- a/dp/inc/cdp_txrx_stats_struct.h +++ b/dp/inc/cdp_txrx_stats_struct.h @@ -1718,6 +1718,7 @@ struct cdp_cfr_rcc_stats { * @tx_ppdu_proc: stats counter for tx ppdu processed * @ack_ba_comes_twice: stats counter for ack_ba_comes twice * @ppdu_drop: stats counter for ppdu_desc drop once threshold reached + * @ppdu_wrap_drop: stats counter for ppdu desc drop on wrap around */ struct cdp_pdev_stats { struct { @@ -1784,6 +1785,7 @@ struct cdp_pdev_stats { uint64_t tx_ppdu_proc; uint64_t ack_ba_comes_twice; uint64_t ppdu_drop; + uint64_t ppdu_wrap_drop; struct { uint64_t num_bufs_consumed; diff --git a/dp/wifi3.0/dp_htt.c b/dp/wifi3.0/dp_htt.c index c3688e88d9..ac7c8cfb82 100644 --- a/dp/wifi3.0/dp_htt.c +++ b/dp/wifi3.0/dp_htt.c @@ -2705,11 +2705,7 @@ static void dp_process_ppdu_stats_user_cmpltn_common_tlv( ppdu_desc->rts_failure = HTT_PPDU_STATS_USER_CMPLTN_COMMON_TLV_RTS_FAILURE_GET(*tag_buf); - /* - * on mpdu success, increase compltn_common_tlv counter - */ - if (ppdu_user_desc->mpdu_success) - ppdu_info->compltn_common_tlv++; + ppdu_info->compltn_common_tlv++; /* * MU BAR may send request to n users but we may received ack only from @@ -3014,7 +3010,7 @@ dp_process_ppdu_stats_user_compltn_flush_tlv(struct dp_pdev *pdev, peer = dp_peer_find_by_id(pdev->soc, peer_id); if (!peer) - return; + goto add_ppdu_to_sched_list; if (ppdu_desc->drop_reason == HTT_FLUSH_EXCESS_RETRIES) { DP_STATS_INC(peer, @@ -3023,6 +3019,14 @@ dp_process_ppdu_stats_user_compltn_flush_tlv(struct dp_pdev *pdev, } dp_peer_unref_del_find_by_id(peer); + +add_ppdu_to_sched_list: + ppdu_info->done = 1; + TAILQ_REMOVE(&pdev->ppdu_info_list, ppdu_info, ppdu_info_list_elem); + pdev->list_depth--; + TAILQ_INSERT_TAIL(&pdev->sched_comp_ppdu_list, ppdu_info, + ppdu_info_list_elem); + pdev->sched_comp_list_depth++; } /** @@ -3156,6 +3160,11 @@ dp_process_ppdu_stats_sch_cmd_status_tlv(struct dp_pdev *pdev, } } + TAILQ_REMOVE(&pdev->ppdu_info_list, ppdu_info, ppdu_info_list_elem); + pdev->list_depth--; + TAILQ_INSERT_TAIL(&pdev->sched_comp_ppdu_list, ppdu_info, + ppdu_info_list_elem); + pdev->sched_comp_list_depth++; } #ifndef WLAN_TX_PKT_CAPTURE_ENH @@ -3509,46 +3518,104 @@ static void dp_ppdu_desc_deliver(struct dp_pdev *pdev, struct ppdu_info *ppdu_info) { + struct ppdu_info *s_ppdu_info = NULL; + struct ppdu_info *ppdu_info_next = NULL; struct cdp_tx_completion_ppdu *ppdu_desc = NULL; qdf_nbuf_t nbuf; + uint32_t time_delta = 0; + bool starved = 0; + bool matched = 0; + bool recv_ack_ba_done = 0; - ppdu_desc = (struct cdp_tx_completion_ppdu *) - qdf_nbuf_data(ppdu_info->nbuf); + if (ppdu_info->tlv_bitmap & + (1 << HTT_PPDU_STATS_USR_COMPLTN_ACK_BA_STATUS_TLV) && + ppdu_info->done) + recv_ack_ba_done = 1; - dp_ppdu_desc_user_stats_update(pdev, ppdu_info); + pdev->last_sched_cmdid = ppdu_info->sched_cmdid; - /* - * Remove from the list - */ - TAILQ_REMOVE(&pdev->ppdu_info_list, ppdu_info, ppdu_info_list_elem); - nbuf = ppdu_info->nbuf; - pdev->list_depth--; - qdf_mem_free(ppdu_info); + s_ppdu_info = TAILQ_FIRST(&pdev->sched_comp_ppdu_list); - qdf_assert_always(nbuf); + TAILQ_FOREACH_SAFE(s_ppdu_info, &pdev->sched_comp_ppdu_list, + ppdu_info_list_elem, ppdu_info_next) { + if (s_ppdu_info->tsf_l32 > ppdu_info->tsf_l32) + time_delta = (MAX_TSF_32 - s_ppdu_info->tsf_l32) + + ppdu_info->tsf_l32; + else + time_delta = ppdu_info->tsf_l32 - s_ppdu_info->tsf_l32; - ppdu_desc = (struct cdp_tx_completion_ppdu *) - qdf_nbuf_data(nbuf); + if (!s_ppdu_info->done && !recv_ack_ba_done) { + if (time_delta < MAX_SCHED_STARVE) { + qdf_err("pdev[%d] ppdu_id[%d] sched_cmdid[%d] TLV_B[0x%x] TSF[%u] D[%d]", + pdev->pdev_id, + s_ppdu_info->ppdu_id, + s_ppdu_info->sched_cmdid, + s_ppdu_info->tlv_bitmap, + s_ppdu_info->tsf_l32, + s_ppdu_info->done); + break; + } + starved = 1; + } - /** - * Deliver PPDU stats only for valid (acked) data frames if - * sniffer mode is not enabled. - * If sniffer mode is enabled, PPDU stats for all frames - * including mgmt/control frames should be delivered to upper layer - */ - if (pdev->tx_sniffer_enable || pdev->mcopy_mode) { - dp_wdi_event_handler(WDI_EVENT_TX_PPDU_DESC, pdev->soc, - nbuf, HTT_INVALID_PEER, - WDI_NO_VAL, pdev->pdev_id); - } else { - if (ppdu_desc->num_mpdu != 0 && ppdu_desc->num_users != 0 && - ppdu_desc->frame_ctrl & HTT_FRAMECTRL_DATATYPE) { + pdev->delivered_sched_cmdid = s_ppdu_info->sched_cmdid; + TAILQ_REMOVE(&pdev->sched_comp_ppdu_list, s_ppdu_info, + ppdu_info_list_elem); + pdev->sched_comp_list_depth--; + nbuf = s_ppdu_info->nbuf; + qdf_assert_always(nbuf); + ppdu_desc = (struct cdp_tx_completion_ppdu *) + qdf_nbuf_data(nbuf); + ppdu_desc->tlv_bitmap = s_ppdu_info->tlv_bitmap; + + if (starved) { + qdf_err("ppdu starved fc[0x%x] h_ftype[%d] tlv_bitmap[0x%x] cs[%d]\n", + ppdu_desc->frame_ctrl, + ppdu_desc->htt_frame_type, + ppdu_desc->tlv_bitmap, + ppdu_desc->user[0].completion_status); + starved = 0; + } + + if (ppdu_info->ppdu_id == s_ppdu_info->ppdu_id && + ppdu_info->sched_cmdid == s_ppdu_info->sched_cmdid) + matched = 1; + + dp_ppdu_desc_user_stats_update(pdev, s_ppdu_info); + + qdf_mem_free(s_ppdu_info); + + /** + * Deliver PPDU stats only for valid (acked) data + * frames if sniffer mode is not enabled. + * If sniffer mode is enabled, PPDU stats + * for all frames including mgmt/control + * frames should be delivered to upper layer + */ + if (pdev->tx_sniffer_enable || pdev->mcopy_mode) { dp_wdi_event_handler(WDI_EVENT_TX_PPDU_DESC, - pdev->soc, nbuf, HTT_INVALID_PEER, - WDI_NO_VAL, pdev->pdev_id); - } else - qdf_nbuf_free(nbuf); + pdev->soc, + nbuf, HTT_INVALID_PEER, + WDI_NO_VAL, + pdev->pdev_id); + } else { + if (ppdu_desc->num_mpdu != 0 && + ppdu_desc->num_users != 0 && + ppdu_desc->frame_ctrl & + HTT_FRAMECTRL_DATATYPE) { + dp_wdi_event_handler(WDI_EVENT_TX_PPDU_DESC, + pdev->soc, + nbuf, HTT_INVALID_PEER, + WDI_NO_VAL, + pdev->pdev_id); + } else { + qdf_nbuf_free(nbuf); + } + } + + if (matched) + break; } return; } @@ -3561,24 +3628,98 @@ void dp_ppdu_desc_deliver(struct dp_pdev *pdev, * @pdev: DP pdev handle * @ppdu_id: PPDU unique identifier * @tlv_type: TLV type received + * @tsf_l32: timestamp received along with ppdu stats indication header + * @max_users: Maximum user for that particular ppdu * * return: ppdu_info per ppdu tlv structure */ static struct ppdu_info *dp_get_ppdu_desc(struct dp_pdev *pdev, uint32_t ppdu_id, - uint8_t tlv_type, uint8_t max_users) + uint8_t tlv_type, uint32_t tsf_l32, + uint8_t max_users) { struct ppdu_info *ppdu_info = NULL; + struct ppdu_info *s_ppdu_info = NULL; + struct ppdu_info *ppdu_info_next = NULL; struct cdp_tx_completion_ppdu *ppdu_desc = NULL; uint32_t size = 0; + struct cdp_tx_completion_ppdu *tmp_ppdu_desc = NULL; + struct cdp_tx_completion_ppdu_user *tmp_user; + uint32_t time_delta; /* * Find ppdu_id node exists or not */ - TAILQ_FOREACH(ppdu_info, &pdev->ppdu_info_list, ppdu_info_list_elem) { - + TAILQ_FOREACH_SAFE(ppdu_info, &pdev->ppdu_info_list, + ppdu_info_list_elem, ppdu_info_next) { if (ppdu_info && (ppdu_info->ppdu_id == ppdu_id)) { - break; + if (ppdu_info->tsf_l32 > tsf_l32) + time_delta = (MAX_TSF_32 - + ppdu_info->tsf_l32) + tsf_l32; + else + time_delta = tsf_l32 - ppdu_info->tsf_l32; + + if (time_delta > WRAP_DROP_TSF_DELTA) { + TAILQ_REMOVE(&pdev->ppdu_info_list, + ppdu_info, ppdu_info_list_elem); + pdev->list_depth--; + pdev->stats.ppdu_wrap_drop++; + tmp_ppdu_desc = + (struct cdp_tx_completion_ppdu *) + qdf_nbuf_data(ppdu_info->nbuf); + tmp_user = &tmp_ppdu_desc->user[0]; + QDF_TRACE(QDF_MODULE_ID_TX_CAPTURE, + QDF_TRACE_LEVEL_INFO_MED, + "S_PID [%d] S_TSF[%u] TLV_BITMAP[0x%x] [CMPLTN - %d ACK_BA - %d] CS[%d] - R_PID[%d] R_TSF[%u] R_TLV_TAG[0x%x]\n", + ppdu_info->ppdu_id, + ppdu_info->tsf_l32, + ppdu_info->tlv_bitmap, + tmp_user->completion_status, + ppdu_info->compltn_common_tlv, + ppdu_info->ack_ba_tlv, + ppdu_id, tsf_l32, tlv_type); + qdf_nbuf_free(ppdu_info->nbuf); + ppdu_info->nbuf = NULL; + qdf_mem_free(ppdu_info); + } else { + break; + } + } + } + + /* + * check if it is ack ba tlv and if it is not there in ppdu info + * list then check it in sched completion ppdu list + */ + if (!ppdu_info && + tlv_type == HTT_PPDU_STATS_USR_COMPLTN_ACK_BA_STATUS_TLV) { + TAILQ_FOREACH(s_ppdu_info, + &pdev->sched_comp_ppdu_list, + ppdu_info_list_elem) { + if (s_ppdu_info && (s_ppdu_info->ppdu_id == ppdu_id)) { + if (s_ppdu_info->tsf_l32 > tsf_l32) + time_delta = (MAX_TSF_32 - + s_ppdu_info->tsf_l32) + + tsf_l32; + else + time_delta = tsf_l32 - + s_ppdu_info->tsf_l32; + if (time_delta < WRAP_DROP_TSF_DELTA) { + ppdu_info = s_ppdu_info; + break; + } + } else { + /* + * ACK BA STATUS TLV comes sequential order + * if we received ack ba status tlv for second + * ppdu and first ppdu is still waiting for + * ACK BA STATUS TLV. Based on fw comment + * we won't receive it tlv later. So we can + * set ppdu info done. + */ + if (s_ppdu_info) + s_ppdu_info->done = 1; + } } } @@ -3663,7 +3804,7 @@ struct ppdu_info *dp_get_ppdu_desc(struct dp_pdev *pdev, uint32_t ppdu_id, } ppdu_info->ppdu_desc->max_users = max_users; - + ppdu_info->tsf_l32 = tsf_l32; /** * No lock is needed because all PPDU TLVs are processed in * same context and this list is updated in same context @@ -3693,6 +3834,7 @@ static struct ppdu_info *dp_htt_process_tlv(struct dp_pdev *pdev, struct ppdu_info *ppdu_info = NULL; struct cdp_tx_completion_ppdu *ppdu_desc = NULL; uint8_t max_users = CDP_MU_MAX_USERS; + uint32_t tsf_l32; uint32_t *msg_word = (uint32_t *) qdf_nbuf_data(htt_t2h_msg); @@ -3701,8 +3843,10 @@ static struct ppdu_info *dp_htt_process_tlv(struct dp_pdev *pdev, msg_word = msg_word + 1; ppdu_id = HTT_T2H_PPDU_STATS_PPDU_ID_GET(*msg_word); - msg_word = msg_word + 3; + msg_word = msg_word + 1; + tsf_l32 = (uint32_t)(*msg_word); + msg_word = msg_word + 2; while (length > 0) { tlv_buf = (uint8_t *)msg_word; tlv_type = HTT_STATS_TLV_TAG_GET(*msg_word); @@ -3744,7 +3888,8 @@ static struct ppdu_info *dp_htt_process_tlv(struct dp_pdev *pdev, max_users = 1; } - ppdu_info = dp_get_ppdu_desc(pdev, ppdu_id, tlv_type, max_users); + ppdu_info = dp_get_ppdu_desc(pdev, ppdu_id, tlv_type, + tsf_l32, max_users); if (!ppdu_info) return NULL; @@ -3804,9 +3949,21 @@ static struct ppdu_info *dp_htt_process_tlv(struct dp_pdev *pdev, if (ppdu_desc->frame_type != CDP_PPDU_FTYPE_CTRL && ppdu_desc->htt_frame_type != HTT_STATS_FTYPE_SGEN_QOS_NULL) { /* - * successful mpdu count should match with both tlv + * most of the time bar frame will have duplicate ack ba + * status tlv */ - if (ppdu_info->compltn_common_tlv != ppdu_info->ack_ba_tlv) + if (ppdu_desc->frame_type == CDP_PPDU_FTYPE_BAR && + (ppdu_info->compltn_common_tlv != ppdu_info->ack_ba_tlv)) + return NULL; + /* + * For data frame, compltn common tlv should match ack ba status + * tlv and completion status. Reason we are checking first user + * for ofdma, completion seen at next MU BAR frm, for mimo + * only for first user completion will be immediate. + */ + if (ppdu_desc->frame_type == CDP_PPDU_FTYPE_DATA && + (ppdu_desc->user[0].completion_status == 0 && + (ppdu_info->compltn_common_tlv != ppdu_info->ack_ba_tlv))) return NULL; } @@ -3822,8 +3979,10 @@ static struct ppdu_info *dp_htt_process_tlv(struct dp_pdev *pdev, (ppdu_info->tlv_bitmap & (1 << HTT_PPDU_STATS_SCH_CMD_STATUS_TLV))) || (ppdu_info->tlv_bitmap & - (1 << HTT_PPDU_STATS_USR_COMPLTN_FLUSH_TLV))) + (1 << HTT_PPDU_STATS_USR_COMPLTN_FLUSH_TLV))) { + ppdu_info->done = 1; return ppdu_info; + } return NULL; } diff --git a/dp/wifi3.0/dp_htt.h b/dp/wifi3.0/dp_htt.h index 4d66b5760e..86fd548f3f 100644 --- a/dp/wifi3.0/dp_htt.h +++ b/dp/wifi3.0/dp_htt.h @@ -137,6 +137,10 @@ void htt_htc_pkt_pool_free(struct htt_soc *soc); #define HTT_GET_STATS_CMN_INDEX(index) \ HTT_PPDU_STATS_COMMON_TLV_##index##_OFFSET +#define MAX_SCHED_STARVE 100000 +#define WRAP_DROP_TSF_DELTA 10000 +#define MAX_TSF_32 0xFFFFFFFF + /** * enum dp_full_mon_config - enum to enable/disable full monitor mode * diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c index 79894f1ad3..5a42021fa9 100644 --- a/dp/wifi3.0/dp_main.c +++ b/dp/wifi3.0/dp_main.c @@ -4038,12 +4038,23 @@ static void dp_htt_ppdu_stats_detach(struct dp_pdev *pdev) struct ppdu_info *ppdu_info, *ppdu_info_next; TAILQ_FOREACH_SAFE(ppdu_info, &pdev->ppdu_info_list, - ppdu_info_list_elem, ppdu_info_next) { + ppdu_info_list_elem, ppdu_info_next) { if (!ppdu_info) break; qdf_assert_always(ppdu_info->nbuf); qdf_nbuf_free(ppdu_info->nbuf); qdf_mem_free(ppdu_info); + pdev->list_depth--; + } + + TAILQ_FOREACH_SAFE(ppdu_info, &pdev->sched_comp_ppdu_list, + ppdu_info_list_elem, ppdu_info_next) { + if (!ppdu_info) + break; + qdf_assert_always(ppdu_info->nbuf); + qdf_nbuf_free(ppdu_info->nbuf); + qdf_mem_free(ppdu_info); + pdev->sched_comp_list_depth--; } if (pdev->ppdu_tlv_buf) @@ -12709,6 +12720,7 @@ static inline QDF_STATUS dp_pdev_init(struct cdp_soc_t *txrx_soc, * initialize ppdu tlv list */ TAILQ_INIT(&pdev->ppdu_info_list); + TAILQ_INIT(&pdev->sched_comp_ppdu_list); pdev->tlv_count = 0; pdev->list_depth = 0; diff --git a/dp/wifi3.0/dp_stats.c b/dp/wifi3.0/dp_stats.c index fd6d75f2a0..54554a9fb9 100644 --- a/dp/wifi3.0/dp_stats.c +++ b/dp/wifi3.0/dp_stats.c @@ -6293,12 +6293,21 @@ dp_print_pdev_tx_stats(struct dp_pdev *pdev) } DP_PRINT_STATS("BA not received for delayed_ba: %d", pdev->stats.cdp_delayed_ba_not_recev); + DP_PRINT_STATS("ppdu info schedule completion list depth: %d", + pdev->sched_comp_list_depth); + DP_PRINT_STATS("cur sched cmdid: %d", pdev->last_sched_cmdid); + DP_PRINT_STATS("delivered sched cmdid: %d", + pdev->delivered_sched_cmdid); + DP_PRINT_STATS("ppdu info list depth: %d", + pdev->list_depth); DP_PRINT_STATS("tx_ppdu_proc: %llu", pdev->stats.tx_ppdu_proc); DP_PRINT_STATS("ack ba comes twice: %llu", pdev->stats.ack_ba_comes_twice); DP_PRINT_STATS("ppdu dropped because of incomplete tlv: %llu", pdev->stats.ppdu_drop); + DP_PRINT_STATS("ppdu dropped because of wrap around: %llu", + pdev->stats.ppdu_wrap_drop); for (i = 0; i < CDP_WDI_NUM_EVENTS; i++) { if (!pdev->stats.wdi_event[i]) diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h index 8b418e35d1..f1ad638234 100644 --- a/dp/wifi3.0/dp_types.h +++ b/dp/wifi3.0/dp_types.h @@ -1540,6 +1540,7 @@ struct ppdu_info { uint32_t ppdu_id; uint32_t sched_cmdid; uint32_t max_ppdu_id; + uint32_t tsf_l32; uint16_t tlv_bitmap; uint16_t last_tlv_cnt; uint16_t last_user:8, @@ -1558,6 +1559,7 @@ struct ppdu_info { #endif uint8_t compltn_common_tlv; uint8_t ack_ba_tlv; + bool done; }; /** @@ -1870,6 +1872,11 @@ struct dp_pdev { #endif /* list of ppdu tlvs */ TAILQ_HEAD(, ppdu_info) ppdu_info_list; + TAILQ_HEAD(, ppdu_info) sched_comp_ppdu_list; + + uint32_t sched_comp_list_depth; + uint16_t delivered_sched_cmdid; + uint16_t last_sched_cmdid; uint32_t tlv_count; uint32_t list_depth; uint32_t ppdu_id;