xref: /wlan-dirver/qcacld-3.0/components/dp/core/src/wlan_dp_rx_thread.c (revision 1ed9249cd9445fcc8a5eabed8f545e03248cddb1)
1 /*
2  * Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
3  * Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for
6  * any purpose with or without fee is hereby granted, provided that the
7  * above copyright notice and this permission notice appear in all
8  * copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11  * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12  * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13  * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17  * PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <wlan_dp_rx_thread.h>
21 #include "dp_peer.h"
22 #include "dp_internal.h"
23 #include "dp_types.h"
24 #include <cdp_txrx_cmn_struct.h>
25 #include <cdp_txrx_peer_ops.h>
26 #include <cds_sched.h>
27 #include "dp_rx.h"
28 #include "wlan_dp_ucfg_api.h"
29 #include "wlan_dp_prealloc.h"
30 #include "wlan_dp_main.h"
31 #include "wlan_dp_public_struct.h"
32 #include "wlan_dp_ucfg_api.h"
33 #include "qdf_nbuf.h"
34 #include "qdf_threads.h"
35 #include "qdf_net_if.h"
36 
37 /* Timeout in ms to wait for a DP rx thread */
38 #ifdef HAL_CONFIG_SLUB_DEBUG_ON
39 #define DP_RX_THREAD_WAIT_TIMEOUT 4000
40 #else
41 #define DP_RX_THREAD_WAIT_TIMEOUT 2000
42 #endif
43 #define DP_RX_THREAD_FLUSH_TIMEOUT 6000
44 #define DP_RX_THREAD_MIN_FLUSH_TIMEOUT 10
45 
46 #ifdef CONFIG_SLUB_DEBUG_ON
47 /* number of rx pkts that thread should yield */
48 #define DP_RX_THREAD_YIELD_PKT_CNT 20000
49 #endif
50 
51 #define DP_RX_TM_DEBUG 0
52 #if DP_RX_TM_DEBUG
53 /**
54  * dp_rx_tm_walk_skb_list() - Walk skb list and print members
55  * @nbuf_list: nbuf list to print
56  *
57  * Returns: None
58  */
59 static inline void dp_rx_tm_walk_skb_list(qdf_nbuf_t nbuf_list)
60 {
61 	qdf_nbuf_t nbuf;
62 	int i = 0;
63 
64 	nbuf = nbuf_list;
65 	while (nbuf) {
66 		dp_debug("%d nbuf:%pK nbuf->next:%pK nbuf->data:%pK", i,
67 			 nbuf, qdf_nbuf_next(nbuf), qdf_nbuf_data(nbuf));
68 		nbuf = qdf_nbuf_next(nbuf);
69 		i++;
70 	}
71 }
72 #else
73 static inline void dp_rx_tm_walk_skb_list(qdf_nbuf_t nbuf_list)
74 { }
75 #endif /* DP_RX_TM_DEBUG */
76 
77 #ifdef DP_RX_REFILL_CPU_PERF_AFFINE_MASK
78 /**
79  * dp_rx_refill_thread_set_affinity - Affine Rx refill threads
80  * @refill_thread: Contains over all rx refill thread info
81  *
82  * Return: None
83  */
84 static void
85 dp_rx_refill_thread_set_affinity(struct dp_rx_refill_thread *refill_thread)
86 {
87 	unsigned int cpus;
88 	char new_mask_str[10];
89 	qdf_cpu_mask new_mask;
90 	int perf_cpu_cluster = hif_get_perf_cluster_bitmap();
91 	int package_id;
92 
93 	qdf_cpumask_clear(&new_mask);
94 	qdf_for_each_online_cpu(cpus) {
95 		package_id = qdf_topology_physical_package_id(cpus);
96 		if (package_id >= 0 && BIT(package_id) & perf_cpu_cluster)
97 			qdf_cpumask_set_cpu(cpus, &new_mask);
98 	}
99 
100 	qdf_thread_set_cpus_allowed_mask(refill_thread->task, &new_mask);
101 
102 	qdf_thread_cpumap_print_to_pagebuf(false, new_mask_str, &new_mask);
103 	dp_debug("Refill Thread CPU mask  %s", new_mask_str);
104 }
105 #else
106 static void
107 dp_rx_refill_thread_set_affinity(struct dp_rx_refill_thread *refill_thread)
108 {
109 }
110 #endif
111 /**
112  * dp_rx_tm_get_soc_handle() - get soc handle from struct dp_rx_tm_handle_cmn
113  * @rx_tm_handle_cmn: rx thread manager cmn handle
114  *
115  * Returns: ol_txrx_soc_handle on success, NULL on failure.
116  */
117 static inline ol_txrx_soc_handle
118 dp_rx_tm_get_soc_handle(struct dp_rx_tm_handle_cmn *rx_tm_handle_cmn)
119 {
120 	struct dp_txrx_handle_cmn *txrx_handle_cmn;
121 	ol_txrx_soc_handle soc;
122 
123 	txrx_handle_cmn =
124 		dp_rx_thread_get_txrx_handle(rx_tm_handle_cmn);
125 
126 	soc = dp_txrx_get_soc_from_ext_handle(txrx_handle_cmn);
127 	return soc;
128 }
129 
130 /**
131  * dp_rx_tm_thread_dump_stats() - display stats for a rx_thread
132  * @rx_thread: rx_thread pointer for which the stats need to be
133  *            displayed
134  *
135  * Returns: None
136  */
137 static void dp_rx_tm_thread_dump_stats(struct dp_rx_thread *rx_thread)
138 {
139 	uint8_t reo_ring_num;
140 	uint32_t off = 0;
141 	char nbuf_queued_string[100];
142 	uint32_t total_queued = 0;
143 	uint32_t temp = 0;
144 
145 	qdf_mem_zero(nbuf_queued_string, sizeof(nbuf_queued_string));
146 
147 	for (reo_ring_num = 0; reo_ring_num < DP_RX_TM_MAX_REO_RINGS;
148 	     reo_ring_num++) {
149 		temp = rx_thread->stats.nbuf_queued[reo_ring_num];
150 		if (!temp)
151 			continue;
152 		total_queued += temp;
153 		if (off >= sizeof(nbuf_queued_string))
154 			continue;
155 		off += qdf_scnprintf(&nbuf_queued_string[off],
156 				     sizeof(nbuf_queued_string) - off,
157 				     "reo[%u]:%u ", reo_ring_num, temp);
158 	}
159 
160 	if (!total_queued)
161 		return;
162 
163 	dp_info("thread:%u - qlen:%u queued:(total:%u %s) dequeued:%u stack:%u gro_flushes: %u gro_flushes_by_vdev_del: %u rx_flushes: %u max_len:%u invalid(peer:%u vdev:%u rx-handle:%u others:%u enq fail:%u)",
164 		rx_thread->id,
165 		qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue),
166 		total_queued,
167 		nbuf_queued_string,
168 		rx_thread->stats.nbuf_dequeued,
169 		rx_thread->stats.nbuf_sent_to_stack,
170 		rx_thread->stats.gro_flushes,
171 		rx_thread->stats.gro_flushes_by_vdev_del,
172 		rx_thread->stats.rx_flushed,
173 		rx_thread->stats.nbufq_max_len,
174 		rx_thread->stats.dropped_invalid_peer,
175 		rx_thread->stats.dropped_invalid_vdev,
176 		rx_thread->stats.dropped_invalid_os_rx_handles,
177 		rx_thread->stats.dropped_others,
178 		rx_thread->stats.dropped_enq_fail);
179 }
180 
181 QDF_STATUS dp_rx_tm_dump_stats(struct dp_rx_tm_handle *rx_tm_hdl)
182 {
183 	int i;
184 
185 	for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) {
186 		if (!rx_tm_hdl->rx_thread[i])
187 			continue;
188 		dp_rx_tm_thread_dump_stats(rx_tm_hdl->rx_thread[i]);
189 	}
190 	return QDF_STATUS_SUCCESS;
191 }
192 
193 #ifdef FEATURE_ALLOW_PKT_DROPPING
194 /*
195  * dp_check_and_update_pending() - Check and Set RX Pending flag
196  * @tm_handle_cmn: DP thread pointer
197  *
198  * Returns: QDF_STATUS_SUCCESS on success or qdf error code on
199  * failure
200  */
201 static inline
202 QDF_STATUS dp_check_and_update_pending(struct dp_rx_tm_handle_cmn
203 				       *tm_handle_cmn)
204 {
205 	struct dp_txrx_handle_cmn *txrx_handle_cmn;
206 	struct dp_rx_tm_handle *rx_tm_hdl =
207 		    (struct dp_rx_tm_handle *)tm_handle_cmn;
208 	struct dp_soc *dp_soc;
209 	uint32_t rx_pending_hl_threshold;
210 	uint32_t rx_pending_lo_threshold;
211 	uint32_t nbuf_queued_total = 0;
212 	uint32_t nbuf_dequeued_total = 0;
213 	uint32_t rx_flushed_total = 0;
214 	uint32_t pending = 0;
215 	int i;
216 
217 	txrx_handle_cmn =
218 		dp_rx_thread_get_txrx_handle(tm_handle_cmn);
219 	if (!txrx_handle_cmn) {
220 		dp_err("invalid txrx_handle_cmn!");
221 		QDF_BUG(0);
222 		return QDF_STATUS_E_FAILURE;
223 	}
224 
225 	dp_soc = (struct dp_soc *)dp_txrx_get_soc_from_ext_handle(
226 					txrx_handle_cmn);
227 	if (!dp_soc) {
228 		dp_err("invalid soc!");
229 		QDF_BUG(0);
230 		return QDF_STATUS_E_FAILURE;
231 	}
232 
233 	rx_pending_hl_threshold = wlan_cfg_rx_pending_hl_threshold(
234 				  dp_soc->wlan_cfg_ctx);
235 	rx_pending_lo_threshold = wlan_cfg_rx_pending_lo_threshold(
236 				  dp_soc->wlan_cfg_ctx);
237 
238 	for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) {
239 		if (likely(rx_tm_hdl->rx_thread[i])) {
240 			nbuf_queued_total +=
241 			    rx_tm_hdl->rx_thread[i]->stats.nbuf_queued_total;
242 			nbuf_dequeued_total +=
243 			    rx_tm_hdl->rx_thread[i]->stats.nbuf_dequeued;
244 			rx_flushed_total +=
245 			    rx_tm_hdl->rx_thread[i]->stats.rx_flushed;
246 		}
247 	}
248 
249 	if (nbuf_queued_total > (nbuf_dequeued_total + rx_flushed_total))
250 		pending = nbuf_queued_total - (nbuf_dequeued_total +
251 					       rx_flushed_total);
252 
253 	if (unlikely(pending > rx_pending_hl_threshold))
254 		qdf_atomic_set(&rx_tm_hdl->allow_dropping, 1);
255 	else if (pending < rx_pending_lo_threshold)
256 		qdf_atomic_set(&rx_tm_hdl->allow_dropping, 0);
257 
258 	return QDF_STATUS_SUCCESS;
259 }
260 
261 #else
262 static inline
263 QDF_STATUS dp_check_and_update_pending(struct dp_rx_tm_handle_cmn
264 				       *tm_handle_cmn)
265 {
266 	return QDF_STATUS_SUCCESS;
267 }
268 #endif
269 
270 /**
271  * dp_rx_tm_thread_enqueue() - enqueue nbuf list into rx_thread
272  * @rx_thread: rx_thread in which the nbuf needs to be queued
273  * @nbuf_list: list of packets to be queued into the thread
274  *
275  * Enqueue packet into rx_thread and wake it up. The function
276  * moves the next pointer of the nbuf_list into the ext list of
277  * the first nbuf for storage into the thread. Only the first
278  * nbuf is queued into the thread nbuf queue. The reverse is
279  * done at the time of dequeue.
280  *
281  * Returns: QDF_STATUS_SUCCESS on success or qdf error code on
282  * failure
283  */
284 static QDF_STATUS dp_rx_tm_thread_enqueue(struct dp_rx_thread *rx_thread,
285 					  qdf_nbuf_t nbuf_list)
286 {
287 	qdf_nbuf_t head_ptr, next_ptr_list;
288 	uint32_t temp_qlen;
289 	uint32_t num_elements_in_nbuf;
290 	uint32_t nbuf_queued;
291 	struct dp_rx_tm_handle_cmn *tm_handle_cmn;
292 	uint8_t reo_ring_num = QDF_NBUF_CB_RX_CTX_ID(nbuf_list);
293 	qdf_wait_queue_head_t *wait_q_ptr;
294 	uint8_t allow_dropping;
295 
296 	tm_handle_cmn = rx_thread->rtm_handle_cmn;
297 
298 	if (!tm_handle_cmn) {
299 		dp_alert("tm_handle_cmn is null!");
300 		QDF_BUG(0);
301 		return QDF_STATUS_E_FAILURE;
302 	}
303 
304 	wait_q_ptr = &rx_thread->wait_q;
305 
306 	if (reo_ring_num >= DP_RX_TM_MAX_REO_RINGS) {
307 		dp_alert("incorrect ring %u", reo_ring_num);
308 		QDF_BUG(0);
309 		return QDF_STATUS_E_FAILURE;
310 	}
311 
312 	num_elements_in_nbuf = QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(nbuf_list);
313 	nbuf_queued = num_elements_in_nbuf;
314 
315 	allow_dropping = qdf_atomic_read(
316 		&((struct dp_rx_tm_handle *)tm_handle_cmn)->allow_dropping);
317 	if (unlikely(allow_dropping)) {
318 		qdf_nbuf_list_free(nbuf_list);
319 		rx_thread->stats.dropped_enq_fail += num_elements_in_nbuf;
320 		nbuf_queued = 0;
321 		goto enq_done;
322 	}
323 
324 	dp_rx_tm_walk_skb_list(nbuf_list);
325 
326 	head_ptr = nbuf_list;
327 
328 	/* Ensure head doesn't have an ext list */
329 	while (qdf_unlikely(head_ptr && qdf_nbuf_get_ext_list(head_ptr))) {
330 		QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(head_ptr) = 1;
331 		num_elements_in_nbuf--;
332 		next_ptr_list = head_ptr->next;
333 		qdf_nbuf_set_next(head_ptr, NULL);
334 		/* count aggregated RX frame into enqueued stats */
335 		nbuf_queued += qdf_nbuf_get_gso_segs(head_ptr);
336 		qdf_nbuf_queue_head_enqueue_tail(&rx_thread->nbuf_queue,
337 						 head_ptr);
338 		head_ptr = next_ptr_list;
339 	}
340 
341 	if (!head_ptr)
342 		goto enq_done;
343 
344 	QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(head_ptr) = num_elements_in_nbuf;
345 
346 	next_ptr_list = head_ptr->next;
347 
348 	if (next_ptr_list) {
349 		/* move ->next pointer to ext list */
350 		qdf_nbuf_append_ext_list(head_ptr, next_ptr_list, 0);
351 		dp_debug("appended next_ptr_list %pK to nbuf %pK ext list %pK",
352 			 qdf_nbuf_next(nbuf_list), nbuf_list,
353 			 qdf_nbuf_get_ext_list(nbuf_list));
354 	}
355 	qdf_nbuf_set_next(head_ptr, NULL);
356 
357 	qdf_nbuf_queue_head_enqueue_tail(&rx_thread->nbuf_queue, head_ptr);
358 
359 enq_done:
360 	temp_qlen = qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue);
361 
362 	rx_thread->stats.nbuf_queued[reo_ring_num] += nbuf_queued;
363 	rx_thread->stats.nbuf_queued_total += nbuf_queued;
364 
365 	dp_check_and_update_pending(tm_handle_cmn);
366 
367 	if (temp_qlen > rx_thread->stats.nbufq_max_len)
368 		rx_thread->stats.nbufq_max_len = temp_qlen;
369 
370 	dp_debug("enqueue packet thread %pK wait queue %pK qlen %u",
371 		 rx_thread, wait_q_ptr,
372 		 qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue));
373 
374 	qdf_set_bit(RX_POST_EVENT, &rx_thread->event_flag);
375 	qdf_wake_up_interruptible(wait_q_ptr);
376 
377 	return QDF_STATUS_SUCCESS;
378 }
379 
380 /**
381  * dp_rx_tm_thread_gro_flush_ind() - Rxthread flush ind post
382  * @rx_thread: rx_thread in which the flush needs to be handled
383  * @flush_code: flush code to differentiate low TPUT flush
384  *
385  * Return: QDF_STATUS_SUCCESS on success or qdf error code on
386  * failure
387  */
388 static QDF_STATUS
389 dp_rx_tm_thread_gro_flush_ind(struct dp_rx_thread *rx_thread,
390 			      enum dp_rx_gro_flush_code flush_code)
391 {
392 	struct dp_rx_tm_handle_cmn *tm_handle_cmn;
393 	qdf_wait_queue_head_t *wait_q_ptr;
394 
395 	tm_handle_cmn = rx_thread->rtm_handle_cmn;
396 	wait_q_ptr = &rx_thread->wait_q;
397 
398 	qdf_atomic_set(&rx_thread->gro_flush_ind, flush_code);
399 
400 	dp_debug("Flush indication received");
401 
402 	qdf_set_bit(RX_POST_EVENT, &rx_thread->event_flag);
403 	qdf_wake_up_interruptible(wait_q_ptr);
404 	return QDF_STATUS_SUCCESS;
405 }
406 
407 /**
408  * dp_rx_thread_adjust_nbuf_list() - create an nbuf list from the frag list
409  * @head: nbuf list to be created
410  *
411  * Returns: void
412  */
413 static void dp_rx_thread_adjust_nbuf_list(qdf_nbuf_t head)
414 {
415 	qdf_nbuf_t next_ptr_list, nbuf_list;
416 
417 	nbuf_list = head;
418 	if (head && QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(head) > 1) {
419 		/* move ext list to ->next pointer */
420 		next_ptr_list = qdf_nbuf_get_ext_list(head);
421 		qdf_nbuf_append_ext_list(head, NULL, 0);
422 		qdf_nbuf_set_next(nbuf_list, next_ptr_list);
423 		dp_rx_tm_walk_skb_list(nbuf_list);
424 	}
425 }
426 
427 /**
428  * dp_rx_tm_thread_dequeue() - dequeue nbuf list from rx_thread
429  * @rx_thread: rx_thread from which the nbuf needs to be dequeued
430  *
431  * Returns: nbuf or nbuf_list dequeued from rx_thread
432  */
433 static qdf_nbuf_t dp_rx_tm_thread_dequeue(struct dp_rx_thread *rx_thread)
434 {
435 	qdf_nbuf_t head;
436 
437 	head = qdf_nbuf_queue_head_dequeue(&rx_thread->nbuf_queue);
438 	dp_rx_thread_adjust_nbuf_list(head);
439 
440 	dp_debug("Dequeued %pK nbuf_list", head);
441 	return head;
442 }
443 
444 #ifdef CONFIG_SLUB_DEBUG_ON
445 /**
446  * dp_rx_thread_should_yield() - check whether rx loop should yield
447  * @iter: iteration of packets received
448  * @rx_thread: rx_thread which should yield
449  *
450  * Returns: should yield or not
451  */
452 static inline bool dp_rx_thread_should_yield(struct dp_rx_thread *rx_thread,
453 					     uint32_t iter)
454 {
455 	if (iter >= DP_RX_THREAD_YIELD_PKT_CNT ||
456 	    qdf_test_bit(RX_VDEV_DEL_EVENT, &rx_thread->event_flag))
457 		return true;
458 	return false;
459 }
460 #else
461 static inline bool dp_rx_thread_should_yield(struct dp_rx_thread *rx_thread,
462 					     uint32_t iter)
463 {
464 	return false;
465 }
466 #endif
467 
468 /**
469  * dp_rx_thread_process_nbufq() - process nbuf queue of a thread
470  * @rx_thread: rx_thread whose nbuf queue needs to be processed
471  *
472  * Returns: 0 on success, error code on failure
473  */
474 static int dp_rx_thread_process_nbufq(struct dp_rx_thread *rx_thread)
475 {
476 	qdf_nbuf_t nbuf_list;
477 	uint8_t vdev_id;
478 	ol_txrx_rx_fp stack_fn;
479 	ol_osif_vdev_handle osif_vdev;
480 	ol_txrx_soc_handle soc;
481 	uint32_t num_list_elements = 0;
482 	uint32_t iterates = 0;
483 
484 	struct dp_txrx_handle_cmn *txrx_handle_cmn;
485 
486 	txrx_handle_cmn =
487 		dp_rx_thread_get_txrx_handle(rx_thread->rtm_handle_cmn);
488 
489 	soc = dp_txrx_get_soc_from_ext_handle(txrx_handle_cmn);
490 	if (!soc) {
491 		dp_err("invalid soc!");
492 		QDF_BUG(0);
493 		return -EFAULT;
494 	}
495 
496 	dp_debug("enter: qlen  %u",
497 		 qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue));
498 
499 	nbuf_list = dp_rx_tm_thread_dequeue(rx_thread);
500 	while (nbuf_list) {
501 		num_list_elements =
502 			QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(nbuf_list);
503 		/* count aggregated RX frame into stats */
504 		num_list_elements += qdf_nbuf_get_gso_segs(nbuf_list);
505 		rx_thread->stats.nbuf_dequeued += num_list_elements;
506 		iterates += num_list_elements;
507 
508 		vdev_id = QDF_NBUF_CB_RX_VDEV_ID(nbuf_list);
509 		cdp_get_os_rx_handles_from_vdev(soc, vdev_id, &stack_fn,
510 						&osif_vdev);
511 		dp_debug("rx_thread %pK sending packet %pK to stack",
512 			 rx_thread, nbuf_list);
513 		if (!stack_fn || !osif_vdev ||
514 		    QDF_STATUS_SUCCESS != stack_fn(osif_vdev, nbuf_list)) {
515 			rx_thread->stats.dropped_invalid_os_rx_handles +=
516 							num_list_elements;
517 			qdf_nbuf_list_free(nbuf_list);
518 		} else {
519 			rx_thread->stats.nbuf_sent_to_stack +=
520 							num_list_elements;
521 		}
522 		if (qdf_unlikely(dp_rx_thread_should_yield(rx_thread,
523 							   iterates))) {
524 			rx_thread->stats.rx_nbufq_loop_yield++;
525 			break;
526 		}
527 		nbuf_list = dp_rx_tm_thread_dequeue(rx_thread);
528 	}
529 
530 	dp_debug("exit: qlen  %u",
531 		 qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue));
532 
533 	return 0;
534 }
535 
536 /**
537  * dp_rx_thread_gro_flush() - flush GRO packets for the RX thread
538  * @rx_thread: rx_thread to be processed
539  * @gro_flush_code: flush code to differentiating flushes
540  *
541  * Return: void
542  */
543 static void dp_rx_thread_gro_flush(struct dp_rx_thread *rx_thread,
544 				   enum dp_rx_gro_flush_code gro_flush_code)
545 {
546 	struct wlan_dp_psoc_context *dp_ctx;
547 
548 	dp_ctx =  dp_get_context();
549 	if (!dp_ctx) {
550 		dp_err("DP context is NULL");
551 		return;
552 	}
553 	dp_debug("flushing packets for thread %u", rx_thread->id);
554 	qdf_local_bh_disable();
555 	dp_ctx->dp_ops.dp_rx_thread_napi_gro_flush(&rx_thread->napi,
556 						   gro_flush_code);
557 	qdf_local_bh_enable();
558 	rx_thread->stats.gro_flushes++;
559 }
560 
561 /**
562  * dp_rx_should_flush() - Determines whether the RX thread should be flushed.
563  * @rx_thread: rx_thread to be processed
564  *
565  * Return: enum dp_rx_gro_flush_code
566  */
567 static inline enum dp_rx_gro_flush_code
568 dp_rx_should_flush(struct dp_rx_thread *rx_thread)
569 {
570 	enum dp_rx_gro_flush_code gro_flush_code;
571 
572 	gro_flush_code = qdf_atomic_read(&rx_thread->gro_flush_ind);
573 	if (qdf_atomic_test_bit(RX_VDEV_DEL_EVENT, &rx_thread->event_flag))
574 		gro_flush_code = DP_RX_GRO_NORMAL_FLUSH;
575 
576 	return gro_flush_code;
577 }
578 
579 /**
580  * dp_rx_thread_sub_loop() - rx thread subloop
581  * @rx_thread: rx_thread to be processed
582  * @shutdown: pointer to shutdown variable
583  *
584  * The function handles shutdown and suspend events from other
585  * threads and processes nbuf queue of a rx thread. In case a
586  * shutdown event is received from some other wlan thread, the
587  * function sets the shutdown pointer to true and returns
588  *
589  * Returns: 0 on success, error code on failure
590  */
591 static int dp_rx_thread_sub_loop(struct dp_rx_thread *rx_thread, bool *shutdown)
592 {
593 	enum dp_rx_gro_flush_code gro_flush_code;
594 
595 	while (true) {
596 		if (qdf_atomic_test_and_clear_bit(RX_SHUTDOWN_EVENT,
597 						  &rx_thread->event_flag)) {
598 			if (qdf_atomic_test_and_clear_bit(RX_SUSPEND_EVENT,
599 							  &rx_thread->event_flag)) {
600 				qdf_event_set(&rx_thread->suspend_event);
601 			}
602 			dp_debug("shutting down (%s) id %d pid %d",
603 				 qdf_get_current_comm(), rx_thread->id,
604 				 qdf_get_current_pid());
605 			*shutdown = true;
606 			break;
607 		}
608 
609 		dp_rx_thread_process_nbufq(rx_thread);
610 
611 		gro_flush_code = dp_rx_should_flush(rx_thread);
612 		/* Only flush when gro_flush_code is either
613 		 * DP_RX_GRO_NORMAL_FLUSH or DP_RX_GRO_LOW_TPUT_FLUSH
614 		 */
615 		if (gro_flush_code != DP_RX_GRO_NOT_FLUSH) {
616 			dp_rx_thread_gro_flush(rx_thread, gro_flush_code);
617 			qdf_atomic_set(&rx_thread->gro_flush_ind, 0);
618 		}
619 
620 		if (qdf_atomic_test_and_clear_bit(RX_VDEV_DEL_EVENT,
621 						  &rx_thread->event_flag)) {
622 			rx_thread->stats.gro_flushes_by_vdev_del++;
623 			qdf_event_set(&rx_thread->vdev_del_event);
624 			if (qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue))
625 				continue;
626 		}
627 
628 		if (qdf_atomic_test_and_clear_bit(RX_SUSPEND_EVENT,
629 						  &rx_thread->event_flag)) {
630 			dp_debug("received suspend ind (%s) id %d pid %d",
631 				 qdf_get_current_comm(), rx_thread->id,
632 				 qdf_get_current_pid());
633 			qdf_event_set(&rx_thread->suspend_event);
634 			dp_debug("waiting for resume (%s) id %d pid %d",
635 				 qdf_get_current_comm(), rx_thread->id,
636 				 qdf_get_current_pid());
637 			qdf_wait_single_event(&rx_thread->resume_event, 0);
638 		}
639 		break;
640 	}
641 	return 0;
642 }
643 
644 /**
645  * dp_rx_thread_loop() - main dp rx thread loop
646  * @arg: pointer to dp_rx_thread structure for the rx thread
647  *
648  * Return: thread exit code
649  */
650 static int dp_rx_thread_loop(void *arg)
651 {
652 	struct dp_rx_thread *rx_thread = arg;
653 	bool shutdown = false;
654 	int status;
655 	QDF_STATUS status_intr;
656 	struct dp_rx_tm_handle_cmn *tm_handle_cmn;
657 
658 	if (!arg) {
659 		dp_err("bad Args passed");
660 		return 0;
661 	}
662 
663 	tm_handle_cmn = rx_thread->rtm_handle_cmn;
664 
665 	qdf_set_user_nice(qdf_get_current_task(), -1);
666 	qdf_set_wake_up_idle(true);
667 
668 	qdf_event_set(&rx_thread->start_event);
669 	dp_info("starting rx_thread (%s) id %d pid %d", qdf_get_current_comm(),
670 		rx_thread->id, qdf_get_current_pid());
671 	while (!shutdown) {
672 		/* This implements the execution model algorithm */
673 		dp_debug("sleeping");
674 		status =
675 		    qdf_wait_queue_interruptible
676 				(rx_thread->wait_q,
677 				 qdf_atomic_test_bit(RX_POST_EVENT,
678 						     &rx_thread->event_flag) ||
679 				 qdf_atomic_test_bit(RX_SUSPEND_EVENT,
680 						     &rx_thread->event_flag) ||
681 				 qdf_atomic_test_bit(RX_VDEV_DEL_EVENT,
682 						     &rx_thread->event_flag));
683 		dp_debug("woken up");
684 		status_intr = qdf_status_from_os_return(status);
685 		if (status_intr == QDF_STATUS_E_RESTART) {
686 			QDF_DEBUG_PANIC("wait_event_interruptible returned -ERESTARTSYS");
687 			break;
688 		}
689 		qdf_atomic_clear_bit(RX_POST_EVENT, &rx_thread->event_flag);
690 		dp_rx_thread_sub_loop(rx_thread, &shutdown);
691 	}
692 
693 	/* If we get here the scheduler thread must exit */
694 	dp_info("exiting (%s) id %d pid %d", qdf_get_current_comm(),
695 		rx_thread->id, qdf_get_current_pid());
696 	qdf_event_set(&rx_thread->shutdown_event);
697 
698 	return 0;
699 }
700 
701 static int dp_rx_refill_thread_sub_loop(struct dp_rx_refill_thread *rx_thread,
702 					bool *shutdown)
703 {
704 	while (true) {
705 		if (qdf_atomic_test_and_clear_bit(RX_REFILL_SHUTDOWN_EVENT,
706 						  &rx_thread->event_flag)) {
707 			if (qdf_atomic_test_and_clear_bit(RX_REFILL_SUSPEND_EVENT,
708 							  &rx_thread->event_flag)) {
709 				qdf_event_set(&rx_thread->suspend_event);
710 			}
711 			dp_debug("shutting down (%s) pid %d",
712 				 qdf_get_current_comm(), qdf_get_current_pid());
713 			*shutdown = true;
714 			break;
715 		}
716 
717 		dp_rx_refill_buff_pool_enqueue((struct dp_soc *)rx_thread->soc);
718 
719 		if (qdf_atomic_test_and_clear_bit(RX_REFILL_SUSPEND_EVENT,
720 						  &rx_thread->event_flag)) {
721 			dp_debug("refill thread received suspend ind (%s) pid %d",
722 				 qdf_get_current_comm(),
723 				 qdf_get_current_pid());
724 			qdf_event_set(&rx_thread->suspend_event);
725 			dp_debug("refill thread waiting for resume (%s) pid %d",
726 				 qdf_get_current_comm(),
727 				 qdf_get_current_pid());
728 			qdf_wait_single_event(&rx_thread->resume_event, 0);
729 		}
730 		break;
731 	}
732 	return 0;
733 }
734 
735 static int dp_rx_refill_thread_loop(void *arg)
736 {
737 	struct dp_rx_refill_thread *rx_thread = arg;
738 	bool shutdown = false;
739 	int status;
740 	QDF_STATUS status_intr;
741 
742 	if (!arg) {
743 		dp_err("bad Args passed");
744 		return 0;
745 	}
746 
747 	qdf_set_user_nice(qdf_get_current_task(), -1);
748 	qdf_set_wake_up_idle(true);
749 
750 	qdf_event_set(&rx_thread->start_event);
751 	dp_info("starting rx_refill_thread (%s) pid %d", qdf_get_current_comm(),
752 		qdf_get_current_pid());
753 	while (!shutdown) {
754 		/* This implements the execution model algorithm */
755 		status =
756 		    qdf_wait_queue_interruptible
757 				(rx_thread->wait_q,
758 				 qdf_atomic_test_bit(RX_REFILL_POST_EVENT,
759 						     &rx_thread->event_flag) ||
760 				 qdf_atomic_test_bit(RX_REFILL_SUSPEND_EVENT,
761 						     &rx_thread->event_flag));
762 
763 		status_intr = qdf_status_from_os_return(status);
764 		if (status_intr == QDF_STATUS_E_RESTART) {
765 			QDF_DEBUG_PANIC("wait_event_interruptible returned -ERESTARTSYS");
766 			break;
767 		}
768 		qdf_atomic_clear_bit(RX_REFILL_POST_EVENT,
769 				     &rx_thread->event_flag);
770 		dp_rx_refill_thread_sub_loop(rx_thread, &shutdown);
771 	}
772 
773 	/* If we get here the scheduler thread must exit */
774 	dp_info("exiting (%s) pid %d", qdf_get_current_comm(),
775 		qdf_get_current_pid());
776 	qdf_event_set(&rx_thread->shutdown_event);
777 
778 	return 0;
779 }
780 
781 /**
782  * dp_rx_tm_thread_napi_poll() - dummy napi poll for rx_thread NAPI
783  * @napi: pointer to DP rx_thread NAPI
784  * @budget: NAPI BUDGET
785  *
786  * Return: 0 as it is not supposed to be polled at all as it is not scheduled.
787  */
788 static int dp_rx_tm_thread_napi_poll(qdf_napi_struct *napi, int budget)
789 {
790 	QDF_DEBUG_PANIC("this napi_poll should not be polled as we don't schedule it");
791 
792 	return 0;
793 }
794 
795 /**
796  * dp_rx_tm_thread_napi_init() - Initialize dummy rx_thread NAPI
797  * @rx_thread: dp_rx_thread structure containing dummy napi and netdev
798  *
799  * Return: None
800  */
801 static void dp_rx_tm_thread_napi_init(struct dp_rx_thread *rx_thread)
802 {
803 	/* Todo - optimize to use only one dummy netdev for all thread napis */
804 	qdf_net_if_create_dummy_if((struct qdf_net_if *)&rx_thread->netdev);
805 	qdf_netif_napi_add(&rx_thread->netdev, &rx_thread->napi,
806 			   dp_rx_tm_thread_napi_poll, 64);
807 	qdf_napi_enable(&rx_thread->napi);
808 }
809 
810 /**
811  * dp_rx_tm_thread_napi_deinit() - De-initialize dummy rx_thread NAPI
812  * @rx_thread: dp_rx_thread handle containing dummy napi and netdev
813  *
814  * Return: None
815  */
816 static void dp_rx_tm_thread_napi_deinit(struct dp_rx_thread *rx_thread)
817 {
818 	qdf_netif_napi_del(&rx_thread->napi);
819 }
820 
821 /*
822  * dp_rx_tm_thread_init() - Initialize dp_rx_thread structure and thread
823  *
824  * @rx_thread: dp_rx_thread structure to be initialized
825  * @id: id of the thread to be initialized
826  *
827  * Return: QDF_STATUS on success, QDF error code on failure
828  */
829 static QDF_STATUS dp_rx_tm_thread_init(struct dp_rx_thread *rx_thread,
830 				       uint8_t id)
831 {
832 	char thread_name[15];
833 	QDF_STATUS qdf_status;
834 
835 	qdf_mem_zero(thread_name, sizeof(thread_name));
836 
837 	if (!rx_thread) {
838 		dp_err("rx_thread is null!");
839 		return QDF_STATUS_E_FAULT;
840 	}
841 	rx_thread->id = id;
842 	rx_thread->event_flag = 0;
843 	qdf_nbuf_queue_head_init(&rx_thread->nbuf_queue);
844 	qdf_event_create(&rx_thread->start_event);
845 	qdf_event_create(&rx_thread->suspend_event);
846 	qdf_event_create(&rx_thread->resume_event);
847 	qdf_event_create(&rx_thread->shutdown_event);
848 	qdf_event_create(&rx_thread->vdev_del_event);
849 	qdf_atomic_init(&rx_thread->gro_flush_ind);
850 	qdf_init_waitqueue_head(&rx_thread->wait_q);
851 	qdf_scnprintf(thread_name, sizeof(thread_name), "dp_rx_thread_%u", id);
852 	dp_info("%s %u", thread_name, id);
853 
854 	if (cdp_cfg_get(dp_rx_tm_get_soc_handle(rx_thread->rtm_handle_cmn),
855 			cfg_dp_gro_enable))
856 		dp_rx_tm_thread_napi_init(rx_thread);
857 
858 	rx_thread->task = qdf_create_thread(dp_rx_thread_loop,
859 					    rx_thread, thread_name);
860 	if (!rx_thread->task) {
861 		dp_err("could not create dp_rx_thread %d", id);
862 		return QDF_STATUS_E_FAILURE;
863 	}
864 
865 	qdf_wake_up_process(rx_thread->task);
866 	qdf_status = qdf_wait_single_event(&rx_thread->start_event, 0);
867 
868 	if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
869 		dp_err("failed waiting for thread creation id %d", id);
870 		return QDF_STATUS_E_FAILURE;
871 	}
872 	return QDF_STATUS_SUCCESS;
873 }
874 
875 /*
876  * dp_rx_tm_thread_deinit() - De-Initialize dp_rx_thread structure and thread
877  * @rx_thread: dp_rx_thread structure to be de-initialized
878  * @id: id of the thread to be initialized
879  *
880  * Return: QDF_STATUS_SUCCESS
881  */
882 static QDF_STATUS dp_rx_tm_thread_deinit(struct dp_rx_thread *rx_thread)
883 {
884 	qdf_event_destroy(&rx_thread->start_event);
885 	qdf_event_destroy(&rx_thread->suspend_event);
886 	qdf_event_destroy(&rx_thread->resume_event);
887 	qdf_event_destroy(&rx_thread->shutdown_event);
888 	qdf_event_destroy(&rx_thread->vdev_del_event);
889 
890 	if (cdp_cfg_get(dp_rx_tm_get_soc_handle(rx_thread->rtm_handle_cmn),
891 			cfg_dp_gro_enable))
892 		dp_rx_tm_thread_napi_deinit(rx_thread);
893 
894 	return QDF_STATUS_SUCCESS;
895 }
896 
897 QDF_STATUS dp_rx_refill_thread_init(struct dp_rx_refill_thread *refill_thread)
898 {
899 	char refill_thread_name[20] = {0};
900 	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
901 
902 	qdf_scnprintf(refill_thread_name, sizeof(refill_thread_name),
903 		      "dp_refill_thread");
904 	dp_info("Initializing %s", refill_thread_name);
905 
906 	refill_thread->state = DP_RX_REFILL_THREAD_INVALID;
907 	refill_thread->event_flag = 0;
908 	qdf_event_create(&refill_thread->start_event);
909 	qdf_event_create(&refill_thread->suspend_event);
910 	qdf_event_create(&refill_thread->resume_event);
911 	qdf_event_create(&refill_thread->shutdown_event);
912 	qdf_init_waitqueue_head(&refill_thread->wait_q);
913 	refill_thread->task = qdf_create_thread(dp_rx_refill_thread_loop,
914 						refill_thread,
915 						refill_thread_name);
916 	if (!refill_thread->task) {
917 		dp_err("could not create dp_rx_refill_thread");
918 		return QDF_STATUS_E_FAILURE;
919 	}
920 	qdf_wake_up_process(refill_thread->task);
921 	qdf_status = qdf_wait_single_event(&refill_thread->start_event,
922 					   0);
923 	if (!QDF_IS_STATUS_SUCCESS(qdf_status)) {
924 		dp_err("failed waiting for refill thread creation status: %d",
925 		       qdf_status);
926 		return QDF_STATUS_E_FAILURE;
927 	}
928 
929 	dp_rx_refill_thread_set_affinity(refill_thread);
930 
931 	refill_thread->state = DP_RX_REFILL_THREAD_RUNNING;
932 	return QDF_STATUS_SUCCESS;
933 }
934 
935 QDF_STATUS dp_rx_refill_thread_deinit(struct dp_rx_refill_thread *refill_thread)
936 {
937 	qdf_set_bit(RX_REFILL_SHUTDOWN_EVENT,
938 		    &refill_thread->event_flag);
939 	qdf_set_bit(RX_REFILL_POST_EVENT,
940 		    &refill_thread->event_flag);
941 	qdf_wake_up_interruptible(&refill_thread->wait_q);
942 	qdf_wait_single_event(&refill_thread->shutdown_event, 0);
943 
944 	qdf_event_destroy(&refill_thread->start_event);
945 	qdf_event_destroy(&refill_thread->suspend_event);
946 	qdf_event_destroy(&refill_thread->resume_event);
947 	qdf_event_destroy(&refill_thread->shutdown_event);
948 
949 	refill_thread->state = DP_RX_REFILL_THREAD_INVALID;
950 	return QDF_STATUS_SUCCESS;
951 }
952 
953 QDF_STATUS dp_rx_tm_init(struct dp_rx_tm_handle *rx_tm_hdl,
954 			 uint8_t num_dp_rx_threads)
955 {
956 	int i;
957 	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
958 
959 	if (num_dp_rx_threads > DP_MAX_RX_THREADS) {
960 		dp_err("unable to initialize %u number of threads. MAX %u",
961 		       num_dp_rx_threads, DP_MAX_RX_THREADS);
962 		return QDF_STATUS_E_INVAL;
963 	}
964 
965 	rx_tm_hdl->num_dp_rx_threads = num_dp_rx_threads;
966 	rx_tm_hdl->state = DP_RX_THREADS_INVALID;
967 
968 	dp_info("initializing %u threads", num_dp_rx_threads);
969 
970 	/* allocate an array to contain the DP RX thread pointers */
971 	rx_tm_hdl->rx_thread = qdf_mem_malloc(num_dp_rx_threads *
972 					      sizeof(struct dp_rx_thread *));
973 
974 	if (qdf_unlikely(!rx_tm_hdl->rx_thread)) {
975 		qdf_status = QDF_STATUS_E_NOMEM;
976 		goto ret;
977 	}
978 
979 	for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) {
980 		rx_tm_hdl->rx_thread[i] =
981 			(struct dp_rx_thread *)
982 			qdf_mem_malloc(sizeof(struct dp_rx_thread));
983 		if (qdf_unlikely(!rx_tm_hdl->rx_thread[i])) {
984 			QDF_ASSERT(0);
985 			qdf_status = QDF_STATUS_E_NOMEM;
986 			goto ret;
987 		}
988 		rx_tm_hdl->rx_thread[i]->rtm_handle_cmn =
989 				(struct dp_rx_tm_handle_cmn *)rx_tm_hdl;
990 		qdf_status =
991 			dp_rx_tm_thread_init(rx_tm_hdl->rx_thread[i], i);
992 		if (!QDF_IS_STATUS_SUCCESS(qdf_status))
993 			break;
994 	}
995 ret:
996 	if (!QDF_IS_STATUS_SUCCESS(qdf_status))
997 		dp_rx_tm_deinit(rx_tm_hdl);
998 	else
999 		rx_tm_hdl->state = DP_RX_THREADS_RUNNING;
1000 
1001 	return qdf_status;
1002 }
1003 
1004 /**
1005  * dp_rx_tm_suspend() - suspend DP RX threads
1006  * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread
1007  *            infrastructure
1008  *
1009  * Return: Success/Failure
1010  */
1011 QDF_STATUS dp_rx_tm_suspend(struct dp_rx_tm_handle *rx_tm_hdl)
1012 {
1013 	int i;
1014 	QDF_STATUS qdf_status;
1015 	struct dp_rx_thread *rx_thread;
1016 
1017 	if (rx_tm_hdl->state == DP_RX_THREADS_SUSPENDED) {
1018 		dp_info("already in suspend state! Ignoring.");
1019 		return QDF_STATUS_E_INVAL;
1020 	}
1021 
1022 	rx_tm_hdl->state = DP_RX_THREADS_SUSPENDING;
1023 
1024 	for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) {
1025 		if (!rx_tm_hdl->rx_thread[i])
1026 			continue;
1027 		qdf_event_reset(&rx_tm_hdl->rx_thread[i]->resume_event);
1028 		qdf_event_reset(&rx_tm_hdl->rx_thread[i]->suspend_event);
1029 		qdf_set_bit(RX_SUSPEND_EVENT,
1030 			    &rx_tm_hdl->rx_thread[i]->event_flag);
1031 		qdf_wake_up_interruptible(&rx_tm_hdl->rx_thread[i]->wait_q);
1032 	}
1033 
1034 	for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) {
1035 		rx_thread = rx_tm_hdl->rx_thread[i];
1036 		if (!rx_thread)
1037 			continue;
1038 		dp_debug("thread %d", i);
1039 		qdf_status = qdf_wait_single_event(&rx_thread->suspend_event,
1040 						   DP_RX_THREAD_WAIT_TIMEOUT);
1041 		if (QDF_IS_STATUS_SUCCESS(qdf_status))
1042 			dp_debug("thread:%d suspended", rx_thread->id);
1043 		else
1044 			goto suspend_fail;
1045 	}
1046 	rx_tm_hdl->state = DP_RX_THREADS_SUSPENDED;
1047 
1048 	return QDF_STATUS_SUCCESS;
1049 
1050 suspend_fail:
1051 	dp_err("thread:%d %s(%d) while waiting for suspend",
1052 	       rx_thread->id,
1053 	       qdf_status == QDF_STATUS_E_TIMEOUT ? "timeout out" : "failed",
1054 	       qdf_status);
1055 
1056 	dp_rx_tm_resume(rx_tm_hdl);
1057 
1058 	return qdf_status;
1059 }
1060 
1061 /**
1062  * dp_rx_refill_thread_suspend() - Suspend DP RX refill threads
1063  * @refill_thread: containing the overall refill thread infrastructure
1064  *
1065  * Return: Success/Failure
1066  */
1067 QDF_STATUS
1068 dp_rx_refill_thread_suspend(struct dp_rx_refill_thread *refill_thread)
1069 {
1070 	QDF_STATUS qdf_status;
1071 
1072 	if (refill_thread->state == DP_RX_REFILL_THREAD_SUSPENDED) {
1073 		dp_info("already in suspend state! Ignoring.");
1074 		return QDF_STATUS_E_INVAL;
1075 	}
1076 
1077 	refill_thread->state = DP_RX_REFILL_THREAD_SUSPENDING;
1078 
1079 	qdf_event_reset(&refill_thread->resume_event);
1080 	qdf_event_reset(&refill_thread->suspend_event);
1081 	qdf_set_bit(RX_REFILL_SUSPEND_EVENT,
1082 		    &refill_thread->event_flag);
1083 	qdf_wake_up_interruptible(&refill_thread->wait_q);
1084 
1085 	qdf_status = qdf_wait_single_event(&refill_thread->suspend_event,
1086 					   DP_RX_THREAD_WAIT_TIMEOUT);
1087 	if (QDF_IS_STATUS_SUCCESS(qdf_status))
1088 		dp_debug("Refill thread  suspended");
1089 	else
1090 		goto suspend_fail;
1091 
1092 	refill_thread->state = DP_RX_REFILL_THREAD_SUSPENDED;
1093 	return QDF_STATUS_SUCCESS;
1094 
1095 suspend_fail:
1096 	dp_err("Refill thread %s(%d) while waiting for suspend",
1097 	       qdf_status == QDF_STATUS_E_TIMEOUT ? "timeout out" : "failed",
1098 	       qdf_status);
1099 
1100 	dp_rx_refill_thread_resume(refill_thread);
1101 
1102 	return qdf_status;
1103 }
1104 
1105 /*
1106  * dp_rx_tm_flush_nbuf_list() - Flush rx thread nbuf list
1107  * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread
1108  *  infrastructure
1109  * @vdev_id: vdev id for which packets are to be flushed
1110  *
1111  * Return: None
1112  */
1113 static inline void
1114 dp_rx_tm_flush_nbuf_list(struct dp_rx_tm_handle *rx_tm_hdl, uint8_t vdev_id)
1115 {
1116 	qdf_nbuf_t nbuf_list, tmp_nbuf_list;
1117 	uint32_t num_list_elements = 0;
1118 	uint64_t lock_time, unlock_time, flush_time;
1119 	qdf_nbuf_t nbuf_list_head = NULL, nbuf_list_next;
1120 	struct dp_rx_thread *rx_thread;
1121 	int i;
1122 
1123 	for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) {
1124 		rx_thread = rx_tm_hdl->rx_thread[i];
1125 		if (!rx_thread)
1126 			continue;
1127 
1128 		qdf_nbuf_queue_head_lock(&rx_thread->nbuf_queue);
1129 		lock_time = qdf_get_log_timestamp();
1130 		QDF_NBUF_QUEUE_WALK_SAFE(&rx_thread->nbuf_queue, nbuf_list,
1131 					 tmp_nbuf_list) {
1132 			if (QDF_NBUF_CB_RX_VDEV_ID(nbuf_list) == vdev_id) {
1133 				qdf_nbuf_unlink_no_lock(nbuf_list,
1134 							&rx_thread->nbuf_queue);
1135 				DP_RX_HEAD_APPEND(nbuf_list_head, nbuf_list);
1136 			}
1137 		}
1138 		qdf_nbuf_queue_head_unlock(&rx_thread->nbuf_queue);
1139 		unlock_time = qdf_get_log_timestamp();
1140 
1141 		while (nbuf_list_head) {
1142 			nbuf_list_next = qdf_nbuf_queue_next(nbuf_list_head);
1143 			qdf_nbuf_set_next(nbuf_list_head, NULL);
1144 			dp_rx_thread_adjust_nbuf_list(nbuf_list_head);
1145 			num_list_elements =
1146 			    QDF_NBUF_CB_RX_NUM_ELEMENTS_IN_LIST(nbuf_list_head);
1147 			rx_thread->stats.rx_flushed += num_list_elements;
1148 			qdf_nbuf_list_free(nbuf_list_head);
1149 			nbuf_list_head = nbuf_list_next;
1150 		}
1151 
1152 		flush_time = qdf_get_log_timestamp();
1153 		dp_info("Thread: %u lock held time: %llu us flush time: %llu us",
1154 			rx_thread->id,
1155 			qdf_log_timestamp_to_usecs(unlock_time - lock_time),
1156 			qdf_log_timestamp_to_usecs(flush_time - unlock_time));
1157 
1158 		qdf_event_reset(&rx_thread->vdev_del_event);
1159 		qdf_set_bit(RX_VDEV_DEL_EVENT, &rx_thread->event_flag);
1160 		qdf_wake_up_interruptible(&rx_thread->wait_q);
1161 		}
1162 }
1163 
1164 /**
1165  * dp_rx_tm_wait_vdev_del_event() - wait on rx thread vdev delete event
1166  * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread
1167  *  infrastructure
1168  *
1169  * Return: None
1170  */
1171 static inline void
1172 dp_rx_tm_wait_vdev_del_event(struct dp_rx_tm_handle *rx_tm_hdl)
1173 {
1174 	QDF_STATUS qdf_status;
1175 	int i;
1176 	struct dp_rx_thread *rx_thread;
1177 	int wait_timeout = DP_RX_THREAD_FLUSH_TIMEOUT;
1178 	uint64_t entry_time, exit_time, wait_time;
1179 
1180 	for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) {
1181 		rx_thread = rx_tm_hdl->rx_thread[i];
1182 		if (!rx_thread)
1183 			continue;
1184 
1185 		entry_time = qdf_get_log_timestamp();
1186 		qdf_status =
1187 			qdf_wait_single_event(&rx_thread->vdev_del_event,
1188 					      wait_timeout);
1189 
1190 		if (QDF_IS_STATUS_SUCCESS(qdf_status))
1191 			dp_debug("thread:%d napi gro flush successfully",
1192 				 rx_thread->id);
1193 		else if (qdf_status == QDF_STATUS_E_TIMEOUT) {
1194 			dp_err("thread:%d timed out waiting for napi gro flush",
1195 			       rx_thread->id);
1196 			/*
1197 			 * If timeout, then force flush in case any rx packets
1198 			 * belong to this vdev is still pending on stack queue,
1199 			 * while net_vdev will be freed soon.
1200 			 */
1201 			dp_rx_thread_gro_flush(rx_thread,
1202 					       DP_RX_GRO_NORMAL_FLUSH);
1203 		} else {
1204 			dp_err("thread:%d event wait failed with status: %d",
1205 			       rx_thread->id, qdf_status);
1206 		}
1207 
1208 		exit_time = qdf_get_log_timestamp();
1209 		wait_time = qdf_do_div(qdf_log_timestamp_to_usecs(exit_time -
1210 								  entry_time),
1211 				       QDF_USEC_PER_MSEC);
1212 		/**
1213 		 *
1214 		 * Maximum wait timeout to flush all thread is
1215 		 * DP_RX_THREAD_MIN_FLUSH_TIMEOUT, This logic
1216 		 * limit maximum wait time of all the thread to
1217 		 * DP_RX_THREAD_FLUSH_TIMEOUT. In case if
1218 		 * DP_RX_THREAD_FLUSH_TIMEOUT is exhausted
1219 		 * give remaining thread DP_RX_THREAD_MIN_FLUSH_TIMEOUT.
1220 		 *
1221 		 * Since all the threads are already woken before calling
1222 		 * wait API. So each thread will get
1223 		 * DP_RX_THREAD_FLUSH_TIMEOUT to set the event.
1224 		 */
1225 		if (wait_timeout > DP_RX_THREAD_MIN_FLUSH_TIMEOUT)
1226 			wait_timeout = (wait_timeout > wait_time) ?
1227 				       (wait_timeout - wait_time) :
1228 				       DP_RX_THREAD_MIN_FLUSH_TIMEOUT;
1229 	}
1230 }
1231 
1232 /**
1233  * dp_rx_tm_flush_by_vdev_id() - flush rx packets by vdev_id in all
1234  * rx thread queues
1235  * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread
1236  * infrastructure
1237  * @vdev_id: vdev id for which packets are to be flushed
1238  *
1239  * Return: QDF_STATUS_SUCCESS
1240  */
1241 QDF_STATUS dp_rx_tm_flush_by_vdev_id(struct dp_rx_tm_handle *rx_tm_hdl,
1242 				     uint8_t vdev_id)
1243 {
1244 	uint64_t entry_time, exit_time;
1245 
1246 	entry_time = qdf_get_log_timestamp();
1247 	dp_rx_tm_flush_nbuf_list(rx_tm_hdl, vdev_id);
1248 	dp_rx_tm_wait_vdev_del_event(rx_tm_hdl);
1249 	exit_time = qdf_get_log_timestamp();
1250 	dp_info("Vdev: %u total flush time: %llu us",
1251 		vdev_id,
1252 		qdf_log_timestamp_to_usecs(exit_time - entry_time));
1253 
1254 	return QDF_STATUS_SUCCESS;
1255 }
1256 
1257 /**
1258  * dp_rx_tm_resume() - resume DP RX threads
1259  * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread
1260  * infrastructure
1261  *
1262  * Return: QDF_STATUS_SUCCESS on resume success. QDF error otherwise.
1263  */
1264 QDF_STATUS dp_rx_tm_resume(struct dp_rx_tm_handle *rx_tm_hdl)
1265 {
1266 	int i;
1267 
1268 	if (rx_tm_hdl->state != DP_RX_THREADS_SUSPENDED &&
1269 	    rx_tm_hdl->state != DP_RX_THREADS_SUSPENDING) {
1270 		dp_info("resume callback received w/o suspend! Ignoring.");
1271 		return QDF_STATUS_E_INVAL;
1272 	}
1273 
1274 	for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) {
1275 		if (!rx_tm_hdl->rx_thread[i])
1276 			continue;
1277 		dp_debug("calling thread %d to resume", i);
1278 
1279 		/* postively reset event_flag for DP_RX_THREADS_SUSPENDING
1280 		 * state
1281 		 */
1282 		qdf_clear_bit(RX_SUSPEND_EVENT,
1283 			      &rx_tm_hdl->rx_thread[i]->event_flag);
1284 		qdf_event_set(&rx_tm_hdl->rx_thread[i]->resume_event);
1285 	}
1286 
1287 	rx_tm_hdl->state = DP_RX_THREADS_RUNNING;
1288 
1289 	return QDF_STATUS_SUCCESS;
1290 }
1291 
1292 /**
1293  * dp_rx_refill_thread_resume() - Resume DP RX refill threads
1294  * @refill_thread: refill_thread containing the overall thread infrastructure
1295  *
1296  * Return: QDF_STATUS_SUCCESS on resume success. QDF error otherwise.
1297  */
1298 QDF_STATUS dp_rx_refill_thread_resume(struct dp_rx_refill_thread *refill_thread)
1299 {
1300 	dp_debug("calling refill thread to resume");
1301 
1302 	if (refill_thread->state != DP_RX_REFILL_THREAD_SUSPENDED &&
1303 	    refill_thread->state != DP_RX_REFILL_THREAD_SUSPENDING) {
1304 		dp_info("resume callback received in %d state ! Ignoring.",
1305 			refill_thread->state);
1306 		return QDF_STATUS_E_INVAL;
1307 	}
1308 
1309 	/* postively reset event_flag for DP_RX_REFILL_THREAD_SUSPENDING
1310 	 * state
1311 	 */
1312 	qdf_clear_bit(RX_REFILL_SUSPEND_EVENT,
1313 		      &refill_thread->event_flag);
1314 	qdf_event_set(&refill_thread->resume_event);
1315 
1316 	refill_thread->state = DP_RX_REFILL_THREAD_RUNNING;
1317 
1318 	return QDF_STATUS_SUCCESS;
1319 }
1320 
1321 /**
1322  * dp_rx_tm_shutdown() - shutdown all DP RX threads
1323  * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread infrastructure
1324  *
1325  * Return: QDF_STATUS_SUCCESS
1326  */
1327 static QDF_STATUS dp_rx_tm_shutdown(struct dp_rx_tm_handle *rx_tm_hdl)
1328 {
1329 	int i;
1330 
1331 	for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) {
1332 		if (!rx_tm_hdl->rx_thread[i] ||
1333 		    rx_tm_hdl->state == DP_RX_THREADS_INVALID)
1334 			continue;
1335 		qdf_set_bit(RX_SHUTDOWN_EVENT,
1336 			    &rx_tm_hdl->rx_thread[i]->event_flag);
1337 		qdf_set_bit(RX_POST_EVENT,
1338 			    &rx_tm_hdl->rx_thread[i]->event_flag);
1339 		qdf_wake_up_interruptible(&rx_tm_hdl->rx_thread[i]->wait_q);
1340 	}
1341 
1342 	for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) {
1343 		if (!rx_tm_hdl->rx_thread[i] ||
1344 		    rx_tm_hdl->state == DP_RX_THREADS_INVALID)
1345 			continue;
1346 		dp_debug("waiting for shutdown of thread %d", i);
1347 		qdf_wait_single_event(&rx_tm_hdl->rx_thread[i]->shutdown_event,
1348 				      0);
1349 	}
1350 	rx_tm_hdl->state = DP_RX_THREADS_INVALID;
1351 	return QDF_STATUS_SUCCESS;
1352 }
1353 
1354 /**
1355  * dp_rx_tm_deinit() - de-initialize RX thread infrastructure
1356  * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread
1357  * infrastructure
1358  *
1359  * Return: QDF_STATUS_SUCCESS
1360  */
1361 QDF_STATUS dp_rx_tm_deinit(struct dp_rx_tm_handle *rx_tm_hdl)
1362 {
1363 	int i = 0;
1364 
1365 	if (!rx_tm_hdl->rx_thread) {
1366 		dp_err("rx_tm_hdl->rx_thread not initialized!");
1367 		return QDF_STATUS_SUCCESS;
1368 	}
1369 
1370 	dp_rx_tm_shutdown(rx_tm_hdl);
1371 
1372 	for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) {
1373 		if (!rx_tm_hdl->rx_thread[i])
1374 			continue;
1375 		dp_rx_tm_thread_deinit(rx_tm_hdl->rx_thread[i]);
1376 		qdf_mem_free(rx_tm_hdl->rx_thread[i]);
1377 	}
1378 
1379 	/* free the array of RX thread pointers*/
1380 	qdf_mem_free(rx_tm_hdl->rx_thread);
1381 	rx_tm_hdl->rx_thread = NULL;
1382 
1383 	return QDF_STATUS_SUCCESS;
1384 }
1385 
1386 /**
1387  * dp_rx_tm_select_thread() - select a DP RX thread for a nbuf
1388  * @rx_tm_hdl: dp_rx_tm_handle containing the overall thread
1389  * infrastructure
1390  * @reo_ring_num: REO ring number corresponding to the thread
1391  *
1392  * The function relies on the presence of QDF_NBUF_CB_RX_CTX_ID passed to it
1393  * from the nbuf list. Depending on the RX_CTX (copy engine or reo
1394  * ring) on which the packet was received, the function selects
1395  * a corresponding rx_thread.
1396  *
1397  * Return: rx thread ID selected for the nbuf
1398  */
1399 static uint8_t dp_rx_tm_select_thread(struct dp_rx_tm_handle *rx_tm_hdl,
1400 				      uint8_t reo_ring_num)
1401 {
1402 	uint8_t selected_rx_thread;
1403 
1404 	selected_rx_thread = reo_ring_num % rx_tm_hdl->num_dp_rx_threads;
1405 	dp_debug("ring_num %d, selected thread %u", reo_ring_num,
1406 		 selected_rx_thread);
1407 
1408 	return selected_rx_thread;
1409 }
1410 
1411 QDF_STATUS dp_rx_tm_enqueue_pkt(struct dp_rx_tm_handle *rx_tm_hdl,
1412 				qdf_nbuf_t nbuf_list)
1413 {
1414 	uint8_t selected_thread_id;
1415 
1416 	selected_thread_id =
1417 		dp_rx_tm_select_thread(rx_tm_hdl,
1418 				       QDF_NBUF_CB_RX_CTX_ID(nbuf_list));
1419 	dp_rx_tm_thread_enqueue(rx_tm_hdl->rx_thread[selected_thread_id],
1420 				nbuf_list);
1421 	return QDF_STATUS_SUCCESS;
1422 }
1423 
1424 QDF_STATUS
1425 dp_rx_tm_gro_flush_ind(struct dp_rx_tm_handle *rx_tm_hdl, int rx_ctx_id,
1426 		       enum dp_rx_gro_flush_code flush_code)
1427 {
1428 	uint8_t selected_thread_id;
1429 
1430 	selected_thread_id = dp_rx_tm_select_thread(rx_tm_hdl, rx_ctx_id);
1431 	dp_rx_tm_thread_gro_flush_ind(rx_tm_hdl->rx_thread[selected_thread_id],
1432 				      flush_code);
1433 
1434 	return QDF_STATUS_SUCCESS;
1435 }
1436 
1437 qdf_napi_struct *dp_rx_tm_get_napi_context(struct dp_rx_tm_handle *rx_tm_hdl,
1438 					   uint8_t rx_ctx_id)
1439 {
1440 	uint8_t selected_thread_id;
1441 
1442 	selected_thread_id = dp_rx_tm_select_thread(rx_tm_hdl, rx_ctx_id);
1443 
1444 	return &rx_tm_hdl->rx_thread[selected_thread_id]->napi;
1445 }
1446 
1447 QDF_STATUS dp_rx_tm_set_cpu_mask(struct dp_rx_tm_handle *rx_tm_hdl,
1448 				 qdf_cpu_mask *new_mask)
1449 {
1450 	int i = 0;
1451 
1452 	for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) {
1453 		if (!rx_tm_hdl->rx_thread[i])
1454 			continue;
1455 		qdf_thread_set_cpus_allowed_mask(rx_tm_hdl->rx_thread[i]->task,
1456 						 new_mask);
1457 	}
1458 	return QDF_STATUS_SUCCESS;
1459 }
1460 
1461 /**
1462  * dp_rx_refill_thread_schedule() - Schedule rx refill thread
1463  * @soc: ol_txrx_soc_handle object
1464  *
1465  */
1466 #ifdef WLAN_FEATURE_RX_PREALLOC_BUFFER_POOL
1467 static void dp_rx_refill_thread_schedule(ol_txrx_soc_handle soc)
1468 {
1469 	struct dp_rx_refill_thread *rx_thread;
1470 	struct dp_txrx_handle *dp_ext_hdl;
1471 
1472 	if (!soc)
1473 		return;
1474 
1475 	dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc);
1476 	if (!dp_ext_hdl)
1477 		return;
1478 
1479 	rx_thread = &dp_ext_hdl->refill_thread;
1480 	qdf_set_bit(RX_REFILL_POST_EVENT, &rx_thread->event_flag);
1481 	qdf_wake_up_interruptible(&rx_thread->wait_q);
1482 }
1483 #else
1484 static void dp_rx_refill_thread_schedule(ol_txrx_soc_handle soc)
1485 {
1486 }
1487 #endif
1488 
1489 /**
1490  * dp_get_rx_threads_num() - Get number of threads in use
1491  * @soc: ol_txrx_soc_handle object
1492  *
1493  * Return: number of threads
1494  */
1495 static uint8_t dp_get_rx_threads_num(ol_txrx_soc_handle soc)
1496 {
1497 	return cdp_get_num_rx_contexts(soc);
1498 }
1499 
1500 QDF_STATUS dp_txrx_init(ol_txrx_soc_handle soc, uint8_t pdev_id,
1501 			struct dp_txrx_config *config)
1502 {
1503 	struct dp_txrx_handle *dp_ext_hdl;
1504 	QDF_STATUS qdf_status = QDF_STATUS_SUCCESS;
1505 	uint8_t num_dp_rx_threads;
1506 	struct dp_pdev *pdev;
1507 	struct dp_soc *dp_soc;
1508 
1509 	if (qdf_unlikely(!soc)) {
1510 		dp_err("soc is NULL");
1511 		return 0;
1512 	}
1513 
1514 	pdev = dp_get_pdev_from_soc_pdev_id_wifi3(cdp_soc_t_to_dp_soc(soc),
1515 						  pdev_id);
1516 	if (!pdev) {
1517 		dp_err("pdev is NULL");
1518 		return 0;
1519 	}
1520 
1521 	dp_ext_hdl = qdf_mem_malloc(sizeof(*dp_ext_hdl));
1522 	if (!dp_ext_hdl) {
1523 		QDF_ASSERT(0);
1524 		return QDF_STATUS_E_NOMEM;
1525 	}
1526 
1527 	dp_info("dp_txrx_handle allocated");
1528 	dp_ext_hdl->soc = soc;
1529 	dp_ext_hdl->pdev = dp_pdev_to_cdp_pdev(pdev);
1530 	cdp_soc_set_dp_txrx_handle(soc, dp_ext_hdl);
1531 	qdf_mem_copy(&dp_ext_hdl->config, config, sizeof(*config));
1532 	dp_ext_hdl->rx_tm_hdl.txrx_handle_cmn =
1533 				dp_txrx_get_cmn_hdl_frm_ext_hdl(dp_ext_hdl);
1534 
1535 	dp_soc = cdp_soc_t_to_dp_soc(soc);
1536 	if (wlan_cfg_is_rx_refill_buffer_pool_enabled(dp_soc->wlan_cfg_ctx)) {
1537 		dp_ext_hdl->refill_thread.soc = soc;
1538 		dp_ext_hdl->refill_thread.enabled = true;
1539 		qdf_status =
1540 			dp_rx_refill_thread_init(&dp_ext_hdl->refill_thread);
1541 		if (qdf_status != QDF_STATUS_SUCCESS) {
1542 			dp_err("Failed to initialize RX refill thread status:%d",
1543 			       qdf_status);
1544 			qdf_mem_free(dp_ext_hdl);
1545 			return qdf_status;
1546 		}
1547 		cdp_register_rx_refill_thread_sched_handler(soc,
1548 							    dp_rx_refill_thread_schedule);
1549 	}
1550 
1551 	num_dp_rx_threads = dp_get_rx_threads_num(soc);
1552 	dp_info("%d RX threads in use", num_dp_rx_threads);
1553 
1554 	if (dp_ext_hdl->config.enable_rx_threads) {
1555 		qdf_status = dp_rx_tm_init(&dp_ext_hdl->rx_tm_hdl,
1556 					   num_dp_rx_threads);
1557 	}
1558 
1559 	if (QDF_IS_STATUS_ERROR(qdf_status))
1560 		dp_txrx_deinit(soc);
1561 
1562 	return qdf_status;
1563 }
1564 
1565 QDF_STATUS dp_txrx_deinit(ol_txrx_soc_handle soc)
1566 {
1567 	struct dp_txrx_handle *dp_ext_hdl;
1568 	struct dp_soc *dp_soc;
1569 
1570 	if (!soc)
1571 		return QDF_STATUS_E_INVAL;
1572 
1573 	dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc);
1574 	if (!dp_ext_hdl)
1575 		return QDF_STATUS_E_FAULT;
1576 
1577 	dp_soc = cdp_soc_t_to_dp_soc(soc);
1578 	if (wlan_cfg_is_rx_refill_buffer_pool_enabled(dp_soc->wlan_cfg_ctx)) {
1579 		dp_rx_refill_thread_deinit(&dp_ext_hdl->refill_thread);
1580 		dp_ext_hdl->refill_thread.soc = NULL;
1581 		dp_ext_hdl->refill_thread.enabled = false;
1582 	}
1583 
1584 	if (dp_ext_hdl->config.enable_rx_threads)
1585 		dp_rx_tm_deinit(&dp_ext_hdl->rx_tm_hdl);
1586 
1587 	qdf_mem_free(dp_ext_hdl);
1588 	dp_info("dp_txrx_handle_t de-allocated");
1589 
1590 	cdp_soc_set_dp_txrx_handle(soc, NULL);
1591 
1592 	return QDF_STATUS_SUCCESS;
1593 }
1594 
1595 /**
1596  * dp_rx_tm_get_pending() - get number of frame in thread
1597  * nbuf queue pending
1598  * @soc: ol_txrx_soc_handle object
1599  *
1600  * Return: number of frames
1601  */
1602 #ifdef FEATURE_WLAN_DP_RX_THREADS
1603 int dp_rx_tm_get_pending(ol_txrx_soc_handle soc)
1604 {
1605 	int i;
1606 	int num_pending = 0;
1607 	struct dp_rx_thread *rx_thread;
1608 	struct dp_txrx_handle *dp_ext_hdl;
1609 	struct dp_rx_tm_handle *rx_tm_hdl;
1610 
1611 	if (!soc)
1612 		return 0;
1613 
1614 	dp_ext_hdl = cdp_soc_get_dp_txrx_handle(soc);
1615 	if (!dp_ext_hdl)
1616 		return 0;
1617 
1618 	rx_tm_hdl = &dp_ext_hdl->rx_tm_hdl;
1619 
1620 	for (i = 0; i < rx_tm_hdl->num_dp_rx_threads; i++) {
1621 		rx_thread = rx_tm_hdl->rx_thread[i];
1622 		if (!rx_thread)
1623 			continue;
1624 		num_pending += qdf_nbuf_queue_head_qlen(&rx_thread->nbuf_queue);
1625 	}
1626 
1627 	if (num_pending)
1628 		dp_debug("pending frames in thread queue %d", num_pending);
1629 
1630 	return num_pending;
1631 }
1632 #else
1633 int dp_rx_tm_get_pending(ol_txrx_soc_handle soc)
1634 {
1635 	return 0;
1636 }
1637 #endif
1638