<div dir="ltr">Hi,<div><br></div><div>This is the required test case based on the paper (<a href="https://people.mpi-sws.org/~bbb/papers/pdf/rtss14f.pdf">https://people.mpi-sws.org/~bbb/papers/pdf/rtss14f.pdf</a>) that tests if the Strong APA scheduler is working as intended. </div><div>I have checked and this compiles with no warnings/errors. </div><div><br></div><div>Thanks.  </div><div>Richi.</div></div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Tue, Jul 28, 2020 at 7:23 PM Richi Dubey <<a href="mailto:richidubey@gmail.com">richidubey@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">---<br>
 testsuites/smptests/smpstrongapa02/init.c     | 358 ++++++++++++++++++<br>
 .../smpstrongapa02/smpstrongapa02.doc         |  26 ++<br>
 .../smpstrongapa02/smpstrongapa02.scn         |   2 +<br>
 3 files changed, 386 insertions(+)<br>
 create mode 100644 testsuites/smptests/smpstrongapa02/init.c<br>
 create mode 100644 testsuites/smptests/smpstrongapa02/smpstrongapa02.doc<br>
 create mode 100644 testsuites/smptests/smpstrongapa02/smpstrongapa02.scn<br>
<br>
diff --git a/testsuites/smptests/smpstrongapa02/init.c b/testsuites/smptests/smpstrongapa02/init.c<br>
new file mode 100644<br>
index 0000000000..e00317d7b2<br>
--- /dev/null<br>
+++ b/testsuites/smptests/smpstrongapa02/init.c<br>
@@ -0,0 +1,358 @@<br>
+/*<br>
+ * Copyright (c) 2020 Richi Dubey <br>
+ * All rights reserved.<br>
+ *<br>
+ * <a href="mailto:richidubey@gmail.com" target="_blank">richidubey@gmail.com</a><br>
+ *<br>
+ * The license and distribution terms for this file may be<br>
+ * found in the file LICENSE in this distribution or at<br>
+ * <a href="http://www.rtems.org/license/LICENSE" rel="noreferrer" target="_blank">http://www.rtems.org/license/LICENSE</a>.<br>
+ */<br>
+#ifdef HAVE_CONFIG_H<br>
+#include "config.h"<br>
+#endif<br>
+<br>
+#include <tmacros.h><br>
+<br>
+#include <rtems.h><br>
+<br>
+const char rtems_test_name[] = "SMPSTRONGAPA 2";<br>
+<br>
+#define CPU_COUNT 3<br>
+<br>
+#define TASK_COUNT 4<br>
+<br>
+#define P(i) (UINT32_C(2) + i)<br>
+<br>
+#define ALL ((UINT32_C(1) << CPU_COUNT) - 1)<br>
+<br>
+#define A(cpu0, cpu1, cpu2) ((cpu2 << 2) | (cpu1 << 1) | cpu0)<br>
+<br>
+typedef enum {<br>
+  T0,<br>
+  T1,<br>
+  T2,<br>
+  T3,<br>
+  IDLE<br>
+} task_index;<br>
+<br>
+typedef struct {<br>
+  enum {<br>
+    KIND_RESET,<br>
+    KIND_SET_PRIORITY,<br>
+    KIND_SET_AFFINITY,<br>
+    KIND_BLOCK,<br>
+    KIND_UNBLOCK<br>
+  } kind;<br>
+<br>
+  task_index index;<br>
+<br>
+  struct {<br>
+    rtems_task_priority priority;<br>
+    uint32_t cpu_set;<br>
+  } data;<br>
+<br>
+  uint8_t expected_cpu_allocations[CPU_COUNT];<br>
+} test_action;<br>
+<br>
+typedef struct {<br>
+  rtems_id timer_id;<br>
+  rtems_id master_id;<br>
+  rtems_id task_ids[TASK_COUNT];<br>
+  size_t action_index;<br>
+} test_context;<br>
+<br>
+#define RESET \<br>
+  { \<br>
+    KIND_RESET, \<br>
+    0, \<br>
+    { 0 }, \<br>
+    { IDLE, IDLE, IDLE} \<br>
+  }<br>
+<br>
+#define SET_PRIORITY(index, prio, cpu0, cpu1, cpu2) \<br>
+  { \<br>
+    KIND_SET_PRIORITY, \<br>
+    index, \<br>
+    { .priority = prio }, \<br>
+    { cpu0, cpu1, cpu2 } \<br>
+  }<br>
+<br>
+#define SET_AFFINITY(index, aff, cpu0, cpu1, cpu2) \<br>
+  { \<br>
+    KIND_SET_AFFINITY, \<br>
+    index, \<br>
+    { .cpu_set = aff }, \<br>
+    { cpu0, cpu1, cpu2 } \<br>
+  }<br>
+<br>
+#define BLOCK(index, cpu0, cpu1, cpu2) \<br>
+  { \<br>
+    KIND_BLOCK, \<br>
+    index, \<br>
+    { 0 }, \<br>
+    { cpu0, cpu1, cpu2 } \<br>
+  }<br>
+<br>
+#define UNBLOCK(index, cpu0, cpu1, cpu2) \<br>
+  { \<br>
+    KIND_UNBLOCK, \<br>
+    index, \<br>
+    { 0 }, \<br>
+    { cpu0, cpu1, cpu2 } \<br>
+  }<br>
+<br>
+static const test_action test_actions[] = {<br>
+  RESET,<br>
+  UNBLOCK(      T0,                    T0, IDLE,   IDLE),<br>
+  UNBLOCK(      T1,                    T0,    T1,  IDLE),<br>
+  UNBLOCK(      T2,                    T0,    T1,    T2),<br>
+  UNBLOCK(      T3,                    T0,    T1,    T2),<br>
+  SET_PRIORITY( T0,  P(0),             T0,    T1,    T2),<br>
+  SET_PRIORITY( T1,  P(1),             T0,    T1,    T2),<br>
+  SET_PRIORITY( T3,  P(3),             T0,    T1,    T2),<br>
+  /*<br>
+   * Introduce Task 2 intially with lowest priority to imitate late arrival<br>
+   */<br>
+  SET_PRIORITY( T2,  P(4),             T0,    T1,    T3),  <br>
+  SET_AFFINITY( T0,   ALL,             T0,    T1,    T3),<br>
+  SET_AFFINITY( T1,   A(0, 1, 1),      T0,    T1,    T3),<br>
+  SET_AFFINITY( T2,   A(1, 0, 0),      T0,    T1,    T3),<br>
+  SET_AFFINITY( T3,   A(0, 1, 1),      T0,    T1,    T3),<br>
+  /*<br>
+   * Show that higher priority task gets dislodged from its processor<br>
+   */<br>
+  SET_PRIORITY( T2,   P(2),            T2,    T0,    T1),<br>
+  RESET<br>
+};<br>
+<br>
+static test_context test_instance;<br>
+<br>
+static void set_priority(rtems_id id, rtems_task_priority prio)<br>
+{<br>
+  rtems_status_code sc;<br>
+<br>
+  sc = rtems_task_set_priority(id, prio, &prio);<br>
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+}<br>
+<br>
+static void set_affinity(rtems_id id, uint32_t cpu_set_32)<br>
+{<br>
+  rtems_status_code sc;<br>
+  cpu_set_t cpu_set;<br>
+  size_t i;<br>
+<br>
+  CPU_ZERO(&cpu_set);<br>
+<br>
+  for (i = 0; i < CPU_COUNT; ++i) {<br>
+    if ((cpu_set_32 & (UINT32_C(1) << i)) != 0) {<br>
+      CPU_SET(i, &cpu_set);<br>
+    }<br>
+  }<br>
+<br>
+  sc = rtems_task_set_affinity(id, sizeof(cpu_set), &cpu_set);<br>
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+}<br>
+<br>
+static void reset(test_context *ctx)<br>
+{<br>
+  rtems_status_code sc;<br>
+  size_t i;<br>
+<br>
+  for (i = CPU_COUNT; i < TASK_COUNT; ++i) {<br>
+    set_priority(ctx->task_ids[i], P(i));<br>
+    set_affinity(ctx->task_ids[i], ALL);<br>
+<br>
+    sc = rtems_task_suspend(ctx->task_ids[i]);<br>
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL || sc == RTEMS_ALREADY_SUSPENDED);<br>
+  }<br>
+<br>
+  for (i = 0; i < CPU_COUNT; ++i) {<br>
+    set_priority(ctx->task_ids[i], P(i));<br>
+<br>
+    sc = rtems_task_resume(ctx->task_ids[i]);<br>
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL || sc == RTEMS_INCORRECT_STATE);<br>
+  }<br>
+<br>
+  /* Order the idle threads explicitly */<br>
+  for (i = 0; i < CPU_COUNT; ++i) {<br>
+    const Per_CPU_Control *c;<br>
+    const Thread_Control *h;<br>
+<br>
+    c = _Per_CPU_Get_by_index(CPU_COUNT - 1 - i);<br>
+    h = c->heir;<br>
+<br>
+    sc = rtems_task_suspend(h->Object.id);<br>
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+  }<br>
+}<br>
+<br>
+static void check_cpu_allocations(test_context *ctx, const test_action *action)<br>
+{<br>
+  size_t i;<br>
+<br>
+  for (i = 0; i < CPU_COUNT; ++i) {<br>
+    task_index e;<br>
+    const Per_CPU_Control *c;<br>
+    const Thread_Control *h;<br>
+<br>
+    e = action->expected_cpu_allocations[i];<br>
+    c = _Per_CPU_Get_by_index(i);<br>
+    h = c->heir;<br>
+<br>
+    if (e != IDLE) {<br>
+      rtems_test_assert(h->Object.id == ctx->task_ids[e]);<br>
+    } else {<br>
+      rtems_test_assert(h->is_idle);<br>
+    }<br>
+  }<br>
+}<br>
+<br>
+/*<br>
+ * Use a timer to execute the actions, since it runs with thread dispatching<br>
+ * disabled.  This is necessary to check the expected processor allocations.<br>
+ */<br>
+static void timer(rtems_id id, void *arg)<br>
+{<br>
+  test_context *ctx;<br>
+  rtems_status_code sc;<br>
+  size_t i;<br>
+<br>
+  ctx = arg;<br>
+  i = ctx->action_index;<br>
+<br>
+  if (i == 0) {<br>
+    sc = rtems_task_suspend(ctx->master_id);<br>
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+  }<br>
+<br>
+  if (i < RTEMS_ARRAY_SIZE(test_actions)) {<br>
+    const test_action *action = &test_actions[i];<br>
+    rtems_id task;<br>
+<br>
+    ctx->action_index = i + 1;<br>
+<br>
+    task = ctx->task_ids[action->index];<br>
+<br>
+    switch (action->kind) {<br>
+      case KIND_SET_PRIORITY:<br>
+        set_priority(task, action->data.priority);<br>
+        break;<br>
+      case KIND_SET_AFFINITY:<br>
+        set_affinity(task, action->data.cpu_set);<br>
+        break;<br>
+      case KIND_BLOCK:<br>
+        sc = rtems_task_suspend(task);<br>
+        rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+        break;<br>
+      case KIND_UNBLOCK:<br>
+        sc = rtems_task_resume(task);<br>
+        rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+        break;<br>
+      default:<br>
+        rtems_test_assert(action->kind == KIND_RESET);<br>
+        reset(ctx);<br>
+        break;<br>
+    }<br>
+<br>
+    check_cpu_allocations(ctx, action);<br>
+<br>
+    sc = rtems_timer_reset(id);<br>
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+  } else {<br>
+    sc = rtems_task_resume(ctx->master_id);<br>
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+<br>
+    sc = rtems_event_transient_send(ctx->master_id);<br>
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+  }<br>
+}<br>
+<br>
+static void do_nothing_task(rtems_task_argument arg)<br>
+{<br>
+  (void) arg;<br>
+<br>
+  while (true) {<br>
+    /* Do nothing */<br>
+  }<br>
+}<br>
+<br>
+static void test(void)<br>
+{<br>
+  test_context *ctx;<br>
+  rtems_status_code sc;<br>
+  size_t i;<br>
+<br>
+  ctx = &test_instance;<br>
+<br>
+  ctx->master_id = rtems_task_self();<br>
+<br>
+  for (i = 0; i < TASK_COUNT; ++i) {<br>
+    sc = rtems_task_create(<br>
+      rtems_build_name(' ', ' ', 'T', '0' + i),<br>
+      P(i),<br>
+      RTEMS_MINIMUM_STACK_SIZE,<br>
+      RTEMS_DEFAULT_MODES,<br>
+      RTEMS_DEFAULT_ATTRIBUTES,<br>
+      &ctx->task_ids[i]<br>
+    );<br>
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+<br>
+    sc = rtems_task_start(ctx->task_ids[i], do_nothing_task, 0);<br>
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+  }<br>
+<br>
+  sc = rtems_timer_create(<br>
+    rtems_build_name('A', 'C', 'T', 'N'),<br>
+    &ctx->timer_id<br>
+  );<br>
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+<br>
+  sc = rtems_timer_fire_after(ctx->timer_id, 1, timer, ctx);<br>
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+<br>
+  sc = rtems_event_transient_receive(RTEMS_WAIT, RTEMS_NO_TIMEOUT);<br>
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+<br>
+  for (i = 0; i < TASK_COUNT; ++i) {<br>
+    sc = rtems_task_delete(ctx->task_ids[i]);<br>
+    rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+  }<br>
+<br>
+  sc = rtems_timer_delete(ctx->timer_id);<br>
+  rtems_test_assert(sc == RTEMS_SUCCESSFUL);<br>
+}<br>
+<br>
+static void Init(rtems_task_argument arg)<br>
+{<br>
+  TEST_BEGIN();<br>
+<br>
+  if (rtems_scheduler_get_processor_maximum() == CPU_COUNT) {<br>
+    test();<br>
+  } else {<br>
+    puts("warning: wrong processor count to run the test");<br>
+  }<br>
+<br>
+  TEST_END();<br>
+  rtems_test_exit(0);<br>
+}<br>
+<br>
+#define CONFIGURE_MICROSECONDS_PER_TICK 1000<br>
+<br>
+#define CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER<br>
+#define CONFIGURE_APPLICATION_NEEDS_SIMPLE_CONSOLE_DRIVER<br>
+<br>
+#define CONFIGURE_MAXIMUM_TASKS (1 + TASK_COUNT)<br>
+#define CONFIGURE_MAXIMUM_TIMERS 1<br>
+<br>
+#define CONFIGURE_MAXIMUM_PROCESSORS CPU_COUNT<br>
+<br>
+#define CONFIGURE_SCHEDULER_STRONG_APA<br>
+<br>
+#define CONFIGURE_INITIAL_EXTENSIONS RTEMS_TEST_INITIAL_EXTENSION<br>
+<br>
+#define CONFIGURE_RTEMS_INIT_TASKS_TABLE<br>
+<br>
+#define CONFIGURE_INIT<br>
+<br>
+#include <rtems/confdefs.h><br>
diff --git a/testsuites/smptests/smpstrongapa02/smpstrongapa02.doc b/testsuites/smptests/smpstrongapa02/smpstrongapa02.doc<br>
new file mode 100644<br>
index 0000000000..c593e1474a<br>
--- /dev/null<br>
+++ b/testsuites/smptests/smpstrongapa02/smpstrongapa02.doc<br>
@@ -0,0 +1,26 @@<br>
+This file describes the directives and concepts tested by this test set.<br>
+<br>
+test set name: smpstrongapa02<br>
+<br>
+directives:<br>
+<br>
+  - _Scheduler_strong_APA_Get_highest_ready()<br>
+  - _Scheduler_strong_APA_Get_lowest_ready()<br>
+  - _Scheduler_strong_APA_Set_affinity()<br>
+  etc<br>
+<br>
+concepts:<br>
+<br>
+  A testsuite that would only execute efficiently <br>
+  (task finish within the deadline if the task set is schedulable)<br>
+  on the Strong APA scheduler but not on the SMP EDF Scheduler or<br>
+  any other SMP scheduler not supporting the dislodging of tasks <br>
+  based on affinity as described by Bradenburg et. al in :<br>
+  <br>
+  Cerqueira, Felipe & Gujarati, Arpan & Brandenburg, Bjorn. (2015).<br>
+  Linux's Processor Affinity API, Refined: Shifting Real-Time Tasks <br>
+  Towards Higher Schedulability. <br>
+  Proceedings - Real-Time Systems Symposium. 2015. 249-259.<br>
+  10.1109/RTSS.2014.29<br>
+<br>
+ <br>
diff --git a/testsuites/smptests/smpstrongapa02/smpstrongapa02.scn b/testsuites/smptests/smpstrongapa02/smpstrongapa02.scn<br>
new file mode 100644<br>
index 0000000000..f88b160cad<br>
--- /dev/null<br>
+++ b/testsuites/smptests/smpstrongapa02/smpstrongapa02.scn<br>
@@ -0,0 +1,2 @@<br>
+*** BEGIN OF TEST SMPSTRONGAPA 2 ***<br>
+*** END OF TEST SMPSTRONGAPA 2 ***<br>
-- <br>
2.17.1<br>
<br>
</blockquote></div>