diff --git a/dp/wifi3.0/dp_htt.c b/dp/wifi3.0/dp_htt.c index 45573b6e30..13a990dbc5 100644 --- a/dp/wifi3.0/dp_htt.c +++ b/dp/wifi3.0/dp_htt.c @@ -2076,6 +2076,176 @@ dp_pktlog_msg_handler(struct htt_soc *soc, } #endif +#ifdef QCA_VDEV_STATS_HW_OFFLOAD_SUPPORT +/* + * dp_vdev_txrx_hw_stats_handler - Handle vdev stats received from FW + * @soc - htt soc handle + * @ msg_word - buffer containing stats + * + * Return: void + */ +static void dp_vdev_txrx_hw_stats_handler(struct htt_soc *soc, + uint32_t *msg_word) +{ + struct dp_soc *dpsoc = (struct dp_soc *)soc->dp_soc; + uint8_t pdev_id; + uint8_t vdev_id; + uint8_t target_pdev_id; + uint16_t payload_size; + struct dp_pdev *pdev; + struct dp_vdev *vdev; + uint8_t *tlv_buf; + uint32_t *tlv_buf_temp; + uint32_t *tag_buf; + htt_tlv_tag_t tlv_type; + uint16_t tlv_length; + uint64_t pkt_count = 0; + uint64_t byte_count = 0; + uint64_t soc_drop_cnt = 0; + struct cdp_pkt_info tx_comp = { 0 }; + struct cdp_pkt_info tx_failed = { 0 }; + + target_pdev_id = + HTT_T2H_VDEVS_TXRX_STATS_PERIODIC_IND_PDEV_ID_GET(*msg_word); + pdev_id = dp_get_host_pdev_id_for_target_pdev_id(dpsoc, + target_pdev_id); + + if (pdev_id >= MAX_PDEV_CNT) + return; + + pdev = dpsoc->pdev_list[pdev_id]; + if (!pdev) { + dp_err("PDEV is NULL for pdev_id:%d", pdev_id); + return; + } + + payload_size = + HTT_T2H_VDEVS_TXRX_STATS_PERIODIC_IND_PAYLOAD_SIZE_GET(*msg_word); + + qdf_trace_hex_dump(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO, + (void *)msg_word, payload_size + 16); + + /* Adjust msg_word to point to the first TLV in buffer */ + msg_word = msg_word + 4; + + /* Parse the received buffer till payload size reaches 0 */ + while (payload_size > 0) { + tlv_buf = (uint8_t *)msg_word; + tlv_buf_temp = msg_word; + tlv_type = HTT_STATS_TLV_TAG_GET(*msg_word); + tlv_length = HTT_STATS_TLV_LENGTH_GET(*msg_word); + + /* Add header size to tlv length*/ + tlv_length += 4; + + switch (tlv_type) { + case HTT_STATS_SOC_TXRX_STATS_COMMON_TAG: + { + tag_buf = tlv_buf_temp + + HTT_VDEV_STATS_GET_INDEX(SOC_DROP_CNT); + soc_drop_cnt = HTT_VDEV_GET_STATS_U64(tag_buf); + DP_STATS_UPD(dpsoc, tx.tqm_drop_no_peer, soc_drop_cnt); + break; + } + case HTT_STATS_VDEV_TXRX_STATS_HW_STATS_TAG: + { + tag_buf = tlv_buf_temp + + HTT_VDEV_STATS_GET_INDEX(VDEV_ID); + vdev_id = (uint8_t)(*tag_buf); + vdev = dp_vdev_get_ref_by_id(dpsoc, vdev_id, + DP_MOD_ID_HTT); + + if (!vdev) + goto invalid_vdev; + + /* Extract received packet count from buffer */ + tag_buf = tlv_buf_temp + + HTT_VDEV_STATS_GET_INDEX(RX_PKT_CNT); + pkt_count = HTT_VDEV_GET_STATS_U64(tag_buf); + DP_STATS_UPD(vdev, rx_i.reo_rcvd_pkt.num, pkt_count); + + /* Extract received packet byte count from buffer */ + tag_buf = tlv_buf_temp + + HTT_VDEV_STATS_GET_INDEX(RX_BYTE_CNT); + byte_count = HTT_VDEV_GET_STATS_U64(tag_buf); + DP_STATS_UPD(vdev, rx_i.reo_rcvd_pkt.bytes, byte_count); + + /* Extract tx success packet count from buffer */ + tag_buf = tlv_buf_temp + + HTT_VDEV_STATS_GET_INDEX(TX_SUCCESS_PKT_CNT); + pkt_count = HTT_VDEV_GET_STATS_U64(tag_buf); + tx_comp.num += pkt_count; + + /* Extract tx success packet byte count from buffer */ + tag_buf = tlv_buf_temp + + HTT_VDEV_STATS_GET_INDEX(TX_SUCCESS_BYTE_CNT); + byte_count = HTT_VDEV_GET_STATS_U64(tag_buf); + tx_comp.bytes += byte_count; + + /* Extract tx retry packet count from buffer */ + tag_buf = tlv_buf_temp + + HTT_VDEV_STATS_GET_INDEX(TX_RETRY_PKT_CNT); + pkt_count = HTT_VDEV_GET_STATS_U64(tag_buf); + tx_comp.num += pkt_count; + tx_failed.num += pkt_count; + + /* Extract tx retry packet byte count from buffer */ + tag_buf = tlv_buf_temp + + HTT_VDEV_STATS_GET_INDEX(TX_RETRY_BYTE_CNT); + pkt_count = HTT_VDEV_GET_STATS_U64(tag_buf); + tx_comp.bytes += byte_count; + tx_failed.bytes += byte_count; + + /* Extract tx drop packet count from buffer */ + tag_buf = tlv_buf_temp + + HTT_VDEV_STATS_GET_INDEX(TX_DROP_PKT_CNT); + pkt_count = HTT_VDEV_GET_STATS_U64(tag_buf); + tx_comp.num += pkt_count; + tx_failed.num += pkt_count; + + /* Extract tx drop packet byte count from buffer */ + tag_buf = tlv_buf_temp + + HTT_VDEV_STATS_GET_INDEX(TX_DROP_BYTE_CNT); + pkt_count = HTT_VDEV_GET_STATS_U64(tag_buf); + tx_comp.bytes += byte_count; + tx_failed.bytes += byte_count; + + /* Extract tx age-out packet count from buffer */ + tag_buf = tlv_buf_temp + + HTT_VDEV_STATS_GET_INDEX(TX_AGE_OUT_PKT_CNT); + pkt_count = HTT_VDEV_GET_STATS_U64(tag_buf); + tx_comp.num += pkt_count; + tx_failed.num += pkt_count; + + /* Extract tx age-out packet byte count from buffer */ + tag_buf = tlv_buf_temp + + HTT_VDEV_STATS_GET_INDEX(TX_AGE_OUT_BYTE_CNT); + pkt_count = HTT_VDEV_GET_STATS_U64(tag_buf); + tx_comp.bytes += byte_count; + tx_failed.bytes += byte_count; + + DP_STATS_UPD(vdev, tx.comp_pkt.num, tx_comp.num); + DP_STATS_UPD(vdev, tx.comp_pkt.bytes, tx_comp.bytes); + + DP_STATS_UPD(vdev, tx.tx_failed, tx_failed.num); + + dp_vdev_unref_delete(dpsoc, vdev, DP_MOD_ID_HTT); + break; + } + default: + qdf_assert(0); + } +invalid_vdev: + msg_word = (uint32_t *)((uint8_t *)tlv_buf + tlv_length); + payload_size -= tlv_length; + } +} +#else +static void dp_vdev_txrx_hw_stats_handler(struct htt_soc *soc, + uint32_t *msg_word) +{} +#endif + /* * time_allow_print() - time allow print * @htt_ring_tt: ringi_id array of timestamps @@ -3052,6 +3222,11 @@ static void dp_htt_t2h_msg_handler(void *context, HTC_PACKET *pkt) dp_rx_mlo_timestamp_ind_handler(soc->dp_soc, msg_word); break; } + case HTT_T2H_MSG_TYPE_VDEVS_TXRX_STATS_PERIODIC_IND: + { + dp_vdev_txrx_hw_stats_handler(soc, msg_word); + break; + } default: break; }; @@ -3385,6 +3560,125 @@ QDF_STATUS dp_h2t_ext_stats_msg_send(struct dp_pdev *pdev, return status; } +#ifdef QCA_VDEV_STATS_HW_OFFLOAD_SUPPORT +#define HTT_VDEV_TXRX_STATS_RESET_BITMASK_L32_MASK 0xFFFFFFFF +#define HTT_VDEV_TXRX_STATS_RESET_BITMASK_U32_MASK 0xFFFFFFFF00000000 +#define HTT_VDEV_TXRX_STATS_RESET_BITMASK_U32_SHIFT 32 + +QDF_STATUS dp_h2t_hw_vdev_stats_config_send(struct dp_soc *dpsoc, + uint8_t pdev_id, bool enable, + bool reset, uint64_t reset_bitmask) +{ + struct htt_soc *soc = dpsoc->htt_handle; + struct dp_htt_htc_pkt *pkt; + qdf_nbuf_t msg; + uint32_t *msg_word; + uint8_t *htt_logger_bufp; + QDF_STATUS status; + int duration; + uint32_t bitmask; + int target_pdev_id; + + msg = qdf_nbuf_alloc( + soc->osdev, + HTT_MSG_BUF_SIZE(sizeof(struct htt_h2t_vdevs_txrx_stats_cfg)), + HTC_HEADER_LEN + HTC_HDR_ALIGNMENT_PADDING, 4, true); + + if (!msg) { + dp_htt_err("%pK: Fail to allocate " + "HTT_H2T_HW_VDEV_TXRX_STATS_CFG_MSG_SZ msg buffer", dpsoc); + return QDF_STATUS_E_NOMEM; + } + + if (pdev_id != INVALID_PDEV_ID) + target_pdev_id = DP_SW2HW_MACID(pdev_id); + else + target_pdev_id = 0; + + duration = + wlan_cfg_get_vdev_stats_hw_offload_timer(dpsoc->wlan_cfg_ctx); + + /* + * 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_vdevs_txrx_stats_cfg))) { + dp_htt_err("%pK: Failed to expand head for HTT_HW_VDEV_STATS" + , dpsoc); + qdf_nbuf_free(msg); + return QDF_STATUS_E_FAILURE; + } + + msg_word = (uint32_t *)qdf_nbuf_data(msg); + + 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_VDEVS_TXRX_STATS_CFG); + HTT_RX_VDEVS_TXRX_STATS_PDEV_ID_SET(*msg_word, target_pdev_id); + + HTT_RX_VDEVS_TXRX_STATS_ENABLE_SET(*msg_word, enable); + + HTT_RX_VDEVS_TXRX_STATS_PERIODIC_INTERVAL_SET(*msg_word, + (duration >> 3)); + + HTT_RX_VDEVS_TXRX_STATS_RESET_STATS_BITS_SET(*msg_word, reset); + + msg_word++; + *msg_word = 0; + bitmask = (reset_bitmask & HTT_VDEV_TXRX_STATS_RESET_BITMASK_L32_MASK); + *msg_word = bitmask; + + msg_word++; + *msg_word = 0; + bitmask = + ((reset_bitmask & HTT_VDEV_TXRX_STATS_RESET_BITMASK_U32_MASK) >> + HTT_VDEV_TXRX_STATS_RESET_BITMASK_U32_SHIFT); + *msg_word = bitmask; + + pkt = htt_htc_pkt_alloc(soc); + if (!pkt) { + dp_htt_err("%pK: Fail to allocate dp_htt_htc_pkt buffer", + dpsoc); + qdf_assert(0); + qdf_nbuf_free(msg); + return QDF_STATUS_E_NOMEM; + } + + 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, + /* tag for no FW response msg */ + HTC_TX_PACKET_TAG_RUNTIME_PUT); + + SET_HTC_PACKET_NET_BUF_CONTEXT(&pkt->htc_pkt, msg); + status = DP_HTT_SEND_HTC_PKT(soc, pkt, + HTT_H2T_MSG_TYPE_VDEVS_TXRX_STATS_CFG, + htt_logger_bufp); + + if (status != QDF_STATUS_SUCCESS) { + qdf_nbuf_free(msg); + htt_htc_pkt_free(soc, pkt); + } + + return status; +} +#else +QDF_STATUS dp_h2t_hw_vdev_stats_config_send(struct dp_soc *dpsoc, + uint8_t pdev_id, bool enable, + bool reset, uint64_t reset_bitmask) +{ + return QDF_STATUS_SUCCESS; +} +#endif + /** * dp_h2t_3tuple_config_send(): function to contruct 3 tuple configuration * HTT message to pass to FW diff --git a/dp/wifi3.0/dp_htt.h b/dp/wifi3.0/dp_htt.h index cce8391371..768f2cc6d4 100644 --- a/dp/wifi3.0/dp_htt.h +++ b/dp/wifi3.0/dp_htt.h @@ -139,6 +139,35 @@ 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 HTT_VDEV_STATS_TLV_SOC_DROP_CNT_OFFSET 1 + +#define HTT_VDEV_STATS_TLV_HDR_OFFSET 0 +#define HTT_VDEV_STATS_TLV_VDEV_ID_OFFSET 1 +#define HTT_VDEV_STATS_TLV_RX_BYTE_CNT_OFFSET 2 +#define HTT_VDEV_STATS_TLV_RX_PKT_CNT_OFFSET 4 +#define HTT_VDEV_STATS_TLV_TX_SUCCESS_BYTE_CNT_OFFSET 6 +#define HTT_VDEV_STATS_TLV_TX_SUCCESS_PKT_CNT_OFFSET 8 +#define HTT_VDEV_STATS_TLV_TX_RETRY_BYTE_CNT_OFFSET 10 +#define HTT_VDEV_STATS_TLV_TX_RETRY_PKT_CNT_OFFSET 12 +#define HTT_VDEV_STATS_TLV_TX_DROP_BYTE_CNT_OFFSET 14 +#define HTT_VDEV_STATS_TLV_TX_DROP_PKT_CNT_OFFSET 16 +#define HTT_VDEV_STATS_TLV_TX_AGE_OUT_BYTE_CNT_OFFSET 18 +#define HTT_VDEV_STATS_TLV_TX_AGE_OUT_PKT_CNT_OFFSET 20 + +#define HTT_VDEV_STATS_GET_INDEX(index) \ + HTT_VDEV_STATS_TLV_##index##_OFFSET + +#define HTT_VDEV_STATS_U32_SHIFT 0x20 +#define HTT_VDEV_STATS_U32_MASK 0xFFFFFFFF00000000 +#define HTT_VDEV_STATS_L32_MASK 0x00000000FFFFFFFF + +#define HTT_VDEV_GET_STATS_U64(msg_word) \ + (((((uint64_t)(*(((uint32_t *)msg_word) + 1))) & HTT_VDEV_STATS_L32_MASK) << \ + HTT_VDEV_STATS_U32_SHIFT) | ((*(uint32_t *)msg_word) & HTT_VDEV_STATS_L32_MASK)) + +#define HTT_VDEV_GET_STATS_U32(msg_word) \ + ((*(uint32_t *)msg_word) & HTT_VDEV_STATS_L32_MASK) + #define MAX_SCHED_STARVE 100000 #define WRAP_DROP_TSF_DELTA 10000 #define MAX_TSF_32 0xFFFFFFFF @@ -885,4 +914,19 @@ dp_htt_rx_flow_fse_operation(struct dp_pdev *pdev, int htt_h2t_full_mon_cfg(struct htt_soc *htt_soc, uint8_t pdev_id, enum dp_full_mon_config); + +/** + * dp_h2t_hw_vdev_stats_config_send: Send HTT command to FW for config + of HW vdev stats + * @dpsoc: Datapath soc handle + * @pdev_id: INVALID_PDEV_ID for all pdevs or 0,1,2 for individual pdev + * @enable: flag to specify enable/disable of stats + * @reset: flag to specify if command is for reset of stats + * @reset_bitmask: bitmask of vdev_id(s) for reset of HW stats + * + * Return: QDF_STATUS + */ +QDF_STATUS dp_h2t_hw_vdev_stats_config_send(struct dp_soc *dpsoc, + uint8_t pdev_id, bool enable, + bool reset, uint64_t reset_bitmask); #endif /* _DP_HTT_H_ */ diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c index df60794109..aece673371 100644 --- a/dp/wifi3.0/dp_main.c +++ b/dp/wifi3.0/dp_main.c @@ -5141,6 +5141,66 @@ static void dp_pdev_flush_pending_vdevs(struct dp_pdev *pdev) } #endif +#ifdef QCA_VDEV_STATS_HW_OFFLOAD_SUPPORT +/** + * dp_vdev_stats_hw_offload_target_config() - Send HTT command to FW + * for enable/disable of HW vdev stats + * @soc: Datapath soc handle + * @pdev_id: INVALID_PDEV_ID for all pdevs or 0,1,2 for individual pdev + * @enable: flag to reprsent enable/disable of hw vdev stats + * + * Return: none + */ +static void dp_vdev_stats_hw_offload_target_config(struct dp_soc *soc, + uint8_t pdev_id, + bool enable) +{ + /* Check SOC level config for HW offload vdev stats support */ + if (!wlan_cfg_get_vdev_stats_hw_offload_config(soc->wlan_cfg_ctx)) { + dp_debug("%pK: HW vdev offload stats is disabled", soc); + return; + } + + /* Send HTT command to FW for enable of stats */ + dp_h2t_hw_vdev_stats_config_send(soc, pdev_id, enable, false, 0); +} + +/** + * dp_vdev_stats_hw_offload_target_clear() - Clear HW vdev stats on target + * @soc: Datapath soc handle + * @pdev_id: pdev_id (0,1,2) + * @bitmask: bitmask with vdev_id(s) for which stats are to be cleared on HW + * + * Return: none + */ +static +void dp_vdev_stats_hw_offload_target_clear(struct dp_soc *soc, uint8_t pdev_id, + uint64_t vdev_id_bitmask) +{ + /* Check SOC level config for HW offload vdev stats support */ + if (!wlan_cfg_get_vdev_stats_hw_offload_config(soc->wlan_cfg_ctx)) { + dp_debug("%pK: HW vdev offload stats is disabled", soc); + return; + } + + /* Send HTT command to FW for reset of stats */ + dp_h2t_hw_vdev_stats_config_send(soc, pdev_id, true, true, + vdev_id_bitmask); +} +#else +static void +dp_vdev_stats_hw_offload_target_config(struct dp_soc *soc, uint8_t pdev_id, + bool enable) +{ +} + +static +void dp_vdev_stats_hw_offload_target_clear(struct dp_soc *soc, uint8_t pdev_id, + uint64_t vdev_id_bitmask) +{ +} +#endif /*QCA_VDEV_STATS_HW_OFFLOAD_SUPPORT */ + /** * dp_pdev_deinit() - Deinit txrx pdev * @txrx_pdev: Datapath PDEV handle @@ -5863,6 +5923,9 @@ dp_soc_attach_target_wifi3(struct cdp_soc_t *cdp_soc) dp_runtime_init(soc); + /* Enable HW vdev offload stats if feature is supported */ + dp_vdev_stats_hw_offload_target_config(soc, INVALID_PDEV_ID, true); + /* initialize work queue for stats processing */ qdf_create_work(0, &soc->htt_stats.work, htt_t2h_stats_handler, soc); @@ -8594,6 +8657,9 @@ dp_txrx_host_stats_clr(struct dp_vdev *vdev, struct dp_soc *soc) vdev->vdev_id); } + dp_vdev_stats_hw_offload_target_clear(soc, vdev->pdev->pdev_id, + vdev->vdev_id); + DP_STATS_CLR(vdev->pdev); DP_STATS_CLR(vdev->pdev->soc); DP_STATS_CLR(vdev);