/* SPDX-License-Identifier: BSD-2-Clause */ /** * @file * * @ingroup RTEMSTestFrameworkImpl * * @brief This source file contains the implementation of the scheduler test * support API. */ /* * Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de) * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include typedef struct { RTEMS_INTERRUPT_LOCK_MEMBER(lock) T_scheduler_log *active; T_scheduler_event_handler handler; void *arg; } T_scheduler_context; static T_scheduler_context T_scheduler_instance = { #ifdef RTEMS_SMP .lock = RTEMS_INTERRUPT_LOCK_INITIALIZER("Test Scheduler"), #endif .active = NULL }; void T_scheduler_set_event_handler(T_scheduler_event_handler handler, void *arg) { T_scheduler_context *ctx; rtems_interrupt_lock_context lock_context; ctx = &T_scheduler_instance; rtems_interrupt_lock_acquire(&ctx->lock, &lock_context); ctx->handler = handler; ctx->arg = arg; rtems_interrupt_lock_release(&ctx->lock, &lock_context); } static void T_scheduler_before_operation(T_scheduler_event *event) { T_scheduler_context *ctx; rtems_interrupt_lock_context lock_context; T_scheduler_log *log; T_scheduler_event_handler handler; void *arg; Per_CPU_Control *cpu_self; ctx = &T_scheduler_instance; log = ctx->active; handler = ctx->handler; if (log == NULL && handler == NULL) { return; } rtems_interrupt_lock_interrupt_disable(&lock_context); cpu_self = _Per_CPU_Get(); event->cpu = _Per_CPU_Get_index( cpu_self ); if (_Per_CPU_Is_ISR_in_progress(cpu_self)) { event->executing = NULL; } else { event->executing = _Per_CPU_Get_executing(cpu_self); } event->instant = T_now(); rtems_interrupt_lock_acquire_isr(&ctx->lock, &lock_context); handler = ctx->handler; arg = ctx->arg; rtems_interrupt_lock_release(&ctx->lock, &lock_context); if (handler != NULL) { (*handler)(arg, event, T_SCHEDULER_BEFORE); } } static void T_scheduler_record_event(T_scheduler_event *event) { T_scheduler_context *ctx; rtems_interrupt_lock_context lock_context; T_scheduler_log *log; T_scheduler_event_handler handler; ctx = &T_scheduler_instance; log = ctx->active; handler = ctx->handler; if (log == NULL && handler == NULL) { return; } rtems_interrupt_lock_acquire(&ctx->lock, &lock_context); #ifdef RTEMS_SMP handler = ctx->handler; #endif if (handler != NULL) { void *arg; arg = ctx->arg; rtems_interrupt_lock_release(&ctx->lock, &lock_context); (*handler)(arg, event, T_SCHEDULER_AFTER); rtems_interrupt_lock_acquire(&ctx->lock, &lock_context); } #ifdef RTEMS_SMP log = ctx->active; #endif if (log != NULL) { size_t recorded; ++log->header.operations; recorded = log->header.recorded; if (recorded < log->header.capacity) { log->header.recorded = recorded + 1; log->events[recorded] = *event; } } rtems_interrupt_lock_release(&ctx->lock, &lock_context); } T_scheduler_log * T_scheduler_record(T_scheduler_log *log) { rtems_interrupt_lock_context lock_context; T_scheduler_log *previous; T_scheduler_context *ctx; if (log != NULL) { log->header.recorded = 0; log->header.operations = 0; } ctx = &T_scheduler_instance; rtems_interrupt_lock_acquire(&ctx->lock, &lock_context); previous = ctx->active; ctx->active = log; rtems_interrupt_lock_release(&ctx->lock, &lock_context); return previous; } T_scheduler_log * T_scheduler_record_2(T_scheduler_log_2 *log) { log->header.capacity = T_ARRAY_SIZE(log->events); return T_scheduler_record((T_scheduler_log *)log); } T_scheduler_log * T_scheduler_record_4(T_scheduler_log_4 *log) { log->header.capacity = T_ARRAY_SIZE(log->events); return T_scheduler_record((T_scheduler_log *)log); } T_scheduler_log * T_scheduler_record_10(T_scheduler_log_10 *log) { log->header.capacity = T_ARRAY_SIZE(log->events); return T_scheduler_record((T_scheduler_log *)log); } T_scheduler_log * T_scheduler_record_20(T_scheduler_log_20 *log) { log->header.capacity = T_ARRAY_SIZE(log->events); return T_scheduler_record((T_scheduler_log *)log); } T_scheduler_log * T_scheduler_record_40(T_scheduler_log_40 *log) { log->header.capacity = T_ARRAY_SIZE(log->events); return T_scheduler_record((T_scheduler_log *)log); } const T_scheduler_event T_scheduler_event_null; const T_scheduler_event * T_scheduler_next(T_scheduler_header *header, T_scheduler_operation operation, size_t *index) { T_scheduler_log *log; size_t i; log = (T_scheduler_log *)header; if (log == NULL) { return &T_scheduler_event_null; } i = *index; while (i < log->header.recorded) { if (operation == T_SCHEDULER_ANY || operation == log->events[i].operation) { *index = i + 1; return &log->events[i]; } ++i; } return &T_scheduler_event_null; } const T_scheduler_event * T_scheduler_next_any(T_scheduler_header *header, size_t *index) { return T_scheduler_next(header, T_SCHEDULER_ANY, index); } void T_scheduler_initialize(const Scheduler_Control *scheduler) { T_scheduler_event event = { .operation = T_SCHEDULER_INITIALIZE }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; T_scheduler_before_operation(&event); (*operations->initialize)(scheduler); T_scheduler_record_event(&event); } void T_scheduler_schedule(const Scheduler_Control *scheduler, Thread_Control *thread) { T_scheduler_event event = { .operation = T_SCHEDULER_SCHEDULE, .thread = thread }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; T_scheduler_before_operation(&event); (*operations->schedule)(scheduler, thread); T_scheduler_record_event(&event); } void T_scheduler_yield(const Scheduler_Control *scheduler, Thread_Control *thread, Scheduler_Node *node) { T_scheduler_event event = { .operation = T_SCHEDULER_YIELD, .thread = thread, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; T_scheduler_before_operation(&event); (*operations->yield)(scheduler, thread, node); T_scheduler_record_event(&event); } void T_scheduler_block(const Scheduler_Control *scheduler, Thread_Control *thread, Scheduler_Node *node) { T_scheduler_event event = { .operation = T_SCHEDULER_BLOCK, .thread = thread, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; T_scheduler_before_operation(&event); (*operations->block)(scheduler, thread, node); T_scheduler_record_event(&event); } void T_scheduler_unblock(const Scheduler_Control *scheduler, Thread_Control *thread, Scheduler_Node *node) { T_scheduler_event event = { .operation = T_SCHEDULER_UNBLOCK, .thread = thread, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; T_scheduler_before_operation(&event); (*operations->unblock)(scheduler, thread, node); T_scheduler_record_event(&event); } void T_scheduler_update_priority(const Scheduler_Control *scheduler, Thread_Control *thread, Scheduler_Node *node) { T_scheduler_event event = { .operation = T_SCHEDULER_UPDATE_PRIORITY, .thread = thread, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; T_scheduler_before_operation(&event); (*operations->update_priority)(scheduler, thread, node); T_scheduler_record_event(&event); } Priority_Control T_scheduler_map_priority(const Scheduler_Control *scheduler, Priority_Control priority) { T_scheduler_event event = { .operation = T_SCHEDULER_MAP_PRIORITY }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; event.map_unmap_priority.in = priority; T_scheduler_before_operation(&event); event.map_unmap_priority.out = (*operations->map_priority)(scheduler, priority); T_scheduler_record_event(&event); return event.map_unmap_priority.out; } Priority_Control T_scheduler_unmap_priority(const Scheduler_Control *scheduler, Priority_Control priority) { T_scheduler_event event = { .operation = T_SCHEDULER_UNMAP_PRIORITY }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; event.map_unmap_priority.in = priority; T_scheduler_before_operation(&event); event.map_unmap_priority.out = (*operations->unmap_priority)(scheduler, priority); T_scheduler_record_event(&event); return event.map_unmap_priority.out; } void T_scheduler_node_initialize(const Scheduler_Control *scheduler, Scheduler_Node *node, Thread_Control *thread, Priority_Control priority) { T_scheduler_event event = { .operation = T_SCHEDULER_NODE_INITIALIZE, .thread = thread, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; T_scheduler_before_operation(&event); (*operations->node_initialize)(scheduler, node, thread, priority); T_scheduler_record_event(&event); } void T_scheduler_node_destroy(const Scheduler_Control *scheduler, Scheduler_Node *node) { T_scheduler_event event = { .operation = T_SCHEDULER_NODE_DESTROY, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; T_scheduler_before_operation(&event); (*operations->node_destroy)(scheduler, node); T_scheduler_record_event(&event); } void T_scheduler_release_job(const Scheduler_Control *scheduler, Thread_Control *thread, Priority_Node *priority, uint64_t deadline, Thread_queue_Context *queue_context) { T_scheduler_event event = { .operation = T_SCHEDULER_RELEASE_JOB, .thread = thread }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; event.release_job.priority = priority; event.release_job.deadline = deadline; T_scheduler_before_operation(&event); (*operations->release_job)(scheduler, thread, priority, deadline, queue_context); T_scheduler_record_event(&event); } void T_scheduler_cancel_job(const Scheduler_Control *scheduler, Thread_Control *thread, Priority_Node *priority, Thread_queue_Context *queue_context) { T_scheduler_event event = { .operation = T_SCHEDULER_CANCEL_JOB, .thread = thread }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; event.cancel_job.priority = priority; T_scheduler_before_operation(&event); (*operations->cancel_job)(scheduler, thread, priority, queue_context); T_scheduler_record_event(&event); } void T_scheduler_start_idle(const Scheduler_Control *scheduler, Thread_Control *thread, Per_CPU_Control *cpu) { T_scheduler_event event = { .operation = T_SCHEDULER_START_IDLE, .thread = thread }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; T_scheduler_before_operation(&event); (*operations->start_idle)(scheduler, thread, cpu); T_scheduler_record_event(&event); } #ifdef RTEMS_SMP bool T_scheduler_ask_for_help(const Scheduler_Control *scheduler, Thread_Control *thread, Scheduler_Node *node) { T_scheduler_event event = { .operation = T_SCHEDULER_ASK_FOR_HELP, .thread = thread, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; T_scheduler_before_operation(&event); event.ask_for_help.success = (*operations->ask_for_help)(scheduler, thread, node); T_scheduler_record_event(&event); return event.ask_for_help.success; } void T_scheduler_reconsider_help_request(const Scheduler_Control *scheduler, Thread_Control *thread, Scheduler_Node *node) { T_scheduler_event event = { .operation = T_SCHEDULER_RECONSIDER_HELP_REQUEST, .thread = thread, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; T_scheduler_before_operation(&event); (*operations->reconsider_help_request)(scheduler, thread, node); T_scheduler_record_event(&event); } void T_scheduler_withdraw_node(const Scheduler_Control *scheduler, Thread_Control *thread, Scheduler_Node *node, Thread_Scheduler_state next_state) { T_scheduler_event event = { .operation = T_SCHEDULER_WITHDRAW_NODE, .thread = thread, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; event.withdraw_node.next_state = next_state; T_scheduler_before_operation(&event); (*operations->withdraw_node)(scheduler, thread, node, next_state); T_scheduler_record_event(&event); } void T_scheduler_make_sticky(const Scheduler_Control *scheduler, Thread_Control *thread, Scheduler_Node *node) { T_scheduler_event event = { .operation = T_SCHEDULER_MAKE_STICKY, .thread = thread, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; T_scheduler_before_operation(&event); (*operations->make_sticky)(scheduler, thread, node); T_scheduler_record_event(&event); } void T_scheduler_clean_sticky(const Scheduler_Control *scheduler, Thread_Control *thread, Scheduler_Node *node) { T_scheduler_event event = { .operation = T_SCHEDULER_CLEAN_STICKY, .thread = thread, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; T_scheduler_before_operation(&event); (*operations->clean_sticky)(scheduler, thread, node); T_scheduler_record_event(&event); } void T_scheduler_pin(const Scheduler_Control *scheduler, Thread_Control *thread, Scheduler_Node *node, Per_CPU_Control *cpu) { T_scheduler_event event = { .operation = T_SCHEDULER_PIN, .thread = thread, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; event.pin_unpin.cpu = cpu; T_scheduler_before_operation(&event); (*operations->pin)(scheduler, thread, node, cpu); T_scheduler_record_event(&event); } void T_scheduler_unpin(const Scheduler_Control *scheduler, Thread_Control *thread, Scheduler_Node *node, Per_CPU_Control *cpu) { T_scheduler_event event = { .operation = T_SCHEDULER_UNPIN, .thread = thread, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; event.pin_unpin.cpu = cpu; T_scheduler_before_operation(&event); (*operations->unpin)(scheduler, thread, node, cpu); T_scheduler_record_event(&event); } void T_scheduler_add_processor(const Scheduler_Control *scheduler, Thread_Control *idle) { T_scheduler_event event = { .operation = T_SCHEDULER_ADD_PROCESSOR, .thread = idle }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; event.add_processor.idle = idle; T_scheduler_before_operation(&event); (*operations->add_processor)(scheduler, idle); T_scheduler_record_event(&event); } Thread_Control * T_scheduler_remove_processor(const Scheduler_Control *scheduler, Per_CPU_Control *cpu) { T_scheduler_event event = { .operation = T_SCHEDULER_REMOVE_PROCESSOR }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; event.remove_processor.cpu = cpu; T_scheduler_before_operation(&event); event.remove_processor.idle = (*operations->remove_processor)(scheduler, cpu); T_scheduler_record_event(&event); return event.remove_processor.idle; } Status_Control T_scheduler_set_affinity(const Scheduler_Control *scheduler, Thread_Control *thread, Scheduler_Node *node, const Processor_mask *affinity) { T_scheduler_event event = { .operation = T_SCHEDULER_SET_AFFINITY, .thread = thread, .node = node }; const Scheduler_Operations *operations; operations = &T_scheduler_operations[_Scheduler_Get_index(scheduler)]; event.set_affinity.affinity = *affinity; T_scheduler_before_operation(&event); event.set_affinity.status = (*operations->set_affinity)(scheduler, thread, node, affinity); T_scheduler_record_event(&event); return event.set_affinity.status; } #endif