1 /* 2 * Copyright (c) 2014-2020 The Linux Foundation. All rights reserved. 3 * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved. 4 * 5 * Permission to use, copy, modify, and/or distribute this software for 6 * any purpose with or without fee is hereby granted, provided that the 7 * above copyright notice and this permission notice appear in all 8 * copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 16 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 * PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <scheduler_core.h> 21 #include <qdf_atomic.h> 22 #include "qdf_flex_mem.h" 23 24 static struct scheduler_ctx g_sched_ctx; 25 static struct scheduler_ctx *gp_sched_ctx; 26 27 DEFINE_QDF_FLEX_MEM_POOL(sched_pool, sizeof(struct scheduler_msg), 28 WLAN_SCHED_REDUCTION_LIMIT); 29 30 #ifdef WLAN_SCHED_HISTORY_SIZE 31 32 #define SCHEDULER_HISTORY_HEADER "|Callback "\ 33 "|Message Type" \ 34 "|Queue Duration(us)|Queue Depth" \ 35 "|Run Duration(us)|" 36 37 #define SCHEDULER_HISTORY_LINE "--------------------------------------" \ 38 "--------------------------------------" \ 39 "--------------------------------------" 40 41 /** 42 * struct sched_history_item - metrics for a scheduler message 43 * @callback: the message's execution callback 44 * @type_id: the message's type_id 45 * @queue_id: Id of the queue the message was added to 46 * @queue_start_us: timestamp when the message was queued in microseconds 47 * @queue_duration_us: duration the message was queued in microseconds 48 * @queue_depth: depth of the queue when the message was queued 49 * @run_start_us: timesatmp when the message started execution in microseconds 50 * @run_duration_us: duration the message was executed in microseconds 51 */ 52 struct sched_history_item { 53 void *callback; 54 uint32_t type_id; 55 QDF_MODULE_ID queue_id; 56 uint64_t queue_start_us; 57 uint32_t queue_duration_us; 58 uint32_t queue_depth; 59 uint64_t run_start_us; 60 uint32_t run_duration_us; 61 }; 62 63 static struct sched_history_item sched_history[WLAN_SCHED_HISTORY_SIZE]; 64 static uint32_t sched_history_index; 65 sched_history_queue(struct scheduler_mq_type * queue,struct scheduler_msg * msg)66 static void sched_history_queue(struct scheduler_mq_type *queue, 67 struct scheduler_msg *msg) 68 { 69 msg->queue_id = queue->qid; 70 msg->queue_depth = qdf_list_size(&queue->mq_list); 71 msg->queued_at_us = qdf_get_log_timestamp_usecs(); 72 } 73 sched_history_start(struct scheduler_msg * msg)74 static void sched_history_start(struct scheduler_msg *msg) 75 { 76 uint64_t started_at_us = qdf_get_log_timestamp_usecs(); 77 struct sched_history_item hist = { 78 .callback = msg->callback, 79 .type_id = msg->type, 80 .queue_start_us = msg->queued_at_us, 81 .queue_duration_us = started_at_us - msg->queued_at_us, 82 .queue_depth = msg->queue_depth, 83 .run_start_us = started_at_us, 84 }; 85 86 sched_history[sched_history_index] = hist; 87 } 88 sched_history_stop(void)89 static void sched_history_stop(void) 90 { 91 struct sched_history_item *hist = &sched_history[sched_history_index]; 92 uint64_t stopped_at_us = qdf_get_log_timestamp_usecs(); 93 94 hist->run_duration_us = stopped_at_us - hist->run_start_us; 95 96 sched_history_index++; 97 sched_history_index %= WLAN_SCHED_HISTORY_SIZE; 98 } 99 sched_history_print(void)100 void sched_history_print(void) 101 { 102 struct sched_history_item *history, *item; 103 uint32_t history_idx; 104 uint32_t idx, index; 105 106 history = qdf_mem_malloc(sizeof(*history) * WLAN_SCHED_HISTORY_SIZE); 107 108 if (!history) { 109 sched_err("Mem alloc failed"); 110 return; 111 } 112 113 qdf_mem_copy(history, &sched_history, 114 (sizeof(*history) * WLAN_SCHED_HISTORY_SIZE)); 115 history_idx = sched_history_index; 116 117 sched_nofl_fatal(SCHEDULER_HISTORY_LINE); 118 sched_nofl_fatal(SCHEDULER_HISTORY_HEADER); 119 sched_nofl_fatal(SCHEDULER_HISTORY_LINE); 120 121 for (idx = 0; idx < WLAN_SCHED_HISTORY_SIZE; idx++) { 122 index = (history_idx + idx) % WLAN_SCHED_HISTORY_SIZE; 123 item = history + index; 124 125 if (!item->callback) 126 continue; 127 128 sched_nofl_fatal("%40pF|%12d|%18d|%11d|%16d|", 129 item->callback, item->type_id, 130 item->queue_duration_us, 131 item->queue_depth, 132 item->run_duration_us); 133 } 134 135 sched_nofl_fatal(SCHEDULER_HISTORY_LINE); 136 137 qdf_mem_free(history); 138 } 139 #else /* WLAN_SCHED_HISTORY_SIZE */ 140 sched_history_queue(struct scheduler_mq_type * queue,struct scheduler_msg * msg)141 static inline void sched_history_queue(struct scheduler_mq_type *queue, 142 struct scheduler_msg *msg) { } sched_history_start(struct scheduler_msg * msg)143 static inline void sched_history_start(struct scheduler_msg *msg) { } sched_history_stop(void)144 static inline void sched_history_stop(void) { } sched_history_print(void)145 void sched_history_print(void) { } 146 147 #endif /* WLAN_SCHED_HISTORY_SIZE */ 148 scheduler_create_ctx(void)149 QDF_STATUS scheduler_create_ctx(void) 150 { 151 qdf_flex_mem_init(&sched_pool); 152 gp_sched_ctx = &g_sched_ctx; 153 154 return QDF_STATUS_SUCCESS; 155 } 156 scheduler_destroy_ctx(void)157 QDF_STATUS scheduler_destroy_ctx(void) 158 { 159 gp_sched_ctx = NULL; 160 qdf_flex_mem_deinit(&sched_pool); 161 162 return QDF_STATUS_SUCCESS; 163 } 164 scheduler_get_context(void)165 struct scheduler_ctx *scheduler_get_context(void) 166 { 167 QDF_BUG(gp_sched_ctx); 168 169 return gp_sched_ctx; 170 } 171 scheduler_mq_init(struct scheduler_mq_type * msg_q)172 static QDF_STATUS scheduler_mq_init(struct scheduler_mq_type *msg_q) 173 { 174 sched_enter(); 175 176 qdf_spinlock_create(&msg_q->mq_lock); 177 qdf_list_create(&msg_q->mq_list, SCHEDULER_CORE_MAX_MESSAGES); 178 179 sched_exit(); 180 181 return QDF_STATUS_SUCCESS; 182 } 183 scheduler_mq_deinit(struct scheduler_mq_type * msg_q)184 static void scheduler_mq_deinit(struct scheduler_mq_type *msg_q) 185 { 186 sched_enter(); 187 188 qdf_list_destroy(&msg_q->mq_list); 189 qdf_spinlock_destroy(&msg_q->mq_lock); 190 191 sched_exit(); 192 } 193 194 static qdf_atomic_t __sched_queue_depth; 195 static qdf_atomic_t __sched_dup_fail_count; 196 scheduler_all_queues_init(struct scheduler_ctx * sched_ctx)197 static QDF_STATUS scheduler_all_queues_init(struct scheduler_ctx *sched_ctx) 198 { 199 QDF_STATUS status; 200 int i; 201 202 sched_enter(); 203 204 QDF_BUG(sched_ctx); 205 if (!sched_ctx) 206 return QDF_STATUS_E_FAILURE; 207 208 qdf_atomic_set(&__sched_queue_depth, 0); 209 210 /* Initialize all message queues */ 211 for (i = 0; i < SCHEDULER_NUMBER_OF_MSG_QUEUE; i++) { 212 status = scheduler_mq_init(&sched_ctx->queue_ctx.sch_msg_q[i]); 213 if (QDF_STATUS_SUCCESS != status) 214 return status; 215 } 216 217 /* Initialize all qid to qidx mapping to invalid values */ 218 for (i = 0; i < QDF_MODULE_ID_MAX; i++) 219 sched_ctx->queue_ctx.scheduler_msg_qid_to_qidx[i] = 220 SCHEDULER_NUMBER_OF_MSG_QUEUE; 221 222 sched_exit(); 223 224 return status; 225 } 226 scheduler_all_queues_deinit(struct scheduler_ctx * sched_ctx)227 static QDF_STATUS scheduler_all_queues_deinit(struct scheduler_ctx *sched_ctx) 228 { 229 int i; 230 231 sched_enter(); 232 233 QDF_BUG(sched_ctx); 234 if (!sched_ctx) 235 return QDF_STATUS_E_FAILURE; 236 237 /* De-Initialize all message queues */ 238 for (i = 0; i < SCHEDULER_NUMBER_OF_MSG_QUEUE; i++) 239 scheduler_mq_deinit(&sched_ctx->queue_ctx.sch_msg_q[i]); 240 241 /* Initialize all qid to qidx mapping to invalid values */ 242 for (i = 0; i < QDF_MODULE_ID_MAX; i++) 243 sched_ctx->queue_ctx.scheduler_msg_qid_to_qidx[i] = 244 SCHEDULER_NUMBER_OF_MSG_QUEUE; 245 246 sched_exit(); 247 248 return QDF_STATUS_SUCCESS; 249 } 250 scheduler_mq_put(struct scheduler_mq_type * msg_q,struct scheduler_msg * msg)251 void scheduler_mq_put(struct scheduler_mq_type *msg_q, 252 struct scheduler_msg *msg) 253 { 254 qdf_spin_lock_irqsave(&msg_q->mq_lock); 255 sched_history_queue(msg_q, msg); 256 qdf_list_insert_back(&msg_q->mq_list, &msg->node); 257 qdf_spin_unlock_irqrestore(&msg_q->mq_lock); 258 } 259 scheduler_mq_put_front(struct scheduler_mq_type * msg_q,struct scheduler_msg * msg)260 void scheduler_mq_put_front(struct scheduler_mq_type *msg_q, 261 struct scheduler_msg *msg) 262 { 263 qdf_spin_lock_irqsave(&msg_q->mq_lock); 264 sched_history_queue(msg_q, msg); 265 qdf_list_insert_front(&msg_q->mq_list, &msg->node); 266 qdf_spin_unlock_irqrestore(&msg_q->mq_lock); 267 } 268 scheduler_mq_get(struct scheduler_mq_type * msg_q)269 struct scheduler_msg *scheduler_mq_get(struct scheduler_mq_type *msg_q) 270 { 271 QDF_STATUS status; 272 qdf_list_node_t *node; 273 274 qdf_spin_lock_irqsave(&msg_q->mq_lock); 275 status = qdf_list_remove_front(&msg_q->mq_list, &node); 276 qdf_spin_unlock_irqrestore(&msg_q->mq_lock); 277 278 if (QDF_IS_STATUS_ERROR(status)) 279 return NULL; 280 281 return qdf_container_of(node, struct scheduler_msg, node); 282 } 283 scheduler_queues_deinit(struct scheduler_ctx * sched_ctx)284 QDF_STATUS scheduler_queues_deinit(struct scheduler_ctx *sched_ctx) 285 { 286 return scheduler_all_queues_deinit(sched_ctx); 287 } 288 scheduler_queues_init(struct scheduler_ctx * sched_ctx)289 QDF_STATUS scheduler_queues_init(struct scheduler_ctx *sched_ctx) 290 { 291 QDF_STATUS status; 292 293 sched_enter(); 294 295 QDF_BUG(sched_ctx); 296 if (!sched_ctx) 297 return QDF_STATUS_E_FAILURE; 298 299 status = scheduler_all_queues_init(sched_ctx); 300 if (QDF_IS_STATUS_ERROR(status)) { 301 scheduler_all_queues_deinit(sched_ctx); 302 sched_err("Failed to initialize the msg queues"); 303 return status; 304 } 305 306 sched_debug("Queue init passed"); 307 308 sched_exit(); 309 310 return QDF_STATUS_SUCCESS; 311 } 312 scheduler_core_msg_dup(struct scheduler_msg * msg)313 struct scheduler_msg *scheduler_core_msg_dup(struct scheduler_msg *msg) 314 { 315 struct scheduler_msg *dup; 316 317 if (qdf_atomic_inc_return(&__sched_queue_depth) > 318 SCHEDULER_CORE_MAX_MESSAGES) 319 goto buffer_full; 320 321 dup = qdf_flex_mem_alloc(&sched_pool); 322 if (!dup) { 323 sched_err("out of memory"); 324 goto dec_queue_count; 325 } 326 327 qdf_mem_copy(dup, msg, sizeof(*dup)); 328 329 qdf_atomic_set(&__sched_dup_fail_count, 0); 330 331 return dup; 332 333 buffer_full: 334 if (qdf_atomic_inc_return(&__sched_dup_fail_count) > 335 SCHEDULER_WRAPPER_MAX_FAIL_COUNT) 336 QDF_DEBUG_PANIC("Scheduler buffer is full"); 337 338 339 dec_queue_count: 340 qdf_atomic_dec(&__sched_queue_depth); 341 342 return NULL; 343 } 344 scheduler_core_msg_free(struct scheduler_msg * msg)345 void scheduler_core_msg_free(struct scheduler_msg *msg) 346 { 347 qdf_flex_mem_free(&sched_pool, msg); 348 qdf_atomic_dec(&__sched_queue_depth); 349 } 350 scheduler_thread_process_queues(struct scheduler_ctx * sch_ctx,bool * shutdown)351 static void scheduler_thread_process_queues(struct scheduler_ctx *sch_ctx, 352 bool *shutdown) 353 { 354 int i; 355 QDF_STATUS status; 356 struct scheduler_msg *msg; 357 358 if (!sch_ctx) { 359 QDF_DEBUG_PANIC("sch_ctx is null"); 360 return; 361 } 362 363 /* start with highest priority queue : timer queue at index 0 */ 364 i = 0; 365 while (i < SCHEDULER_NUMBER_OF_MSG_QUEUE) { 366 /* Check if MC needs to shutdown */ 367 if (qdf_atomic_test_bit(MC_SHUTDOWN_EVENT_MASK, 368 &sch_ctx->sch_event_flag)) { 369 sched_debug("scheduler thread signaled to shutdown"); 370 *shutdown = true; 371 372 /* Check for any Suspend Indication */ 373 if (qdf_atomic_test_and_clear_bit(MC_SUSPEND_EVENT_MASK, 374 &sch_ctx->sch_event_flag)) { 375 /* Unblock anyone waiting on suspend */ 376 if (gp_sched_ctx->hdd_callback) 377 gp_sched_ctx->hdd_callback(); 378 } 379 380 break; 381 } 382 383 msg = scheduler_mq_get(&sch_ctx->queue_ctx.sch_msg_q[i]); 384 if (!msg) { 385 /* check next queue */ 386 i++; 387 continue; 388 } 389 390 if (sch_ctx->queue_ctx.scheduler_msg_process_fn[i]) { 391 sch_ctx->watchdog_msg_type = msg->type; 392 sch_ctx->watchdog_callback = msg->callback; 393 394 sched_history_start(msg); 395 qdf_timer_start(&sch_ctx->watchdog_timer, 396 sch_ctx->timeout); 397 status = sch_ctx->queue_ctx. 398 scheduler_msg_process_fn[i](msg); 399 qdf_timer_stop(&sch_ctx->watchdog_timer); 400 sched_history_stop(); 401 402 if (QDF_IS_STATUS_ERROR(status)) 403 sched_err("Failed processing Qid[%d] message", 404 sch_ctx->queue_ctx.sch_msg_q[i].qid); 405 406 scheduler_core_msg_free(msg); 407 } 408 409 /* start again with highest priority queue at index 0 */ 410 i = 0; 411 } 412 413 /* Check for any Suspend Indication */ 414 if (qdf_atomic_test_and_clear_bit(MC_SUSPEND_EVENT_MASK, 415 &sch_ctx->sch_event_flag)) { 416 qdf_spin_lock(&sch_ctx->sch_thread_lock); 417 qdf_event_reset(&sch_ctx->resume_sch_event); 418 /* controller thread suspend completion callback */ 419 if (gp_sched_ctx->hdd_callback) 420 gp_sched_ctx->hdd_callback(); 421 qdf_spin_unlock(&sch_ctx->sch_thread_lock); 422 /* Wait for resume indication */ 423 qdf_wait_single_event(&sch_ctx->resume_sch_event, 0); 424 } 425 426 return; /* Nothing to process wait on wait queue */ 427 } 428 scheduler_thread(void * arg)429 int scheduler_thread(void *arg) 430 { 431 struct scheduler_ctx *sch_ctx = (struct scheduler_ctx *)arg; 432 int retWaitStatus = 0; 433 bool shutdown = false; 434 435 if (!arg) { 436 QDF_DEBUG_PANIC("arg is null"); 437 return 0; 438 } 439 qdf_set_user_nice(current, -2); 440 441 /* Ack back to the context from which the main controller thread 442 * has been created 443 */ 444 qdf_event_set(&sch_ctx->sch_start_event); 445 sched_debug("scheduler thread %d (%s) starting up", 446 current->pid, current->comm); 447 448 while (!shutdown) { 449 /* This implements the execution model algorithm */ 450 retWaitStatus = qdf_wait_queue_interruptible( 451 sch_ctx->sch_wait_queue, 452 qdf_atomic_test_bit(MC_POST_EVENT_MASK, 453 &sch_ctx->sch_event_flag) || 454 qdf_atomic_test_bit(MC_SUSPEND_EVENT_MASK, 455 &sch_ctx->sch_event_flag)); 456 457 if (retWaitStatus == -ERESTARTSYS) 458 QDF_DEBUG_PANIC("Scheduler received -ERESTARTSYS"); 459 460 qdf_atomic_clear_bit(MC_POST_EVENT_MASK, &sch_ctx->sch_event_flag); 461 scheduler_thread_process_queues(sch_ctx, &shutdown); 462 } 463 464 /* If we get here the scheduler thread must exit */ 465 sched_debug("Scheduler thread exiting"); 466 qdf_event_set(&sch_ctx->sch_shutdown); 467 468 return 0; 469 } 470 scheduler_flush_single_queue(struct scheduler_mq_type * mq)471 static void scheduler_flush_single_queue(struct scheduler_mq_type *mq) 472 { 473 struct scheduler_msg *msg; 474 QDF_STATUS (*flush_cb)(struct scheduler_msg *); 475 476 while ((msg = scheduler_mq_get(mq))) { 477 if (msg->flush_callback) { 478 sched_debug("Calling flush callback; type: %x", 479 msg->type); 480 flush_cb = msg->flush_callback; 481 flush_cb(msg); 482 } else if (msg->bodyptr) { 483 sched_debug("Freeing scheduler msg bodyptr; type: %x", 484 msg->type); 485 qdf_mem_free(msg->bodyptr); 486 } 487 488 scheduler_core_msg_free(msg); 489 } 490 } 491 scheduler_queues_flush(struct scheduler_ctx * sched_ctx)492 void scheduler_queues_flush(struct scheduler_ctx *sched_ctx) 493 { 494 struct scheduler_mq_type *mq; 495 int i; 496 497 sched_debug("Flushing scheduler message queues"); 498 499 for (i = 0; i < SCHEDULER_NUMBER_OF_MSG_QUEUE; i++) { 500 mq = &sched_ctx->queue_ctx.sch_msg_q[i]; 501 scheduler_flush_single_queue(mq); 502 } 503 } 504 505