Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:24:44

0001 /* SPDX-License-Identifier: BSD-2-Clause */
0002 
0003 /*
0004  * Copyright (c) 2020 Richi Dubey ( richidubey@gmail.com )
0005  * Copyright (C) 2016, 2017 embedded brains GmbH & Co. KG
0006  *
0007  * Redistribution and use in source and binary forms, with or without
0008  * modification, are permitted provided that the following conditions
0009  * are met:
0010  * 1. Redistributions of source code must retain the above copyright
0011  *    notice, this list of conditions and the following disclaimer.
0012  * 2. Redistributions in binary form must reproduce the above copyright
0013  *    notice, this list of conditions and the following disclaimer in the
0014  *    documentation and/or other materials provided with the distribution.
0015  *
0016  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
0017  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0018  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0019  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
0020  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
0021  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
0022  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
0023  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
0024  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0025  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
0026  * POSSIBILITY OF SUCH DAMAGE.
0027  */
0028 
0029 #ifdef HAVE_CONFIG_H
0030 #include "config.h"
0031 #endif
0032 
0033 #include <tmacros.h>
0034 
0035 #include <rtems.h>
0036 
0037 const char rtems_test_name[] = "SMPSTRONGAPA 1";
0038 
0039 #define CPU_COUNT 4
0040 
0041 #define TASK_COUNT 5
0042 
0043 #define P(i) (UINT32_C(2) + i)
0044 
0045 #define ALL ((UINT32_C(1) << CPU_COUNT) - 1)
0046 
0047 #define A(cpu0, cpu1, cpu2, cpu3) ( (cpu3 << 3) | (cpu2 << 2) | (cpu1 << 1)| cpu0 )
0048 
0049 typedef enum {
0050   T0,
0051   T1,
0052   T2,
0053   T3,
0054   T4,
0055   IDLE
0056 } task_index;
0057 
0058 typedef struct {
0059   enum {
0060     KIND_RESET,
0061     KIND_SET_PRIORITY,
0062     KIND_SET_AFFINITY,
0063     KIND_BLOCK,
0064     KIND_UNBLOCK
0065   } kind;
0066 
0067   task_index index;
0068 
0069   struct {
0070     rtems_task_priority priority;
0071     uint32_t cpu_set;
0072   } data;
0073 
0074   uint8_t expected_cpu_allocations[CPU_COUNT];
0075 } test_action;
0076 
0077 typedef struct {
0078   rtems_id timer_id;
0079   rtems_id master_id;
0080   rtems_id task_ids[TASK_COUNT];
0081   size_t action_index;
0082 } test_context;
0083 
0084 #define RESET \
0085   { \
0086     KIND_RESET, \
0087     0, \
0088     { 0 }, \
0089     { IDLE, IDLE, IDLE, IDLE } \
0090   }
0091 
0092 #define SET_PRIORITY(index, prio, cpu0, cpu1, cpu2, cpu3) \
0093   { \
0094     KIND_SET_PRIORITY, \
0095     index, \
0096     { .priority = prio }, \
0097     { cpu0, cpu1, cpu2, cpu3 } \
0098   }
0099 
0100 #define SET_AFFINITY(index, aff, cpu0, cpu1, cpu2, cpu3) \
0101   { \
0102     KIND_SET_AFFINITY, \
0103     index, \
0104     { .cpu_set = aff }, \
0105     { cpu0, cpu1, cpu2, cpu3 } \
0106   }
0107 
0108 #define BLOCK(index, cpu0, cpu1, cpu2, cpu3) \
0109   { \
0110     KIND_BLOCK, \
0111     index, \
0112     { 0 }, \
0113     { cpu0, cpu1, cpu2, cpu3 } \
0114   }
0115 
0116 #define UNBLOCK(index, cpu0, cpu1, cpu2, cpu3) \
0117   { \
0118     KIND_UNBLOCK, \
0119     index, \
0120     { 0 }, \
0121     { cpu0, cpu1, cpu2, cpu3 } \
0122   }
0123 
0124 static const test_action test_actions[] = {
0125   RESET,
0126   UNBLOCK(      T0,         T0,  IDLE, IDLE, IDLE),
0127   UNBLOCK(      T1,         T0,    T1, IDLE, IDLE),
0128   UNBLOCK(      T2,         T0,    T1,   T2, IDLE),
0129   UNBLOCK(      T3,         T0,    T1,   T2,   T3),
0130   UNBLOCK(      T4,         T0,    T1,   T2,   T3),
0131   SET_PRIORITY( T0,  P(0),      T0,    T1,   T2,   T3),
0132   SET_PRIORITY( T1,  P(1),      T0,    T1,   T2,   T3),
0133   SET_PRIORITY( T2,  P(2),      T0,    T1,   T2,   T3),
0134   SET_PRIORITY( T4,  P(4),      T0,    T1,   T2,   T3),
0135   /*
0136    * Introduce Task 3 intially with lowest priority to imitate late arrival
0137    */
0138   SET_PRIORITY( T3,  P(8),      T0,    T1,   T2,   T4),
0139   SET_AFFINITY( T0,   ALL,      T0,    T1,   T2,   T4),
0140   SET_AFFINITY( T1,   A(0, 1, 0, 0),    T0,    T1,   T2,   T4),
0141   SET_AFFINITY( T2,   A(0, 0, 1, 0),    T0,    T1,   T2,   T4),
0142   SET_AFFINITY( T4,   A(0, 0, 0, 1),    T0,    T1,   T2,   T4),
0143   /*
0144    *Set affinity of Task 4 only to CPU1, so that we can check shifting
0145    */
0146   SET_AFFINITY( T3,   A(1, 0, 0, 0),    T0,    T1,   T2,   T4),
0147   /*
0148    * Show that higher priority task gets dislodged from its processor
0149    * by a lower priority task !
0150    * and goes to the cpu that is executing the task with lowest priority
0151    * (among all cpus).
0152    */
0153   SET_PRIORITY( T3,   P(3),         T3,    T1,   T2,   T0),
0154   RESET
0155 };
0156 
0157 static test_context test_instance;
0158 
0159 static void set_priority(rtems_id id, rtems_task_priority prio)
0160 {
0161   rtems_status_code sc;
0162 
0163   sc = rtems_task_set_priority(id, prio, &prio);
0164   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0165 }
0166 
0167 static void set_affinity(rtems_id id, uint32_t cpu_set_32)
0168 {
0169   rtems_status_code sc;
0170   cpu_set_t cpu_set;
0171   size_t i;
0172 
0173   CPU_ZERO(&cpu_set);
0174 
0175   for (i = 0; i < CPU_COUNT; ++i) {
0176     if ((cpu_set_32 & (UINT32_C(1) << i)) != 0) {
0177       CPU_SET(i, &cpu_set);
0178     }
0179   }
0180 
0181   sc = rtems_task_set_affinity(id, sizeof(cpu_set), &cpu_set);
0182   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0183 }
0184 
0185 static void reset(test_context *ctx)
0186 {
0187   rtems_status_code sc;
0188   size_t i;
0189 
0190   for (i = CPU_COUNT; i < TASK_COUNT; ++i) {
0191     set_priority(ctx->task_ids[i], P(i));
0192     set_affinity(ctx->task_ids[i], ALL);
0193 
0194     sc = rtems_task_suspend(ctx->task_ids[i]);
0195     rtems_test_assert(sc == RTEMS_SUCCESSFUL || sc == RTEMS_ALREADY_SUSPENDED);
0196   }
0197 
0198   for (i = 0; i < CPU_COUNT; ++i) {
0199     set_priority(ctx->task_ids[i], P(i));
0200 
0201     sc = rtems_task_resume(ctx->task_ids[i]);
0202     rtems_test_assert(sc == RTEMS_SUCCESSFUL || sc == RTEMS_INCORRECT_STATE);
0203   }
0204 
0205   /* Order the idle threads explicitly */
0206   for (i = 0; i < CPU_COUNT; ++i) {
0207     const Per_CPU_Control *c;
0208     const Thread_Control *h;
0209 
0210     c = _Per_CPU_Get_by_index(CPU_COUNT - 1 - i);
0211     h = c->heir;
0212 
0213     sc = rtems_task_suspend(h->Object.id);
0214     rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0215   }
0216 }
0217 
0218 static void check_cpu_allocations(test_context *ctx, const test_action *action)
0219 {
0220   size_t i;
0221 
0222   for (i = 0; i < CPU_COUNT; ++i) {
0223     task_index e;
0224     const Per_CPU_Control *c;
0225     const Thread_Control *h;
0226 
0227     e = action->expected_cpu_allocations[i];
0228     c = _Per_CPU_Get_by_index(i);
0229     h = c->heir;
0230 
0231     if (e != IDLE) {
0232       rtems_test_assert(h->Object.id == ctx->task_ids[e]);
0233     } else {
0234       rtems_test_assert(h->is_idle);
0235     }
0236   }
0237 }
0238 
0239 /*
0240  * Use a timer to execute the actions, since it runs with thread dispatching
0241  * disabled.  This is necessary to check the expected processor allocations.
0242  */
0243 static void timer(rtems_id id, void *arg)
0244 {
0245   test_context *ctx;
0246   rtems_status_code sc;
0247   size_t i;
0248 
0249   ctx = arg;
0250   i = ctx->action_index;
0251 
0252   if (i == 0) {
0253     sc = rtems_task_suspend(ctx->master_id);
0254     rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0255   }
0256 
0257   if (i < RTEMS_ARRAY_SIZE(test_actions)) {
0258     const test_action *action = &test_actions[i];
0259     rtems_id task;
0260 
0261     ctx->action_index = i + 1;
0262 
0263     task = ctx->task_ids[action->index];
0264 
0265     switch (action->kind) {
0266       case KIND_SET_PRIORITY:
0267         set_priority(task, action->data.priority);
0268         break;
0269       case KIND_SET_AFFINITY:
0270         set_affinity(task, action->data.cpu_set);
0271         break;
0272       case KIND_BLOCK:
0273         sc = rtems_task_suspend(task);
0274         rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0275         break;
0276       case KIND_UNBLOCK:
0277         sc = rtems_task_resume(task);
0278         rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0279         break;
0280       default:
0281         rtems_test_assert(action->kind == KIND_RESET);
0282         reset(ctx);
0283         break;
0284     }
0285 
0286     check_cpu_allocations(ctx, action);
0287 
0288     sc = rtems_timer_reset(id);
0289     rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0290   } else {
0291     sc = rtems_task_resume(ctx->master_id);
0292     rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0293 
0294     sc = rtems_event_transient_send(ctx->master_id);
0295     rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0296   }
0297 }
0298 
0299 static void do_nothing_task(rtems_task_argument arg)
0300 {
0301   (void) arg;
0302 
0303   while (true) {
0304     /* Do nothing */
0305   }
0306 }
0307 
0308 static void test(void)
0309 {
0310   test_context *ctx;
0311   rtems_status_code sc;
0312   size_t i;
0313 
0314   ctx = &test_instance;
0315 
0316   ctx->master_id = rtems_task_self();
0317 
0318   for (i = 0; i < TASK_COUNT; ++i) {
0319     sc = rtems_task_create(
0320       rtems_build_name(' ', ' ', 'T', '0' + i),
0321       P(i),
0322       RTEMS_MINIMUM_STACK_SIZE,
0323       RTEMS_DEFAULT_MODES,
0324       RTEMS_DEFAULT_ATTRIBUTES,
0325       &ctx->task_ids[i]
0326     );
0327     rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0328 
0329     sc = rtems_task_start(ctx->task_ids[i], do_nothing_task, 0);
0330     rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0331   }
0332 
0333   sc = rtems_timer_create(
0334     rtems_build_name('A', 'C', 'T', 'N'),
0335     &ctx->timer_id
0336   );
0337   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0338 
0339   sc = rtems_timer_fire_after(ctx->timer_id, 1, timer, ctx);
0340   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0341 
0342   sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
0343   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0344 
0345   for (i = 0; i < TASK_COUNT; ++i) {
0346     sc = rtems_task_delete(ctx->task_ids[i]);
0347     rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0348   }
0349 
0350   sc = rtems_timer_delete(ctx->timer_id);
0351   rtems_test_assert(sc == RTEMS_SUCCESSFUL);
0352 }
0353 
0354 static void Init(rtems_task_argument arg)
0355 {
0356   TEST_BEGIN();
0357 
0358   if (rtems_scheduler_get_processor_maximum() == CPU_COUNT) {
0359     test();
0360   } else {
0361     puts("warning: wrong processor count to run the test");
0362   }
0363 
0364   TEST_END();
0365   rtems_test_exit(0);
0366 }
0367 
0368 #define CONFIGURE_MICROSECONDS_PER_TICK 1000
0369 
0370 #define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
0371 #define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER
0372 
0373 #define CONFIGURE_MAXIMUM_TASKS (1 + TASK_COUNT)
0374 #define CONFIGURE_MAXIMUM_TIMERS 1
0375 
0376 #define CONFIGURE_MAXIMUM_PROCESSORS CPU_COUNT
0377 
0378 #define CONFIGURE_SCHEDULER_STRONG_APA
0379 
0380 #define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION
0381 
0382 #define CONFIGURE_RTEMS_INIT_TASKS_TABLE
0383 
0384 #define CONFIGURE_INIT
0385 
0386 #include <rtems/confdefs.h>