Browse Source

qcacmn: vdev stats Support for MLO Hybrid Mode

Add vdev stats support for DP MLO Unified
and Hybrid Mode.
Add new CDP API for getting vdev stats for
NON_MLD interface.
Add support to get vdev stats for specific
vdev which is a part of MLO.

Change-Id: I72c8b25958649d473e1b65cee21810cd86951187
CRs-Fixed: 3455607
Kenvish Butani 2 years ago
parent
commit
ef68ac521f

+ 28 - 0
dp/inc/cdp_txrx_host_stats.h

@@ -1240,4 +1240,32 @@ static inline QDF_STATUS cdp_clear_pdev_obss_pd_stats(
 	return soc->ops->host_stats_ops->clear_pdev_obss_pd_stats(
 	return soc->ops->host_stats_ops->clear_pdev_obss_pd_stats(
 					soc, pdev_id, req);
 					soc, pdev_id, req);
 }
 }
+
+/*
+ * cdp_host_get_interface_stats - Get vdev stats for ath interface
+ * @soc: soc handle
+ * @vdev_id: vdev_id
+ * @buf: buffer to hold vdev_stats
+ *
+ * return: QDF_STATUS
+ */
+static inline QDF_STATUS
+cdp_host_get_interface_stats(ol_txrx_soc_handle soc,
+			     uint8_t vdev_id,
+			     struct cdp_vdev_stats *buf)
+{
+	if (!soc || !soc->ops) {
+		QDF_BUG(0);
+		return QDF_STATUS_E_FAILURE;
+	}
+
+	if (!soc->ops->host_stats_ops ||
+	    !soc->ops->host_stats_ops->txrx_get_interface_stats)
+		return QDF_STATUS_E_FAILURE;
+
+	return soc->ops->host_stats_ops->txrx_get_interface_stats(soc,
+								  vdev_id,
+								  buf,
+								  true);
+}
 #endif /* _CDP_TXRX_HOST_STATS_H_ */
 #endif /* _CDP_TXRX_HOST_STATS_H_ */

+ 7 - 2
dp/inc/cdp_txrx_mlo.h

@@ -190,12 +190,14 @@ static inline void cdp_mlo_update_delta_tqm(ol_txrx_soc_handle soc,
  * @soc: soc handle
  * @soc: soc handle
  * @vdev_id: vdev_id of one of the vdev's of the MLD group
  * @vdev_id: vdev_id of one of the vdev's of the MLD group
  * @buf: buffer to hold vdev_stats
  * @buf: buffer to hold vdev_stats
+ * @link_vdev_only: flag to indicate if stats are required for specific vdev
  *
  *
  * return: QDF_STATUS
  * return: QDF_STATUS
  */
  */
 static inline QDF_STATUS
 static inline QDF_STATUS
 cdp_mlo_get_mld_vdev_stats(ol_txrx_soc_handle soc,
 cdp_mlo_get_mld_vdev_stats(ol_txrx_soc_handle soc,
-			   uint8_t vdev_id, struct cdp_vdev_stats *buf)
+			   uint8_t vdev_id, struct cdp_vdev_stats *buf,
+			   bool link_vdev_only)
 {
 {
 	if (!soc || !soc->ops) {
 	if (!soc || !soc->ops) {
 		QDF_BUG(0);
 		QDF_BUG(0);
@@ -205,6 +207,9 @@ cdp_mlo_get_mld_vdev_stats(ol_txrx_soc_handle soc,
 	if (!soc->ops->mlo_ops || !soc->ops->mlo_ops->mlo_get_mld_vdev_stats)
 	if (!soc->ops->mlo_ops || !soc->ops->mlo_ops->mlo_get_mld_vdev_stats)
 		return QDF_STATUS_E_FAILURE;
 		return QDF_STATUS_E_FAILURE;
 
 
-	return soc->ops->mlo_ops->mlo_get_mld_vdev_stats(soc, vdev_id, buf);
+	return soc->ops->mlo_ops->mlo_get_mld_vdev_stats(soc,
+							 vdev_id,
+							 buf,
+							 link_vdev_only);
 }
 }
 #endif /*_CDP_TXRX_MLO_H_*/
 #endif /*_CDP_TXRX_MLO_H_*/

+ 8 - 2
dp/inc/cdp_txrx_ops.h

@@ -180,7 +180,8 @@ struct cdp_mlo_ops {
 	void (*mlo_update_mlo_ts_offset)(struct cdp_soc_t *soc_hdl,
 	void (*mlo_update_mlo_ts_offset)(struct cdp_soc_t *soc_hdl,
 					 uint64_t offset);
 					 uint64_t offset);
 	QDF_STATUS (*mlo_get_mld_vdev_stats)(struct cdp_soc_t *soc,
 	QDF_STATUS (*mlo_get_mld_vdev_stats)(struct cdp_soc_t *soc,
-					     uint8_t vdev_id, void *buf);
+					     uint8_t vdev_id, void *buf,
+					     bool link_vdev_only);
 };
 };
 #endif
 #endif
 
 
@@ -1167,6 +1168,7 @@ struct cdp_mon_ops {
  * @txrx_get_peer_extd_rate_link_stats:
  * @txrx_get_peer_extd_rate_link_stats:
  * @get_pdev_obss_stats:
  * @get_pdev_obss_stats:
  * @clear_pdev_obss_pd_stats:
  * @clear_pdev_obss_pd_stats:
+ * @txrx_get_interface_stats:
  */
  */
 struct cdp_host_stats_ops {
 struct cdp_host_stats_ops {
 	int (*txrx_host_stats_get)(struct cdp_soc_t *soc, uint8_t vdev_id,
 	int (*txrx_host_stats_get)(struct cdp_soc_t *soc, uint8_t vdev_id,
@@ -1253,7 +1255,7 @@ struct cdp_host_stats_ops {
 	QDF_STATUS
 	QDF_STATUS
 		(*txrx_reset_peer_stats)(struct cdp_soc_t *soc,
 		(*txrx_reset_peer_stats)(struct cdp_soc_t *soc,
 					 uint8_t vdev_id, uint8_t *peer_mac);
 					 uint8_t vdev_id, uint8_t *peer_mac);
-	int
+	QDF_STATUS
 		(*txrx_get_vdev_stats)(struct cdp_soc_t *soc, uint8_t vdev_id,
 		(*txrx_get_vdev_stats)(struct cdp_soc_t *soc, uint8_t vdev_id,
 				       void *buf, bool is_aggregate);
 				       void *buf, bool is_aggregate);
 	int
 	int
@@ -1366,6 +1368,10 @@ struct cdp_host_stats_ops {
 	QDF_STATUS (*clear_pdev_obss_pd_stats)(struct cdp_soc_t *soc,
 	QDF_STATUS (*clear_pdev_obss_pd_stats)(struct cdp_soc_t *soc,
 					       uint8_t pdev_id,
 					       uint8_t pdev_id,
 					       struct cdp_txrx_stats_req *req);
 					       struct cdp_txrx_stats_req *req);
+	QDF_STATUS (*txrx_get_interface_stats)(struct cdp_soc_t *soc,
+					       uint8_t vdev_id,
+					       void *buf,
+					       bool is_aggregate);
 };
 };
 
 
 /**
 /**

+ 20 - 0
dp/wifi3.0/be/dp_be.c

@@ -2379,6 +2379,17 @@ static void dp_txrx_set_mlo_mcast_primary_vdev_param_be(
 				WDI_NO_VAL, params.pdev_id);
 				WDI_NO_VAL, params.pdev_id);
 	}
 	}
 }
 }
+
+static
+void dp_get_vdev_stats_for_unmap_peer_be(struct dp_vdev *vdev,
+					 struct dp_peer *peer,
+					 struct cdp_vdev_stats **vdev_stats)
+{
+	struct dp_vdev_be *be_vdev = dp_get_be_vdev_from_dp_vdev(vdev);
+
+	if (!IS_DP_LEGACY_PEER(peer))
+		*vdev_stats = &be_vdev->mlo_stats;
+}
 #else
 #else
 static void dp_txrx_set_mlo_mcast_primary_vdev_param_be(
 static void dp_txrx_set_mlo_mcast_primary_vdev_param_be(
 					struct dp_vdev *vdev,
 					struct dp_vdev *vdev,
@@ -2470,6 +2481,13 @@ QDF_STATUS dp_txrx_get_vdev_mcast_param_be(struct dp_soc *soc,
 {
 {
 	return QDF_STATUS_SUCCESS;
 	return QDF_STATUS_SUCCESS;
 }
 }
+
+static
+void dp_get_vdev_stats_for_unmap_peer_be(struct dp_vdev *vdev,
+					 struct dp_peer *peer,
+					 struct cdp_vdev_stats **vdev_stats)
+{
+}
 #endif
 #endif
 
 
 #ifdef DP_TX_IMPLICIT_RBM_MAPPING
 #ifdef DP_TX_IMPLICIT_RBM_MAPPING
@@ -2902,6 +2920,8 @@ void dp_initialize_arch_ops_be(struct dp_arch_ops *arch_ops)
 	arch_ops->reo_remap_config = dp_reo_remap_config_be;
 	arch_ops->reo_remap_config = dp_reo_remap_config_be;
 	arch_ops->txrx_get_vdev_mcast_param = dp_txrx_get_vdev_mcast_param_be;
 	arch_ops->txrx_get_vdev_mcast_param = dp_txrx_get_vdev_mcast_param_be;
 	arch_ops->txrx_srng_init = dp_srng_init_be;
 	arch_ops->txrx_srng_init = dp_srng_init_be;
+	arch_ops->dp_get_vdev_stats_for_unmap_peer =
+					dp_get_vdev_stats_for_unmap_peer_be;
 #if defined(DP_POWER_SAVE) || defined(FEATURE_RUNTIME_PM)
 #if defined(DP_POWER_SAVE) || defined(FEATURE_RUNTIME_PM)
 	arch_ops->dp_update_ring_hptp = dp_update_ring_hptp;
 	arch_ops->dp_update_ring_hptp = dp_update_ring_hptp;
 #endif
 #endif

+ 2 - 0
dp/wifi3.0/be/dp_be.h

@@ -399,6 +399,7 @@ struct dp_pdev_be {
  * @bank_id: bank_id to be used for TX
  * @bank_id: bank_id to be used for TX
  * @vdev_id_check_en: flag if HW vdev_id check is enabled for vdev
  * @vdev_id_check_en: flag if HW vdev_id check is enabled for vdev
  * @partner_vdev_list: partner list used for Intra-BSS
  * @partner_vdev_list: partner list used for Intra-BSS
+ * @mlo_stats: structure to hold stats for mlo unmapped peers
  * @seq_num: DP MLO seq number
  * @seq_num: DP MLO seq number
  * @mcast_primary: MLO Mcast primary vdev
  * @mcast_primary: MLO Mcast primary vdev
  */
  */
@@ -408,6 +409,7 @@ struct dp_vdev_be {
 	uint8_t vdev_id_check_en;
 	uint8_t vdev_id_check_en;
 #ifdef WLAN_MLO_MULTI_CHIP
 #ifdef WLAN_MLO_MULTI_CHIP
 	uint8_t partner_vdev_list[WLAN_MAX_MLO_CHIPS][WLAN_MAX_MLO_LINKS_PER_SOC];
 	uint8_t partner_vdev_list[WLAN_MAX_MLO_CHIPS][WLAN_MAX_MLO_LINKS_PER_SOC];
+	struct cdp_vdev_stats mlo_stats;
 #ifdef WLAN_FEATURE_11BE_MLO
 #ifdef WLAN_FEATURE_11BE_MLO
 #ifdef WLAN_MCAST_MLO
 #ifdef WLAN_MCAST_MLO
 	uint16_t seq_num;
 	uint16_t seq_num;

+ 211 - 16
dp/wifi3.0/be/mlo/dp_mlo.c

@@ -544,27 +544,178 @@ static void dp_mlo_update_mlo_ts_offset(struct cdp_soc_t *soc_hdl,
 }
 }
 
 
 #ifdef CONFIG_MLO_SINGLE_DEV
 #ifdef CONFIG_MLO_SINGLE_DEV
-static void dp_mlo_aggregate_mld_vdev_stats(struct dp_vdev_be *be_vdev,
-					    struct dp_vdev *ptnr_vdev,
-					    void *arg)
+/**
+ * dp_aggregate_vdev_ingress_stats() - aggregate vdev ingress stats
+ * @tgt_vdev_stats: target vdev buffer
+ * @src_vdev_stats: source vdev buffer
+ *
+ * return: void
+ */
+static inline
+void dp_aggregate_vdev_ingress_stats(
+			struct cdp_vdev_stats *tgt_vdev_stats,
+			struct cdp_vdev_stats *src_vdev_stats)
 {
 {
-	struct cdp_vdev_stats *tgt_vdev_stats = (struct cdp_vdev_stats *)arg;
-	struct cdp_vdev_stats *src_vdev_stats = &ptnr_vdev->stats;
-
 	/* Aggregate vdev ingress stats */
 	/* Aggregate vdev ingress stats */
 	DP_UPDATE_INGRESS_STATS(tgt_vdev_stats, src_vdev_stats);
 	DP_UPDATE_INGRESS_STATS(tgt_vdev_stats, src_vdev_stats);
+}
 
 
+/**
+ * dp_aggregate_vdev_stats_for_unmapped_peers() - aggregate unmap peer stats
+ * @tgt_vdev_stats: target vdev buffer
+ * @src_vdev_stats: source vdev buffer
+ *
+ * return: void
+ */
+static inline
+void dp_aggregate_vdev_stats_for_unmapped_peers(
+			struct cdp_vdev_stats *tgt_vdev_stats,
+			struct cdp_vdev_stats *src_vdev_stats)
+{
 	/* Aggregate unmapped peers stats */
 	/* Aggregate unmapped peers stats */
-	DP_UPDATE_PER_PKT_STATS(tgt_vdev_stats, src_vdev_stats);
-	DP_UPDATE_EXTD_STATS(tgt_vdev_stats, src_vdev_stats);
+	DP_UPDATE_VDEV_STATS_FOR_UNMAPPED_PEERS(tgt_vdev_stats, src_vdev_stats);
+}
+
+/**
+ * dp_aggregate_all_vdev_stats() - aggregate vdev ingress and unmap peer stats
+ * @tgt_vdev_stats: target vdev buffer
+ * @src_vdev_stats: source vdev buffer
+ *
+ * return: void
+ */
+static inline
+void dp_aggregate_all_vdev_stats(
+			struct cdp_vdev_stats *tgt_vdev_stats,
+			struct cdp_vdev_stats *src_vdev_stats)
+{
+	dp_aggregate_vdev_ingress_stats(tgt_vdev_stats, src_vdev_stats);
+	dp_aggregate_vdev_stats_for_unmapped_peers(tgt_vdev_stats,
+						   src_vdev_stats);
+}
+
+/**
+ * dp_aggregate_interface_stats_based_on_peer_type() - aggregate stats at
+ * VDEV level based on peer type connected to vdev
+ * @vdev: DP VDEV handle
+ * @vdev_stats: target vdev stats pointer
+ * @peer_type: type of peer - MLO Link or Legacy peer
+ *
+ * return: void
+ */
+static
+void dp_aggregate_interface_stats_based_on_peer_type(
+					struct dp_vdev *vdev,
+					struct cdp_vdev_stats *vdev_stats,
+					enum dp_peer_type peer_type)
+{
+	struct cdp_vdev_stats *tgt_vdev_stats = NULL;
+	struct dp_vdev_be *be_vdev = NULL;
+
+	if (!vdev || !vdev->pdev)
+		return;
+
+	tgt_vdev_stats = vdev_stats;
+	be_vdev = dp_get_be_vdev_from_dp_vdev(vdev);
+	if (!be_vdev)
+		return;
+
+	if (peer_type == DP_PEER_TYPE_LEGACY) {
+		dp_aggregate_all_vdev_stats(tgt_vdev_stats,
+					    &vdev->stats);
+	} else {
+		dp_aggregate_vdev_ingress_stats(tgt_vdev_stats,
+						&vdev->stats);
+		dp_aggregate_vdev_stats_for_unmapped_peers(
+						tgt_vdev_stats,
+						&be_vdev->mlo_stats);
+	}
+
+	/* Aggregate associated peer stats */
+	dp_vdev_iterate_specific_peer_type(vdev,
+					   dp_update_vdev_stats,
+					   vdev_stats,
+					   DP_MOD_ID_GENERIC_STATS,
+					   peer_type);
+}
+
+/**
+ * dp_aggregate_interface_stats() - aggregate stats at VDEV level
+ * @vdev: DP VDEV handle
+ * @vdev_stats: target vdev stats pointer
+ *
+ * return: void
+ */
+static
+void dp_aggregate_interface_stats(struct dp_vdev *vdev,
+				  struct cdp_vdev_stats *vdev_stats)
+{
+	struct dp_vdev_be *be_vdev = NULL;
+
+	if (!vdev || !vdev->pdev)
+		return;
 
 
-	/* Aggregate associated peers stats */
-	dp_vdev_iterate_peer(ptnr_vdev, dp_update_vdev_stats, tgt_vdev_stats,
+	be_vdev = dp_get_be_vdev_from_dp_vdev(vdev);
+	if (!be_vdev)
+		return;
+
+	dp_aggregate_all_vdev_stats(vdev_stats, &be_vdev->mlo_stats);
+	dp_aggregate_all_vdev_stats(vdev_stats, &vdev->stats);
+
+	dp_vdev_iterate_peer(vdev, dp_update_vdev_stats, vdev_stats,
 			     DP_MOD_ID_GENERIC_STATS);
 			     DP_MOD_ID_GENERIC_STATS);
+
+	dp_update_vdev_rate_stats(vdev_stats, &vdev->stats);
+
+#if defined(FEATURE_PERPKT_INFO) && WDI_EVENT_ENABLE
+	dp_wdi_event_handler(WDI_EVENT_UPDATE_DP_STATS, vdev->pdev->soc,
+			     vdev_stats, vdev->vdev_id,
+			     UPDATE_VDEV_STATS, vdev->pdev->pdev_id);
+#endif
+}
+
+/**
+ * dp_mlo_aggr_ptnr_iface_stats() - aggregate mlo partner vdev stats
+ * @be_vdev: vdev handle
+ * @ptnr_vdev: partner vdev handle
+ * @arg: target buffer for aggregation
+ *
+ * return: void
+ */
+static
+void dp_mlo_aggr_ptnr_iface_stats(struct dp_vdev_be *be_vdev,
+				  struct dp_vdev *ptnr_vdev,
+				  void *arg)
+{
+	struct cdp_vdev_stats *tgt_vdev_stats = (struct cdp_vdev_stats *)arg;
+
+	dp_aggregate_interface_stats(ptnr_vdev, tgt_vdev_stats);
+}
+
+/**
+ * dp_mlo_aggr_ptnr_iface_stats_mlo_links() - aggregate mlo partner vdev stats
+ * based on peer type
+ * @be_vdev: vdev handle
+ * @ptnr_vdev: partner vdev handle
+ * @arg: target buffer for aggregation
+ *
+ * return: void
+ */
+static
+void dp_mlo_aggr_ptnr_iface_stats_mlo_links(
+					struct dp_vdev_be *be_vdev,
+					struct dp_vdev *ptnr_vdev,
+					void *arg)
+{
+	struct cdp_vdev_stats *tgt_vdev_stats = (struct cdp_vdev_stats *)arg;
+
+	dp_aggregate_interface_stats_based_on_peer_type(ptnr_vdev,
+							tgt_vdev_stats,
+							DP_PEER_TYPE_MLO_LINK);
 }
 }
 
 
 static QDF_STATUS dp_mlo_get_mld_vdev_stats(struct cdp_soc_t *soc_hdl,
 static QDF_STATUS dp_mlo_get_mld_vdev_stats(struct cdp_soc_t *soc_hdl,
-					    uint8_t vdev_id, void *buf)
+					    uint8_t vdev_id, void *buf,
+					    bool link_vdev_only)
 {
 {
 	struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
 	struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
 	struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
 	struct dp_soc_be *be_soc = dp_get_be_soc_from_dp_soc(soc);
@@ -584,12 +735,56 @@ static QDF_STATUS dp_mlo_get_mld_vdev_stats(struct cdp_soc_t *soc_hdl,
 
 
 	vdev_stats = (struct cdp_vdev_stats *)buf;
 	vdev_stats = (struct cdp_vdev_stats *)buf;
 
 
-	dp_aggregate_vdev_stats(vdev, buf);
+	if (DP_MLD_MODE_HYBRID_NONBOND == soc->mld_mode_ap &&
+	    vdev->opmode == wlan_op_mode_ap) {
+		dp_aggregate_interface_stats_based_on_peer_type(
+						vdev, buf,
+						DP_PEER_TYPE_MLO_LINK);
+		if (link_vdev_only)
+			goto complete;
+
+		/* Aggregate stats from partner vdevs */
+		dp_mlo_iter_ptnr_vdev(be_soc, vdev_be,
+				      dp_mlo_aggr_ptnr_iface_stats_mlo_links,
+				      buf,
+				      DP_MOD_ID_GENERIC_STATS);
+	} else {
+		dp_aggregate_interface_stats(vdev, buf);
+
+		if (link_vdev_only)
+			goto complete;
+
+		/* Aggregate stats from partner vdevs */
+		dp_mlo_iter_ptnr_vdev(be_soc, vdev_be,
+				      dp_mlo_aggr_ptnr_iface_stats, buf,
+				      DP_MOD_ID_GENERIC_STATS);
+	}
+
+complete:
+	dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_GENERIC_STATS);
+	return QDF_STATUS_SUCCESS;
+}
+
+QDF_STATUS
+dp_txrx_get_interface_stats(struct cdp_soc_t *soc_hdl,
+			    uint8_t vdev_id,
+			    void *buf,
+			    bool is_aggregate)
+{
+	struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
+	struct dp_vdev *vdev = dp_vdev_get_ref_by_id(soc, vdev_id,
+						     DP_MOD_ID_GENERIC_STATS);
+	if (!vdev)
+		return QDF_STATUS_E_FAILURE;
 
 
-	/* Aggregate stats from partner vdevs */
-	dp_mlo_iter_ptnr_vdev(be_soc, vdev_be,
-			      dp_mlo_aggregate_mld_vdev_stats, buf,
-			      DP_MOD_ID_GENERIC_STATS);
+	if (DP_MLD_MODE_HYBRID_NONBOND == soc->mld_mode_ap &&
+	    vdev->opmode == wlan_op_mode_ap) {
+		dp_aggregate_interface_stats_based_on_peer_type(
+						vdev, buf,
+						DP_PEER_TYPE_LEGACY);
+	} else {
+		dp_aggregate_interface_stats(vdev, buf);
+	}
 
 
 	dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_GENERIC_STATS);
 	dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_GENERIC_STATS);
 
 

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

@@ -1891,6 +1891,15 @@ void dp_update_vdev_stats_on_peer_unmap(struct dp_vdev *vdev,
 #define DP_UPDATE_11BE_STATS(_tgtobj, _srcobj)
 #define DP_UPDATE_11BE_STATS(_tgtobj, _srcobj)
 #endif
 #endif
 
 
+#define DP_UPDATE_BASIC_STATS(_tgtobj, _srcobj) \
+	do { \
+		_tgtobj->tx.comp_pkt.num += _srcobj->tx.comp_pkt.num; \
+		_tgtobj->tx.comp_pkt.bytes += _srcobj->tx.comp_pkt.bytes; \
+		_tgtobj->tx.tx_failed += _srcobj->tx.tx_failed; \
+		_tgtobj->rx.to_stack.num += _srcobj->rx.to_stack.num; \
+		_tgtobj->rx.to_stack.bytes += _srcobj->rx.to_stack.bytes; \
+	} while (0)
+
 #define DP_UPDATE_PER_PKT_STATS(_tgtobj, _srcobj) \
 #define DP_UPDATE_PER_PKT_STATS(_tgtobj, _srcobj) \
 	do { \
 	do { \
 		uint8_t i; \
 		uint8_t i; \
@@ -2176,6 +2185,13 @@ void dp_update_vdev_stats_on_peer_unmap(struct dp_vdev *vdev,
 		DP_UPDATE_11BE_STATS(_tgtobj, _srcobj); \
 		DP_UPDATE_11BE_STATS(_tgtobj, _srcobj); \
 	} while (0)
 	} while (0)
 
 
+#define DP_UPDATE_VDEV_STATS_FOR_UNMAPPED_PEERS(_tgtobj, _srcobj) \
+	do { \
+		DP_UPDATE_BASIC_STATS(_tgtobj, _srcobj); \
+		DP_UPDATE_PER_PKT_STATS(_tgtobj, _srcobj); \
+		DP_UPDATE_EXTD_STATS(_tgtobj, _srcobj); \
+	} while (0)
+
 #define DP_UPDATE_INGRESS_STATS(_tgtobj, _srcobj) \
 #define DP_UPDATE_INGRESS_STATS(_tgtobj, _srcobj) \
 	do { \
 	do { \
 		uint8_t i = 0; \
 		uint8_t i = 0; \
@@ -2832,6 +2848,22 @@ uint32_t dp_reo_status_ring_handler(struct dp_intr *int_ctx,
 void dp_aggregate_vdev_stats(struct dp_vdev *vdev,
 void dp_aggregate_vdev_stats(struct dp_vdev *vdev,
 			     struct cdp_vdev_stats *vdev_stats);
 			     struct cdp_vdev_stats *vdev_stats);
 
 
+/**
+ * dp_txrx_get_interface_stats() - get vdev stats for ath interface
+ * @soc_hdl: CDP SoC handle
+ * @vdev_id: vdev Id
+ * @buf: buffer for vdev stats
+ * @is_aggregate: for aggregation
+ *
+ * Return: QDF_STATUS
+ */
+
+QDF_STATUS
+dp_txrx_get_interface_stats(struct cdp_soc_t *soc_hdl,
+			    uint8_t vdev_id,
+			    void *buf,
+			    bool is_aggregate);
+
 /**
 /**
  * dp_rx_bar_stats_cb() - BAR received stats callback
  * dp_rx_bar_stats_cb() - BAR received stats callback
  * @soc: SOC handle
  * @soc: SOC handle

+ 13 - 8
dp/wifi3.0/dp_main.c

@@ -5737,7 +5737,6 @@ bool dp_check_pdev_exists(struct dp_soc *soc, struct dp_pdev *data)
 void dp_aggregate_vdev_stats(struct dp_vdev *vdev,
 void dp_aggregate_vdev_stats(struct dp_vdev *vdev,
 			     struct cdp_vdev_stats *vdev_stats)
 			     struct cdp_vdev_stats *vdev_stats)
 {
 {
-
 	if (!vdev || !vdev->pdev)
 	if (!vdev || !vdev->pdev)
 		return;
 		return;
 
 
@@ -5752,8 +5751,8 @@ void dp_aggregate_vdev_stats(struct dp_vdev *vdev,
 
 
 #if defined(FEATURE_PERPKT_INFO) && WDI_EVENT_ENABLE
 #if defined(FEATURE_PERPKT_INFO) && WDI_EVENT_ENABLE
 	dp_wdi_event_handler(WDI_EVENT_UPDATE_DP_STATS, vdev->pdev->soc,
 	dp_wdi_event_handler(WDI_EVENT_UPDATE_DP_STATS, vdev->pdev->soc,
-			     vdev_stats, vdev->vdev_id,
-			     UPDATE_VDEV_STATS, vdev->pdev->pdev_id);
+			    vdev_stats, vdev->vdev_id,
+			    UPDATE_VDEV_STATS, vdev->pdev->pdev_id);
 #endif
 #endif
 }
 }
 
 
@@ -8190,10 +8189,11 @@ dp_txrx_reset_peer_stats(struct cdp_soc_t *soc, uint8_t vdev_id,
  * @buf: buffer for vdev stats
  * @buf: buffer for vdev stats
  * @is_aggregate: are aggregate stats being collected
  * @is_aggregate: are aggregate stats being collected
  *
  *
- * Return: int
+ * Return: QDF_STATUS
  */
  */
-static int dp_txrx_get_vdev_stats(struct cdp_soc_t *soc_hdl, uint8_t vdev_id,
-				  void *buf, bool is_aggregate)
+static QDF_STATUS
+dp_txrx_get_vdev_stats(struct cdp_soc_t *soc_hdl, uint8_t vdev_id,
+		       void *buf, bool is_aggregate)
 {
 {
 	struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
 	struct dp_soc *soc = cdp_soc_t_to_dp_soc(soc_hdl);
 	struct cdp_vdev_stats *vdev_stats;
 	struct cdp_vdev_stats *vdev_stats;
@@ -8201,7 +8201,7 @@ static int dp_txrx_get_vdev_stats(struct cdp_soc_t *soc_hdl, uint8_t vdev_id,
 						     DP_MOD_ID_CDP);
 						     DP_MOD_ID_CDP);
 
 
 	if (!vdev)
 	if (!vdev)
-		return 1;
+		return QDF_STATUS_E_RESOURCES;
 
 
 	vdev_stats = (struct cdp_vdev_stats *)buf;
 	vdev_stats = (struct cdp_vdev_stats *)buf;
 
 
@@ -8212,7 +8212,7 @@ static int dp_txrx_get_vdev_stats(struct cdp_soc_t *soc_hdl, uint8_t vdev_id,
 	}
 	}
 
 
 	dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_CDP);
 	dp_vdev_unref_delete(soc, vdev, DP_MOD_ID_CDP);
-	return 0;
+	return QDF_STATUS_SUCCESS;
 }
 }
 
 
 /**
 /**
@@ -10908,6 +10908,11 @@ static struct cdp_host_stats_ops dp_ops_host_stats = {
 					dp_get_peer_extd_rate_link_stats,
 					dp_get_peer_extd_rate_link_stats,
 	.get_pdev_obss_stats = dp_get_obss_stats,
 	.get_pdev_obss_stats = dp_get_obss_stats,
 	.clear_pdev_obss_pd_stats = dp_clear_pdev_obss_pd_stats,
 	.clear_pdev_obss_pd_stats = dp_clear_pdev_obss_pd_stats,
+#ifdef CONFIG_MLO_SINGLE_DEV
+	.txrx_get_interface_stats  = dp_txrx_get_interface_stats,
+#else
+	.txrx_get_interface_stats  = dp_txrx_get_vdev_stats,
+#endif
 	/* TODO */
 	/* TODO */
 };
 };
 
 

+ 45 - 0
dp/wifi3.0/dp_peer.h

@@ -2330,6 +2330,51 @@ dp_peer_update_state(struct dp_soc *soc,
 		QDF_MAC_ADDR_REF(peer->mac_addr.raw));
 		QDF_MAC_ADDR_REF(peer->mac_addr.raw));
 }
 }
 
 
+/**
+ * dp_vdev_iterate_specific_peer_type() - API to iterate through vdev peer
+ * list based on type of peer (Legacy or MLD peer)
+ *
+ * @vdev: DP vdev context
+ * @func: function to be called for each peer
+ * @arg: argument need to be passed to func
+ * @mod_id: module_id
+ * @peer_type: type of peer - MLO Link Peer or Legacy Peer
+ *
+ * Return: void
+ */
+static inline void
+dp_vdev_iterate_specific_peer_type(struct dp_vdev *vdev,
+				   dp_peer_iter_func *func,
+				   void *arg, enum dp_mod_id mod_id,
+				   enum dp_peer_type peer_type)
+{
+	struct dp_peer *peer;
+	struct dp_peer *tmp_peer;
+	struct dp_soc *soc = NULL;
+
+	if (!vdev || !vdev->pdev || !vdev->pdev->soc)
+		return;
+
+	soc = vdev->pdev->soc;
+
+	qdf_spin_lock_bh(&vdev->peer_list_lock);
+	TAILQ_FOREACH_SAFE(peer, &vdev->peer_list,
+			   peer_list_elem,
+			   tmp_peer) {
+		if (dp_peer_get_ref(soc, peer, mod_id) ==
+					QDF_STATUS_SUCCESS) {
+			if ((peer_type == DP_PEER_TYPE_LEGACY &&
+			     (IS_DP_LEGACY_PEER(peer))) ||
+			    (peer_type == DP_PEER_TYPE_MLO_LINK &&
+			     (IS_MLO_DP_LINK_PEER(peer)))) {
+				(*func)(soc, peer, arg);
+			}
+			dp_peer_unref_delete(peer, mod_id);
+		}
+	}
+	qdf_spin_unlock_bh(&vdev->peer_list_lock);
+}
+
 #ifdef REO_SHARED_QREF_TABLE_EN
 #ifdef REO_SHARED_QREF_TABLE_EN
 void dp_peer_rx_reo_shared_qaddr_delete(struct dp_soc *soc,
 void dp_peer_rx_reo_shared_qaddr_delete(struct dp_soc *soc,
 					struct dp_peer *peer);
 					struct dp_peer *peer);

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

@@ -8661,6 +8661,11 @@ void dp_update_vdev_stats_on_peer_unmap(struct dp_vdev *vdev,
 	uint8_t link_id = 0;
 	uint8_t link_id = 0;
 	struct dp_pdev *pdev = vdev->pdev;
 	struct dp_pdev *pdev = vdev->pdev;
 
 
+	if (soc && soc->arch_ops.dp_get_vdev_stats_for_unmap_peer)
+		soc->arch_ops.dp_get_vdev_stats_for_unmap_peer(vdev,
+							       peer,
+							       &vdev_stats);
+
 	txrx_peer = dp_get_txrx_peer(peer);
 	txrx_peer = dp_get_txrx_peer(peer);
 	if (!txrx_peer)
 	if (!txrx_peer)
 		goto link_stats;
 		goto link_stats;

+ 18 - 0
dp/wifi3.0/dp_types.h

@@ -328,6 +328,18 @@ enum dp_mod_id {
 	DP_MOD_ID_MAX,
 	DP_MOD_ID_MAX,
 };
 };
 
 
+/**
+ * enum dp_peer_type - DP peer type
+ * @DP_PEER_TYPE_LEGACY:
+ * @DP_PEER_TYPE_MLO_LINK:
+ * @DP_PEER_TYPE_MLO:
+ */
+enum dp_peer_type {
+	DP_PEER_TYPE_LEGACY,
+	DP_PEER_TYPE_MLO_LINK,
+	DP_PEER_TYPE_MLO,
+};
+
 #define DP_PDEV_ITERATE_VDEV_LIST(_pdev, _vdev) \
 #define DP_PDEV_ITERATE_VDEV_LIST(_pdev, _vdev) \
 	TAILQ_FOREACH((_vdev), &(_pdev)->vdev_list, vdev_list_elem)
 	TAILQ_FOREACH((_vdev), &(_pdev)->vdev_list, vdev_list_elem)
 
 
@@ -2247,6 +2259,7 @@ enum dp_context_type {
  * @dp_tx_desc_pool_alloc: Allocate arch specific TX descriptor pool
  * @dp_tx_desc_pool_alloc: Allocate arch specific TX descriptor pool
  * @dp_tx_desc_pool_free: Free arch specific TX descriptor pool
  * @dp_tx_desc_pool_free: Free arch specific TX descriptor pool
  * @txrx_srng_init: Init txrx srng
  * @txrx_srng_init: Init txrx srng
+ * @dp_get_vdev_stats_for_unmap_peer: Get vdev stats pointer for unmap peer
  * @ppeds_handle_attached:
  * @ppeds_handle_attached:
  * @txrx_soc_ppeds_interrupt_stop:
  * @txrx_soc_ppeds_interrupt_stop:
  * @txrx_soc_ppeds_interrupt_start:
  * @txrx_soc_ppeds_interrupt_start:
@@ -2493,6 +2506,11 @@ struct dp_arch_ops {
 
 
 	QDF_STATUS (*txrx_srng_init)(struct dp_soc *soc, struct dp_srng *srng,
 	QDF_STATUS (*txrx_srng_init)(struct dp_soc *soc, struct dp_srng *srng,
 				     int ring_type, int ring_num, int mac_id);
 				     int ring_type, int ring_num, int mac_id);
+
+	void (*dp_get_vdev_stats_for_unmap_peer)(
+					struct dp_vdev *vdev,
+					struct dp_peer *peer,
+					struct cdp_vdev_stats **vdev_stats);
 #ifdef WLAN_SUPPORT_PPEDS
 #ifdef WLAN_SUPPORT_PPEDS
 	void (*txrx_soc_ppeds_interrupt_stop)(struct dp_soc *soc);
 	void (*txrx_soc_ppeds_interrupt_stop)(struct dp_soc *soc);
 	void (*txrx_soc_ppeds_interrupt_start)(struct dp_soc *soc);
 	void (*txrx_soc_ppeds_interrupt_start)(struct dp_soc *soc);

+ 9 - 0
dp/wifi3.0/li/dp_li.c

@@ -635,6 +635,13 @@ static uint8_t dp_get_hw_link_id_li(struct dp_pdev *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
+static void dp_get_vdev_stats_for_unmap_peer_li(
+					struct dp_vdev *vdev,
+					struct dp_peer *peer,
+					struct cdp_vdev_stats **vdev_stats)
+{
+}
+
 void dp_initialize_arch_ops_li(struct dp_arch_ops *arch_ops)
 void dp_initialize_arch_ops_li(struct dp_arch_ops *arch_ops)
 {
 {
 #ifndef QCA_HOST_MODE_WIFI_DISABLED
 #ifndef QCA_HOST_MODE_WIFI_DISABLED
@@ -710,6 +717,8 @@ void dp_initialize_arch_ops_li(struct dp_arch_ops *arch_ops)
 	arch_ops->txrx_get_vdev_mcast_param = dp_txrx_get_vdev_mcast_param_li;
 	arch_ops->txrx_get_vdev_mcast_param = dp_txrx_get_vdev_mcast_param_li;
 	arch_ops->get_hw_link_id = dp_get_hw_link_id_li;
 	arch_ops->get_hw_link_id = dp_get_hw_link_id_li;
 	arch_ops->txrx_srng_init = dp_srng_init_li;
 	arch_ops->txrx_srng_init = dp_srng_init_li;
+	arch_ops->dp_get_vdev_stats_for_unmap_peer =
+					dp_get_vdev_stats_for_unmap_peer_li;
 #if defined(DP_POWER_SAVE) || defined(FEATURE_RUNTIME_PM)
 #if defined(DP_POWER_SAVE) || defined(FEATURE_RUNTIME_PM)
 	arch_ops->dp_update_ring_hptp = dp_update_ring_hptp;
 	arch_ops->dp_update_ring_hptp = dp_update_ring_hptp;
 #endif
 #endif