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