Procházet zdrojové kódy

qca-wifi: FR 61891 Per Client Link Quality Metrics

Adding new metrics to existing RDK Plume statistics

PHY Metrics Tx

1. phy_rate_actual_su - Single User Rate
2. phy_rate_actual_mu - Multi User Rate
3. ofdma_usage -  OFDMA Usage count
4. mu_mimo_usage - MU MIMO Usage count
5. bw_usage_avg - Average BW
6. bw_usage_max - Max BW Ratio to total PPDUs
7. pkt_error_rate(mpdu) - MPDU packet error rate
8. num_ppdus - Number of PPDUs
9. num_bytes - Number of PPDU bytes

PHY Metrics Rx

1. phy_rate_actual_su - Single User Rate
2. phy_rate_actual_mu - Multi User Rate
3. ofdma_usage -  OFDMA Usage count
4. mu_mimo_usage - MU MIMO Usage count
5. bw_usage_avg - Average BW
6. bw_usage_max - Max BW Ratio to total PPDUs
7. pkt_error_rate(mpdu) - MPDU packet error rate
8. num_ppdus - Number of PPDUs
9. num_bytes - Number of PPDU bytes

RSSI
1. su_rssi - Single user Rx RSSI
2. ack_rssi - ACK RSSI

CRs-Fixed: 2740231
Change-Id: Ie62157195d87f61640aa7bc65b2f02f629ff195d
phadiman před 5 roky
rodič
revize
8ad7491fdd
3 změnil soubory, kde provedl 424 přidání a 49 odebrání
  1. 38 0
      dp/inc/dp_rate_stats.h
  2. 82 0
      dp/inc/dp_rate_stats_pub.h
  3. 304 49
      dp/src/dp_rate_stats.c

+ 38 - 0
dp/inc/dp_rate_stats.h

@@ -43,6 +43,20 @@
 
 struct cdp_pdev;
 
+/**
+ * enum dp_ppdu_type - enum for ppdu_type
+ * @DP_PPDU_TYPE_SU: single user PPDU
+ * @DP_PPDU_TYPE_MU_MIMO: multi user mimo ppdu
+ * @DP_PPDU_TYPE_MU_OFDMA: multi user ofdma ppdu
+ * @DP_PPDU_TYPE_MU_OFDMA_MIMO: multi user mimo/ofdma ppdu
+ */
+enum dp_ppdu_type {
+	DP_PPDU_TYPE_SU,
+	DP_PPDU_TYPE_MU_MIMO,
+	DP_PPDU_TYPE_MU_OFDMA,
+	DP_PPDU_TYPE_MU_OFDMA_MIMO,
+};
+
 /**
  * struct wlan_peer_tx_rate_stats - peer tx rate statistics
  * @stats: array containing tx rate stats
@@ -71,10 +85,32 @@ struct wlan_peer_rx_rate_stats {
 	qdf_spinlock_t lock;
 };
 
+/**
+ * struct wlan_peer_rx_link_stats - Peer Rx Link statistics
+ * @stats: array containing rx rate stats
+ * @lock: lock protecting list
+ */
+struct wlan_peer_rx_link_stats {
+	struct wlan_rx_link_stats stats;
+	qdf_spinlock_t lock;
+};
+
+/**
+ * struct wlan_peer_tx_link_stats - Peer Tx Link statistics
+ * @stats: array containing rx rate stats
+ * @lock: lock protecting list
+ */
+struct wlan_peer_tx_link_stats {
+	struct wlan_tx_link_stats stats;
+	qdf_spinlock_t lock;
+};
+
 /**
  * struct wlan_peer_rate_stats - Peer rate statistics ctx
  * @tx: tx rate statistics
  * @rx: rx rate statistics
+ * @tx_link_stats: tx link quality stats
+ * @rx_link_stats: rx link quality stats
  * @mac_addr: peer MAC address
  * @peer_cookie: cookie for unique session of peer
  * @pdev_id: id of dp pdev
@@ -82,6 +118,8 @@ struct wlan_peer_rx_rate_stats {
 struct wlan_peer_rate_stats_ctx {
 	struct wlan_peer_tx_rate_stats tx;
 	struct wlan_peer_rx_rate_stats rx;
+	struct wlan_peer_rx_link_stats rx_link_stats;
+	struct wlan_peer_tx_link_stats tx_link_stats;
 	uint8_t mac_addr[WLAN_MAC_ADDR_LEN];
 	uint64_t peer_cookie;
 	uint8_t pdev_id;

+ 82 - 0
dp/inc/dp_rate_stats_pub.h

@@ -84,6 +84,8 @@ enum wlan_peer_rate_stats_cmd {
 	DP_PEER_RX_RATE_STATS,
 	DP_PEER_TX_RATE_STATS,
 	DP_PEER_SOJOURN_STATS,
+	DP_PEER_RX_LINK_STATS,
+	DP_PEER_TX_LINK_STATS,
 };
 
 /** struct wlan_tx_rate_stats - Tx packet rate info
@@ -146,6 +148,86 @@ struct wlan_tx_sojourn_stats {
 	qdf_ewma_tx_lag avg_sojourn_msdu[WLAN_DATA_TID_MAX];
 };
 
+#define BW_USAGE_MAX_SIZE 4
+
+/**
+ * struct wlan_peer_bw_stats - per link bw related stats
+ * @usage_total - sum of total BW (20, 40, 80, 160)
+ * @usage_avg - @usage_total / number of PPDUs (avg BW)
+ * @usage_counter - each BW usage counter
+ * @usage_max - number of pkts in max BW mode
+ */
+struct wlan_peer_bw_stats {
+	uint32_t usage_total;
+	uint32_t usage_counter[BW_USAGE_MAX_SIZE];
+	uint8_t usage_avg;
+	uint8_t usage_max;
+};
+
+/**
+ * struct wlan_rx_link_stats - Peer Rx link statistics
+ * @num_ppdus - number of ppdus per user
+ * @bytes - number of bytes per user
+ * @phy_rate_lpf_avg_su - SU packet LPF averaged rx rate
+ * @phy_rate_actual_su - SU packet rounded average rx rate
+ * @phy_rate_lpf_avg_mu - MU packet LPF averaged rx rate
+ * @phy_rate_actual_mu - MUpacket rounded average rx rate
+ * @ofdma_usage - number of packet in OFDMA
+ * @mu_mimo_usage - number of pakcets in MU MIMO
+ * @bw - average BW and max BW related structure
+ * @su_rssi - single user RSSI
+ * @mpdu_retries - number of retried MPDUs
+ * @pkt_error_rate - average packet error rate
+ * @num_mpdus - total number of mpdus
+ */
+struct wlan_rx_link_stats {
+	uint64_t bytes;
+	uint32_t num_ppdus;
+	uint32_t phy_rate_lpf_avg_su;
+	uint32_t phy_rate_actual_su;
+	uint32_t phy_rate_lpf_avg_mu;
+	uint32_t phy_rate_actual_mu;
+	uint32_t ofdma_usage;
+	uint32_t mu_mimo_usage;
+	struct wlan_peer_bw_stats bw;
+	qdf_ewma_rx_rssi su_rssi;
+	uint32_t mpdu_retries;
+	uint32_t num_mpdus;
+	uint8_t pkt_error_rate;
+};
+
+/**
+ * struct wlan_tx_link_stats - Peer tx link statistics
+ * @num_ppdus - number of ppdus per user
+ * @bytes - number of bytes per user
+ * @phy_rate_lpf_avg_su - SU packet LPF averaged tx rate
+ * @phy_rate_actual_su - SU packet rounded average tx rate
+ * @phy_rate_lpf_avg_mu - MU packet LPF averaged tx rate
+ * @phy_rate_actual_mu - MUpacket rounded average tx rate
+ * @ofdma_usage - number of packet in OFDMA
+ * @mu_mimo_usage - number of pakcets in MU MIMO
+ * @bw - average BW and max BW related structure
+ * @ack_rssi - averaged ACK rssi
+ * @mpdu_failed - number of failed MPDUs
+ * @mpdu_success - number of success MPDUs
+ * @pkt_error_rate - average packet error rate
+ */
+struct wlan_tx_link_stats {
+	uint64_t bytes;
+	uint32_t num_ppdus;
+	uint32_t phy_rate_lpf_avg_su;
+	uint32_t phy_rate_actual_su;
+	uint32_t phy_rate_lpf_avg_mu;
+	uint32_t phy_rate_actual_mu;
+	uint32_t ofdma_usage;
+	uint32_t mu_mimo_usage;
+	struct wlan_peer_bw_stats bw;
+	qdf_ewma_rx_rssi ack_rssi;
+	uint32_t mpdu_failed;
+	uint32_t mpdu_success;
+	uint8_t pkt_error_rate;
+};
+
 /**
  * struct wlan_peer_rate_stats_intf - Interface structure to
  * flush stats to user spave entity

+ 304 - 49
dp/src/dp_rate_stats.c

@@ -26,6 +26,14 @@
 
 #ifdef QCA_SUPPORT_RDK_STATS
 
+/* Calculate actual BW from BW ENUM as in
+ * x = 0 for 20MHz
+ * x = 1 for 40MHz
+ * x = 2 for 80MHz
+ * x = 3 for 160MHz
+ */
+#define GET_BW_FROM_BW_ENUM(x) ((20) * (1 << x))
+
 static void
 wlan_peer_read_ewma_avg_rssi(struct wlan_rx_rate_stats *rx_stats)
 {
@@ -134,15 +142,114 @@ wlan_peer_flush_tx_rate_stats(struct wlan_soc_rate_stats_ctx *soc_stats_ctx,
 		tx_stats->stats[idx].rix = INVALID_CACHE_IDX;
 }
 
+static void
+wlan_peer_flush_tx_link_stats(struct wlan_soc_rate_stats_ctx *soc_stats_ctx,
+			      struct wlan_peer_rate_stats_ctx *stats_ctx)
+{
+	struct wlan_peer_rate_stats_intf buf;
+	struct wlan_peer_tx_link_stats *tx_stats;
+	uint8_t bw_max_idx;
+
+	if (!soc_stats_ctx) {
+		qdf_info("soc stats context is NULL\n");
+		return;
+	}
+
+	tx_stats = &stats_ctx->tx_link_stats;
+
+	buf.stats = (struct wlan_tx_link_stats *)&tx_stats->stats;
+	buf.buf_len = sizeof(struct wlan_peer_tx_link_stats);
+	buf.stats_type = DP_PEER_TX_LINK_STATS;
+	buf.cookie = stats_ctx->peer_cookie;
+
+	tx_stats->stats.ack_rssi.internal =
+			qdf_ewma_rx_rssi_read(&tx_stats->stats.ack_rssi);
+	tx_stats->stats.phy_rate_actual_su =
+			dp_ath_rate_out(tx_stats->stats.phy_rate_lpf_avg_su);
+	tx_stats->stats.phy_rate_actual_mu =
+			dp_ath_rate_out(tx_stats->stats.phy_rate_lpf_avg_mu);
+
+	if (tx_stats->stats.num_ppdus) {
+		tx_stats->stats.bw.usage_avg = tx_stats->stats.bw.usage_total /
+					       tx_stats->stats.num_ppdus;
+		bw_max_idx = tx_stats->stats.bw.usage_max;
+		tx_stats->stats.bw.usage_max =
+			(tx_stats->stats.bw.usage_counter[bw_max_idx] * 100) /
+			 tx_stats->stats.num_ppdus;
+	}
+
+	if (tx_stats->stats.mpdu_success)
+		tx_stats->stats.pkt_error_rate =
+			(tx_stats->stats.mpdu_failed * 100) /
+			 tx_stats->stats.mpdu_success;
+
+	qdf_mem_copy(buf.peer_mac, stats_ctx->mac_addr, WLAN_MAC_ADDR_LEN);
+	cdp_peer_flush_rate_stats(soc_stats_ctx->soc,
+				  stats_ctx->pdev_id, &buf);
+
+	qdf_mem_zero(&tx_stats->stats, sizeof(struct wlan_tx_link_stats));
+}
+
+static void
+wlan_peer_flush_rx_link_stats(struct wlan_soc_rate_stats_ctx *soc_stats_ctx,
+			      struct wlan_peer_rate_stats_ctx *stats_ctx)
+{
+	struct wlan_peer_rate_stats_intf buf;
+	struct wlan_peer_rx_link_stats *rx_stats;
+	uint8_t bw_max_idx;
+
+	if (!soc_stats_ctx) {
+		qdf_info("soc stats context is NULL\n");
+		return;
+	}
+
+	rx_stats = &stats_ctx->rx_link_stats;
+
+	buf.stats = (struct wlan_rx_link_stats *)&rx_stats->stats;
+	buf.buf_len = sizeof(struct wlan_peer_rx_link_stats);
+	buf.stats_type = DP_PEER_RX_LINK_STATS;
+	buf.cookie = stats_ctx->peer_cookie;
+
+	rx_stats->stats.su_rssi.internal =
+			qdf_ewma_rx_rssi_read(&rx_stats->stats.su_rssi);
+	rx_stats->stats.phy_rate_actual_su =
+			dp_ath_rate_out(rx_stats->stats.phy_rate_lpf_avg_su);
+	rx_stats->stats.phy_rate_actual_mu =
+			dp_ath_rate_out(rx_stats->stats.phy_rate_lpf_avg_mu);
+	if (rx_stats->stats.num_ppdus) {
+		rx_stats->stats.bw.usage_avg = rx_stats->stats.bw.usage_total /
+					       rx_stats->stats.num_ppdus;
+		bw_max_idx = rx_stats->stats.bw.usage_max;
+		rx_stats->stats.bw.usage_max =
+			(rx_stats->stats.bw.usage_counter[bw_max_idx] * 100) /
+			 rx_stats->stats.num_ppdus;
+	}
+
+	if (rx_stats->stats.num_mpdus)
+		rx_stats->stats.pkt_error_rate =
+					(rx_stats->stats.mpdu_retries * 100) /
+					 rx_stats->stats.num_mpdus;
+
+	qdf_mem_copy(buf.peer_mac, stats_ctx->mac_addr, WLAN_MAC_ADDR_LEN);
+	cdp_peer_flush_rate_stats(soc_stats_ctx->soc,
+				  stats_ctx->pdev_id, &buf);
+
+	qdf_mem_zero(&rx_stats->stats, sizeof(struct wlan_rx_link_stats));
+}
+
 static void
 wlan_peer_flush_rate_stats(struct wlan_soc_rate_stats_ctx *soc_stats_ctx,
 			   struct wlan_peer_rate_stats_ctx *stats_ctx)
 {
 	struct wlan_peer_tx_rate_stats *tx_stats;
 	struct wlan_peer_rx_rate_stats *rx_stats;
+	struct wlan_peer_tx_link_stats *tx_link_stats;
+	struct wlan_peer_rx_link_stats *rx_link_stats;
 
 	tx_stats = &stats_ctx->tx;
 	rx_stats = &stats_ctx->rx;
+	tx_link_stats = &stats_ctx->tx_link_stats;
+	rx_link_stats = &stats_ctx->rx_link_stats;
 
 	RATE_STATS_LOCK_ACQUIRE(&tx_stats->lock);
 	wlan_peer_flush_tx_rate_stats(soc_stats_ctx, stats_ctx);
@@ -151,6 +258,14 @@ wlan_peer_flush_rate_stats(struct wlan_soc_rate_stats_ctx *soc_stats_ctx,
 	RATE_STATS_LOCK_ACQUIRE(&rx_stats->lock);
 	wlan_peer_flush_rx_rate_stats(soc_stats_ctx, stats_ctx);
 	RATE_STATS_LOCK_RELEASE(&rx_stats->lock);
+
+	RATE_STATS_LOCK_ACQUIRE(&tx_link_stats->lock);
+	wlan_peer_flush_tx_link_stats(soc_stats_ctx, stats_ctx);
+	RATE_STATS_LOCK_RELEASE(&tx_link_stats->lock);
+
+	RATE_STATS_LOCK_ACQUIRE(&rx_link_stats->lock);
+	wlan_peer_flush_rx_link_stats(soc_stats_ctx, stats_ctx);
+	RATE_STATS_LOCK_RELEASE(&rx_link_stats->lock);
 }
 
 void wlan_peer_rate_stats_flush_req(void *ctx, enum WDI_EVENT event,
@@ -196,6 +311,133 @@ __wlan_peer_update_rx_rate_stats(struct wlan_rx_rate_stats *__rx_stats,
 	}
 }
 
+static void
+wlan_peer_update_tx_link_stats(struct wlan_soc_rate_stats_ctx *soc_stats_ctx,
+			       struct cdp_tx_completion_ppdu *cdp_tx_ppdu)
+{
+	struct cdp_tx_completion_ppdu_user *ppdu_user;
+	struct wlan_peer_rate_stats_ctx *stats_ctx;
+	struct wlan_tx_link_stats *tx_stats;
+	uint8_t user_idx;
+
+	for (user_idx = 0; user_idx < cdp_tx_ppdu->num_users; user_idx++) {
+		ppdu_user = &cdp_tx_ppdu->user[user_idx];
+
+		stats_ctx = (struct wlan_peer_rate_stats_ctx *)
+			ppdu_user->cookie;
+
+		if (qdf_unlikely(!stats_ctx)) {
+			qdf_warn("peer rate stats ctx is NULL, return");
+			qdf_warn("peer_mac:  " QDF_MAC_ADDR_STR,
+				 QDF_MAC_ADDR_ARRAY(stats_ctx->mac_addr));
+			continue;
+		}
+
+		tx_stats = &stats_ctx->tx_link_stats.stats;
+
+		RATE_STATS_LOCK_ACQUIRE(&stats_ctx->tx_link_stats.lock);
+
+		tx_stats->num_ppdus += ppdu_user->long_retries + 1;
+		tx_stats->bytes += ppdu_user->success_bytes;
+		tx_stats->mpdu_failed += ppdu_user->mpdu_failed;
+		tx_stats->mpdu_success += ppdu_user->mpdu_success;
+
+		if (ppdu_user->ppdu_type == DP_PPDU_TYPE_SU) {
+			tx_stats->phy_rate_lpf_avg_su =
+				dp_ath_rate_lpf(tx_stats->phy_rate_lpf_avg_su,
+						ppdu_user->tx_ratekbps);
+		} else if (ppdu_user->ppdu_type == DP_PPDU_TYPE_MU_OFDMA ||
+			   ppdu_user->ppdu_type == DP_PPDU_TYPE_MU_MIMO) {
+			tx_stats->phy_rate_lpf_avg_mu =
+				dp_ath_rate_lpf(tx_stats->phy_rate_lpf_avg_mu,
+						ppdu_user->tx_ratekbps);
+
+			if (ppdu_user->ppdu_type == DP_PPDU_TYPE_MU_OFDMA)
+				tx_stats->ofdma_usage++;
+
+			if (ppdu_user->ppdu_type == DP_PPDU_TYPE_MU_MIMO)
+				tx_stats->mu_mimo_usage++;
+		}
+
+		tx_stats->bw.usage_total += GET_BW_FROM_BW_ENUM(ppdu_user->bw);
+
+		if (ppdu_user->bw < BW_USAGE_MAX_SIZE) {
+			if (tx_stats->bw.usage_max < ppdu_user->bw)
+				tx_stats->bw.usage_max = ppdu_user->bw;
+			tx_stats->bw.usage_counter[ppdu_user->bw]++;
+		}
+
+		if (ppdu_user->ack_rssi_valid)
+			qdf_ewma_rx_rssi_add(&tx_stats->ack_rssi,
+					     ppdu_user->usr_ack_rssi);
+
+		RATE_STATS_LOCK_RELEASE(&stats_ctx->tx_link_stats.lock);
+	}
+}
+
+static void
+wlan_peer_update_rx_link_stats(struct wlan_soc_rate_stats_ctx *soc_stats_ctx,
+			       struct cdp_rx_indication_ppdu *cdp_rx_ppdu)
+{
+	struct cdp_rx_stats_ppdu_user *ppdu_user;
+	struct wlan_peer_rate_stats_ctx *stats_ctx;
+	struct wlan_rx_link_stats *rx_stats;
+	uint8_t user_idx;
+
+	for (user_idx = 0; user_idx < cdp_rx_ppdu->num_users; user_idx++) {
+		ppdu_user = &cdp_rx_ppdu->user[user_idx];
+
+		stats_ctx = (struct wlan_peer_rate_stats_ctx *)
+			ppdu_user->cookie;
+
+		if (qdf_unlikely(!stats_ctx)) {
+			qdf_warn("peer rate stats ctx is NULL, return");
+			qdf_warn("peer_mac:  " QDF_MAC_ADDR_STR,
+				 QDF_MAC_ADDR_ARRAY(stats_ctx->mac_addr));
+			continue;
+		}
+
+		rx_stats = &stats_ctx->rx_link_stats.stats;
+
+		RATE_STATS_LOCK_ACQUIRE(&stats_ctx->rx_link_stats.lock);
+
+		rx_stats->num_ppdus++;
+		rx_stats->bytes += cdp_rx_ppdu->num_bytes;
+		rx_stats->mpdu_retries += ppdu_user->retries;
+		rx_stats->num_mpdus += ppdu_user->mpdu_cnt_fcs_ok;
+
+		if (cdp_rx_ppdu->u.ppdu_type == DP_PPDU_TYPE_SU) {
+			rx_stats->phy_rate_lpf_avg_su =
+				dp_ath_rate_lpf(rx_stats->phy_rate_lpf_avg_su,
+						cdp_rx_ppdu->rx_ratekbps);
+			qdf_ewma_rx_rssi_add(&rx_stats->su_rssi,
+					     cdp_rx_ppdu->rssi);
+		} else if (cdp_rx_ppdu->u.ppdu_type == DP_PPDU_TYPE_MU_OFDMA ||
+			   cdp_rx_ppdu->u.ppdu_type == DP_PPDU_TYPE_MU_MIMO) {
+			rx_stats->phy_rate_lpf_avg_mu =
+				dp_ath_rate_lpf(rx_stats->phy_rate_lpf_avg_mu,
+						cdp_rx_ppdu->rx_ratekbps);
+
+			if (cdp_rx_ppdu->u.ppdu_type == DP_PPDU_TYPE_MU_OFDMA)
+				rx_stats->ofdma_usage++;
+
+			if (cdp_rx_ppdu->u.ppdu_type == DP_PPDU_TYPE_MU_MIMO)
+				rx_stats->mu_mimo_usage++;
+		}
+
+		rx_stats->bw.usage_total +=
+					GET_BW_FROM_BW_ENUM(cdp_rx_ppdu->u.bw);
+
+		if (cdp_rx_ppdu->u.bw < BW_USAGE_MAX_SIZE) {
+			if (rx_stats->bw.usage_max < cdp_rx_ppdu->u.bw)
+				rx_stats->bw.usage_max = cdp_rx_ppdu->u.bw;
+			rx_stats->bw.usage_counter[cdp_rx_ppdu->u.bw]++;
+		}
+
+		RATE_STATS_LOCK_RELEASE(&stats_ctx->rx_link_stats.lock);
+	}
+}
+
 static void
 wlan_peer_update_rx_rate_stats(struct wlan_soc_rate_stats_ctx *soc_stats_ctx,
 			       struct cdp_rx_indication_ppdu *cdp_rx_ppdu)
@@ -203,65 +445,72 @@ wlan_peer_update_rx_rate_stats(struct wlan_soc_rate_stats_ctx *soc_stats_ctx,
 	struct wlan_peer_rate_stats_ctx *stats_ctx;
 	struct wlan_peer_rx_rate_stats *rx_stats;
 	struct wlan_rx_rate_stats *__rx_stats;
+	struct cdp_rx_stats_ppdu_user *ppdu_user;
 	uint8_t cache_idx;
+	uint8_t user_idx;
 	bool idx_match = false;
 
-	stats_ctx = (struct wlan_peer_rate_stats_ctx *)cdp_rx_ppdu->cookie;
-
-	if (qdf_unlikely(!stats_ctx)) {
-		qdf_warn("peer rate stats ctx is NULL, return");
-		qdf_warn("peer_mac:  " QDF_MAC_ADDR_STR,
-			 QDF_MAC_ADDR_ARRAY(cdp_rx_ppdu->mac_addr));
-		return;
-	}
+	for (user_idx = 0; user_idx < cdp_rx_ppdu->num_users; user_idx++) {
+		ppdu_user = &cdp_rx_ppdu->user[user_idx];
+		stats_ctx = (struct wlan_peer_rate_stats_ctx *)
+			     ppdu_user->cookie;
 
-	rx_stats = &stats_ctx->rx;
+		if (qdf_unlikely(!stats_ctx)) {
+			qdf_warn("peer rate stats ctx is NULL, return");
+			qdf_warn("peer_mac:  " QDF_MAC_ADDR_STR,
+				 QDF_MAC_ADDR_ARRAY(cdp_rx_ppdu->mac_addr));
+			continue;
+		}
 
-	if (qdf_unlikely(!cdp_rx_ppdu->rx_ratekbps ||
-			 cdp_rx_ppdu->rix > DP_RATE_TABLE_SIZE)) {
-		return;
-	}
+		rx_stats = &stats_ctx->rx;
 
-	RATE_STATS_LOCK_ACQUIRE(&rx_stats->lock);
-	if (qdf_likely(rx_stats->cur_rix == cdp_rx_ppdu->rix)) {
-		__rx_stats = &rx_stats->stats[rx_stats->cur_cache_idx];
-		__wlan_peer_update_rx_rate_stats(__rx_stats,
-						 cdp_rx_ppdu);
+		if (qdf_unlikely(!cdp_rx_ppdu->rx_ratekbps ||
+				 cdp_rx_ppdu->rix > DP_RATE_TABLE_SIZE)) {
+			return;
+		}
 
-		soc_stats_ctx->rxs_last_idx_cache_hit++;
-		goto done;
-	}
+		RATE_STATS_LOCK_ACQUIRE(&rx_stats->lock);
+		if (qdf_likely(rx_stats->cur_rix == cdp_rx_ppdu->rix)) {
+			__rx_stats = &rx_stats->stats[rx_stats->cur_cache_idx];
+			__wlan_peer_update_rx_rate_stats(__rx_stats,
+							 cdp_rx_ppdu);
+			soc_stats_ctx->rxs_last_idx_cache_hit++;
+			RATE_STATS_LOCK_RELEASE(&rx_stats->lock);
+			continue;
+		}
 
-	/* check if cache is available */
-	for (cache_idx = 0; cache_idx < WLANSTATS_CACHE_SIZE; cache_idx++) {
-		__rx_stats = &rx_stats->stats[cache_idx];
-		if ((__rx_stats->rix == INVALID_CACHE_IDX) ||
-		    (__rx_stats->rix == cdp_rx_ppdu->rix)) {
-			idx_match = true;
-			break;
+		/* check if cache is available */
+		for (cache_idx = 0; cache_idx < WLANSTATS_CACHE_SIZE; cache_idx++) {
+			__rx_stats = &rx_stats->stats[cache_idx];
+			if ((__rx_stats->rix == INVALID_CACHE_IDX) ||
+			    (__rx_stats->rix == cdp_rx_ppdu->rix)) {
+				idx_match = true;
+				break;
+			}
 		}
+		/* if index matches or found empty index, update stats to that
+		 * cache index else flush cache and update stats to cache index
+		 * zero
+		 */
+		if (idx_match) {
+			__wlan_peer_update_rx_rate_stats(__rx_stats,
+							 cdp_rx_ppdu);
+			rx_stats->cur_rix = cdp_rx_ppdu->rix;
+			rx_stats->cur_cache_idx = cache_idx;
+			soc_stats_ctx->rxs_cache_hit++;
+			RATE_STATS_LOCK_RELEASE(&rx_stats->lock);
+			continue;
+		} else {
+			wlan_peer_flush_rx_rate_stats(soc_stats_ctx, stats_ctx);
+			__rx_stats = &rx_stats->stats[0];
+			__wlan_peer_update_rx_rate_stats(__rx_stats,
+							 cdp_rx_ppdu);
+			rx_stats->cur_rix = cdp_rx_ppdu->rix;
+			rx_stats->cur_cache_idx = 0;
+			soc_stats_ctx->rxs_cache_miss++;
+		}
+		RATE_STATS_LOCK_RELEASE(&rx_stats->lock);
 	}
-	/* if index matches or found empty index, update stats to that
-	 * cache index else flush cache and update stats to cache index zero
-	 */
-	if (idx_match) {
-		__wlan_peer_update_rx_rate_stats(__rx_stats,
-						 cdp_rx_ppdu);
-		rx_stats->cur_rix = cdp_rx_ppdu->rix;
-		rx_stats->cur_cache_idx = cache_idx;
-		soc_stats_ctx->rxs_cache_hit++;
-		goto done;
-	} else {
-		wlan_peer_flush_rx_rate_stats(soc_stats_ctx, stats_ctx);
-		__rx_stats = &rx_stats->stats[0];
-		__wlan_peer_update_rx_rate_stats(__rx_stats,
-						 cdp_rx_ppdu);
-		rx_stats->cur_rix = cdp_rx_ppdu->rix;
-		rx_stats->cur_cache_idx = 0;
-		soc_stats_ctx->rxs_cache_miss++;
-	}
-done:
-	RATE_STATS_LOCK_RELEASE(&rx_stats->lock);
 }
 
 static inline void
@@ -424,12 +673,14 @@ void wlan_peer_update_rate_stats(void *ctx,
 		cdp_tx_ppdu = (struct cdp_tx_completion_ppdu *)
 					qdf_nbuf_data(nbuf);
 		wlan_peer_update_tx_rate_stats(soc_stats_ctx, cdp_tx_ppdu);
+		wlan_peer_update_tx_link_stats(soc_stats_ctx, cdp_tx_ppdu);
 		qdf_nbuf_free(nbuf);
 		break;
 	case WDI_EVENT_RX_PPDU_DESC:
 		cdp_rx_ppdu = (struct cdp_rx_indication_ppdu *)
 					qdf_nbuf_data(nbuf);
 		wlan_peer_update_rx_rate_stats(soc_stats_ctx, cdp_rx_ppdu);
+		wlan_peer_update_rx_link_stats(soc_stats_ctx, cdp_rx_ppdu);
 		qdf_nbuf_free(nbuf);
 		break;
 	case WDI_EVENT_TX_SOJOURN_STAT:
@@ -462,6 +713,8 @@ void wlan_peer_create_event_handler(void *pdev, enum WDI_EVENT event,
 	qdf_mem_zero(stats, sizeof(*stats));
 	RATE_STATS_LOCK_CREATE(&stats->tx.lock);
 	RATE_STATS_LOCK_CREATE(&stats->rx.lock);
+	RATE_STATS_LOCK_CREATE(&stats->tx_link_stats.lock);
+	RATE_STATS_LOCK_CREATE(&stats->rx_link_stats.lock);
 	qdf_mem_copy(stats->mac_addr, peer_info->mac_addr, QDF_MAC_ADDR_SIZE);
 	stats->peer_cookie = peer_info->cookie;
 	stats->pdev_id = peer_info->pdev_id;
@@ -488,6 +741,8 @@ void wlan_peer_destroy_event_handler(void *ctx, enum WDI_EVENT event,
 		wlan_peer_flush_rate_stats(ctx, stats);
 		RATE_STATS_LOCK_DESTROY(&stats->tx.lock);
 		RATE_STATS_LOCK_DESTROY(&stats->rx.lock);
+		RATE_STATS_LOCK_DESTROY(&stats->tx_link_stats.lock);
+		RATE_STATS_LOCK_DESTROY(&stats->rx_link_stats.lock);
 		qdf_mem_free(stats);
 		qdf_info("DEBUG DEiniitialized rate stats");
 	}