summaryrefslogtreecommitdiffstats
path: root/cpukit/libtest/t-test-scheduler.c
diff options
context:
space:
mode:
Diffstat (limited to 'cpukit/libtest/t-test-scheduler.c')
-rw-r--r--cpukit/libtest/t-test-scheduler.c658
1 files changed, 658 insertions, 0 deletions
diff --git a/cpukit/libtest/t-test-scheduler.c b/cpukit/libtest/t-test-scheduler.c
new file mode 100644
index 0000000000..d9df0cc97b
--- /dev/null
+++ b/cpukit/libtest/t-test-scheduler.c
@@ -0,0 +1,658 @@
+/* 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 & Co. KG
+ *
+ * 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 <rtems/test-scheduler.h>
+
+#include <rtems.h>
+#include <rtems/score/percpu.h>
+#include <rtems/score/schedulerimpl.h>
+
+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