1 /* 2 * Copyright (c) 2014-2018 The Linux Foundation. All rights reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for 5 * any purpose with or without fee is hereby granted, provided that the 6 * above copyright notice and this permission notice appear in all 7 * copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 * PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <scheduler_api.h> 20 #include <scheduler_core.h> 21 #include <qdf_atomic.h> 22 23 QDF_STATUS scheduler_disable(void) 24 { 25 struct scheduler_ctx *sched_ctx; 26 27 sched_info("Disabling Scheduler"); 28 29 sched_ctx = scheduler_get_context(); 30 QDF_BUG(sched_ctx); 31 if (!sched_ctx) { 32 sched_err("sched_ctx is NULL"); 33 return QDF_STATUS_E_INVAL; 34 } 35 36 /* send shutdown signal to scheduler thread */ 37 qdf_atomic_set_bit(MC_SHUTDOWN_EVENT_MASK, &sched_ctx->sch_event_flag); 38 qdf_atomic_set_bit(MC_POST_EVENT_MASK, &sched_ctx->sch_event_flag); 39 qdf_wake_up_interruptible(&sched_ctx->sch_wait_queue); 40 41 /* wait for scheduler thread to shutdown */ 42 qdf_wait_single_event(&sched_ctx->sch_shutdown, 0); 43 sched_ctx->sch_thread = NULL; 44 45 /* flush any unprocessed scheduler messages */ 46 scheduler_queues_flush(sched_ctx); 47 48 return QDF_STATUS_SUCCESS; 49 } 50 51 static inline void scheduler_watchdog_notify(struct scheduler_ctx *sched) 52 { 53 char symbol[QDF_SYMBOL_LEN]; 54 55 if (sched->watchdog_callback) 56 qdf_sprint_symbol(symbol, sched->watchdog_callback); 57 58 sched_err("WLAN_BUG_RCA: Callback %s (type 0x%x) exceeded its allotted time of %ds", 59 sched->watchdog_callback ? symbol : "<null>", 60 sched->watchdog_msg_type, SCHEDULER_WATCHDOG_TIMEOUT / 1000); 61 } 62 63 #ifdef CONFIG_SLUB_DEBUG_ON 64 static void scheduler_watchdog_timeout(void *arg) 65 { 66 struct scheduler_ctx *sched = arg; 67 68 scheduler_watchdog_notify(sched); 69 if (sched->sch_thread) 70 qdf_print_thread_trace(sched->sch_thread); 71 72 /* avoid crashing during shutdown */ 73 if (qdf_atomic_test_bit(MC_SHUTDOWN_EVENT_MASK, &sched->sch_event_flag)) 74 return; 75 76 sched_fatal("Going down for Scheduler Watchdog Bite!"); 77 QDF_BUG(0); 78 } 79 #else 80 static void scheduler_watchdog_timeout(void *arg) 81 { 82 scheduler_watchdog_notify((struct scheduler_ctx *)arg); 83 } 84 #endif 85 86 QDF_STATUS scheduler_enable(void) 87 { 88 struct scheduler_ctx *sched_ctx; 89 90 sched_info("Enabling Scheduler"); 91 92 sched_ctx = scheduler_get_context(); 93 QDF_BUG(sched_ctx); 94 if (!sched_ctx) { 95 sched_err("sched_ctx is null"); 96 return QDF_STATUS_E_INVAL; 97 } 98 99 qdf_atomic_clear_bit(MC_SHUTDOWN_EVENT_MASK, 100 &sched_ctx->sch_event_flag); 101 qdf_atomic_clear_bit(MC_POST_EVENT_MASK, 102 &sched_ctx->sch_event_flag); 103 104 /* create the scheduler thread */ 105 sched_ctx->sch_thread = qdf_create_thread(scheduler_thread, sched_ctx, 106 "scheduler_thread"); 107 if (IS_ERR(sched_ctx->sch_thread)) { 108 sched_err("Failed to create scheduler thread"); 109 return QDF_STATUS_E_RESOURCES; 110 } 111 112 sched_info("Scheduler thread created"); 113 114 /* wait for the scheduler thread to startup */ 115 qdf_wake_up_process(sched_ctx->sch_thread); 116 qdf_wait_single_event(&sched_ctx->sch_start_event, 0); 117 118 sched_info("Scheduler thread started"); 119 120 return QDF_STATUS_SUCCESS; 121 } 122 123 QDF_STATUS scheduler_init(void) 124 { 125 QDF_STATUS status; 126 struct scheduler_ctx *sched_ctx; 127 128 sched_info("Initializing Scheduler"); 129 130 status = scheduler_create_ctx(); 131 if (QDF_IS_STATUS_ERROR(status)) { 132 sched_err("Failed to create context; status:%d", status); 133 return status; 134 } 135 136 sched_ctx = scheduler_get_context(); 137 QDF_BUG(sched_ctx); 138 if (!sched_ctx) { 139 sched_err("sched_ctx is null"); 140 status = QDF_STATUS_E_FAILURE; 141 goto ctx_destroy; 142 } 143 144 status = scheduler_queues_init(sched_ctx); 145 if (QDF_IS_STATUS_ERROR(status)) { 146 sched_err("Failed to init queues; status:%d", status); 147 goto ctx_destroy; 148 } 149 150 status = qdf_event_create(&sched_ctx->sch_start_event); 151 if (QDF_IS_STATUS_ERROR(status)) { 152 sched_err("Failed to create start event; status:%d", status); 153 goto queues_deinit; 154 } 155 156 status = qdf_event_create(&sched_ctx->sch_shutdown); 157 if (QDF_IS_STATUS_ERROR(status)) { 158 sched_err("Failed to create shutdown event; status:%d", status); 159 goto start_event_destroy; 160 } 161 162 status = qdf_event_create(&sched_ctx->resume_sch_event); 163 if (QDF_IS_STATUS_ERROR(status)) { 164 sched_err("Failed to create resume event; status:%d", status); 165 goto shutdown_event_destroy; 166 } 167 168 qdf_spinlock_create(&sched_ctx->sch_thread_lock); 169 qdf_init_waitqueue_head(&sched_ctx->sch_wait_queue); 170 sched_ctx->sch_event_flag = 0; 171 qdf_timer_init(NULL, 172 &sched_ctx->watchdog_timer, 173 &scheduler_watchdog_timeout, 174 sched_ctx, 175 QDF_TIMER_TYPE_SW); 176 177 qdf_register_mc_timer_callback(scheduler_mc_timer_callback); 178 179 return QDF_STATUS_SUCCESS; 180 181 shutdown_event_destroy: 182 qdf_event_destroy(&sched_ctx->sch_shutdown); 183 184 start_event_destroy: 185 qdf_event_destroy(&sched_ctx->sch_start_event); 186 187 queues_deinit: 188 scheduler_queues_deinit(sched_ctx); 189 190 ctx_destroy: 191 scheduler_destroy_ctx(); 192 193 return status; 194 } 195 196 QDF_STATUS scheduler_deinit(void) 197 { 198 QDF_STATUS status; 199 struct scheduler_ctx *sched_ctx; 200 201 sched_info("Deinitializing Scheduler"); 202 203 sched_ctx = scheduler_get_context(); 204 QDF_BUG(sched_ctx); 205 if (!sched_ctx) { 206 sched_err("sched_ctx is null"); 207 return QDF_STATUS_E_INVAL; 208 } 209 210 qdf_timer_free(&sched_ctx->watchdog_timer); 211 qdf_spinlock_destroy(&sched_ctx->sch_thread_lock); 212 qdf_event_destroy(&sched_ctx->resume_sch_event); 213 qdf_event_destroy(&sched_ctx->sch_shutdown); 214 qdf_event_destroy(&sched_ctx->sch_start_event); 215 216 status = scheduler_queues_deinit(sched_ctx); 217 if (QDF_IS_STATUS_ERROR(status)) 218 sched_err("Failed to deinit queues; status:%d", status); 219 220 status = scheduler_destroy_ctx(); 221 if (QDF_IS_STATUS_ERROR(status)) 222 sched_err("Failed to destroy context; status:%d", status); 223 224 return QDF_STATUS_SUCCESS; 225 } 226 227 QDF_STATUS scheduler_post_msg_by_priority(QDF_MODULE_ID qid, 228 struct scheduler_msg *msg, 229 bool is_high_priority) 230 { 231 uint8_t qidx; 232 struct scheduler_mq_type *target_mq; 233 struct scheduler_msg *queue_msg; 234 struct scheduler_ctx *sched_ctx; 235 236 if (!msg) { 237 sched_err("msg is null"); 238 return QDF_STATUS_E_INVAL; 239 } 240 241 sched_ctx = scheduler_get_context(); 242 if (!sched_ctx) { 243 sched_err("sched_ctx is null"); 244 return QDF_STATUS_E_INVAL; 245 } 246 247 if (!sched_ctx->sch_thread) { 248 sched_err("Cannot post message; scheduler thread is stopped"); 249 return QDF_STATUS_E_FAILURE; 250 } 251 252 if (msg->reserved != 0 && msg->reserved != SYS_MSG_COOKIE) { 253 QDF_DEBUG_PANIC("Scheduler messages must be initialized"); 254 return QDF_STATUS_E_FAILURE; 255 } 256 257 /* Target_If is a special message queue in phase 3 convergence beacause 258 * its used by both legacy WMA and as well as new UMAC components which 259 * directly populate callback handlers in message body. 260 * 1) WMA legacy messages should not have callback 261 * 2) New target_if message needs to have valid callback 262 * Clear callback handler for legacy WMA messages such that in case 263 * if someone is sending legacy WMA message from stack which has 264 * uninitialized callback then its handled properly. Also change 265 * legacy WMA message queue id to target_if queue such that its always 266 * handled in right order. 267 */ 268 if (QDF_MODULE_ID_WMA == qid) { 269 msg->callback = NULL; 270 /* change legacy WMA message id to new target_if mq id */ 271 qid = QDF_MODULE_ID_TARGET_IF; 272 } 273 274 qidx = sched_ctx->queue_ctx.scheduler_msg_qid_to_qidx[qid]; 275 if (qidx >= SCHEDULER_NUMBER_OF_MSG_QUEUE) { 276 sched_err("Scheduler is deinitialized ignore msg"); 277 return QDF_STATUS_E_FAILURE; 278 } 279 280 if (!sched_ctx->queue_ctx.scheduler_msg_process_fn[qidx]) { 281 sched_err("callback not registered for qid[%d]", qid); 282 QDF_ASSERT(0); 283 return QDF_STATUS_E_FAILURE; 284 } 285 286 target_mq = &(sched_ctx->queue_ctx.sch_msg_q[qidx]); 287 288 queue_msg = scheduler_core_msg_dup(msg); 289 if (!queue_msg) 290 return QDF_STATUS_E_NOMEM; 291 292 if (is_high_priority) 293 scheduler_mq_put_front(target_mq, queue_msg); 294 else 295 scheduler_mq_put(target_mq, queue_msg); 296 297 qdf_atomic_set_bit(MC_POST_EVENT_MASK, &sched_ctx->sch_event_flag); 298 qdf_wake_up_interruptible(&sched_ctx->sch_wait_queue); 299 300 return QDF_STATUS_SUCCESS; 301 } 302 303 QDF_STATUS scheduler_register_module(QDF_MODULE_ID qid, 304 scheduler_msg_process_fn_t callback) 305 { 306 struct scheduler_mq_ctx *ctx; 307 struct scheduler_ctx *sched_ctx = scheduler_get_context(); 308 309 sched_enter(); 310 311 if (!sched_ctx) { 312 QDF_ASSERT(0); 313 sched_err("sched_ctx is NULL"); 314 return QDF_STATUS_E_FAILURE; 315 } 316 317 if (sched_ctx->sch_last_qidx >= SCHEDULER_NUMBER_OF_MSG_QUEUE) { 318 sched_err("Already registered max %d no of message queues", 319 SCHEDULER_NUMBER_OF_MSG_QUEUE); 320 return QDF_STATUS_E_FAILURE; 321 } 322 323 ctx = &sched_ctx->queue_ctx; 324 ctx->scheduler_msg_qid_to_qidx[qid] = sched_ctx->sch_last_qidx; 325 ctx->sch_msg_q[sched_ctx->sch_last_qidx].qid = qid; 326 ctx->scheduler_msg_process_fn[sched_ctx->sch_last_qidx] = callback; 327 sched_ctx->sch_last_qidx++; 328 329 sched_exit(); 330 331 return QDF_STATUS_SUCCESS; 332 } 333 334 QDF_STATUS scheduler_deregister_module(QDF_MODULE_ID qid) 335 { 336 struct scheduler_mq_ctx *ctx; 337 struct scheduler_ctx *sched_ctx = scheduler_get_context(); 338 uint8_t qidx; 339 340 sched_enter(); 341 342 if (!sched_ctx) { 343 QDF_ASSERT(0); 344 sched_err("sched_ctx is NULL"); 345 return QDF_STATUS_E_FAILURE; 346 } 347 348 ctx = &sched_ctx->queue_ctx; 349 qidx = ctx->scheduler_msg_qid_to_qidx[qid]; 350 ctx->scheduler_msg_process_fn[qidx] = NULL; 351 sched_ctx->sch_last_qidx--; 352 ctx->scheduler_msg_qid_to_qidx[qidx] = SCHEDULER_NUMBER_OF_MSG_QUEUE; 353 354 sched_exit(); 355 356 return QDF_STATUS_SUCCESS; 357 } 358 359 void scheduler_resume(void) 360 { 361 struct scheduler_ctx *sched_ctx = scheduler_get_context(); 362 363 if (sched_ctx) 364 qdf_event_set(&sched_ctx->resume_sch_event); 365 } 366 367 void scheduler_register_hdd_suspend_callback(hdd_suspend_callback callback) 368 { 369 struct scheduler_ctx *sched_ctx = scheduler_get_context(); 370 371 if (sched_ctx) 372 sched_ctx->hdd_callback = callback; 373 } 374 void scheduler_wake_up_controller_thread(void) 375 { 376 struct scheduler_ctx *sched_ctx = scheduler_get_context(); 377 378 if (sched_ctx) 379 qdf_wake_up_interruptible(&sched_ctx->sch_wait_queue); 380 } 381 void scheduler_set_event_mask(uint32_t event_mask) 382 { 383 struct scheduler_ctx *sched_ctx = scheduler_get_context(); 384 385 if (sched_ctx) 386 qdf_atomic_set_bit(event_mask, &sched_ctx->sch_event_flag); 387 } 388 389 void scheduler_clear_event_mask(uint32_t event_mask) 390 { 391 struct scheduler_ctx *sched_ctx = scheduler_get_context(); 392 393 if (sched_ctx) 394 qdf_atomic_clear_bit(event_mask, &sched_ctx->sch_event_flag); 395 } 396 397 QDF_STATUS scheduler_target_if_mq_handler(struct scheduler_msg *msg) 398 { 399 QDF_STATUS status; 400 struct scheduler_ctx *sched_ctx = scheduler_get_context(); 401 QDF_STATUS (*target_if_msg_handler)(struct scheduler_msg *); 402 403 if (NULL == msg || NULL == sched_ctx) { 404 sched_err("msg %pK sch %pK", msg, sched_ctx); 405 return QDF_STATUS_E_FAILURE; 406 } 407 408 target_if_msg_handler = msg->callback; 409 410 /* Target_If is a special message queue in phase 3 convergence beacause 411 * its used by both legacy WMA and as well as new UMAC components. New 412 * UMAC components directly pass their message handlers as callback in 413 * message body. 414 * 1) All Legacy WMA messages do not contain message callback so invoke 415 * registered legacy WMA handler. Scheduler message posting APIs 416 * makes sure legacy WMA messages do not have callbacks. 417 * 2) For new messages which have valid callbacks invoke their callbacks 418 * directly. 419 */ 420 if (NULL == target_if_msg_handler) 421 status = sched_ctx->legacy_wma_handler(msg); 422 else 423 status = target_if_msg_handler(msg); 424 425 return status; 426 } 427 428 QDF_STATUS scheduler_os_if_mq_handler(struct scheduler_msg *msg) 429 { 430 QDF_STATUS (*os_if_msg_handler)(struct scheduler_msg *); 431 432 if (NULL == msg) { 433 sched_err("Msg is NULL"); 434 return QDF_STATUS_E_FAILURE; 435 } 436 437 os_if_msg_handler = msg->callback; 438 439 if (NULL == os_if_msg_handler) { 440 sched_err("Msg callback is NULL"); 441 QDF_ASSERT(0); 442 return QDF_STATUS_E_FAILURE; 443 } 444 os_if_msg_handler(msg); 445 446 return QDF_STATUS_SUCCESS; 447 } 448 449 QDF_STATUS scheduler_timer_q_mq_handler(struct scheduler_msg *msg) 450 { 451 QDF_STATUS status; 452 struct scheduler_ctx *sched_ctx = scheduler_get_context(); 453 qdf_mc_timer_callback_t timer_q_msg_handler; 454 455 if (NULL == msg || NULL == sched_ctx) { 456 sched_err("msg %pK sch %pK", msg, sched_ctx); 457 return QDF_STATUS_E_FAILURE; 458 } 459 460 timer_q_msg_handler = msg->callback; 461 462 /* Timer message handler */ 463 if (SYS_MSG_COOKIE == msg->reserved && 464 SYS_MSG_ID_MC_TIMER == msg->type) { 465 if (timer_q_msg_handler) { 466 status = QDF_STATUS_SUCCESS; 467 timer_q_msg_handler(msg->bodyptr); 468 } else { 469 sched_err("Timer cb is null"); 470 status = QDF_STATUS_E_FAILURE; 471 } 472 473 return status; 474 } else { 475 /* Legacy sys message handler */ 476 status = sched_ctx->legacy_sys_handler(msg); 477 478 return status; 479 } 480 } 481 482 QDF_STATUS scheduler_scan_mq_handler(struct scheduler_msg *msg) 483 { 484 QDF_STATUS (*scan_q_msg_handler)(struct scheduler_msg *); 485 486 if (NULL == msg) { 487 sched_err("Msg is NULL"); 488 return QDF_STATUS_E_FAILURE; 489 } 490 491 scan_q_msg_handler = msg->callback; 492 493 if (NULL == scan_q_msg_handler) { 494 sched_err("Msg callback is NULL"); 495 QDF_ASSERT(0); 496 return QDF_STATUS_E_FAILURE; 497 } 498 scan_q_msg_handler(msg); 499 500 return QDF_STATUS_SUCCESS; 501 } 502 503 QDF_STATUS scheduler_register_wma_legacy_handler(scheduler_msg_process_fn_t 504 wma_callback) 505 { 506 struct scheduler_ctx *sched_ctx = scheduler_get_context(); 507 508 if (NULL == sched_ctx) { 509 sched_err("scheduler context is null"); 510 return QDF_STATUS_E_FAILURE; 511 } 512 513 sched_ctx->legacy_wma_handler = wma_callback; 514 515 return QDF_STATUS_SUCCESS; 516 } 517 518 QDF_STATUS scheduler_register_sys_legacy_handler(scheduler_msg_process_fn_t 519 sys_callback) 520 { 521 struct scheduler_ctx *sched_ctx = scheduler_get_context(); 522 523 if (NULL == sched_ctx) { 524 sched_err("scheduler context is null"); 525 return QDF_STATUS_E_FAILURE; 526 } 527 528 sched_ctx->legacy_sys_handler = sys_callback; 529 530 return QDF_STATUS_SUCCESS; 531 } 532 533 QDF_STATUS scheduler_deregister_wma_legacy_handler(void) 534 { 535 struct scheduler_ctx *sched_ctx = scheduler_get_context(); 536 537 if (NULL == sched_ctx) { 538 sched_err("scheduler context is null"); 539 return QDF_STATUS_E_FAILURE; 540 } 541 542 sched_ctx->legacy_wma_handler = NULL; 543 544 return QDF_STATUS_SUCCESS; 545 } 546 547 QDF_STATUS scheduler_deregister_sys_legacy_handler(void) 548 { 549 struct scheduler_ctx *sched_ctx = scheduler_get_context(); 550 551 if (NULL == sched_ctx) { 552 sched_err("scheduler context is null"); 553 return QDF_STATUS_E_FAILURE; 554 } 555 556 sched_ctx->legacy_sys_handler = NULL; 557 558 return QDF_STATUS_SUCCESS; 559 } 560 561 static QDF_STATUS scheduler_msg_flush_noop(struct scheduler_msg *msg) 562 { 563 return QDF_STATUS_SUCCESS; 564 } 565 566 void scheduler_mc_timer_callback(unsigned long data) 567 { 568 qdf_mc_timer_t *timer = (qdf_mc_timer_t *)data; 569 struct scheduler_msg msg = {0}; 570 QDF_STATUS status; 571 572 qdf_mc_timer_callback_t callback = NULL; 573 void *user_data = NULL; 574 QDF_TIMER_TYPE type = QDF_TIMER_TYPE_SW; 575 576 QDF_ASSERT(timer); 577 578 if (timer == NULL) { 579 sched_err("Null pointer passed in!"); 580 return; 581 } 582 583 qdf_spin_lock_irqsave(&timer->platform_info.spinlock); 584 585 switch (timer->state) { 586 case QDF_TIMER_STATE_STARTING: 587 /* we are in this state because someone just started the timer, 588 * MC timer got started and expired, but the time content have 589 * not been updated this is a rare race condition! 590 */ 591 timer->state = QDF_TIMER_STATE_STOPPED; 592 status = QDF_STATUS_E_ALREADY; 593 break; 594 595 case QDF_TIMER_STATE_STOPPED: 596 status = QDF_STATUS_E_ALREADY; 597 break; 598 599 case QDF_TIMER_STATE_UNUSED: 600 status = QDF_STATUS_E_EXISTS; 601 break; 602 603 case QDF_TIMER_STATE_RUNNING: 604 /* need to go to stop state here because the call-back function 605 * may restart timer (to emulate periodic timer) 606 */ 607 timer->state = QDF_TIMER_STATE_STOPPED; 608 /* copy the relevant timer information to local variables; 609 * once we exits from this critical section, the timer content 610 * may be modified by other tasks 611 */ 612 callback = timer->callback; 613 user_data = timer->user_data; 614 type = timer->type; 615 status = QDF_STATUS_SUCCESS; 616 break; 617 618 default: 619 QDF_ASSERT(0); 620 status = QDF_STATUS_E_FAULT; 621 break; 622 } 623 624 qdf_spin_unlock_irqrestore(&timer->platform_info.spinlock); 625 626 if (QDF_STATUS_SUCCESS != status) { 627 sched_err("TIMER callback called in a wrong state=%d", 628 timer->state); 629 return; 630 } 631 632 qdf_try_allowing_sleep(type); 633 634 if (callback == NULL) { 635 sched_err("No TIMER callback, Couldn't enqueue timer to any queue"); 636 QDF_ASSERT(0); 637 return; 638 } 639 640 /* serialize to scheduler controller thread */ 641 msg.type = SYS_MSG_ID_MC_TIMER; 642 msg.reserved = SYS_MSG_COOKIE; 643 msg.callback = callback; 644 msg.bodyptr = user_data; 645 msg.bodyval = 0; 646 647 /* bodyptr points to user data, do not free it during msg flush */ 648 msg.flush_callback = scheduler_msg_flush_noop; 649 650 if (scheduler_post_msg(QDF_MODULE_ID_SYS, &msg) == QDF_STATUS_SUCCESS) 651 return; 652 sched_err("Could not enqueue timer to timer queue"); 653 } 654