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  /**
21   *  DOC: CDS Scheduler Implementation
22   */
23  
24  #include <cds_api.h>
25  #include <ani_global.h>
26  #include <sir_types.h>
27  #include <qdf_types.h>
28  #include <lim_api.h>
29  #include <sme_api.h>
30  #include <wlan_qct_sys.h>
31  #include "cds_sched.h"
32  #include <wlan_hdd_power.h>
33  #include "wma_types.h"
34  #include <linux/spinlock.h>
35  #include <linux/kthread.h>
36  #include <linux/cpu.h>
37  #ifdef RX_PERFORMANCE
38  #include <linux/sched/types.h>
39  #endif
40  #include "wlan_dp_ucfg_api.h"
41  
42  /*
43   * The following commit was introduced in v5.17:
44   * cead18552660 ("exit: Rename complete_and_exit to kthread_complete_and_exit")
45   * Use the old name for kernels before 5.17
46   */
47  #if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 17, 0))
48  /**
49   * kthread_complete_and_exit - completes the thread and exit
50   * @c: thread or task to be completed
51   * @s: exit code
52   */
53  #define kthread_complete_and_exit(c, s) complete_and_exit(c, s)
54  #endif
55  
56  static spinlock_t ssr_protect_lock;
57  
58  struct shutdown_notifier {
59  	struct list_head list;
60  	void (*cb)(void *priv);
61  	void *priv;
62  };
63  
64  struct list_head shutdown_notifier_head;
65  
66  enum notifier_state {
67  	NOTIFIER_STATE_NONE,
68  	NOTIFIER_STATE_NOTIFYING,
69  } notifier_state;
70  
71  static p_cds_sched_context gp_cds_sched_context;
72  
73  #ifdef WLAN_DP_LEGACY_OL_RX_THREAD
74  static int cds_ol_rx_thread(void *arg);
75  static uint32_t affine_cpu;
76  static QDF_STATUS cds_alloc_ol_rx_pkt_freeq(p_cds_sched_context pSchedContext);
77  
78  #define CDS_CORE_PER_CLUSTER (4)
79  /*Maximum 2 clusters supported*/
80  #define CDS_MAX_CPU_CLUSTERS 2
81  
82  #define CDS_CPU_CLUSTER_TYPE_LITTLE 0
83  #define CDS_CPU_CLUSTER_TYPE_PERF 1
84  
85  static inline
cds_set_cpus_allowed_ptr_with_cpu(struct task_struct * task,unsigned long cpu)86  int cds_set_cpus_allowed_ptr_with_cpu(struct task_struct *task,
87  				      unsigned long cpu)
88  {
89  	return set_cpus_allowed_ptr(task, cpumask_of(cpu));
90  }
91  
92  static inline
cds_set_cpus_allowed_ptr_with_mask(struct task_struct * task,qdf_cpu_mask * new_mask)93  int cds_set_cpus_allowed_ptr_with_mask(struct task_struct *task,
94  				       qdf_cpu_mask *new_mask)
95  {
96  	return set_cpus_allowed_ptr(task, new_mask);
97  }
98  
cds_set_rx_thread_cpu_mask(uint8_t cpu_affinity_mask)99  void cds_set_rx_thread_cpu_mask(uint8_t cpu_affinity_mask)
100  {
101  	p_cds_sched_context sched_context = get_cds_sched_ctxt();
102  
103  	if (!sched_context) {
104  		qdf_err("invalid context");
105  		return;
106  	}
107  	sched_context->conf_rx_thread_cpu_mask = cpu_affinity_mask;
108  }
109  
cds_set_rx_thread_ul_cpu_mask(uint8_t cpu_affinity_mask)110  void cds_set_rx_thread_ul_cpu_mask(uint8_t cpu_affinity_mask)
111  {
112  	p_cds_sched_context sched_context = get_cds_sched_ctxt();
113  
114  	if (!sched_context) {
115  		qdf_err("invalid context");
116  		return;
117  	}
118  	sched_context->conf_rx_thread_ul_affinity = cpu_affinity_mask;
119  }
120  
121  #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 4, 0))
122  /**
123   * cds_rx_thread_log_cpu_affinity_change() - Log Rx thread affinity change
124   * @core_affine_cnt: Available cores
125   * @tput_req: Throughput request
126   * @old_mask: Old affinity mask
127   * @new_mask: New affinity mask
128   *
129   * Return: NONE
130   */
cds_rx_thread_log_cpu_affinity_change(unsigned char core_affine_cnt,int tput_req,struct cpumask * old_mask,struct cpumask * new_mask)131  static void cds_rx_thread_log_cpu_affinity_change(unsigned char core_affine_cnt,
132  						  int tput_req,
133  						  struct cpumask *old_mask,
134  						  struct cpumask *new_mask)
135  {
136  	char new_mask_str[10];
137  	char old_mask_str[10];
138  
139  	qdf_mem_zero(new_mask_str, sizeof(new_mask_str));
140  	qdf_mem_zero(new_mask_str, sizeof(old_mask_str));
141  
142  	cpumap_print_to_pagebuf(false, old_mask_str, old_mask);
143  	cpumap_print_to_pagebuf(false, new_mask_str, new_mask);
144  
145  	cds_debug("num online cores %d, high tput req %d, Rx_thread old mask %s new mask %s",
146  		  core_affine_cnt, tput_req, old_mask_str, new_mask_str);
147  }
148  #else
cds_rx_thread_log_cpu_affinity_change(unsigned char core_affine_cnt,int tput_req,struct cpumask * old_mask,struct cpumask * new_mask)149  static void cds_rx_thread_log_cpu_affinity_change(unsigned char core_affine_cnt,
150  						  int tput_req,
151  						  struct cpumask *old_mask,
152  						  struct cpumask *new_mask)
153  {
154  }
155  #endif
156  
157  /**
158   * cds_sched_find_attach_cpu - find available cores and attach to required core
159   * @pSchedContext:	wlan scheduler context
160   * @high_throughput:	high throughput is required or not
161   *
162   * Find current online cores.
163   * During high TPUT,
164   * 1) If user INI configured cores, affine to those cores
165   * 2) Otherwise perf cores.
166   * 3) Otherwise to all cores.
167   *
168   * During low TPUT, set affinity to any core, let system decide.
169   *
170   * Return: 0 success
171   *         1 fail
172   */
cds_sched_find_attach_cpu(p_cds_sched_context pSchedContext,bool high_throughput)173  static int cds_sched_find_attach_cpu(p_cds_sched_context pSchedContext,
174  	bool high_throughput)
175  {
176  	unsigned char core_affine_count = 0;
177  	qdf_cpu_mask new_mask;
178  	unsigned long cpus;
179  	struct cds_config_info *cds_cfg;
180  
181  	cds_debug("num possible cpu %d", num_possible_cpus());
182  
183  	qdf_cpumask_clear(&new_mask);
184  
185  	if (high_throughput) {
186  		/* Get Online perf/pwr CPU count */
187  		for_each_online_cpu(cpus) {
188  			if (topology_physical_package_id(cpus) >
189  							CDS_MAX_CPU_CLUSTERS) {
190  				cds_err("can handle max %d clusters, returning...",
191  					CDS_MAX_CPU_CLUSTERS);
192  				goto err;
193  			}
194  
195  			if (pSchedContext->conf_rx_thread_cpu_mask) {
196  				if (pSchedContext->conf_rx_thread_cpu_mask &
197  								(1 << cpus))
198  					qdf_cpumask_set_cpu(cpus, &new_mask);
199  			} else if (topology_physical_package_id(cpus) ==
200  						 CDS_CPU_CLUSTER_TYPE_PERF) {
201  				qdf_cpumask_set_cpu(cpus, &new_mask);
202  			}
203  
204  			core_affine_count++;
205  		}
206  	} else {
207  		/* Attach to all cores, let scheduler decide */
208  		qdf_cpumask_setall(&new_mask);
209  	}
210  
211  	cds_rx_thread_log_cpu_affinity_change(core_affine_count,
212  				(int)pSchedContext->high_throughput_required,
213  				&pSchedContext->rx_thread_cpu_mask,
214  				&new_mask);
215  
216  	if (!cpumask_equal(&pSchedContext->rx_thread_cpu_mask, &new_mask)) {
217  		cds_cfg = cds_get_ini_config();
218  		cpumask_copy(&pSchedContext->rx_thread_cpu_mask, &new_mask);
219  		if (cds_cfg && cds_cfg->enable_dp_rx_threads)
220  			ucfg_dp_txrx_set_cpu_mask(cds_get_context(QDF_MODULE_ID_SOC),
221  						  &new_mask);
222  		else
223  			cds_set_cpus_allowed_ptr_with_mask(pSchedContext->ol_rx_thread,
224  							   &new_mask);
225  	}
226  
227  	return 0;
228  err:
229  	return 1;
230  }
231  
cds_sched_handle_cpu_hot_plug(void)232  int cds_sched_handle_cpu_hot_plug(void)
233  {
234  	p_cds_sched_context pSchedContext = get_cds_sched_ctxt();
235  
236  	if (!pSchedContext) {
237  		cds_err("invalid context");
238  		return 1;
239  	}
240  
241  	if (cds_is_load_or_unload_in_progress())
242  		return 0;
243  
244  	mutex_lock(&pSchedContext->affinity_lock);
245  	if (cds_sched_find_attach_cpu(pSchedContext,
246  		pSchedContext->high_throughput_required)) {
247  		cds_err("handle hot plug fail");
248  		mutex_unlock(&pSchedContext->affinity_lock);
249  		return 1;
250  	}
251  	mutex_unlock(&pSchedContext->affinity_lock);
252  	return 0;
253  }
254  
cds_sched_handle_rx_thread_affinity_req(bool high_throughput)255  void cds_sched_handle_rx_thread_affinity_req(bool high_throughput)
256  {
257  	p_cds_sched_context pschedcontext = get_cds_sched_ctxt();
258  	unsigned long cpus;
259  	qdf_cpu_mask new_mask;
260  	unsigned char core_affine_count = 0;
261  
262  	if (!pschedcontext || !pschedcontext->ol_rx_thread)
263  		return;
264  
265  	if (cds_is_load_or_unload_in_progress()) {
266  		cds_err("load or unload in progress");
267  		return;
268  	}
269  
270  	if (pschedcontext->rx_affinity_required == high_throughput)
271  		return;
272  
273  	pschedcontext->rx_affinity_required = high_throughput;
274  	qdf_cpumask_clear(&new_mask);
275  	if (!high_throughput) {
276  		/* Attach to all cores, let scheduler decide */
277  		qdf_cpumask_setall(&new_mask);
278  		goto affine_thread;
279  	}
280  	for_each_online_cpu(cpus) {
281  		if (topology_physical_package_id(cpus) >
282  		    CDS_MAX_CPU_CLUSTERS) {
283  			cds_err("can handle max %d clusters ",
284  				CDS_MAX_CPU_CLUSTERS);
285  			return;
286  		}
287  		if (pschedcontext->conf_rx_thread_ul_affinity &&
288  		    (pschedcontext->conf_rx_thread_ul_affinity &
289  				 (1 << cpus)))
290  			qdf_cpumask_set_cpu(cpus, &new_mask);
291  
292  		core_affine_count++;
293  	}
294  
295  affine_thread:
296  	cds_rx_thread_log_cpu_affinity_change(
297  		core_affine_count,
298  		(int)pschedcontext->rx_affinity_required,
299  		&pschedcontext->rx_thread_cpu_mask,
300  		&new_mask);
301  
302  	mutex_lock(&pschedcontext->affinity_lock);
303  	if (!cpumask_equal(&pschedcontext->rx_thread_cpu_mask, &new_mask)) {
304  		cpumask_copy(&pschedcontext->rx_thread_cpu_mask, &new_mask);
305  		cds_set_cpus_allowed_ptr_with_mask(pschedcontext->ol_rx_thread,
306  						   &new_mask);
307  	}
308  	mutex_unlock(&pschedcontext->affinity_lock);
309  }
310  
cds_sched_handle_throughput_req(bool high_tput_required)311  int cds_sched_handle_throughput_req(bool high_tput_required)
312  {
313  	p_cds_sched_context pSchedContext = get_cds_sched_ctxt();
314  
315  	if (!pSchedContext) {
316  		cds_err("invalid context");
317  		return 1;
318  	}
319  
320  	if (cds_is_load_or_unload_in_progress()) {
321  		cds_err("load or unload in progress");
322  		return 0;
323  	}
324  
325  	mutex_lock(&pSchedContext->affinity_lock);
326  	if (pSchedContext->high_throughput_required != high_tput_required) {
327  		pSchedContext->high_throughput_required = high_tput_required;
328  		if (cds_sched_find_attach_cpu(pSchedContext,
329  					      high_tput_required)) {
330  			mutex_unlock(&pSchedContext->affinity_lock);
331  			return 1;
332  		}
333  	}
334  	mutex_unlock(&pSchedContext->affinity_lock);
335  	return 0;
336  }
337  
338  /**
339   * cds_cpu_hotplug_multi_cluster() - calls the multi-cluster hotplug handler,
340   *	when on a multi-cluster platform
341   *
342   * Return: QDF_STATUS
343   */
cds_cpu_hotplug_multi_cluster(void)344  static QDF_STATUS cds_cpu_hotplug_multi_cluster(void)
345  {
346  	int cpus;
347  	unsigned int multi_cluster = 0;
348  
349  	for_each_online_cpu(cpus) {
350  		multi_cluster = topology_physical_package_id(cpus);
351  	}
352  
353  	if (!multi_cluster)
354  		return QDF_STATUS_E_NOSUPPORT;
355  
356  	if (cds_sched_handle_cpu_hot_plug())
357  		return QDF_STATUS_E_FAILURE;
358  
359  	return QDF_STATUS_SUCCESS;
360  }
361  
362  /**
363   * __cds_cpu_hotplug_notify() - CPU hotplug event handler
364   * @cpu: CPU Id of the CPU generating the event
365   * @cpu_up: true if the CPU is online
366   *
367   * Return: None
368   */
__cds_cpu_hotplug_notify(uint32_t cpu,bool cpu_up)369  static void __cds_cpu_hotplug_notify(uint32_t cpu, bool cpu_up)
370  {
371  	unsigned long pref_cpu = 0;
372  	p_cds_sched_context pSchedContext = get_cds_sched_ctxt();
373  	int i;
374  
375  	if (!pSchedContext || !pSchedContext->ol_rx_thread)
376  		return;
377  
378  	if (cds_is_load_or_unload_in_progress() || cds_is_driver_recovering())
379  		return;
380  
381  	cds_debug("'%s' event on CPU %u (of %d); Currently affine to CPU %u",
382  		  cpu_up ? "Up" : "Down", cpu, num_possible_cpus(), affine_cpu);
383  
384  	/* try multi-cluster scheduling first */
385  	if (QDF_IS_STATUS_SUCCESS(cds_cpu_hotplug_multi_cluster()))
386  		return;
387  
388  	if (cpu_up) {
389  		if (affine_cpu != 0)
390  			return;
391  
392  		for_each_online_cpu(i) {
393  			if (i == 0)
394  				continue;
395  			pref_cpu = i;
396  			break;
397  		}
398  	} else {
399  		if (cpu != affine_cpu)
400  			return;
401  
402  		affine_cpu = 0;
403  		for_each_online_cpu(i) {
404  			if (i == 0)
405  				continue;
406  			pref_cpu = i;
407  			break;
408  		}
409  	}
410  
411  	if (pref_cpu == 0)
412  		return;
413  
414  	if (pSchedContext->ol_rx_thread &&
415  	    !cds_set_cpus_allowed_ptr_with_cpu(pSchedContext->ol_rx_thread,
416  					       pref_cpu))
417  		affine_cpu = pref_cpu;
418  }
419  
420  /**
421   * cds_cpu_hotplug_notify() - cpu core up/down notification handler wrapper
422   * @cpu: CPU Id of the CPU generating the event
423   * @cpu_up: true if the CPU is online
424   *
425   * Return: None
426   */
cds_cpu_hotplug_notify(uint32_t cpu,bool cpu_up)427  static void cds_cpu_hotplug_notify(uint32_t cpu, bool cpu_up)
428  {
429  	struct qdf_op_sync *op_sync;
430  
431  	if (qdf_op_protect(&op_sync))
432  		return;
433  
434  	__cds_cpu_hotplug_notify(cpu, cpu_up);
435  
436  	qdf_op_unprotect(op_sync);
437  }
438  
cds_cpu_online_cb(void * context,uint32_t cpu)439  static void cds_cpu_online_cb(void *context, uint32_t cpu)
440  {
441  	cds_cpu_hotplug_notify(cpu, true);
442  }
443  
cds_cpu_before_offline_cb(void * context,uint32_t cpu)444  static void cds_cpu_before_offline_cb(void *context, uint32_t cpu)
445  {
446  	cds_cpu_hotplug_notify(cpu, false);
447  }
448  #endif /* WLAN_DP_LEGACY_OL_RX_THREAD */
449  
cds_sched_open(void * p_cds_context,p_cds_sched_context pSchedContext,uint32_t SchedCtxSize)450  QDF_STATUS cds_sched_open(void *p_cds_context,
451  			  p_cds_sched_context pSchedContext,
452  			  uint32_t SchedCtxSize)
453  {
454  	cds_debug("Opening the CDS Scheduler");
455  	/* Sanity checks */
456  	if ((!p_cds_context) || (!pSchedContext)) {
457  		cds_err("Null params being passed");
458  		return QDF_STATUS_E_FAILURE;
459  	}
460  	if (sizeof(cds_sched_context) != SchedCtxSize) {
461  		cds_debug("Incorrect CDS Sched Context size passed");
462  		return QDF_STATUS_E_INVAL;
463  	}
464  	qdf_mem_zero(pSchedContext, sizeof(cds_sched_context));
465  #ifdef WLAN_DP_LEGACY_OL_RX_THREAD
466  	spin_lock_init(&pSchedContext->ol_rx_thread_lock);
467  	init_waitqueue_head(&pSchedContext->ol_rx_wait_queue);
468  	init_completion(&pSchedContext->ol_rx_start_event);
469  	init_completion(&pSchedContext->ol_suspend_rx_event);
470  	init_completion(&pSchedContext->ol_resume_rx_event);
471  	init_completion(&pSchedContext->ol_rx_shutdown);
472  	pSchedContext->ol_rx_event_flag = 0;
473  	spin_lock_init(&pSchedContext->ol_rx_queue_lock);
474  	spin_lock_init(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
475  	INIT_LIST_HEAD(&pSchedContext->ol_rx_thread_queue);
476  	spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
477  	INIT_LIST_HEAD(&pSchedContext->cds_ol_rx_pkt_freeq);
478  	spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
479  	if (cds_alloc_ol_rx_pkt_freeq(pSchedContext) != QDF_STATUS_SUCCESS)
480  		goto pkt_freeqalloc_failure;
481  	qdf_cpuhp_register(&pSchedContext->cpuhp_event_handle,
482  			   NULL,
483  			   cds_cpu_online_cb,
484  			   cds_cpu_before_offline_cb);
485  	mutex_init(&pSchedContext->affinity_lock);
486  	pSchedContext->high_throughput_required = false;
487  	pSchedContext->rx_affinity_required = false;
488  	pSchedContext->active_staid = OL_TXRX_INVALID_LOCAL_PEER_ID;
489  #endif
490  	gp_cds_sched_context = pSchedContext;
491  
492  #ifdef WLAN_DP_LEGACY_OL_RX_THREAD
493  	pSchedContext->ol_rx_thread = kthread_create(cds_ol_rx_thread,
494  						       pSchedContext,
495  						       "cds_ol_rx_thread");
496  	if (IS_ERR(pSchedContext->ol_rx_thread)) {
497  
498  		cds_alert("Could not Create CDS OL RX Thread");
499  		goto OL_RX_THREAD_START_FAILURE;
500  
501  	}
502  	wake_up_process(pSchedContext->ol_rx_thread);
503  	cds_debug("CDS OL RX thread Created");
504  	wait_for_completion_interruptible(&pSchedContext->ol_rx_start_event);
505  	cds_debug("CDS OL Rx Thread has started");
506  #endif
507  	/* We're good now: Let's get the ball rolling!!! */
508  	cds_debug("CDS Scheduler successfully Opened");
509  	return QDF_STATUS_SUCCESS;
510  #ifdef WLAN_DP_LEGACY_OL_RX_THREAD
511  OL_RX_THREAD_START_FAILURE:
512  	qdf_cpuhp_unregister(&pSchedContext->cpuhp_event_handle);
513  	cds_free_ol_rx_pkt_freeq(gp_cds_sched_context);
514  pkt_freeqalloc_failure:
515  #endif
516  	gp_cds_sched_context = NULL;
517  
518  	return QDF_STATUS_E_RESOURCES;
519  
520  } /* cds_sched_open() */
521  
522  #ifdef WLAN_DP_LEGACY_OL_RX_THREAD
cds_free_ol_rx_pkt_freeq(p_cds_sched_context pSchedContext)523  void cds_free_ol_rx_pkt_freeq(p_cds_sched_context pSchedContext)
524  {
525  	struct cds_ol_rx_pkt *pkt;
526  
527  	spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
528  	while (!list_empty(&pSchedContext->cds_ol_rx_pkt_freeq)) {
529  		pkt = list_entry((&pSchedContext->cds_ol_rx_pkt_freeq)->next,
530  			typeof(*pkt), list);
531  		list_del(&pkt->list);
532  		spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
533  		qdf_mem_free(pkt);
534  		spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
535  	}
536  	spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
537  }
538  
539  /**
540   * cds_alloc_ol_rx_pkt_freeq() - Function to allocate free buffer queue
541   * @pSchedContext: pointer to the global CDS Sched Context
542   *
543   * This API allocates CDS_MAX_OL_RX_PKT number of cds message buffers
544   * which are used for Rx data processing.
545   *
546   * Return: status of memory allocation
547   */
cds_alloc_ol_rx_pkt_freeq(p_cds_sched_context pSchedContext)548  static QDF_STATUS cds_alloc_ol_rx_pkt_freeq(p_cds_sched_context pSchedContext)
549  {
550  	struct cds_ol_rx_pkt *pkt, *tmp;
551  	int i;
552  
553  	for (i = 0; i < CDS_MAX_OL_RX_PKT; i++) {
554  		pkt = qdf_mem_malloc(sizeof(*pkt));
555  		if (!pkt) {
556  			cds_err("Vos packet allocation for ol rx thread failed");
557  			goto free;
558  		}
559  		spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
560  		list_add_tail(&pkt->list, &pSchedContext->cds_ol_rx_pkt_freeq);
561  		spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
562  	}
563  
564  	return QDF_STATUS_SUCCESS;
565  
566  free:
567  	spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
568  	list_for_each_entry_safe(pkt, tmp, &pSchedContext->cds_ol_rx_pkt_freeq,
569  				 list) {
570  		list_del(&pkt->list);
571  		spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
572  		qdf_mem_free(pkt);
573  		spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
574  	}
575  	spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
576  	return QDF_STATUS_E_NOMEM;
577  }
578  
579  void
cds_free_ol_rx_pkt(p_cds_sched_context pSchedContext,struct cds_ol_rx_pkt * pkt)580  cds_free_ol_rx_pkt(p_cds_sched_context pSchedContext,
581  		    struct cds_ol_rx_pkt *pkt)
582  {
583  	memset(pkt, 0, sizeof(*pkt));
584  	spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
585  	list_add_tail(&pkt->list, &pSchedContext->cds_ol_rx_pkt_freeq);
586  	spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
587  }
588  
cds_alloc_ol_rx_pkt(p_cds_sched_context pSchedContext)589  struct cds_ol_rx_pkt *cds_alloc_ol_rx_pkt(p_cds_sched_context pSchedContext)
590  {
591  	struct cds_ol_rx_pkt *pkt;
592  
593  	spin_lock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
594  	if (list_empty(&pSchedContext->cds_ol_rx_pkt_freeq)) {
595  		spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
596  		return NULL;
597  	}
598  	pkt = list_first_entry(&pSchedContext->cds_ol_rx_pkt_freeq,
599  			       struct cds_ol_rx_pkt, list);
600  	list_del(&pkt->list);
601  	spin_unlock_bh(&pSchedContext->cds_ol_rx_pkt_freeq_lock);
602  	return pkt;
603  }
604  
605  void
cds_indicate_rxpkt(p_cds_sched_context pSchedContext,struct cds_ol_rx_pkt * pkt)606  cds_indicate_rxpkt(p_cds_sched_context pSchedContext,
607  		   struct cds_ol_rx_pkt *pkt)
608  {
609  	spin_lock_bh(&pSchedContext->ol_rx_queue_lock);
610  	list_add_tail(&pkt->list, &pSchedContext->ol_rx_thread_queue);
611  	spin_unlock_bh(&pSchedContext->ol_rx_queue_lock);
612  	set_bit(RX_POST_EVENT, &pSchedContext->ol_rx_event_flag);
613  	wake_up_interruptible(&pSchedContext->ol_rx_wait_queue);
614  }
615  
cds_close_rx_thread(void)616  QDF_STATUS cds_close_rx_thread(void)
617  {
618  	cds_debug("invoked");
619  
620  	if (!gp_cds_sched_context) {
621  		cds_err("!gp_cds_sched_context");
622  		return QDF_STATUS_E_FAILURE;
623  	}
624  
625  	if (!gp_cds_sched_context->ol_rx_thread)
626  		return QDF_STATUS_SUCCESS;
627  
628  	/* Shut down Tlshim Rx thread */
629  	set_bit(RX_SHUTDOWN_EVENT, &gp_cds_sched_context->ol_rx_event_flag);
630  	set_bit(RX_POST_EVENT, &gp_cds_sched_context->ol_rx_event_flag);
631  	wake_up_interruptible(&gp_cds_sched_context->ol_rx_wait_queue);
632  	wait_for_completion(&gp_cds_sched_context->ol_rx_shutdown);
633  	gp_cds_sched_context->ol_rx_thread = NULL;
634  	cds_drop_rxpkt_by_staid(gp_cds_sched_context, WLAN_MAX_STA_COUNT);
635  	cds_free_ol_rx_pkt_freeq(gp_cds_sched_context);
636  	qdf_cpuhp_unregister(&gp_cds_sched_context->cpuhp_event_handle);
637  
638  	return QDF_STATUS_SUCCESS;
639  } /* cds_close_rx_thread */
640  
cds_drop_rxpkt_by_staid(p_cds_sched_context pSchedContext,uint16_t staId)641  void cds_drop_rxpkt_by_staid(p_cds_sched_context pSchedContext, uint16_t staId)
642  {
643  	struct list_head local_list;
644  	struct cds_ol_rx_pkt *pkt, *tmp;
645  	qdf_nbuf_t buf, next_buf;
646  	uint32_t timeout = 0;
647  
648  	INIT_LIST_HEAD(&local_list);
649  	spin_lock_bh(&pSchedContext->ol_rx_queue_lock);
650  	if (list_empty(&pSchedContext->ol_rx_thread_queue)) {
651  		spin_unlock_bh(&pSchedContext->ol_rx_queue_lock);
652  		return;
653  	}
654  	list_for_each_entry_safe(pkt, tmp, &pSchedContext->ol_rx_thread_queue,
655  								list) {
656  		if (pkt->staId == staId || staId == WLAN_MAX_STA_COUNT)
657  			list_move_tail(&pkt->list, &local_list);
658  	}
659  	spin_unlock_bh(&pSchedContext->ol_rx_queue_lock);
660  
661  	list_for_each_entry_safe(pkt, tmp, &local_list, list) {
662  		list_del(&pkt->list);
663  		buf = pkt->Rxpkt;
664  		while (buf) {
665  			next_buf = qdf_nbuf_queue_next(buf);
666  			qdf_nbuf_free(buf);
667  			buf = next_buf;
668  		}
669  		cds_free_ol_rx_pkt(pSchedContext, pkt);
670  	}
671  
672  	while (pSchedContext->active_staid == staId &&
673  	       timeout <= CDS_ACTIVE_STAID_CLEANUP_TIMEOUT) {
674  		if (qdf_in_interrupt())
675  			qdf_mdelay(CDS_ACTIVE_STAID_CLEANUP_DELAY);
676  		else
677  			qdf_sleep(CDS_ACTIVE_STAID_CLEANUP_DELAY);
678  		timeout += CDS_ACTIVE_STAID_CLEANUP_DELAY;
679  	}
680  
681  	if (pSchedContext->active_staid == staId)
682  		cds_err("Failed to cleanup RX packets for staId:%u", staId);
683  }
684  
685  /**
686   * cds_rx_from_queue() - function to process pending Rx packets
687   * @pSchedContext: Pointer to the global CDS Sched Context
688   *
689   * This api traverses the pending buffer list and calling the callback.
690   * This callback would essentially send the packet to HDD.
691   *
692   * Return: none
693   */
cds_rx_from_queue(p_cds_sched_context pSchedContext)694  static void cds_rx_from_queue(p_cds_sched_context pSchedContext)
695  {
696  	struct cds_ol_rx_pkt *pkt;
697  	uint16_t sta_id;
698  
699  	spin_lock_bh(&pSchedContext->ol_rx_queue_lock);
700  	while (!list_empty(&pSchedContext->ol_rx_thread_queue)) {
701  		pkt = list_first_entry(&pSchedContext->ol_rx_thread_queue,
702  				       struct cds_ol_rx_pkt, list);
703  		list_del(&pkt->list);
704  		pSchedContext->active_staid = pkt->staId;
705  		spin_unlock_bh(&pSchedContext->ol_rx_queue_lock);
706  		sta_id = pkt->staId;
707  		pkt->callback(pkt->context, pkt->Rxpkt, sta_id);
708  		cds_free_ol_rx_pkt(pSchedContext, pkt);
709  		spin_lock_bh(&pSchedContext->ol_rx_queue_lock);
710  		pSchedContext->active_staid = OL_TXRX_INVALID_LOCAL_PEER_ID;
711  	}
712  	spin_unlock_bh(&pSchedContext->ol_rx_queue_lock);
713  }
714  
715  /**
716   * cds_ol_rx_thread() - cds main tlshim rx thread
717   * @arg: pointer to the global CDS Sched Context
718   *
719   * This api is the thread handler for Tlshim Data packet processing.
720   *
721   * Return: thread exit code
722   */
cds_ol_rx_thread(void * arg)723  static int cds_ol_rx_thread(void *arg)
724  {
725  	p_cds_sched_context pSchedContext = (p_cds_sched_context) arg;
726  	bool shutdown = false;
727  	int status;
728  
729  #ifdef RX_THREAD_PRIORITY
730  	struct sched_param scheduler_params = {0};
731  
732  	scheduler_params.sched_priority = 1;
733  	sched_setscheduler(current, SCHED_FIFO, &scheduler_params);
734  #else
735  	set_user_nice(current, -1);
736  #endif
737  
738  	qdf_set_wake_up_idle(true);
739  
740  	complete(&pSchedContext->ol_rx_start_event);
741  
742  	while (!shutdown) {
743  		status =
744  			wait_event_interruptible(pSchedContext->ol_rx_wait_queue,
745  						 test_bit(RX_POST_EVENT,
746  							  &pSchedContext->ol_rx_event_flag)
747  						 || test_bit(RX_SUSPEND_EVENT,
748  							     &pSchedContext->ol_rx_event_flag));
749  		if (status == -ERESTARTSYS)
750  			break;
751  
752  		clear_bit(RX_POST_EVENT, &pSchedContext->ol_rx_event_flag);
753  		while (true) {
754  			if (test_bit(RX_SHUTDOWN_EVENT,
755  				     &pSchedContext->ol_rx_event_flag)) {
756  				clear_bit(RX_SHUTDOWN_EVENT,
757  					  &pSchedContext->ol_rx_event_flag);
758  				if (test_bit(RX_SUSPEND_EVENT,
759  					     &pSchedContext->ol_rx_event_flag)) {
760  					clear_bit(RX_SUSPEND_EVENT,
761  						  &pSchedContext->ol_rx_event_flag);
762  					complete
763  						(&pSchedContext->ol_suspend_rx_event);
764  				}
765  				cds_debug("Shutting down OL RX Thread");
766  				shutdown = true;
767  				break;
768  			}
769  			cds_rx_from_queue(pSchedContext);
770  
771  			if (test_bit(RX_SUSPEND_EVENT,
772  				     &pSchedContext->ol_rx_event_flag)) {
773  				clear_bit(RX_SUSPEND_EVENT,
774  					  &pSchedContext->ol_rx_event_flag);
775  				spin_lock(&pSchedContext->ol_rx_thread_lock);
776  				INIT_COMPLETION
777  					(pSchedContext->ol_resume_rx_event);
778  				complete(&pSchedContext->ol_suspend_rx_event);
779  				spin_unlock(&pSchedContext->ol_rx_thread_lock);
780  				wait_for_completion_interruptible
781  					(&pSchedContext->ol_resume_rx_event);
782  			}
783  			break;
784  		}
785  	}
786  
787  	cds_debug("Exiting CDS OL rx thread");
788  	kthread_complete_and_exit(&pSchedContext->ol_rx_shutdown, 0);
789  
790  	return 0;
791  }
792  
cds_resume_rx_thread(void)793  void cds_resume_rx_thread(void)
794  {
795  	p_cds_sched_context cds_sched_context;
796  
797  	cds_sched_context = get_cds_sched_ctxt();
798  	if (!cds_sched_context) {
799  		cds_err("cds_sched_context is NULL");
800  		return;
801  	}
802  
803  	complete(&cds_sched_context->ol_resume_rx_event);
804  }
805  #endif
806  
cds_sched_close(void)807  QDF_STATUS cds_sched_close(void)
808  {
809  	cds_debug("invoked");
810  
811  	if (!gp_cds_sched_context) {
812  		cds_err("!gp_cds_sched_context");
813  		return QDF_STATUS_E_FAILURE;
814  	}
815  
816  	cds_close_rx_thread();
817  
818  	gp_cds_sched_context = NULL;
819  	return QDF_STATUS_SUCCESS;
820  } /* cds_sched_close() */
821  
get_cds_sched_ctxt(void)822  p_cds_sched_context get_cds_sched_ctxt(void)
823  {
824  	/* Make sure that Vos Scheduler context has been initialized */
825  	if (!gp_cds_sched_context)
826  		cds_err("!gp_cds_sched_context");
827  
828  	return gp_cds_sched_context;
829  }
830  
cds_ssr_protect_init(void)831  void cds_ssr_protect_init(void)
832  {
833  	spin_lock_init(&ssr_protect_lock);
834  	INIT_LIST_HEAD(&shutdown_notifier_head);
835  }
836  
cds_shutdown_notifier_register(void (* cb)(void * priv),void * priv)837  QDF_STATUS cds_shutdown_notifier_register(void (*cb)(void *priv), void *priv)
838  {
839  	struct shutdown_notifier *notifier;
840  	unsigned long irq_flags;
841  
842  	notifier = qdf_mem_malloc(sizeof(*notifier));
843  
844  	if (!notifier)
845  		return QDF_STATUS_E_NOMEM;
846  
847  	/*
848  	 * This logic can be simpilfied if there is separate state maintained
849  	 * for shutdown and reinit. Right now there is only recovery in progress
850  	 * state and it doesn't help to check against it as during reinit some
851  	 * of the modules may need to register the call backs.
852  	 * For now this logic added to avoid notifier registration happen while
853  	 * this function is trying to call the call back with the notification.
854  	 */
855  	spin_lock_irqsave(&ssr_protect_lock, irq_flags);
856  	if (notifier_state == NOTIFIER_STATE_NOTIFYING) {
857  		spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
858  		qdf_mem_free(notifier);
859  		return -EINVAL;
860  	}
861  
862  	notifier->cb = cb;
863  	notifier->priv = priv;
864  
865  	list_add_tail(&notifier->list, &shutdown_notifier_head);
866  	spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
867  
868  	return 0;
869  }
870  
cds_shutdown_notifier_purge(void)871  void cds_shutdown_notifier_purge(void)
872  {
873  	struct shutdown_notifier *notifier, *temp;
874  	unsigned long irq_flags;
875  
876  	spin_lock_irqsave(&ssr_protect_lock, irq_flags);
877  	list_for_each_entry_safe(notifier, temp,
878  				 &shutdown_notifier_head, list) {
879  		list_del(&notifier->list);
880  		spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
881  
882  		qdf_mem_free(notifier);
883  
884  		spin_lock_irqsave(&ssr_protect_lock, irq_flags);
885  	}
886  
887  	spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
888  }
889  
cds_shutdown_notifier_call(void)890  void cds_shutdown_notifier_call(void)
891  {
892  	struct shutdown_notifier *notifier;
893  	unsigned long irq_flags;
894  
895  	spin_lock_irqsave(&ssr_protect_lock, irq_flags);
896  	notifier_state = NOTIFIER_STATE_NOTIFYING;
897  
898  	list_for_each_entry(notifier, &shutdown_notifier_head, list) {
899  		spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
900  
901  		notifier->cb(notifier->priv);
902  
903  		spin_lock_irqsave(&ssr_protect_lock, irq_flags);
904  	}
905  
906  	notifier_state = NOTIFIER_STATE_NONE;
907  	spin_unlock_irqrestore(&ssr_protect_lock, irq_flags);
908  }
909  
cds_get_gfp_flags(void)910  int cds_get_gfp_flags(void)
911  {
912  	int flags = GFP_KERNEL;
913  
914  	if (in_interrupt() || in_atomic() || irqs_disabled())
915  		flags = GFP_ATOMIC;
916  
917  	return flags;
918  }
919  
920  /**
921   * cds_get_rx_thread_pending(): get rx thread status
922   * @soc: ol_txrx_soc_handle object
923   *
924   * Return: 1 if rx thread is not empty.
925   *	   0 if rx thread is empty
926   */
927  #ifdef WLAN_DP_LEGACY_OL_RX_THREAD
cds_get_rx_thread_pending(ol_txrx_soc_handle soc)928  int cds_get_rx_thread_pending(ol_txrx_soc_handle soc)
929  {
930  	p_cds_sched_context cds_sched_context = get_cds_sched_ctxt();
931  
932  	if (!cds_sched_context) {
933  		cds_err("cds_sched_context is NULL");
934  		return 0;
935  	}
936  
937  	spin_lock_bh(&cds_sched_context->ol_rx_queue_lock);
938  
939  	if (list_empty(&cds_sched_context->ol_rx_thread_queue)) {
940  		spin_unlock_bh(&cds_sched_context->ol_rx_queue_lock);
941  		return 0;
942  	}
943  
944  	/* In helium there is no scope to get no of pending frames
945  	 * in rx thread, Hence return 1 if frames are queued
946  	 */
947  	spin_unlock_bh(&cds_sched_context->ol_rx_queue_lock);
948  	return 1;
949  }
950  #endif
951