summaryrefslogblamecommitdiffstats
path: root/testsuites/smptests/smpschededf02/init.c
blob: fae2d76bf3e4645723ecf5263c499a23858753c8 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  
                                                                       












                                                          
                   

      
                    












                                               







              









                      
                   

























































                                               














                                          


                                                                              
        













                                          




                                                                               
        









                                          

                                                              


                                          




                                                                             







                                          




                                                                        





                                          






























                                                               





                                                                             



















                                                                               






                                                                               
















                                                                               
                 































































































                                                                            
                                               











                                                                



                                         




















                                                                   
                                                             











                                                           
                                                         














                                                                 
/*
 * Copyright (c) 2016, 2020 embedded brains GmbH.  All rights reserved.
 *
 *  embedded brains GmbH
 *  Dornierstr. 4
 *  82178 Puchheim
 *  Germany
 *  <rtems@embedded-brains.de>
 *
 * The license and distribution terms for this file may be
 * found in the file LICENSE in this distribution or at
 * http://www.rtems.org/license/LICENSE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <tmacros.h>

#include <rtems.h>

const char rtems_test_name[] = "SMPSCHEDEDF 2";

#define CPU_COUNT 2

#define TASK_COUNT 5

#define P(i) (UINT32_C(2) + i)

#define A(cpu0, cpu1) ((cpu1 << 1) | cpu0)

typedef enum {
  T0,
  T1,
  T2,
  T3,
  T4,
  IDLE
} task_index;

typedef struct {
  enum {
    KIND_RESET,
    KIND_SET_PRIORITY,
    KIND_SET_AFFINITY,
    KIND_BLOCK,
    KIND_UNBLOCK
  } kind;

  task_index index;

  struct {
    rtems_task_priority priority;
    uint32_t cpu_set;
  } data;

  uint8_t expected_cpu_allocations[CPU_COUNT];
} test_action;

typedef struct {
  rtems_id timer_id;
  rtems_id master_id;
  rtems_id task_ids[TASK_COUNT];
  size_t action_index;
} test_context;

#define RESET \
  { \
    KIND_RESET, \
    0, \
    { 0 }, \
    { IDLE, IDLE } \
  }

#define SET_PRIORITY(index, prio, cpu0, cpu1) \
  { \
    KIND_SET_PRIORITY, \
    index, \
    { .priority = prio }, \
    { cpu0, cpu1 } \
  }

#define SET_AFFINITY(index, aff, cpu0, cpu1) \
  { \
    KIND_SET_AFFINITY, \
    index, \
    { .cpu_set = aff }, \
    { cpu0, cpu1 } \
  }

#define BLOCK(index, cpu0, cpu1) \
  { \
    KIND_BLOCK, \
    index, \
    { 0 }, \
    { cpu0, cpu1 } \
  }

#define UNBLOCK(index, cpu0, cpu1) \
  { \
    KIND_UNBLOCK, \
    index, \
    { 0 }, \
    { cpu0, cpu1 } \
  }

static const test_action test_actions[] = {
  RESET,
  UNBLOCK(      T0,             T0, IDLE),
  UNBLOCK(      T1,             T0,   T1),
  UNBLOCK(      T3,             T0,   T1),
  SET_PRIORITY( T1,  P(2),      T0,   T1),
  SET_PRIORITY( T3,  P(1),      T0,   T3),
  BLOCK(        T3,             T0,   T1),
  SET_AFFINITY( T1,  A(1, 1),   T0,   T1),
  SET_AFFINITY( T1,  A(1, 0),   T1,   T0),
  SET_AFFINITY( T1,  A(1, 1),   T1,   T0),
  SET_AFFINITY( T1,  A(1, 0),   T1,   T0),
  SET_AFFINITY( T1,  A(0, 1),   T0,   T1),
  BLOCK(        T0,           IDLE,   T1),
  UNBLOCK(      T0,             T0,   T1),
  BLOCK(        T1,             T0, IDLE),
  UNBLOCK(      T1,             T0,   T1),
  /*
   * Show that FIFO order is honoured across all threads of the same priority.
   */
  RESET,
  SET_PRIORITY( T1,  P(0),    IDLE, IDLE),
  SET_PRIORITY( T2,  P(1),    IDLE, IDLE),
  SET_PRIORITY( T3,  P(1),    IDLE, IDLE),
  SET_AFFINITY( T3,  A(1, 0), IDLE, IDLE),
  SET_PRIORITY( T4,  P(1),    IDLE, IDLE),
  SET_AFFINITY( T4,  A(1, 0), IDLE, IDLE),
  UNBLOCK(      T0,             T0, IDLE),
  UNBLOCK(      T1,             T0,   T1),
  UNBLOCK(      T2,             T0,   T1),
  UNBLOCK(      T3,             T0,   T1),
  UNBLOCK(      T4,             T0,   T1),
  BLOCK(        T1,             T0,   T2),
  BLOCK(        T2,             T3,   T0),
  BLOCK(        T3,             T4,   T0),
  /*
   * Schedule a high priority affine thread directly with a low priority affine
   * thread in the corresponding ready queue.  In this case we, remove the
   * affine ready queue in _Scheduler_EDF_SMP_Allocate_processor().
   */
  RESET,
  UNBLOCK(      T0,             T0, IDLE),
  UNBLOCK(      T1,             T0,   T1),
  SET_PRIORITY( T1,  P(2),      T0,   T1),
  SET_AFFINITY( T3,  A(0, 1),   T0,   T1),
  UNBLOCK(      T3,             T0,   T1),
  SET_PRIORITY( T2,  P(1),      T0,   T1),
  SET_AFFINITY( T2,  A(0, 1),   T0,   T1),
  UNBLOCK(      T2,             T0,   T2),
  BLOCK(        T1,             T0,   T2),
  BLOCK(        T2,             T0,   T3),
  /* Force migration of a higher priority one-to-all thread */
  RESET,
  UNBLOCK(      T0,             T0, IDLE),
  SET_AFFINITY( T1,  A(1, 0),   T0, IDLE),
  UNBLOCK(      T1,             T1,   T0),
  /*
   * Block a one-to-one thread while having a non-empty affine ready queue on
   * the same processor.
   */
  RESET,
  SET_AFFINITY( T1,  A(1, 0), IDLE, IDLE),
  SET_AFFINITY( T3,  A(1, 0), IDLE, IDLE),
  UNBLOCK(      T0,             T0, IDLE),
  UNBLOCK(      T1,             T1,   T0),
  UNBLOCK(      T2,             T1,   T0),
  UNBLOCK(      T3,             T1,   T0),
  BLOCK(        T1,             T2,   T0),
  BLOCK(        T0,             T3,   T2),
  /*
   * Make sure that a one-to-one thread does not get the wrong processor
   * allocated after selecting the highest ready thread.
   */
  RESET,
  SET_AFFINITY( T1,  A(1, 0), IDLE, IDLE),
  SET_AFFINITY( T2,  A(1, 0), IDLE, IDLE),
  UNBLOCK(      T0,             T0, IDLE),
  UNBLOCK(      T1,             T1,   T0),
  UNBLOCK(      T2,             T1,   T0),
  BLOCK(        T0,             T1, IDLE),
  RESET
};

static test_context test_instance;

static void set_priority(rtems_id id, rtems_task_priority prio)
{
  rtems_status_code sc;

  sc = rtems_task_set_priority(id, prio, &prio);
  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}

static void set_affinity(rtems_id id, uint32_t cpu_set_32)
{
  rtems_status_code sc;
  cpu_set_t cpu_set;
  size_t i;

  CPU_ZERO(&cpu_set);

  for (i = 0; i < CPU_COUNT; ++i) {
    if ((cpu_set_32 & (UINT32_C(1) << i)) != 0) {
      CPU_SET(i, &cpu_set);
    }
  }

  sc = rtems_task_set_affinity(id, sizeof(cpu_set), &cpu_set);
  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}

/*
 * The goal of the reset() function is to bring back a defined initial system
 * state for each test case.  All tasks of the test shall be suspended.  The
 * idle threads shall be ordered in the scheduled chain according to the CPU
 * index.
 */
static void reset(test_context *ctx)
{
  rtems_status_code sc;
  size_t i;

  for (i = 0; i < TASK_COUNT; ++i) {
    set_priority(ctx->task_ids[i], P(i));
    set_affinity(ctx->task_ids[i], A(1, 1));
  }

  for (i = CPU_COUNT; i < TASK_COUNT; ++i) {
    sc = rtems_task_suspend(ctx->task_ids[i]);
    rtems_test_assert(sc == RTEMS_SUCCESSFUL || sc == RTEMS_ALREADY_SUSPENDED);
  }

  for (i = 0; i < CPU_COUNT; ++i) {
    sc = rtems_task_resume(ctx->task_ids[i]);
    rtems_test_assert(sc == RTEMS_SUCCESSFUL || sc == RTEMS_INCORRECT_STATE);
  }

  /*
   * Order the idle threads explicitly.  Test cases may move the idle threads
   * around.  We have to ensure that the idle threads are ordered according to
   * the CPU index, otherwise the processor allocations cannot be specified for
   * a test case.  The idle threads of a scheduler have all the same priority,
   * so we have to take the FIFO ordering within a priority group into account.
   */
  for (i = 0; i < CPU_COUNT; ++i) {
    const Per_CPU_Control *c;
    const Thread_Control *h;

    c = _Per_CPU_Get_by_index(CPU_COUNT - 1 - i);
    h = c->heir;

    sc = rtems_task_suspend(h->Object.id);
    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
  }
}

static void check_cpu_allocations(test_context *ctx, const test_action *action)
{
  size_t i;

  for (i = 0; i < CPU_COUNT; ++i) {
    task_index e;
    const Per_CPU_Control *c;
    const Thread_Control *h;

    e = action->expected_cpu_allocations[i];
    c = _Per_CPU_Get_by_index(i);
    h = c->heir;

    if (e != IDLE) {
      rtems_test_assert(h->Object.id == ctx->task_ids[e]);
    } else {
      rtems_test_assert(h->is_idle);
    }
  }
}

/*
 * Use a timer to execute the actions, since it runs with thread dispatching
 * disabled.  This is necessary to check the expected processor allocations.
 */
static void timer(rtems_id id, void *arg)
{
  test_context *ctx;
  rtems_status_code sc;
  size_t i;

  ctx = arg;
  i = ctx->action_index;

  if (i == 0) {
    sc = rtems_task_suspend(ctx->master_id);
    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
  }

  if (i < RTEMS_ARRAY_SIZE(test_actions)) {
    const test_action *action = &test_actions[i];
    rtems_id task;

    ctx->action_index = i + 1;

    task = ctx->task_ids[action->index];

    switch (action->kind) {
      case KIND_SET_PRIORITY:
        set_priority(task, action->data.priority);
        break;
      case KIND_SET_AFFINITY:
        set_affinity(task, action->data.cpu_set);
        break;
      case KIND_BLOCK:
        sc = rtems_task_suspend(task);
        rtems_test_assert(sc == RTEMS_SUCCESSFUL);
        break;
      case KIND_UNBLOCK:
        sc = rtems_task_resume(task);
        rtems_test_assert(sc == RTEMS_SUCCESSFUL);
        break;
      default:
        rtems_test_assert(action->kind == KIND_RESET);
        reset(ctx);
        break;
    }

    check_cpu_allocations(ctx, action);

    sc = rtems_timer_reset(id);
    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
  } else {
    sc = rtems_task_resume(ctx->master_id);
    rtems_test_assert(sc == RTEMS_SUCCESSFUL);

    sc = rtems_event_transient_send(ctx->master_id);
    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
  }
}

static void do_nothing_task(rtems_task_argument arg)
{
  (void) arg;

  while (true) {
    /* Do nothing */
  }
}

static void test(void)
{
  test_context *ctx;
  rtems_status_code sc;
  size_t i;

  ctx = &test_instance;

  ctx->master_id = rtems_task_self();

  for (i = 0; i < TASK_COUNT; ++i) {
    sc = rtems_task_create(
      rtems_build_name(' ', ' ', 'T', '0' + i),
      P(i),
      RTEMS_MINIMUM_STACK_SIZE,
      RTEMS_DEFAULT_MODES,
      RTEMS_DEFAULT_ATTRIBUTES,
      &ctx->task_ids[i]
    );
    rtems_test_assert(sc == RTEMS_SUCCESSFUL);

    sc = rtems_task_start(ctx->task_ids[i], do_nothing_task, 0);
    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
  }

  sc = rtems_timer_create(
    rtems_build_name('A', 'C', 'T', 'N'),
    &ctx->timer_id
  );
  rtems_test_assert(sc == RTEMS_SUCCESSFUL);

  sc = rtems_timer_fire_after(ctx->timer_id, 1, timer, ctx);
  rtems_test_assert(sc == RTEMS_SUCCESSFUL);

  sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);
  rtems_test_assert(sc == RTEMS_SUCCESSFUL);

  for (i = 0; i < TASK_COUNT; ++i) {
    sc = rtems_task_delete(ctx->task_ids[i]);
    rtems_test_assert(sc == RTEMS_SUCCESSFUL);
  }

  sc = rtems_timer_delete(ctx->timer_id);
  rtems_test_assert(sc == RTEMS_SUCCESSFUL);
}

static void Init(rtems_task_argument arg)
{
  TEST_BEGIN();

  if (rtems_scheduler_get_processor_maximum() == CPU_COUNT) {
    test();
  } else {
    puts("warning: wrong processor count to run the test");
  }

  TEST_END();
  rtems_test_exit(0);
}

#define CONFIGURE_MICROSECONDS_PER_TICK 1000

#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER

#define CONFIGURE_MAXIMUM_TASKS (1 + TASK_COUNT)
#define CONFIGURE_MAXIMUM_TIMERS 1

#define CONFIGURE_MAXIMUM_PROCESSORS CPU_COUNT

#define CONFIGURE_SCHEDULER_EDF_SMP

#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION

#define CONFIGURE_RTEMS_INIT_TASKS_TABLE

#define CONFIGURE_INIT

#include <rtems/confdefs.h>