Эх сурвалжийг харах

qcacmn: Add support for telemetry agent stats

For telemetry agent, some more statistic support is required.
Hence, add support for telemetry agent required stats.

Change-Id: Ieb2210a4a5dfe6fa0a0dd46a85f2d9ae483bb746
CRs-Fixed: 3210797
Himanshu Batra 3 жил өмнө
parent
commit
768250e291

+ 4 - 0
dp/inc/cdp_txrx_cmn_struct.h

@@ -2415,6 +2415,8 @@ struct cdp_rx_stats_ppdu_user {
  * @sig_b_sym: Number of symbols of HE-SIG-B
  * @sig_b_sym: Number of symbols of HE-SIG-B
  * @sig_b_comp: Compression mode of HE-SIG-B
  * @sig_b_comp: Compression mode of HE-SIG-B
  * @he_crc: CRC for HE-SIG contents
  * @he_crc: CRC for HE-SIG contents
+ * @usr_nss_sum: Sum of user nss
+ * @usr_ru_tones_sum: Sum of user ru_tones
  */
  */
 struct cdp_rx_indication_ppdu {
 struct cdp_rx_indication_ppdu {
 	uint32_t ppdu_id;
 	uint32_t ppdu_id;
@@ -2510,6 +2512,8 @@ struct cdp_rx_indication_ppdu {
 		 sig_b_comp:1,
 		 sig_b_comp:1,
 		 he_crc:4;
 		 he_crc:4;
 #endif
 #endif
+	uint8_t usr_nss_sum;
+	uint32_t usr_ru_tones_sum;
 };
 };
 
 
 /**
 /**

+ 56 - 0
dp/inc/cdp_txrx_host_stats.h

@@ -979,4 +979,60 @@ cdp_get_pdev_tid_stats(ol_txrx_soc_handle soc, uint8_t pdev_id,
 	return soc->ops->host_stats_ops->txrx_get_pdev_tid_stats(soc, pdev_id,
 	return soc->ops->host_stats_ops->txrx_get_pdev_tid_stats(soc, pdev_id,
 								 tid_stats);
 								 tid_stats);
 }
 }
+
+#ifdef WLAN_TELEMETRY_STATS_SUPPORT
+/**
+ * cdp_get_pdev_telemetry_stats(): function to get pdev telemetry stats
+ * @soc: soc handle
+ * @pdev_id: pdev id
+ * @stats: pointer to pdev telemetry stats
+ *
+ * return: status
+ */
+static inline QDF_STATUS cdp_get_pdev_telemetry_stats(
+				ol_txrx_soc_handle soc,
+				uint8_t pdev_id,
+				struct cdp_pdev_telemetry_stats *stats)
+{
+	if (!soc || !soc->ops) {
+		dp_cdp_debug("Invalid Instance");
+		QDF_BUG(0);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (!soc->ops->host_stats_ops ||
+	    !soc->ops->host_stats_ops->txrx_pdev_telemetry_stats)
+		return QDF_STATUS_E_FAILURE;
+
+	return soc->ops->host_stats_ops->txrx_pdev_telemetry_stats(
+					soc, pdev_id, stats);
+}
+
+/**
+ * cdp_get_peer_telemetry_stats(): function to get peer telemetry stats
+ * @soc: soc handle
+ * @addr: peer address
+ * @stats: pointer to peer telemetry stats
+ *
+ * return: status
+ */
+static inline QDF_STATUS cdp_get_peer_telemetry_stats(
+				ol_txrx_soc_handle soc,
+				uint8_t *addr,
+				struct cdp_peer_telemetry_stats *stats)
+{
+	if (!soc || !soc->ops) {
+		dp_cdp_debug("Invalid Instance");
+		QDF_BUG(0);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (!soc->ops->host_stats_ops ||
+	    !soc->ops->host_stats_ops->txrx_peer_telemetry_stats)
+		return QDF_STATUS_E_FAILURE;
+
+	return soc->ops->host_stats_ops->txrx_peer_telemetry_stats(
+					soc, addr, stats);
+}
+#endif
 #endif /* _CDP_TXRX_HOST_STATS_H_ */
 #endif /* _CDP_TXRX_HOST_STATS_H_ */

+ 12 - 0
dp/inc/cdp_txrx_ops.h

@@ -1170,6 +1170,18 @@ struct cdp_host_stats_ops {
 	QDF_STATUS
 	QDF_STATUS
 	(*txrx_get_pdev_tid_stats)(struct cdp_soc_t *soc, uint8_t pdev_id,
 	(*txrx_get_pdev_tid_stats)(struct cdp_soc_t *soc, uint8_t pdev_id,
 				   struct cdp_tid_stats_intf *tid_stats);
 				   struct cdp_tid_stats_intf *tid_stats);
+#ifdef WLAN_TELEMETRY_STATS_SUPPORT
+	QDF_STATUS
+		(*txrx_pdev_telemetry_stats)(
+				struct cdp_soc_t *soc,
+				uint8_t pdev_id,
+				struct cdp_pdev_telemetry_stats *stats);
+	QDF_STATUS
+		(*txrx_peer_telemetry_stats)(
+				struct cdp_soc_t *soc,
+				uint8_t *addr,
+				struct cdp_peer_telemetry_stats *stats);
+#endif
 };
 };
 
 
 struct cdp_wds_ops {
 struct cdp_wds_ops {

+ 34 - 0
dp/inc/cdp_txrx_stats_struct.h

@@ -2628,6 +2628,36 @@ struct cdp_soc_stats {
 	} mec;
 	} mec;
 };
 };
 
 
+#ifdef WLAN_TELEMETRY_STATS_SUPPORT
+/**
+ * struct cdp_pdev_telemetry_stats- Structure to hold pdev telemetry stats
+ * @tx_mpdu_failed: Tx mpdu failed
+ * @tx_mpdu_total: Total tx mpdus
+ */
+struct cdp_pdev_telemetry_stats {
+	uint32_t tx_mpdu_failed;
+	uint32_t tx_mpdu_total;
+};
+
+/**
+ * struct cdp_peer_telemetry_stats- Structure to hold peer telemetry stats
+ * @tx_mpdu_retried: Tx mpdus retried
+ * @tx_mpdu_total: Total tx mpdus
+ * @rx_mpdu_retried: Rx mpdus retried
+ * @rx_mpdu_total: Total rx mpdus
+ * @airtime_consumption: airtime consumption of that peer
+ * @snr: peer average snr
+ */
+struct cdp_peer_telemetry_stats {
+	uint32_t tx_mpdu_retried;
+	uint32_t tx_mpdu_total;
+	uint32_t rx_mpdu_retried;
+	uint32_t rx_mpdu_total;
+	uint8_t airtime_consumption;
+	uint8_t snr;
+};
+#endif
+
 /* struct cdp_pdev_stats - pdev stats
 /* struct cdp_pdev_stats - pdev stats
  * @msdu_not_done: packets dropped because msdu done bit not set
  * @msdu_not_done: packets dropped because msdu done bit not set
  * @mec:Multicast Echo check
  * @mec:Multicast Echo check
@@ -2670,6 +2700,7 @@ struct cdp_soc_stats {
  * @ppdu_drop: stats counter for ppdu_desc drop once threshold reached
  * @ppdu_drop: stats counter for ppdu_desc drop once threshold reached
  * @ppdu_wrap_drop: stats counter for ppdu desc drop on wrap around
  * @ppdu_wrap_drop: stats counter for ppdu desc drop on wrap around
  * @peer_unauth_rx_pkt_drop: stats counter for drops due to unauthorized peer
  * @peer_unauth_rx_pkt_drop: stats counter for drops due to unauthorized peer
+ * @telemetry_stats: pdev telemetry stats
  */
  */
 struct cdp_pdev_stats {
 struct cdp_pdev_stats {
 	struct {
 	struct {
@@ -2753,6 +2784,9 @@ struct cdp_pdev_stats {
 	} rx_refill_buff_pool;
 	} rx_refill_buff_pool;
 
 
 	uint32_t peer_unauth_rx_pkt_drop;
 	uint32_t peer_unauth_rx_pkt_drop;
+#ifdef WLAN_TELEMETRY_STATS_SUPPORT
+	struct cdp_pdev_telemetry_stats telemetry_stats;
+#endif
 };
 };
 
 
 /* struct cdp_peer_hmwds_ast_add_status - hmwds peer ast add status
 /* struct cdp_peer_hmwds_ast_add_status - hmwds peer ast add status

+ 28 - 0
dp/wifi3.0/dp_internal.h

@@ -3582,4 +3582,32 @@ dp_get_rx_hash_key_bytes(struct cdp_lro_hash_config *lro_hash)
 			     (sizeof(lro_hash->toeplitz_hash_ipv6[0]) *
 			     (sizeof(lro_hash->toeplitz_hash_ipv6[0]) *
 			      LRO_IPV6_SEED_ARR_SZ));
 			      LRO_IPV6_SEED_ARR_SZ));
 }
 }
+
+#ifdef WLAN_TELEMETRY_STATS_SUPPORT
+/*
+ * dp_get_pdev_telemetry_stats- API to get pdev telemetry stats
+ * @soc_hdl: soc handle
+ * @pdev_id: id of pdev handle
+ * @stats: pointer to pdev telemetry stats
+ *
+ * Return: QDF_STATUS_SUCCESS: Success
+ *         QDF_STATUS_E_FAILURE: Error
+ */
+QDF_STATUS
+dp_get_pdev_telemetry_stats(struct cdp_soc_t *soc_hdl, uint8_t pdev_id,
+			    struct cdp_pdev_telemetry_stats *stats);
+
+/*
+ * dp_get_peer_telemetry_stats- API to get peer telemetry stats
+ * @soc_hdl: soc handle
+ * @addr: peer mac
+ * @stats: pointer to peer telemetry stats
+ *
+ * Return: QDF_STATUS_SUCCESS: Success
+ *         QDF_STATUS_E_FAILURE: Error
+ */
+QDF_STATUS
+dp_get_peer_telemetry_stats(struct cdp_soc_t *soc_hdl, uint8_t *addr,
+			    struct cdp_peer_telemetry_stats *stats);
+#endif /* WLAN_TELEMETRY_STATS_SUPPORT */
 #endif /* #ifndef _DP_INTERNAL_H_ */
 #endif /* #ifndef _DP_INTERNAL_H_ */

+ 4 - 0
dp/wifi3.0/dp_main.c

@@ -12737,6 +12737,10 @@ static struct cdp_host_stats_ops dp_ops_host_stats = {
 	.is_tx_delay_stats_enabled = dp_check_vdev_tx_delay_stats_enabled,
 	.is_tx_delay_stats_enabled = dp_check_vdev_tx_delay_stats_enabled,
 #endif
 #endif
 	.txrx_get_pdev_tid_stats = dp_pdev_get_tid_stats,
 	.txrx_get_pdev_tid_stats = dp_pdev_get_tid_stats,
+#ifdef WLAN_TELEMETRY_STATS_SUPPORT
+	.txrx_pdev_telemetry_stats = dp_get_pdev_telemetry_stats,
+	.txrx_peer_telemetry_stats = dp_get_peer_telemetry_stats,
+#endif
 	/* TODO */
 	/* TODO */
 };
 };
 
 

+ 35 - 0
dp/wifi3.0/dp_stats.c

@@ -8538,3 +8538,38 @@ dp_pdev_get_tx_capture_stats(struct cdp_soc_t *soc_hdl, uint8_t pdev_id,
 	return QDF_STATUS_E_FAILURE;
 	return QDF_STATUS_E_FAILURE;
 }
 }
 #endif /* WLAN_TX_PKT_CAPTURE_ENH */
 #endif /* WLAN_TX_PKT_CAPTURE_ENH */
+
+#ifdef WLAN_TELEMETRY_STATS_SUPPORT
+QDF_STATUS
+dp_get_pdev_telemetry_stats(struct cdp_soc_t *soc_hdl, uint8_t pdev_id,
+			    struct cdp_pdev_telemetry_stats *stats)
+{
+	struct dp_soc *soc = (struct dp_soc *)soc_hdl;
+	struct dp_pdev *pdev = dp_get_pdev_from_soc_pdev_id_wifi3(soc, pdev_id);
+
+	if (!pdev)
+		return QDF_STATUS_E_FAILURE;
+
+	stats->tx_mpdu_failed = pdev->stats.telemetry_stats.tx_mpdu_failed;
+	stats->tx_mpdu_total = pdev->stats.telemetry_stats.tx_mpdu_total;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS
+dp_get_peer_telemetry_stats(struct cdp_soc_t *soc_hdl, uint8_t *addr,
+			    struct cdp_peer_telemetry_stats *stats)
+{
+	struct dp_soc *soc = (struct dp_soc *)soc_hdl;
+	struct dp_peer *peer = dp_peer_find_hash_find(soc, addr, 0, DP_VDEV_ALL,
+						      DP_MOD_ID_MISC);
+
+	if (!peer)
+		return QDF_STATUS_E_FAILURE;
+
+	dp_monitor_peer_telemetry_stats(peer, stats);
+	dp_peer_unref_delete(peer, DP_MOD_ID_MISC);
+
+	return QDF_STATUS_SUCCESS;
+}
+#endif

+ 76 - 8
dp/wifi3.0/monitor/dp_mon.c

@@ -2132,6 +2132,47 @@ QDF_STATUS dp_rx_populate_cbf_hdr(struct dp_soc *soc,
 }
 }
 
 
 #ifdef ATH_SUPPORT_EXT_STAT
 #ifdef ATH_SUPPORT_EXT_STAT
+#ifdef WLAN_TELEMETRY_STATS_SUPPORT
+/* dp_peer_update_telemetry_stats- update peer telemetry stats
+ * @peer : Datapath peer
+ */
+static inline
+void dp_peer_update_telemetry_stats(struct dp_peer *peer)
+{
+	struct dp_pdev *pdev;
+	struct dp_vdev *vdev;
+	struct dp_mon_peer *mon_peer = NULL;
+	uint8_t idx;
+
+	vdev = peer->vdev;
+	if (!vdev)
+		return;
+
+	pdev = vdev->pdev;
+	if (!pdev)
+		return;
+
+	mon_peer = peer->monitor_peer;
+	if (qdf_likely(mon_peer)) {
+		DP_STATS_INC(pdev, telemetry_stats.tx_mpdu_failed,
+			     mon_peer->stats.tx.retries);
+		DP_STATS_INC(pdev, telemetry_stats.tx_mpdu_total,
+			     mon_peer->stats.tx.tx_mpdus_tried);
+		idx = mon_peer->stats.airtime_consumption.avg_consumption.idx;
+		mon_peer->stats.airtime_consumption.avg_consumption.avg_consumption_per_sec[idx] =
+				mon_peer->stats.airtime_consumption.consumption;
+		mon_peer->stats.airtime_consumption.consumption = 0;
+		mon_peer->stats.airtime_consumption.avg_consumption.idx++;
+		if (idx == MAX_CONSUMPTION_TIME)
+			mon_peer->stats.airtime_consumption.avg_consumption.idx = 0;
+	}
+}
+#else
+static inline
+void dp_peer_update_telemetry_stats(struct dp_peer *peer)
+{ }
+#endif
+
 /*dp_peer_cal_clients_stats_update - update peer stats on cal client timer
 /*dp_peer_cal_clients_stats_update - update peer stats on cal client timer
  * @soc : Datapath SOC
  * @soc : Datapath SOC
  * @peer : Datapath peer
  * @peer : Datapath peer
@@ -2153,6 +2194,8 @@ dp_peer_cal_clients_stats_update(struct dp_soc *soc,
 	if (!tgt_peer || !(tgt_peer->txrx_peer))
 	if (!tgt_peer || !(tgt_peer->txrx_peer))
 		return;
 		return;
 
 
+	dp_peer_update_telemetry_stats(peer);
+
 	txrx_peer = tgt_peer->txrx_peer;
 	txrx_peer = tgt_peer->txrx_peer;
 	peer_stats_intf.to_stack = txrx_peer->to_stack;
 	peer_stats_intf.to_stack = txrx_peer->to_stack;
 	peer_stats_intf.tx_success =
 	peer_stats_intf.tx_success =
@@ -4045,24 +4088,46 @@ static void dp_process_ppdu_tag(struct dp_pdev *pdev,
 	}
 	}
 }
 }
 
 
-#ifdef WLAN_ATF_ENABLE
+#ifdef WLAN_TELEMETRY_STATS_SUPPORT
+static inline
+void dp_ppdu_desc_user_airtime_consumption_update(
+			struct dp_peer *peer,
+			struct cdp_tx_completion_ppdu_user *user)
+{
+	struct dp_mon_peer *mon_peer = NULL;
+
+	mon_peer = peer->monitor_peer;
+	if (qdf_unlikely(!mon_peer))
+		return;
+
+	DP_STATS_INC(mon_peer, airtime_consumption.consumption,
+		     user->phy_tx_time_us);
+}
+#else
+static inline
+void dp_ppdu_desc_user_airtime_consumption_update(
+			struct dp_peer *peer,
+			struct cdp_tx_completion_ppdu_user *user)
+{ }
+#endif
+#if defined(WLAN_ATF_ENABLE) || defined(WLAN_TELEMETRY_STATS_SUPPORT)
 static void
 static void
 dp_ppdu_desc_user_phy_tx_time_update(struct dp_pdev *pdev,
 dp_ppdu_desc_user_phy_tx_time_update(struct dp_pdev *pdev,
+				     struct dp_peer *peer,
 				     struct cdp_tx_completion_ppdu *ppdu_desc,
 				     struct cdp_tx_completion_ppdu *ppdu_desc,
 				     struct cdp_tx_completion_ppdu_user *user)
 				     struct cdp_tx_completion_ppdu_user *user)
 {
 {
 	uint32_t nss_ru_width_sum = 0;
 	uint32_t nss_ru_width_sum = 0;
-	struct dp_mon_pdev *mon_pdev = NULL;
+	struct dp_mon_peer *mon_peer = NULL;
 
 
-	if (!pdev || !ppdu_desc || !user)
+	if (!pdev || !ppdu_desc || !user || !peer)
 		return;
 		return;
 
 
-	mon_pdev = pdev->monitor_pdev;
-
-	if (!mon_pdev || !mon_pdev->dp_atf_stats_enable)
+	if (ppdu_desc->frame_type != CDP_PPDU_FTYPE_DATA)
 		return;
 		return;
 
 
-	if (ppdu_desc->frame_type != CDP_PPDU_FTYPE_DATA)
+	mon_peer = peer->monitor_peer;
+	if (qdf_unlikely(!mon_peer))
 		return;
 		return;
 
 
 	nss_ru_width_sum = ppdu_desc->usr_nss_sum * ppdu_desc->usr_ru_tones_sum;
 	nss_ru_width_sum = ppdu_desc->usr_nss_sum * ppdu_desc->usr_ru_tones_sum;
@@ -4084,10 +4149,13 @@ dp_ppdu_desc_user_phy_tx_time_update(struct dp_pdev *pdev,
 		user->phy_tx_time_us = (ppdu_desc->phy_ppdu_tx_time_us *
 		user->phy_tx_time_us = (ppdu_desc->phy_ppdu_tx_time_us *
 				user->nss * user->ru_tones) / nss_ru_width_sum;
 				user->nss * user->ru_tones) / nss_ru_width_sum;
 	}
 	}
+
+	dp_ppdu_desc_user_airtime_consumption_update(peer, user);
 }
 }
 #else
 #else
 static void
 static void
 dp_ppdu_desc_user_phy_tx_time_update(struct dp_pdev *pdev,
 dp_ppdu_desc_user_phy_tx_time_update(struct dp_pdev *pdev,
+				     struct dp_peer *peer,
 				     struct cdp_tx_completion_ppdu *ppdu_desc,
 				     struct cdp_tx_completion_ppdu *ppdu_desc,
 				     struct cdp_tx_completion_ppdu_user *user)
 				     struct cdp_tx_completion_ppdu_user *user)
 {
 {
@@ -4191,7 +4259,7 @@ dp_ppdu_desc_user_stats_update(struct dp_pdev *pdev,
 					   ppdu_desc->ack_rssi);
 					   ppdu_desc->ack_rssi);
 		}
 		}
 
 
-		dp_ppdu_desc_user_phy_tx_time_update(pdev, ppdu_desc,
+		dp_ppdu_desc_user_phy_tx_time_update(pdev, peer, ppdu_desc,
 						     &ppdu_desc->user[i]);
 						     &ppdu_desc->user[i]);
 
 
 		dp_peer_unref_delete(peer, DP_MOD_ID_TX_PPDU_STATS);
 		dp_peer_unref_delete(peer, DP_MOD_ID_TX_PPDU_STATS);

+ 42 - 0
dp/wifi3.0/monitor/dp_mon.h

@@ -823,6 +823,17 @@ struct dp_mon_soc {
 #endif
 #endif
 };
 };
 
 
+#ifdef WLAN_TELEMETRY_STATS_SUPPORT
+#define MAX_CONSUMPTION_TIME 5 /* in sec */
+struct dp_mon_peer_airtime_consumption {
+	uint32_t consumption;
+	struct {
+		uint32_t avg_consumption_per_sec[MAX_CONSUMPTION_TIME];
+		uint8_t idx;
+	} avg_consumption;
+};
+#endif
+
 /**
 /**
  * struct dp_mon_peer_stats - Monitor peer stats
  * struct dp_mon_peer_stats - Monitor peer stats
  */
  */
@@ -830,6 +841,9 @@ struct dp_mon_peer_stats {
 #ifdef QCA_ENHANCED_STATS_SUPPORT
 #ifdef QCA_ENHANCED_STATS_SUPPORT
 	dp_mon_peer_tx_stats tx;
 	dp_mon_peer_tx_stats tx;
 	dp_mon_peer_rx_stats rx;
 	dp_mon_peer_rx_stats rx;
+#ifdef WLAN_TELEMETRY_STATS_SUPPORT
+	struct dp_mon_peer_airtime_consumption airtime_consumption;
+#endif
 #endif
 #endif
 };
 };
 
 
@@ -4089,4 +4103,32 @@ dp_lite_mon_rx_mpdu_process(struct dp_pdev *pdev,
 	return QDF_STATUS_E_FAILURE;
 	return QDF_STATUS_E_FAILURE;
 }
 }
 #endif
 #endif
+
+#ifdef WLAN_TELEMETRY_STATS_SUPPORT
+static inline
+void dp_monitor_peer_telemetry_stats(struct dp_peer *peer,
+				     struct cdp_peer_telemetry_stats *stats)
+{
+	struct dp_mon_peer_stats *mon_peer_stats = NULL;
+	uint8_t idx = 0;
+	uint32_t consumption = 0;
+
+	if (qdf_unlikely(!peer->monitor_peer))
+		return;
+
+	mon_peer_stats = &peer->monitor_peer->stats;
+	for (idx = 0; idx < MAX_CONSUMPTION_TIME; idx++)
+		consumption +=
+			mon_peer_stats->airtime_consumption.avg_consumption.avg_consumption_per_sec[idx];
+	/* consumption is in micro seconds, convert it to seconds and
+	 * then calculate %age per 5 sec
+	 */
+	stats->airtime_consumption = ((consumption * 100) / (MAX_CONSUMPTION_TIME * 1000000));
+	stats->tx_mpdu_retried = mon_peer_stats->tx.retries;
+	stats->tx_mpdu_total = mon_peer_stats->tx.tx_mpdus_tried;
+	stats->rx_mpdu_retried = mon_peer_stats->rx.mpdu_retry_cnt;
+	stats->rx_mpdu_total = mon_peer_stats->rx.rx_mpdus;
+	stats->snr = CDP_SNR_OUT(mon_peer_stats->rx.avg_snr);
+}
+#endif
 #endif /* _DP_MON_H_ */
 #endif /* _DP_MON_H_ */

+ 41 - 0
dp/wifi3.0/monitor/dp_rx_mon.c

@@ -523,6 +523,7 @@ dp_rx_populate_cdp_indication_ppdu_user(struct dp_pdev *pdev,
 		    cdp_rx_ppdu->u.ppdu_type == HAL_RX_TYPE_MU_MIMO) {
 		    cdp_rx_ppdu->u.ppdu_type == HAL_RX_TYPE_MU_MIMO) {
 			if (rx_user_status->mu_ul_info_valid) {
 			if (rx_user_status->mu_ul_info_valid) {
 				rx_stats_peruser->nss = rx_user_status->nss;
 				rx_stats_peruser->nss = rx_user_status->nss;
+				cdp_rx_ppdu->usr_nss_sum += rx_stats_peruser->nss;
 				rx_stats_peruser->mcs = rx_user_status->mcs;
 				rx_stats_peruser->mcs = rx_user_status->mcs;
 				rx_stats_peruser->mu_ul_info_valid =
 				rx_stats_peruser->mu_ul_info_valid =
 					rx_user_status->mu_ul_info_valid;
 					rx_user_status->mu_ul_info_valid;
@@ -530,6 +531,8 @@ dp_rx_populate_cdp_indication_ppdu_user(struct dp_pdev *pdev,
 					rx_user_status->ofdma_ru_start_index;
 					rx_user_status->ofdma_ru_start_index;
 				rx_stats_peruser->ofdma_ru_width =
 				rx_stats_peruser->ofdma_ru_width =
 					rx_user_status->ofdma_ru_width;
 					rx_user_status->ofdma_ru_width;
+				cdp_rx_ppdu->usr_ru_tones_sum +=
+					rx_stats_peruser->ofdma_ru_width;
 				rx_stats_peruser->user_index = i;
 				rx_stats_peruser->user_index = i;
 				ru_size = rx_user_status->ofdma_ru_size;
 				ru_size = rx_user_status->ofdma_ru_size;
 				/*
 				/*
@@ -820,6 +823,43 @@ static inline uint8_t dp_get_bw_offset_frm_bw(struct dp_soc *soc,
 }
 }
 #endif
 #endif
 
 
+#ifdef WLAN_TELEMETRY_STATS_SUPPORT
+static void
+dp_ppdu_desc_user_rx_time_update(struct dp_pdev *pdev,
+				 struct dp_peer *peer,
+				 struct cdp_rx_indication_ppdu *ppdu_desc,
+				 struct cdp_rx_stats_ppdu_user *user)
+{
+	uint32_t nss_ru_width_sum = 0;
+	struct dp_mon_peer *mon_peer = NULL;
+	uint16_t rx_time_us;
+
+	if (!pdev || !ppdu_desc || !user || !peer)
+		return;
+
+	mon_peer = peer->monitor_peer;
+	if (qdf_unlikely(!mon_peer))
+		return;
+
+	nss_ru_width_sum = ppdu_desc->usr_nss_sum * ppdu_desc->usr_ru_tones_sum;
+	if (!nss_ru_width_sum)
+		nss_ru_width_sum = 1;
+
+	rx_time_us = (ppdu_desc->duration *
+			user->nss * user->ofdma_ru_width) / nss_ru_width_sum;
+
+	DP_STATS_INC(mon_peer, airtime_consumption.consumption,
+		     rx_time_us);
+}
+#else
+static inline void
+dp_ppdu_desc_user_rx_time_update(struct dp_pdev *pdev,
+				 struct dp_peer *peer,
+				 struct cdp_rx_indication_ppdu *ppdu_desc,
+				 struct cdp_rx_stats_ppdu_user *user)
+{ }
+#endif
+
 static void dp_rx_stats_update(struct dp_pdev *pdev,
 static void dp_rx_stats_update(struct dp_pdev *pdev,
 			       struct cdp_rx_indication_ppdu *ppdu)
 			       struct cdp_rx_indication_ppdu *ppdu)
 {
 {
@@ -1024,6 +1064,7 @@ static void dp_rx_stats_update(struct dp_pdev *pdev,
 
 
 		dp_send_stats_event(pdev, peer, ppdu_user->peer_id);
 		dp_send_stats_event(pdev, peer, ppdu_user->peer_id);
 
 
+		dp_ppdu_desc_user_rx_time_update(pdev, peer, ppdu, ppdu_user);
 		dp_peer_unref_delete(peer, DP_MOD_ID_RX_PPDU_STATS);
 		dp_peer_unref_delete(peer, DP_MOD_ID_RX_PPDU_STATS);
 	}
 	}
 }
 }