qcacmn: Restrict the tx descriptors for low mem config

For low mem config, use user configured max tx descriptors to limit the
allocation of software tx descriptors.

Use dp_txrx_pflow_update_pdev_params function to print stats and to update
pdev param.

Change-Id: I8fa6f0bb8841de68e8dc205ffcb0fde264f1b0e0
CRs-Fixed: 2414452
diff --git a/dp/wifi3.0/dp_internal.h b/dp/wifi3.0/dp_internal.h
index d399ecb..05d461b 100644
--- a/dp/wifi3.0/dp_internal.h
+++ b/dp/wifi3.0/dp_internal.h
@@ -935,4 +935,21 @@
 }
 #endif
 
+#ifdef CONFIG_WIN
+/**
+ * dp_pdev_print_delay_stats(): Print pdev level delay stats
+ * @pdev: DP_PDEV handle
+ *
+ * Return:void
+ */
+void dp_pdev_print_delay_stats(struct dp_pdev *pdev);
+
+/**
+ * dp_pdev_print_tid_stats(): Print pdev level tid stats
+ * @pdev: DP_PDEV handle
+ *
+ * Return:void
+ */
+void dp_pdev_print_tid_stats(struct dp_pdev *pdev);
+#endif /* CONFIG_WIN */
 #endif /* #ifndef _DP_INTERNAL_H_ */
diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c
index 04ac34d..74a630e 100644
--- a/dp/wifi3.0/dp_main.c
+++ b/dp/wifi3.0/dp_main.c
@@ -3424,6 +3424,8 @@
 			     &dp_iterate_update_peer_list);
 	qdf_event_create(&pdev->fw_peer_stats_event);
 
+	pdev->num_tx_allowed = wlan_cfg_get_num_tx_desc(soc->wlan_cfg_ctx);
+
 	return (struct cdp_pdev *)pdev;
 
 fail1:
@@ -9339,6 +9341,78 @@
 	return value;
 }
 
+#ifdef CONFIG_WIN
+/**
+ * dp_tx_flow_ctrl_configure_pdev() - Configure flow control params
+ * @pdev_hdl: datapath pdev handle
+ * @param: ol ath params
+ * @value: value of the flag
+ * @buff: Buffer to be passed
+ *
+ * Implemented this function same as legacy function. In legacy code, single
+ * function is used to display stats and update pdev params.
+ *
+ * Return: 0 for success. nonzero for failure.
+ */
+static uint32_t dp_tx_flow_ctrl_configure_pdev(void *pdev_handle,
+					       enum _ol_ath_param_t param,
+					       uint32_t value, void *buff)
+{
+	struct dp_soc *soc = NULL;
+	struct dp_pdev *pdev = (struct dp_pdev *)pdev_handle;
+
+	if (qdf_unlikely(!pdev))
+		return 1;
+
+	soc = pdev->soc;
+	if (!soc)
+		return 1;
+
+	switch (param) {
+	case OL_ATH_PARAM_VIDEO_DELAY_STATS_FC:
+		if (value)
+			pdev->delay_stats_flag = true;
+		else
+			pdev->delay_stats_flag = false;
+		break;
+	case OL_ATH_PARAM_VIDEO_STATS_FC:
+		qdf_print("------- TID Stats ------\n");
+		dp_pdev_print_tid_stats(pdev);
+		qdf_print("------ Delay Stats ------\n");
+		dp_pdev_print_delay_stats(pdev);
+		break;
+	case OL_ATH_PARAM_TOTAL_Q_SIZE:
+		{
+			uint32_t tx_min, tx_max;
+
+			tx_min = wlan_cfg_get_min_tx_desc(soc->wlan_cfg_ctx);
+			tx_max = wlan_cfg_get_num_tx_desc(soc->wlan_cfg_ctx);
+
+			if (!buff) {
+				if ((value >= tx_min) && (value <= tx_max)) {
+					pdev->num_tx_allowed = value;
+				} else {
+					QDF_TRACE(QDF_MODULE_ID_DP,
+						  QDF_TRACE_LEVEL_INFO,
+						  "Failed to update num_tx_allowed, Q_min = %d Q_max = %d",
+						  tx_min, tx_max);
+					break;
+				}
+			} else {
+				*(int *)buff = pdev->num_tx_allowed;
+			}
+		}
+		break;
+	default:
+		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
+			  "%s: not handled param %d ", __func__, param);
+		break;
+	}
+
+	return 0;
+}
+#endif
+
 static struct cdp_cmn_ops dp_ops_cmn = {
 	.txrx_soc_attach_target = dp_soc_attach_target_wifi3,
 	.txrx_vdev_attach = dp_vdev_attach_wifi3,
@@ -9499,7 +9573,7 @@
 
 #ifdef CONFIG_WIN
 static struct cdp_pflow_ops dp_ops_pflow = {
-	dp_pdev_tid_stats_display,
+	dp_tx_flow_ctrl_configure_pdev,
 };
 #endif /* CONFIG_WIN */
 
diff --git a/dp/wifi3.0/dp_stats.c b/dp/wifi3.0/dp_stats.c
index a3a10f4..4b89c81 100644
--- a/dp/wifi3.0/dp_stats.c
+++ b/dp/wifi3.0/dp_stats.c
@@ -3565,14 +3565,7 @@
 	return intfrm_delay_bucket[index];
 }
 
-/**
- * dp_pdev_print_tid_stats(): Print pdev level tid stats
- * @pdev: DP_PDEV handle
- *
- * Return:void
- */
-static inline void
-dp_pdev_print_tid_stats(struct dp_pdev *pdev)
+void dp_pdev_print_tid_stats(struct dp_pdev *pdev)
 {
 	struct cdp_tid_stats *tid_stats;
 	struct cdp_tid_tx_stats *txstats;
@@ -3640,14 +3633,7 @@
 	}
 }
 
-/**
- * dp_pdev_print_delay_stats(): Print pdev level delay stats
- * @pdev: DP_PDEV handle
- *
- * Return:void
- */
-static inline void
-dp_pdev_print_delay_stats(struct dp_pdev *pdev)
+void dp_pdev_print_delay_stats(struct dp_pdev *pdev)
 {
 	struct dp_soc *soc = pdev->soc;
 	struct cdp_tid_tx_stats *txstats = NULL;
@@ -3736,47 +3722,4 @@
 		DP_PRINT_STATS("Avg = %u\n", rxstats->to_stack_delay.avg_delay);
 	}
 }
-
-/**
- * dp_pdev_tid_stats_display() - Pdev TID stats display
- * @pdev_hdl: datapath pdev handle
- * @param: ol ath params
- * @value: value of the flag
- * @buff: Buffer to be passed
- *
- * Return: 0 for success. nonzero for failure.
- */
-uint32_t dp_pdev_tid_stats_display(void *pdev_handle,
-		enum _ol_ath_param_t param, uint32_t value, void *buff)
-{
-	struct dp_soc *soc = NULL;
-	struct dp_pdev *pdev = (struct dp_pdev *)pdev_handle;
-
-	if (qdf_unlikely(!pdev))
-		return 1;
-
-	soc = pdev->soc;
-	if (!soc)
-		return 1;
-
-	switch (param) {
-	case OL_ATH_PARAM_VIDEO_DELAY_STATS_FC:
-		if (value)
-			pdev->delay_stats_flag = true;
-		else
-			pdev->delay_stats_flag = false;
-		break;
-	case OL_ATH_PARAM_VIDEO_STATS_FC:
-		qdf_print("------- TID Stats ------\n");
-		dp_pdev_print_tid_stats(pdev);
-		qdf_print("------ Delay Stats ------\n");
-		dp_pdev_print_delay_stats(pdev);
-		break;
-	default:
-		qdf_print("%s: not handled param %d ", __func__, param);
-		break;
-	}
-
-	return 0;
-}
 #endif
diff --git a/dp/wifi3.0/dp_tx.c b/dp/wifi3.0/dp_tx.c
index 193d7e6..35661ac 100644
--- a/dp/wifi3.0/dp_tx.c
+++ b/dp/wifi3.0/dp_tx.c
@@ -649,6 +649,39 @@
 				      msdu_id, QDF_TX));
 }
 
+#ifdef QCA_512M_CONFIG
+/**
+ * dp_tx_pdev_pflow_control - Check if allocated tx descriptors reached max
+ * tx descriptor configured value
+ * @vdev: DP vdev handle
+ *
+ * Return: true if allocated tx descriptors reached max configured value, else
+ * false.
+ */
+static inline bool
+dp_tx_pdev_pflow_control(struct dp_vdev *vdev)
+{
+	struct dp_pdev *pdev = vdev->pdev;
+
+	if (qdf_atomic_read(&pdev->num_tx_outstanding) >=
+			pdev->num_tx_allowed) {
+		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
+			  "%s: queued packets are more than max tx, drop the frame",
+			  __func__);
+		DP_STATS_INC(vdev, tx_i.dropped.desc_na.num, 1);
+		return true;
+	}
+
+	return false;
+}
+#else
+static inline bool
+dp_tx_pdev_pflow_control(struct dp_vdev *vdev)
+{
+	return false;
+}
+#endif
+
 /**
  * dp_tx_desc_prepare_single - Allocate and prepare Tx descriptor
  * @vdev: DP vdev handle
@@ -675,6 +708,9 @@
 	struct dp_pdev *pdev = vdev->pdev;
 	struct dp_soc *soc = pdev->soc;
 
+	if (dp_tx_pdev_pflow_control(vdev))
+		return NULL;
+
 	/* Allocate software Tx descriptor */
 	tx_desc = dp_tx_desc_alloc(soc, desc_pool_id);
 	if (qdf_unlikely(!tx_desc)) {
@@ -810,6 +846,9 @@
 	struct dp_pdev *pdev = vdev->pdev;
 	struct dp_soc *soc = pdev->soc;
 
+	if (dp_tx_pdev_pflow_control(vdev))
+		return NULL;
+
 	/* Allocate software Tx descriptor */
 	tx_desc = dp_tx_desc_alloc(soc, desc_pool_id);
 	if (!tx_desc) {
diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h
index 561e219..2b1e68b 100644
--- a/dp/wifi3.0/dp_types.h
+++ b/dp/wifi3.0/dp_types.h
@@ -1393,6 +1393,9 @@
 
 	/* qdf_event for fw_peer_stats */
 	qdf_event_t fw_peer_stats_event;
+
+	/* User configured max number of tx buffers */
+	uint32_t num_tx_allowed;
 };
 
 struct dp_peer;
diff --git a/wlan_cfg/cfg_dp.h b/wlan_cfg/cfg_dp.h
index 315b32a..4e0198d 100644
--- a/wlan_cfg/cfg_dp.h
+++ b/wlan_cfg/cfg_dp.h
@@ -143,7 +143,7 @@
 #define WLAN_CFG_TX_COMP_RING_SIZE_MAX 0x80000
 
 #define WLAN_CFG_NUM_TX_DESC_MIN  1024
-#define WLAN_CFG_NUM_TX_DESC_MAX  0x320000
+#define WLAN_CFG_NUM_TX_DESC_MAX  32768
 
 #define WLAN_CFG_NUM_TX_EXT_DESC_MIN  1024
 #define WLAN_CFG_NUM_TX_EXT_DESC_MAX  0x80000
diff --git a/wlan_cfg/wlan_cfg.c b/wlan_cfg/wlan_cfg.c
index 201d08e..c6c3d27 100644
--- a/wlan_cfg/wlan_cfg.c
+++ b/wlan_cfg/wlan_cfg.c
@@ -267,6 +267,7 @@
 	wlan_cfg_ctx->num_tx_ext_desc_pool = cfg_get(psoc,
 						     CFG_DP_TX_EXT_DESC_POOLS);
 	wlan_cfg_ctx->num_tx_desc = cfg_get(psoc, CFG_DP_TX_DESC);
+	wlan_cfg_ctx->min_tx_desc = WLAN_CFG_NUM_TX_DESC_MIN;
 	wlan_cfg_ctx->num_tx_ext_desc = cfg_get(psoc, CFG_DP_TX_EXT_DESC);
 	wlan_cfg_ctx->htt_packet_type = cfg_get(psoc, CFG_DP_HTT_PACKET_TYPE);
 	wlan_cfg_ctx->max_peer_id = cfg_get(psoc, CFG_DP_MAX_PEER_ID);
@@ -684,6 +685,11 @@
 	cfg->num_tx_desc = num_desc;
 }
 
+int wlan_cfg_get_min_tx_desc(struct wlan_cfg_dp_soc_ctxt *cfg)
+{
+	return cfg->min_tx_desc;
+}
+
 int wlan_cfg_get_num_tx_ext_desc(struct wlan_cfg_dp_soc_ctxt *cfg)
 {
 	return cfg->num_tx_ext_desc;
diff --git a/wlan_cfg/wlan_cfg.h b/wlan_cfg/wlan_cfg.h
index 2d3a4ca..dc6ca70 100644
--- a/wlan_cfg/wlan_cfg.h
+++ b/wlan_cfg/wlan_cfg.h
@@ -96,6 +96,7 @@
  * @num_tx_desc_pool: Number of Tx Descriptor pools
  * @num_tx_ext_desc_pool: Number of Tx MSDU extension Descriptor pools
  * @num_tx_desc: Number of Tx Descriptors per pool
+ * @min_tx_desc: Minimum number of Tx Descriptors per pool
  * @num_tx_ext_desc: Number of Tx MSDU extension Descriptors per pool
  * @max_peer_id: Maximum value of peer id that FW can assign for a client
  * @htt_packet_type: Default 802.11 encapsulation type for any VAP created
@@ -149,6 +150,7 @@
 	int num_tx_desc_pool;
 	int num_tx_ext_desc_pool;
 	int num_tx_desc;
+	int min_tx_desc;
 	int num_tx_ext_desc;
 	int max_peer_id;
 	int htt_packet_type;
@@ -648,6 +650,14 @@
 int wlan_cfg_get_num_tx_desc(struct wlan_cfg_dp_soc_ctxt *wlan_cfg_ctx);
 
 /*
+ * wlan_cfg_get_min_tx_desc() - Minimum number of Tx Descriptors per pool
+ * @wlan_cfg_ctx - Configuration Handle
+ *
+ * Return: num_tx_desc
+ */
+int wlan_cfg_get_min_tx_desc(struct wlan_cfg_dp_soc_ctxt *wlan_cfg_ctx);
+
+/*
  * wlan_cfg_set_num_tx_desc() - Set the number of Tx Descriptors per pool
  *
  * @wlan_cfg_ctx - Configuration Handle