qcacmn: Check for ring approaching full during RX

Check if REO ring is near full at the end of dp_rx_process. In case the
ring is near full, reap the packets in the ring (and replenish, send to
upper layer) until the quota allows. Ignore the HIF yield time
limit in such cases.

This change is needed to prevent back pressure from the REO ring(in case
it gets full). Backpressure from REO ring (to LMAC) may lead to a
watchdog and eventually a FW crash. Hence, avoid such a scenario by
reaping as many packets as the 'quota' allows when the REO ring is in
aforementioned condition.

A sid-effect of this change would be that at times the RX softirq may run
longer (till the quota limit) than the configured HIF yield time.
However, this logic is not expected to kick-in in perf builds. The issue
is reported for a defconfig build where lots debug options are enabled
in the kernel which can slow the processing down.

Change-Id: I2eb6544c159ec5957d10386b1750fd96473fe13a
CRs-Fixed: 2540964
diff --git a/dp/wifi3.0/dp_rx.c b/dp/wifi3.0/dp_rx.c
index 363f980..659c225 100644
--- a/dp/wifi3.0/dp_rx.c
+++ b/dp/wifi3.0/dp_rx.c
@@ -1649,9 +1649,46 @@
 #endif
 
 /**
+ * dp_rx_srng_get_num_pending() - get number of pending entries
+ * @hal_soc: hal soc opaque pointer
+ * @hal_ring: opaque pointer to the HAL Rx Ring
+ * @num_entries: number of entries in the hal_ring.
+ * @near_full: pointer to a boolean. This is set if ring is near full.
+ *
+ * The function returns the number of entries in a destination ring which are
+ * yet to be reaped. The function also checks if the ring is near full.
+ * If more than half of the ring needs to be reaped, the ring is considered
+ * approaching full.
+ * The function useses hal_srng_dst_num_valid_locked to get the number of valid
+ * entries. It should not be called within a SRNG lock. HW pointer value is
+ * synced into cached_hp.
+ *
+ * Return: Number of pending entries if any
+ */
+static
+uint32_t dp_rx_srng_get_num_pending(hal_soc_handle_t hal_soc,
+				    hal_ring_handle_t hal_ring_hdl,
+				    uint32_t num_entries,
+				    bool *near_full)
+{
+	uint32_t num_pending = 0;
+
+	num_pending = hal_srng_dst_num_valid_locked(hal_soc,
+						    hal_ring_hdl,
+						    true);
+
+	if (num_entries && (num_pending >= num_entries >> 1))
+		*near_full = true;
+	else
+		*near_full = false;
+
+	return num_pending;
+}
+
+/**
  * dp_rx_process() - Brain of the Rx processing functionality
  *		     Called from the bottom half (tasklet/NET_RX_SOFTIRQ)
- * @soc: core txrx main context
+ * @int_ctx: per interrupt context
  * @hal_ring: opaque pointer to the HAL Rx Ring, which will be serviced
  * @reo_ring_num: ring number (0, 1, 2 or 3) of the reo ring.
  * @quota: No. of units (packets) that can be serviced in one shot.
@@ -1662,14 +1699,16 @@
  * Return: uint32_t: No. of elements processed
  */
 uint32_t dp_rx_process(struct dp_intr *int_ctx, hal_ring_handle_t hal_ring_hdl,
-		       uint8_t reo_ring_num, uint32_t quota)
+			    uint8_t reo_ring_num, uint32_t quota)
 {
 	hal_ring_desc_t ring_desc;
 	hal_soc_handle_t hal_soc;
 	struct dp_rx_desc *rx_desc = NULL;
 	qdf_nbuf_t nbuf, next;
+	bool near_full;
 	union dp_rx_desc_list_elem_t *head[MAX_PDEV_CNT];
 	union dp_rx_desc_list_elem_t *tail[MAX_PDEV_CNT];
+	uint32_t num_pending;
 	uint32_t rx_bufs_used = 0, rx_buf_cookie;
 	uint32_t l2_hdr_offset = 0;
 	uint16_t msdu_len = 0;
@@ -1703,6 +1742,7 @@
 	bool is_prev_msdu_last = true;
 	uint32_t num_entries_avail = 0;
 	uint32_t rx_ol_pkt_cnt = 0;
+	uint32_t num_entries = 0;
 
 	DP_HIST_INIT();
 
@@ -1713,6 +1753,7 @@
 	scn = soc->hif_handle;
 	hif_pm_runtime_mark_dp_rx_busy(scn);
 	intr_id = int_ctx->dp_intr_id;
+	num_entries = hal_srng_get_num_entries(hal_soc, hal_ring_hdl);
 
 more_data:
 	/* reset local variables here to be re-used in the function */
@@ -2196,12 +2237,23 @@
 				       deliver_list_tail);
 
 	if (dp_rx_enable_eol_data_check(soc) && rx_bufs_used) {
-		if (quota &&
-		    hal_srng_dst_peek_sync_locked(soc->hal_soc,
-						  hal_ring_hdl)) {
-			DP_STATS_INC(soc, rx.hp_oos2, 1);
-			if (!hif_exec_should_yield(scn, intr_id))
-				goto more_data;
+		if (quota) {
+			num_pending =
+				dp_rx_srng_get_num_pending(hal_soc,
+							   hal_ring_hdl,
+							   num_entries,
+							   &near_full);
+			if (num_pending) {
+				DP_STATS_INC(soc, rx.hp_oos2, 1);
+
+				if (!hif_exec_should_yield(scn, intr_id))
+					goto more_data;
+
+				if (qdf_unlikely(near_full)) {
+					DP_STATS_INC(soc, rx.near_full, 1);
+					goto more_data;
+				}
+			}
 		}
 
 		if (vdev && vdev->osif_gro_flush && rx_ol_pkt_cnt) {
diff --git a/dp/wifi3.0/dp_stats.c b/dp/wifi3.0/dp_stats.c
index 71d7f46..4077be0 100644
--- a/dp/wifi3.0/dp_stats.c
+++ b/dp/wifi3.0/dp_stats.c
@@ -5068,6 +5068,8 @@
 		total_packets = 0;
 		DP_PRINT_STATS("Packets on ring %u:", ring);
 		for (core = 0; core < num_possible_cpus(); core++) {
+			if (!soc->stats.rx.ring_packets[core][ring])
+				continue;
 			DP_PRINT_STATS("Packets arriving on core %u: %llu",
 				       core,
 				       soc->stats.rx.ring_packets[core][ring]);
@@ -5154,11 +5156,14 @@
 		DP_PRINT_STATS("delivered %u msdus ( %llu bytes),",
 			       pdev->stats.rx.to_stack.num,
 			       pdev->stats.rx.to_stack.bytes);
-		for (i = 0; i <  CDP_MAX_RX_RINGS; i++)
+		for (i = 0; i <  CDP_MAX_RX_RINGS; i++) {
+			if (!pdev->stats.rx.rcvd_reo[i].num)
+				continue;
 			DP_PRINT_STATS(
 				       "received on reo[%d] %u msdus( %llu bytes),",
 				       i, pdev->stats.rx.rcvd_reo[i].num,
 				       pdev->stats.rx.rcvd_reo[i].bytes);
+		}
 		DP_PRINT_STATS("intra-bss packets %u msdus ( %llu bytes),",
 			       pdev->stats.rx.intra_bss.pkts.num,
 			       pdev->stats.rx.intra_bss.pkts.bytes);
@@ -5187,6 +5192,7 @@
 			       pdev->soc->stats.rx.err.pkt_delivered_no_peer);
 
 		DP_PRINT_STATS("Reo Statistics");
+		DP_PRINT_STATS("near_full: %u ", soc->stats.rx.near_full);
 		DP_PRINT_STATS("rbm error: %u msdus",
 			       pdev->soc->stats.rx.err.invalid_rbm);
 		DP_PRINT_STATS("hal ring access fail: %u msdus",
@@ -5617,7 +5623,7 @@
 	DP_PRINT_STATS("INT:     Total  |txComps|reo[0] |reo[1] |reo[2] |reo[3] |mon    |rx_err | wbm   |reo_sta|rxdm2hst|hst2rxdm|");
 	for (i = 0; i < WLAN_CFG_INT_NUM_CONTEXTS; i++) {
 		intr_stats = &soc->intr_ctx[i].intr_stats;
-		DP_PRINT_STATS("%3u[%d]: %7u %7u %7u %7u %7u %7u %7u %7u %7u %7u %8u %8u",
+		DP_PRINT_STATS("%3u[%3d]: %7u %7u %7u %7u %7u %7u %7u %7u %7u %7u %8u %8u",
 			       i,
 			       hif_get_int_ctx_irq_num(soc->hif_handle, i),
 			       intr_stats->num_masks,
@@ -5676,6 +5682,8 @@
 	DP_PRINT_STATS("RX frag err: %d", soc->stats.rx.rx_frag_err);
 
 	DP_PRINT_STATS("RX HP out_of_sync: %d", soc->stats.rx.hp_oos2);
+	DP_PRINT_STATS("RX Ring Near Full: %d", soc->stats.rx.near_full);
+
 	DP_PRINT_STATS("RX Reap Loop Pkt Limit Hit: %d",
 		       soc->stats.rx.reap_loop_pkt_limit_hit);
 	DP_PRINT_STATS("RX DESC invalid magic: %u",
diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h
index cb32f05..312abc2 100644
--- a/dp/wifi3.0/dp_types.h
+++ b/dp/wifi3.0/dp_types.h
@@ -693,6 +693,8 @@
 		uint32_t reap_loop_pkt_limit_hit;
 		/* Head pointer Out of sync at the end of dp_rx_process */
 		uint32_t hp_oos2;
+		/* Rx ring near full */
+		uint32_t near_full;
 		struct {
 			/* Invalid RBM error count */
 			uint32_t invalid_rbm;
diff --git a/hal/wifi3.0/hal_api.h b/hal/wifi3.0/hal_api.h
index 20d2fc0..8b7b158 100644
--- a/hal/wifi3.0/hal_api.h
+++ b/hal/wifi3.0/hal_api.h
@@ -789,9 +789,10 @@
  * @sync_hw_ptr: Sync cached head pointer with HW
  *
  */
-static inline uint32_t hal_srng_dst_num_valid(void *hal_soc,
-					      hal_ring_handle_t hal_ring_hdl,
-					      int sync_hw_ptr)
+static inline
+uint32_t hal_srng_dst_num_valid(void *hal_soc,
+				hal_ring_handle_t hal_ring_hdl,
+				int sync_hw_ptr)
 {
 	struct hal_srng *srng = (struct hal_srng *)hal_ring_hdl;
 	uint32_t hp;
@@ -811,6 +812,33 @@
 }
 
 /**
+ * hal_srng_dst_num_valid_locked - Returns num valid entries to be processed
+ *
+ * @hal_soc: Opaque HAL SOC handle
+ * @hal_ring_hdl: Destination ring pointer
+ * @sync_hw_ptr: Sync cached head pointer with HW
+ *
+ * Returns number of valid entries to be processed by the host driver. The
+ * function takes up SRNG lock.
+ *
+ * Return: Number of valid destination entries
+ */
+static inline uint32_t
+hal_srng_dst_num_valid_locked(hal_soc_handle_t hal_soc,
+			      hal_ring_handle_t hal_ring_hdl,
+			      int sync_hw_ptr)
+{
+	uint32_t num_valid;
+	struct hal_srng *srng = (struct hal_srng *)hal_ring_hdl;
+
+	SRNG_LOCK(&srng->lock);
+	num_valid = hal_srng_dst_num_valid(hal_soc, hal_ring_hdl, sync_hw_ptr);
+	SRNG_UNLOCK(&srng->lock);
+
+	return num_valid;
+}
+
+/**
  * hal_srng_src_reap_next - Reap next entry from a source ring and move reap
  * pointer. This can be used to release any buffers associated with completed
  * ring entries. Note that this should not be used for posting new descriptor
@@ -1385,6 +1413,23 @@
 }
 
 /**
+ * hal_srng_get_num_entries - Get total entries in the HAL Srng
+ *
+ * @hal_soc: Opaque HAL SOC handle
+ * @hal_ring_hdl: Ring pointer (Source or Destination ring)
+ *
+ * Return: total number of entries in hal ring
+ */
+static inline
+uint32_t hal_srng_get_num_entries(hal_soc_handle_t hal_soc_hdl,
+				  hal_ring_handle_t hal_ring_hdl)
+{
+	struct hal_srng *srng = (struct hal_srng *)hal_ring_hdl;
+
+	return srng->num_entries;
+}
+
+/**
  * hal_get_srng_params - Retrieve SRNG parameters for a given ring from HAL
  *
  * @hal_soc: Opaque HAL SOC handle