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(¬ifier->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(¬ifier->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