From c72a3122754bee5f9fd0197efe58afa35dd4de40 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 17 Sep 2021 18:19:27 +0200 Subject: spec: Specify timeout with priority inherit --- spec/rtems/sem/req/timeout.yml | 29 +- spec/score/tq/req/flush-fifo.yml | 11 +- spec/score/tq/req/flush-priority-inherit.yml | 11 +- spec/score/tq/req/flush-priority.yml | 11 +- spec/score/tq/req/timeout-priority-inherit.yml | 414 +++++++++++++++++++++++++ spec/score/tq/req/timeout.yml | 128 ++------ 6 files changed, 493 insertions(+), 111 deletions(-) create mode 100644 spec/score/tq/req/timeout-priority-inherit.yml diff --git a/spec/rtems/sem/req/timeout.yml b/spec/rtems/sem/req/timeout.yml index 57b32b07..ce7dc3b4 100644 --- a/spec/rtems/sem/req/timeout.yml +++ b/spec/rtems/sem/req/timeout.yml @@ -15,6 +15,12 @@ post-conditions: text: | The semaphore obtain timeout actions shall be done as specified by ${/score/tq/req/timeout}. + - name: TimeoutPriorityInherit + test-code: | + ${/score/tq/req/timeout-priority-inherit:/test-run}( &ctx->tq_ctx ); + text: | + The semaphore obtain timeout actions shall be done as specified by + ${/score/tq/req/timeout-priority-inherit}. test-epilogue: null test-prologue: null pre-conditions: @@ -123,6 +129,7 @@ test-includes: - string.h test-local-includes: - tr-tq-timeout.h +- tr-tq-timeout-priority-inherit.h - tx-thread-queue.h - tx-support.h test-prepare: | @@ -153,17 +160,17 @@ text: | transition-map: - enabled-by: true post-conditions: - Action: Timeout + Action: + - if: + pre-conditions: + Class: + - PrioInherit + - MrsP + then: TimeoutPriorityInherit + - else: Timeout pre-conditions: Class: all Discipline: all -- enabled-by: true - post-conditions: NoMrsP - pre-conditions: - Class: - - MrsP - Discipline: - - Priority - enabled-by: true post-conditions: NeedsPriorityDiscipline pre-conditions: @@ -173,9 +180,9 @@ transition-map: - MrsP Discipline: - FIFO -- enabled-by: RTEMS_SMP - post-conditions: - Action: Timeout +- enabled-by: + not: RTEMS_SMP + post-conditions: NoMrsP pre-conditions: Class: - MrsP diff --git a/spec/score/tq/req/flush-fifo.yml b/spec/score/tq/req/flush-fifo.yml index f7842175..8c543ad7 100644 --- a/spec/score/tq/req/flush-fifo.yml +++ b/spec/score/tq/req/flush-fifo.yml @@ -140,13 +140,20 @@ test-support: | TQFlush( ctx->tq_ctx ); } - static void SchedulerEvent( void *arg, const T_scheduler_event *event ) + static void SchedulerEvent( + void *arg, + const T_scheduler_event *event, + T_scheduler_when when + ) { Context *ctx; ctx = arg; - if ( event->operation == T_SCHEDULER_BLOCK ) { + if ( + when == T_SCHEDULER_BEFORE && + event->operation == T_SCHEDULER_BLOCK + ) { ctx->request.handler = Flush; ctx->request.arg = ctx; CallWithinISRSubmit( &ctx->request ); diff --git a/spec/score/tq/req/flush-priority-inherit.yml b/spec/score/tq/req/flush-priority-inherit.yml index 002933ce..eef2856e 100644 --- a/spec/score/tq/req/flush-priority-inherit.yml +++ b/spec/score/tq/req/flush-priority-inherit.yml @@ -251,13 +251,20 @@ test-support: | TQFlush( ctx->tq_ctx ); } - static void SchedulerEvent( void *arg, const T_scheduler_event *event ) + static void SchedulerEvent( + void *arg, + const T_scheduler_event *event, + T_scheduler_when when + ) { Context *ctx; ctx = arg; - if ( event->operation == T_SCHEDULER_BLOCK ) { + if ( + when == T_SCHEDULER_BEFORE && + event->operation == T_SCHEDULER_BLOCK + ) { ctx->request.handler = Flush; ctx->request.arg = ctx; CallWithinISRSubmit( &ctx->request ); diff --git a/spec/score/tq/req/flush-priority.yml b/spec/score/tq/req/flush-priority.yml index 322d81dd..c4bb20c5 100644 --- a/spec/score/tq/req/flush-priority.yml +++ b/spec/score/tq/req/flush-priority.yml @@ -165,13 +165,20 @@ test-support: | TQFlush( ctx->tq_ctx ); } - static void SchedulerEvent( void *arg, const T_scheduler_event *event ) + static void SchedulerEvent( + void *arg, + const T_scheduler_event *event, + T_scheduler_when when + ) { Context *ctx; ctx = arg; - if ( event->operation == T_SCHEDULER_BLOCK ) { + if ( + when == T_SCHEDULER_BEFORE && + event->operation == T_SCHEDULER_BLOCK + ) { ctx->request.handler = Flush; ctx->request.arg = ctx; CallWithinISRSubmit( &ctx->request ); diff --git a/spec/score/tq/req/timeout-priority-inherit.yml b/spec/score/tq/req/timeout-priority-inherit.yml new file mode 100644 index 00000000..9d963783 --- /dev/null +++ b/spec/score/tq/req/timeout-priority-inherit.yml @@ -0,0 +1,414 @@ +SPDX-License-Identifier: CC-BY-SA-4.0 OR BSD-2-Clause +copyrights: +- Copyright (C) 2021 embedded brains GmbH (http://www.embedded-brains.de) +enabled-by: true +functional-type: action +links: +- role: requirement-refinement + uid: ../if/group +post-conditions: +- name: Status + states: + - name: Ok + test-code: | + T_eq_int( + ctx->tq_ctx->status[ TQ_BLOCKER_A ], + TQConvertStatus( ctx->tq_ctx, STATUS_SUCCESSFUL ) + ); + text: | + The return status of the directive call shall be derived from + ${../../status/if/successful:/name}. + - name: Timeout + test-code: | + T_eq_int( + ctx->tq_ctx->status[ TQ_BLOCKER_A ], + TQConvertStatus( ctx->tq_ctx, STATUS_TIMEOUT ) + ); + text: | + The return status of the directive call shall be derived from + ${../../status/if/timeout:/name}. + test-epilogue: null + test-prologue: null +- name: Unblock + states: + - name: 'Yes' + test-code: | + T_eq_ptr( GetUnblock( ctx, &i ), GetTCB( ctx, TQ_BLOCKER_A ) ); + T_eq_ptr( GetUnblock( ctx, &i ), NULL ); + text: | + The thread of the timeout operation shall be unblocked by the timeout + operation. + - name: 'No' + test-code: | + T_eq_ptr( GetUnblock( ctx, &i ), NULL ); + text: | + The thread of the timeout operation shall not be unblocked by the timeout + operation. + test-epilogue: null + test-prologue: | + size_t i; + + i = 0; +pre-conditions: +- name: EnqueueVariant + states: + - name: Blocking + test-code: | + if ( ctx->tq_ctx->enqueue_variant != TQ_ENQUEUE_BLOCKS ) { + ${.:skip} + } + text: | + Where the thread queue enqueue operation is blocking. + - name: Sticky + test-code: | + if ( ctx->tq_ctx->enqueue_variant != TQ_ENQUEUE_STICKY ) { + ${.:skip} + } + text: | + Where the thread queue enqueue operation is sticky. + test-epilogue: null + test-prologue: null +- name: Scheduler + states: + - name: Same + test-code: | + ctx->other_scheduler = false; + + if ( ctx->tq_ctx->enqueue_variant == TQ_ENQUEUE_STICKY ) { + TQSetScheduler( + ctx->tq_ctx, + TQ_BLOCKER_A, + SCHEDULER_A_ID, + PRIO_LOW + ); + RemoveProcessor( SCHEDULER_B_ID, 1 ); + AddProcessor( SCHEDULER_A_ID, 1 ); + ctx->restore_scheduler = true; + } else { + TQSetScheduler( + ctx->tq_ctx, + TQ_BLOCKER_A, + SCHEDULER_A_ID, + PRIO_ULTRA_HIGH + ); + } + text: | + While the ${/glossary/scheduler-home:/term} of the thread is equal to the + ${/glossary/scheduler-home:/term} of the thread queue owner. + - name: Other + test-code: | + ctx->other_scheduler = true; + + TQSetScheduler( + ctx->tq_ctx, + TQ_BLOCKER_A, + SCHEDULER_B_ID, + PRIO_NORMAL + ); + text: | + While the ${/glossary/scheduler-home:/term} of the thread is not equal to + the ${/glossary/scheduler-home:/term} of the thread queue owner. + test-epilogue: null + test-prologue: null +- name: WaitState + states: + - name: Blocked + test-code: | + if ( ctx->tq_ctx->enqueue_variant == TQ_ENQUEUE_STICKY ) { + T_unreachable(); + } else { + TQEnqueuePrepare( ctx->tq_ctx ); + TQClearDone( ctx->tq_ctx, TQ_BLOCKER_A ); + TQSendAndWaitForExecutionStop( + ctx->tq_ctx, + TQ_BLOCKER_A, + TQ_EVENT_ENQUEUE + ); + Tick( ctx ); + TQWaitForDone( ctx->tq_ctx, TQ_BLOCKER_A ); + TQEnqueueDone( ctx->tq_ctx ); + } + text: | + While the thread of the timeout operation is in the blocked wait state. + - name: IntendToBlock + test-code: | + TQEnqueuePrepare( ctx->tq_ctx ); + + if ( ctx->tq_ctx->enqueue_variant == TQ_ENQUEUE_STICKY ) { + Per_CPU_Control *cpu; + + TQSendAndWaitForIntendToBlock( + ctx->tq_ctx, + TQ_BLOCKER_A, + TQ_EVENT_ENQUEUE + ); + cpu = _Thread_Get_CPU( ctx->tq_ctx->worker_tcb[ TQ_BLOCKER_A ] ); + + /* + * We have to make sure that the worker thread inserted its thread + * timer. Checking the intend to block wait state is not enough to + * ensure this. + */ + while ( cpu->thread_dispatch_disable_level != 0 ) { + /* Wait */ + } + + Tick( ctx ); + WaitForExecutionStop( ctx->tq_ctx->worker_id[ TQ_BLOCKER_A ] ); + } else { + T_scheduler_set_event_handler( SchedulerBlock, ctx ); + TQSendAndWaitForExecutionStop( + ctx->tq_ctx, + TQ_BLOCKER_A, + TQ_EVENT_ENQUEUE + ); + } + + TQEnqueueDone( ctx->tq_ctx ); + text: | + While the thread of the timeout operation is in the intend to block wait + state. + - name: ReadyAgain + test-code: | + TQEnqueuePrepare( ctx->tq_ctx ); + + if ( ctx->tq_ctx->enqueue_variant == TQ_ENQUEUE_STICKY ) { + TQSendAndWaitForIntendToBlock( + ctx->tq_ctx, + TQ_BLOCKER_A, + TQ_EVENT_ENQUEUE | TQ_EVENT_TIMEOUT | TQ_EVENT_SURRENDER | + TQ_EVENT_SCHEDULER_RECORD_STOP + ); + TQSchedulerRecordStart( ctx->tq_ctx ); + TQEnqueueDone( ctx->tq_ctx ); + WaitForExecutionStop( ctx->tq_ctx->worker_id[ TQ_BLOCKER_A ] ); + } else { + TQSendAndWaitForExecutionStop( + ctx->tq_ctx, + TQ_BLOCKER_A, + TQ_EVENT_ENQUEUE + ); + T_scheduler_set_event_handler( SchedulerUnblock, ctx ); + TQEnqueueDone( ctx->tq_ctx ); + TQSendAndWaitForExecutionStop( + ctx->tq_ctx, + TQ_BLOCKER_A, + TQ_EVENT_SURRENDER + ); + } + text: | + While the thread of the timeout operation is in the ready again wait + state. + test-epilogue: null + test-prologue: null +rationale: null +references: [] +requirement-type: functional +skip-reasons: + StickyHasNoBlocking: | + When a sticky thread queue enqueue operation is performed, the blocked wait + state cannot occur. + OnlyOneCPU: | + Where the system was built with SMP support disabled, exactly one scheduler + is present in an application using exactly one processor. +test-action: | + /* + * The action is performed by the ``WaitState`` pre-condition preparation. + */ +test-brief: null +test-cleanup: | + if ( ctx->restore_scheduler ) { + RemoveProcessor( SCHEDULER_A_ID, 1 ); + AddProcessor( SCHEDULER_B_ID, 1 ); + } +test-context: +- brief: | + This member contains the call within ISR request. + description: null + member: | + CallWithinISRRequest request; +- brief: | + If this member is true, then the enqueued thread shall use a home scheduler + other than the home scheduler of the owner. + description: null + member: | + bool other_scheduler +- brief: | + If this member is true, then the processor set of the schedulers shall be + restored. + description: null + member: | + bool restore_scheduler +test-context-support: null +test-description: null +test-header: + code: null + freestanding: false + includes: [] + local-includes: + - tx-thread-queue.h + run-params: + - description: | + is the thread queue test context. + dir: inout + name: tq_ctx + specifier: TQContext *${.:name} + target: testsuites/validation/tr-tq-timeout-priority-inherit.h +test-includes: +- rtems/score/smpimpl.h +- rtems/score/threadimpl.h +test-local-includes: +- tx-support.h +- tr-tq-timeout-priority-inherit.h +test-prepare: | + ctx->restore_scheduler = false; +test-setup: + brief: null + code: | + ctx->request.arg = ctx; + TQReset( ctx->tq_ctx ); + TQSetScheduler( + ctx->tq_ctx, + TQ_HELPER_OTHER, + SCHEDULER_A_ID, + PRIO_NORMAL + ); + description: null +test-stop: null +test-support: | + typedef ${.:/test-context-type} Context; + + static const rtems_tcb *GetUnblock( Context *ctx, size_t *index ) + { + return TQGetNextUnblock( ctx->tq_ctx, index )->thread; + } + + static const rtems_tcb *GetTCB( Context *ctx, TQWorkerKind worker ) + { + return ctx->tq_ctx->worker_tcb[ worker ]; + } + + static void Tick( void *arg ) + { + Context *ctx; + + ctx = arg; + TQSchedulerRecordStart( ctx->tq_ctx ); + FinalClockTick(); + TQSchedulerRecordStop( ctx->tq_ctx ); + } + + static void SchedulerBlock( + void *arg, + const T_scheduler_event *event, + T_scheduler_when when + ) + { + Context *ctx; + + ctx = arg; + + if ( + when == T_SCHEDULER_BEFORE && + event->operation == T_SCHEDULER_BLOCK + ) { + T_scheduler_set_event_handler( NULL, NULL ); + ctx->request.handler = Tick; + CallWithinISRSubmit( &ctx->request ); + } + } + + static void ThreadTimeout( void *arg ) + { + Context *ctx; + + ctx = arg; + TQSchedulerRecordStart( ctx->tq_ctx ); + _Thread_Timeout( + &ctx->tq_ctx->worker_tcb[ TQ_BLOCKER_A ]->Timer.Watchdog + ); + TQSchedulerRecordStop( ctx->tq_ctx ); + } + + static void SchedulerUnblock( + void *arg, + const T_scheduler_event *event, + T_scheduler_when when + ) + { + Context *ctx; + + ctx = arg; + + if ( + when == T_SCHEDULER_BEFORE && + event->operation == T_SCHEDULER_UNBLOCK + ) { + T_scheduler_set_event_handler( NULL, NULL ); + + if ( ctx->other_scheduler ) { + #if defined(RTEMS_SMP) + _SMP_Unicast_action( 1, ThreadTimeout, ctx ); + #else + T_unreachable(); + #endif + } else { + ctx->request.handler = ThreadTimeout; + CallWithinISRSubmit( &ctx->request ); + } + } + } +test-target: testsuites/validation/tr-tq-timeout-priority-inherit.c +test-teardown: + brief: null + code: | + TQReset( ctx->tq_ctx ); + description: null +text: | + When the thread queue enqueue operation timed out. +transition-map: +- enabled-by: true + post-conditions: + Status: Timeout + Unblock: 'Yes' + pre-conditions: + EnqueueVariant: + - Blocking + Scheduler: all + WaitState: + - Blocked +- enabled-by: true + post-conditions: + Status: Timeout + Unblock: 'No' + pre-conditions: + EnqueueVariant: all + Scheduler: all + WaitState: + - IntendToBlock +- enabled-by: true + post-conditions: + Status: Ok + Unblock: 'No' + pre-conditions: + EnqueueVariant: all + Scheduler: all + WaitState: + - ReadyAgain +- enabled-by: true + post-conditions: StickyHasNoBlocking + pre-conditions: + EnqueueVariant: + - Sticky + Scheduler: all + WaitState: + - Blocked +- enabled-by: + not: RTEMS_SMP + post-conditions: OnlyOneCPU + pre-conditions: + EnqueueVariant: all + Scheduler: + - Other + WaitState: all +type: requirement diff --git a/spec/score/tq/req/timeout.yml b/spec/score/tq/req/timeout.yml index 7e26d5fc..a0aa387f 100644 --- a/spec/score/tq/req/timeout.yml +++ b/spec/score/tq/req/timeout.yml @@ -36,13 +36,13 @@ post-conditions: T_eq_ptr( GetUnblock( ctx, &i ), GetTCB( ctx, TQ_BLOCKER_A ) ); T_eq_ptr( GetUnblock( ctx, &i ), NULL ); text: | - The thread of the timeout operation shall not be unblocked by the timeout + The thread of the timeout operation shall be unblocked by the timeout operation. - name: 'No' test-code: | T_eq_ptr( GetUnblock( ctx, &i ), NULL ); text: | - The thread of the timeout operation shall be unblocked by the timeout + The thread of the timeout operation shall not be unblocked by the timeout operation. test-epilogue: null test-prologue: | @@ -50,70 +50,23 @@ post-conditions: i = 0; pre-conditions: -- name: EnqueueVariant - states: - - name: Blocking - test-code: | - if ( ctx->tq_ctx->enqueue_variant != TQ_ENQUEUE_BLOCKS ) { - ${.:skip} - } - text: | - Where the thread queue enqueue operation is blocking. - - name: Sticky - test-code: | - if ( ctx->tq_ctx->enqueue_variant != TQ_ENQUEUE_STICKY ) { - ${.:skip} - } - text: | - Where the thread queue enqueue operation is sticky. - test-epilogue: null - test-prologue: null - name: WaitState states: - name: Blocked test-code: | - if ( ctx->tq_ctx->enqueue_variant == TQ_ENQUEUE_STICKY ) { - T_unreachable(); - } else { - TQEnqueuePrepare( ctx->tq_ctx ); - TQSend( ctx->tq_ctx, TQ_BLOCKER_A, TQ_EVENT_ENQUEUE ); - Yield(); - Tick( ctx ); - TQEnqueueDone( ctx->tq_ctx ); - } + TQEnqueuePrepare( ctx->tq_ctx ); + TQSend( ctx->tq_ctx, TQ_BLOCKER_A, TQ_EVENT_ENQUEUE ); + Yield(); + Tick( ctx ); + TQEnqueueDone( ctx->tq_ctx ); text: | While the thread of the timeout operation is in the blocked wait state. - name: IntendToBlock test-code: | TQEnqueuePrepare( ctx->tq_ctx ); - - if ( ctx->tq_ctx->enqueue_variant == TQ_ENQUEUE_STICKY ) { - Per_CPU_Control *cpu; - - TQSendAndWaitForIntendToBlock( - ctx->tq_ctx, - TQ_BLOCKER_A, - TQ_EVENT_ENQUEUE - ); - cpu = _Thread_Get_CPU( ctx->tq_ctx->worker_tcb[ TQ_BLOCKER_A ] ); - - /* - * We have to make sure that the worker thread inserted its thread - * timer. Checking the intend to block wait state is not enough to - * ensure this. - */ - while ( cpu->thread_dispatch_disable_level != 0 ) { - /* Wait */ - } - - Tick( ctx ); - WaitForExecutionStop( ctx->tq_ctx->worker_id[ TQ_BLOCKER_A ] ); - } else { - T_scheduler_set_event_handler( SchedulerBlock, ctx ); - TQSend( ctx->tq_ctx, TQ_BLOCKER_A, TQ_EVENT_ENQUEUE ); - Yield(); - } - + T_scheduler_set_event_handler( SchedulerBlock, ctx ); + TQSend( ctx->tq_ctx, TQ_BLOCKER_A, TQ_EVENT_ENQUEUE ); + Yield(); TQEnqueueDone( ctx->tq_ctx ); text: | While the thread of the timeout operation is in the intend to block wait @@ -121,24 +74,11 @@ pre-conditions: - name: ReadyAgain test-code: | TQEnqueuePrepare( ctx->tq_ctx ); - - if ( ctx->tq_ctx->enqueue_variant == TQ_ENQUEUE_STICKY ) { - TQSendAndWaitForIntendToBlock( - ctx->tq_ctx, - TQ_BLOCKER_A, - TQ_EVENT_ENQUEUE | TQ_EVENT_TIMEOUT | TQ_EVENT_SURRENDER | - TQ_EVENT_SCHEDULER_RECORD_STOP - ); - TQSchedulerRecordStart( ctx->tq_ctx ); - TQEnqueueDone( ctx->tq_ctx ); - WaitForExecutionStop( ctx->tq_ctx->worker_id[ TQ_BLOCKER_A ] ); - } else { - TQSend( ctx->tq_ctx, TQ_BLOCKER_A, TQ_EVENT_ENQUEUE ); - Yield(); - T_scheduler_set_event_handler( SchedulerUnblock, ctx ); - TQEnqueueDone( ctx->tq_ctx ); - TQSend( ctx->tq_ctx, TQ_BLOCKER_A, TQ_EVENT_SURRENDER ); - } + TQSend( ctx->tq_ctx, TQ_BLOCKER_A, TQ_EVENT_ENQUEUE ); + Yield(); + T_scheduler_set_event_handler( SchedulerUnblock, ctx ); + TQEnqueueDone( ctx->tq_ctx ); + TQSend( ctx->tq_ctx, TQ_BLOCKER_A, TQ_EVENT_SURRENDER ); text: | While the thread of the timeout operation is in the ready again wait state. @@ -147,10 +87,7 @@ pre-conditions: rationale: null references: [] requirement-type: functional -skip-reasons: - StickyHasNoBlocking: | - When a sticky thread queue enqueue operation is performed, the blocked wait - state cannot occur. +skip-reasons: {} test-action: | /* * The action is performed by the ``WaitState`` pre-condition preparation. @@ -225,13 +162,20 @@ test-support: | TQSchedulerRecordStop( ctx->tq_ctx ); } - static void SchedulerBlock( void *arg, const T_scheduler_event *event ) + static void SchedulerBlock( + void *arg, + const T_scheduler_event *event, + T_scheduler_when when + ) { Context *ctx; ctx = arg; - if ( event->operation == T_SCHEDULER_BLOCK ) { + if ( + when == T_SCHEDULER_BEFORE && + event->operation == T_SCHEDULER_BLOCK + ) { T_scheduler_set_event_handler( NULL, NULL ); ctx->request.handler = Tick; CallWithinISRSubmit( &ctx->request ); @@ -250,13 +194,20 @@ test-support: | TQSchedulerRecordStop( ctx->tq_ctx ); } - static void SchedulerUnblock( void *arg, const T_scheduler_event *event ) + static void SchedulerUnblock( + void *arg, + const T_scheduler_event *event, + T_scheduler_when when + ) { Context *ctx; ctx = arg; - if ( event->operation == T_SCHEDULER_UNBLOCK ) { + if ( + when == T_SCHEDULER_BEFORE && + event->operation == T_SCHEDULER_UNBLOCK + ) { T_scheduler_set_event_handler( NULL, NULL ); ctx->request.handler = ThreadTimeout; CallWithinISRSubmit( &ctx->request ); @@ -276,8 +227,6 @@ transition-map: Status: Timeout Unblock: 'Yes' pre-conditions: - EnqueueVariant: - - Blocking WaitState: - Blocked - enabled-by: true @@ -285,7 +234,6 @@ transition-map: Status: Timeout Unblock: 'No' pre-conditions: - EnqueueVariant: all WaitState: - IntendToBlock - enabled-by: true @@ -293,14 +241,6 @@ transition-map: Status: Ok Unblock: 'No' pre-conditions: - EnqueueVariant: all WaitState: - ReadyAgain -- enabled-by: true - post-conditions: StickyHasNoBlocking - pre-conditions: - EnqueueVariant: - - Sticky - WaitState: - - Blocked type: requirement -- cgit v1.2.3