mcs: add periodic scheduling

This commit adds periodic scheduling with sporadic servers.
This commit is contained in:
Anna Lyons 2016-11-04 15:03:12 +11:00 committed by Kent Mcleod
parent debdaa7d9a
commit 34c1f920b1
27 changed files with 878 additions and 119 deletions

View file

@ -119,13 +119,18 @@ static inline void debug_printTCB(tcb_t *tcb)
}
word_t core = SMP_TERNARY(tcb->tcbAffinity, 0);
printf("%15s\t%p\t%20lu\t%lu\n", state, (void *) getRestartPC(tcb), tcb->tcbPriority, core);
printf("%15s\t%p\t%20lu\t%lu", state, (void *) getRestartPC(tcb), tcb->tcbPriority, core);
#ifdef CONFIG_KERNEL_MCS
printf("\t%lu", (word_t) thread_state_get_tcbInReleaseQueue(tcb->tcbState));
#endif
printf("\n");
}
static inline void debug_dumpScheduler(void)
{
printf("Dumping all tcbs!\n");
printf("Name \tState \tIP \t Prio \t Core\n");
printf("Name \tState \tIP \t Prio \t Core%s\n",
config_set(CONFIG_KERNEL_MCS) ? "\t InReleaseQueue" : "");
printf("--------------------------------------------------------------------------------------\n");
for (tcb_t *curr = NODE_STATE(ksDebugTCBs); curr != NULL; curr = curr->tcbDebugNext) {
debug_printTCB(curr);

122
include/kernel/sporadic.h Normal file
View file

@ -0,0 +1,122 @@
/*
* Copyright 2019, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
* See "LICENSE_GPLv2.txt" for details.
*
* @TAG(DATA61_GPL)
*/
#ifndef __KERNEL_SPORADIC_H
#define __KERNEL_SPORADIC_H
/* This header presents the interface for sporadic servers,
* implemented according to Stankcovich et. al in
* "Defects of the POSIX Spoardic Server and How to correct them",
* although without the priority management.
*
* Briefly, a sporadic server is a period and a queue of refills. Each
* refill consists of an amount, and a period. No thread is allowed to consume
* more than amount ticks per period.
*
* The sum of all refill amounts in the refill queue is always the budget of the scheduling context -
* that is it should never change, unless it is being updated / configured.
*
* Every time budget is consumed, that amount of budget is scheduled
* for reuse in period time. If the refill queue is full (the queue's
* minimum size is 2, and can be configured by the user per scheduling context
* above this) the next refill is merged.
*/
#include <types.h>
#include <util.h>
#include <object/structures.h>
#include <machine/timer.h>
#include <model/statedata.h>
/* To do an operation in the kernel, the thread must have
* at least this much budget - see comment on refill_sufficient */
#define MIN_BUDGET_US (2u * getKernelWcetUs())
#define MIN_BUDGET (2u * getKernelWcetTicks())
/* Short hand for accessing refill queue items */
#define REFILL_INDEX(sc, index) ((sc)->scRefills[(index)])
#define REFILL_HEAD(sc) REFILL_INDEX((sc), (sc)->scRefillHead)
#define REFILL_TAIL(sc) REFILL_INDEX((sc), (sc)->scRefillTail)
/* Return the amount of items currently in the refill queue */
static inline word_t refill_size(sched_context_t *sc)
{
if (sc->scRefillHead <= sc->scRefillTail) {
return (sc->scRefillTail - sc->scRefillHead + 1u);
}
return sc->scRefillTail + 1u + (sc->scRefillMax - sc->scRefillHead);
}
static inline bool_t refill_single(sched_context_t *sc)
{
return sc->scRefillHead == sc->scRefillTail;
}
/* Return the amount of budget this scheduling context
* has available if usage is charged to it. */
static inline ticks_t refill_capacity(sched_context_t *sc, ticks_t usage)
{
if (unlikely(usage > REFILL_HEAD(sc).rAmount)) {
return 0;
}
return REFILL_HEAD(sc).rAmount - usage;
}
/*
* Return true if the head refill has sufficient capacity
* to enter and exit the kernel after usage is charged to it.
*/
static inline bool_t refill_sufficient(sched_context_t *sc, ticks_t usage)
{
return refill_capacity(sc, usage) >= MIN_BUDGET;
}
/*
* Return true if the refill is eligible to be used.
* This indicates if the thread bound to the sc can be placed
* into the scheduler, otherwise it needs to go into the release queue
* to wait.
*/
static inline bool_t refill_ready(sched_context_t *sc)
{
return REFILL_HEAD(sc).rTime <= (NODE_STATE(ksCurTime) + getKernelWcetTicks());
}
/* Create a new refill in a non-active sc */
void refill_new(sched_context_t *sc, word_t max_refills, ticks_t budget, ticks_t period);
/* Update refills in an active sc without violating bandwidth constraints */
void refill_update(sched_context_t *sc, ticks_t new_period, ticks_t new_budget, word_t new_max_refills);
/* Charge the head refill its entire amount.
*
* `used` amount from its current replenishment without
* depleting the budget, i.e refill_expired returns false.
*
* return any uncharged usage.
*/
ticks_t refill_budget_check(sched_context_t *sc, ticks_t used);
/*
* Charge a scheduling context `used` amount from its
* current refill. This will split the refill, leaving whatever is
* left over at the head of the refill.
*/
void refill_split_check(sched_context_t *sc, ticks_t used);
/*
* This is called when a thread is eligible to start running: it
* iterates through the refills queue and merges any
* refills that overlap.
*/
void refill_unblock_check(sched_context_t *sc);
#endif

View file

@ -16,6 +16,7 @@
#include <object/structures.h>
#include <arch/machine.h>
#ifdef CONFIG_KERNEL_MCS
#include <kernel/sporadic.h>
#include <machine/timer.h>
#include <mode/machine.h>
#endif
@ -85,28 +86,22 @@ static inline bool_t isHighestPrio(word_t dom, prio_t prio)
}
#ifdef CONFIG_KERNEL_MCS
static inline bool_t isCurThreadExpired(void)
{
return NODE_STATE(ksCurThread)->tcbSchedContext->scRemaining <
(NODE_STATE(ksConsumed) + getKernelWcetTicks());
}
static inline bool_t isCurDomainExpired(void)
{
return CONFIG_NUM_DOMAINS > 1 &&
NODE_STATE(ksDomainTime) < (NODE_STATE(ksConsumed) + getKernelWcetTicks());
ksDomainTime < (NODE_STATE(ksConsumed) + getKernelWcetTicks());
}
static inline void commitTime(sched_context_t *sc)
static inline void commitTime(void)
{
assert(sc->scCore == SMP_TERNARY(getCurrentCPUIndex(), 0));
if (unlikely(sc->scRemaining < NODE_STATE(ksConsumed))) {
/* avoid underflow */
sc->scRemaining = 0;
} else {
sc->scRemaining -= NODE_STATE(ksConsumed);
}
if (likely(NODE_STATE(ksConsumed) > 0 && (NODE_STATE(ksCurThread) != NODE_STATE(ksIdleThread)))) {
assert(refill_sufficient(NODE_STATE(ksCurSC), NODE_STATE(ksConsumed)));
assert(refill_ready(NODE_STATE(ksCurSC)));
refill_split_check(NODE_STATE(ksCurSC), NODE_STATE(ksConsumed));
assert(refill_sufficient(NODE_STATE(ksCurSC), 0));
assert(refill_ready(NODE_STATE(ksCurSC)));
}
if (CONFIG_NUM_DOMAINS > 1) {
if (unlikely(ksDomainTime < NODE_STATE(ksConsumed))) {
ksDomainTime = 0;
@ -168,6 +163,12 @@ static inline void updateRestartPC(tcb_t *tcb)
}
#ifdef CONFIG_KERNEL_MCS
/* End the timeslice for the current thread.
* This will recharge the threads timeslice and place it at the
* end of the scheduling queue for its priority.
*/
void endTimeslice(void);
/* Update the kernels timestamp and stores in ksCurTime.
* The difference between the previous kernel timestamp and the one just read
* is stored in ksConsumed.
@ -191,34 +192,65 @@ static inline void updateTimestamp(void)
* @return true if the thread/domain has enough budget to
* get through the current kernel operation.
*/
bool_t checkBudget(void);
static inline bool_t checkBudget(void)
{
/* currently running thread must have available capacity */
assert(refill_ready(NODE_STATE(ksCurSC)));
if (unlikely(NODE_STATE(ksCurThread) == NODE_STATE(ksIdleThread))) {
return true;
}
ticks_t capacity = refill_capacity(NODE_STATE(ksCurSC), NODE_STATE(ksConsumed));
if (unlikely(capacity < MIN_BUDGET)) {
if (capacity == 0) {
NODE_STATE(ksConsumed) = refill_budget_check(NODE_STATE(ksCurSC), NODE_STATE(ksConsumed));
}
if (NODE_STATE(ksConsumed) > 0) {
refill_split_check(NODE_STATE(ksCurSC), NODE_STATE(ksConsumed));
}
NODE_STATE(ksConsumed) = 0;
NODE_STATE(ksCurTime) += 1llu;
if (likely(isRunnable(NODE_STATE(ksCurThread)))) {
endTimeslice();
rescheduleRequired();
}
return false;
} else if (unlikely(isCurDomainExpired())) {
commitTime();
rescheduleRequired();
return false;
}
return true;
}
/* Everything checkBudget does, but also set the thread
* state to ThreadState_Restart. To be called from kernel entries
* where the operation should be restarted once the current thread
* has budget again.
*/
bool_t checkBudgetRestart(void);
static inline bool_t checkBudgetRestart(void)
{
assert(isRunnable(NODE_STATE(ksCurThread)));
bool_t result = checkBudget();
if (!result) {
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
}
return result;
}
/* Set the next kernel tick, which is either the end of the current
* domains timeslice OR the end of the current threads timeslice.
*/
void setNextInterrupt(void);
/* End the timeslice for the current thread.
* This will recharge the threads timeslice and place it at the
* end of the scheduling queue for its priority.
*/
void endTimeslice(void);
static inline void checkReschedule(void)
{
if (isCurThreadExpired()) {
endTimeslice();
} else if (isCurDomainExpired()) {
rescheduleRequired();
}
}
/* Wake any periodic threads that are ready for budget recharge */
void awaken(void);
/* Place the thread bound to this scheduling context in the release queue
* of periodic threads waiting for budget recharge */
void postpone(sched_context_t *sc);
#endif
#endif

View file

@ -71,6 +71,7 @@ NODE_STATE_DECLARE(tcb_t, *ksIdleThread);
NODE_STATE_DECLARE(tcb_t, *ksSchedulerAction);
#ifdef CONFIG_KERNEL_MCS
NODE_STATE_DECLARE(tcb_t, *ksReleaseHead);
NODE_STATE_DECLARE(time_t, ksConsumed);
NODE_STATE_DECLARE(time_t, ksCurTime);
NODE_STATE_DECLARE(bool_t, ksReprogram);

View file

@ -291,18 +291,35 @@ struct tcb {
typedef struct tcb tcb_t;
#ifdef CONFIG_KERNEL_MCS
typedef struct refill {
/* Absolute timestamp from when this refill can be used */
ticks_t rTime;
/* Amount of ticks that can be used from this refill */
ticks_t rAmount;
} refill_t;
#define MIN_REFILLS 2u
#define MAX_REFILLS (MIN_REFILLS + seL4_MaxRefills)
struct sched_context {
/* budget for this sc -- remaining is refilled from this value */
ticks_t scBudget;
/* period for this sc -- controls rate at which budget is replenished */
ticks_t scPeriod;
/* core this scheduling context provides time for - 0 if uniprocessor */
word_t scCore;
/* current budget for this tcb (timeslice) -- refilled from budget */
ticks_t scRemaining;
/* thread that this scheduling context is bound to */
tcb_t *scTcb;
/* Amount of refills this sc tracks */
word_t scRefillMax;
/* Index of the head of the refill circular buffer */
word_t scRefillHead;
/* Index of the tail of the refill circular buffer */
word_t scRefillTail;
/* circular buffer of budget refills, ordered by rAmount */
refill_t scRefills[MAX_REFILLS];
};
#endif

View file

@ -296,6 +296,9 @@ block DebugException {
block thread_state(blockingIPCBadge, blockingIPCCanGrant,
blockingIPCCanGrantReply, blockingIPCIsCall,
tcbQueued, blockingObject,
#ifdef CONFIG_KERNEL_MCS
tcbInReleaseQueue,
#endif
tsType) {
field blockingIPCBadge 28
field blockingIPCCanGrant 1
@ -304,9 +307,16 @@ block thread_state(blockingIPCBadge, blockingIPCCanGrant,
padding 1
-- this is fastpath-specific. it is useful to be able to write
-- tsType and without changing tcbQueued
-- tsType and without changing tcbQueued or tcbInReleaseQueue
#ifdef CONFIG_KERNEL_MCS
padding 30
#else
padding 31
#endif
field tcbQueued 1
#ifdef CONFIG_KERNEL_MCS
field tcbInReleaseQueue 1
#endif
field_high blockingObject 28
field tsType 4

View file

@ -382,15 +382,25 @@ block DebugException {
-- Thread state: size = 24 bytes
block thread_state(blockingIPCBadge, blockingIPCCanGrant,
blockingIPCCanGrantReply, blockingIPCIsCall,
tcbQueued, blockingObject,
tsType) {
tcbQueued,
#ifdef CONFIG_KERNEL_MCS
tcbInReleaseQueue,
#endif
blockingObject, tsType) {
field blockingIPCBadge 64
#ifdef CONFIG_KERNEL_MCS
padding 59
#else
padding 60
#endif
field blockingIPCCanGrant 1
field blockingIPCCanGrantReply 1
field blockingIPCIsCall 1
field tcbQueued 1
#ifdef CONFIG_KERNEL_MCS
field tcbInReleaseQueue 1
#endif
#if BF_CANONICAL_RANGE == 48
padding 16

View file

@ -54,6 +54,11 @@ void tcbSchedDequeue(tcb_t *tcb);
void tcbDebugAppend(tcb_t *tcb);
void tcbDebugRemove(tcb_t *tcb);
#endif
#ifdef CONFIG_KERNEL_MCS
void tcbReleaseRemove(tcb_t *tcb);
void tcbReleaseEnqueue(tcb_t *tcb);
tcb_t *tcbReleaseDequeue(void);
#endif
#ifdef ENABLE_SMP_SUPPORT
void remoteQueueUpdate(tcb_t *tcb);

View file

@ -611,6 +611,8 @@
description="Timeslice in microseconds, when the budget expires the thread will be pre-empted."/>
<param dir="in" name="period" type="seL4_Time"
description="Period in microseconds, the budget is replenished every time the period expires."/>
<param dir="in" name="max_refills" type="seL4_Word"
description="extra max refills for sporadic real time tasks"/>
</method>
</interface>

View file

@ -79,4 +79,9 @@ typedef enum {
SEL4_FORCE_LONG_ENUM(seL4_LookupFailureType),
} seL4_LookupFailureType;
#ifdef CONFIG_KERNEL_MCS
#define seL4_MinRefills 0
#define seL4_MaxRefills 10
#endif
#endif /* __API_CONSTANTS_H */

View file

@ -152,7 +152,7 @@ enum {
#define seL4_EndpointBits 4
#define seL4_NotificationBits 4
#define seL4_SchedContextBits 5
#define seL4_SchedContextBits 8
#ifdef CONFIG_ARM_HYPERVISOR_SUPPORT
#define seL4_PageTableBits 12

View file

@ -125,7 +125,7 @@ enum {
#define seL4_TCBBits 11
#define seL4_EndpointBits 4
#define seL4_NotificationBits 5
#define seL4_SchedContextBits 5
#define seL4_SchedContextBits 8
#define seL4_PageTableBits 12
#define seL4_PageTableEntryBits 3

View file

@ -46,7 +46,7 @@
#define seL4_ASIDPoolBits 12
#define seL4_ASIDPoolIndexBits 10
#define seL4_WordSizeBits 2
#define seL4_SchedContextBits 5
#define seL4_SchedContextBits 8
#define seL4_HugePageBits 30 /* 1GB */
#define seL4_PDPTBits 0

View file

@ -60,7 +60,7 @@
#define seL4_NumASIDPoolsBits 3
#define seL4_ASIDPoolBits 12
#define seL4_ASIDPoolIndexBits 9
#define seL4_SchedContextBits 5
#define seL4_SchedContextBits 8
/* Untyped size limits */
#define seL4_MinUntypedBits 4

View file

@ -454,8 +454,9 @@ static void handleRecv(bool_t isBlocking)
#ifdef CONFIG_KERNEL_MCS
static inline void mcsIRQ(irq_t irq)
{
commitTime(ksCurSC);
checkReschedule();
if (checkBudget()) {
commitTime();
}
}
#else
#define mcsIRQ(irq)
@ -464,9 +465,19 @@ static inline void mcsIRQ(irq_t irq)
static void handleYield(void)
{
#ifdef CONFIG_KERNEL_MCS
/* checkBudgetRestart should have failed if we got here */
assert(refill_sufficient(NODE_STATE(ksCurSC), NODE_STATE(ksConsumed)));
/* Yield the current remaining budget */
refill_budget_check(NODE_STATE(ksCurSC), REFILL_HEAD(NODE_STATE(ksCurSC)).rAmount);
/* we just charged all of the time to the yielding thread */
NODE_STATE(ksConsumed) = 0;
endTimeslice();
#else
tcbSchedDequeue(NODE_STATE(ksCurThread));
SCHED_APPEND_CURRENT_TCB;
rescheduleRequired();
#endif
}
exception_t handleSyscall(syscall_t syscall)

View file

@ -600,6 +600,10 @@ BOOT_CODE VISIBLE void init_kernel(
fail("Kernel init failed for some reason :(");
}
#ifdef CONFIG_KERNEL_MCS
NODE_STATE(ksCurTime) = getCurrentTime();
NODE_STATE(ksConsumed) = 0;
#endif
schedule();
activateThread();
}

View file

@ -728,6 +728,11 @@ BOOT_CODE VISIBLE void boot_sys(
ARCH_NODE_STATE(x86KScurInterrupt) = int_invalid;
ARCH_NODE_STATE(x86KSPendingInterrupt) = int_invalid;
#ifdef CONFIG_KERNEL_MCS
NODE_STATE(ksCurTime) = getCurrentTime();
NODE_STATE(ksConsumed) = 0;
#endif
schedule();
activateThread();
}

View file

@ -44,4 +44,7 @@ add_sources(
src/smp/lock.c
src/smp/ipi.c
)
add_sources(DEP KernelIsMCS CFILES src/object/schedcontext.c src/object/schedcontrol.c)
add_sources(
DEP KernelIsMCS
CFILES src/object/schedcontext.c src/object/schedcontrol.c src/kernel/sporadic.c
)

View file

@ -324,10 +324,9 @@ BOOT_CODE cap_t create_it_asid_pool(cap_t root_cnode_cap)
BOOT_CODE static bool_t configure_sched_context(tcb_t *tcb, sched_context_t *sc_pptr, ticks_t timeslice)
{
tcb->tcbSchedContext = sc_pptr;
tcb->tcbSchedContext->scBudget = timeslice;
tcb->tcbSchedContext->scRemaining = timeslice;
tcb->tcbSchedContext->scTcb = tcb;
refill_new(tcb->tcbSchedContext, MIN_REFILLS, timeslice, 0);
tcb->tcbSchedContext->scTcb = tcb;
return true;
}
@ -430,6 +429,7 @@ BOOT_CODE tcb_t *create_initial_thread(cap_t root_cnode_cap, cap_t it_pd_cap, vp
NODE_STATE(ksConsumed) = 0;
NODE_STATE(ksReprogram) = true;
NODE_STATE(ksReleaseHead) = NULL;
#endif
tcb->tcbPriority = seL4_MaxPrio;

336
src/kernel/sporadic.c Normal file
View file

@ -0,0 +1,336 @@
/*
* Copyright 2019, Data61
* Commonwealth Scientific and Industrial Research Organisation (CSIRO)
* ABN 41 687 119 230.
*
* This software may be distributed and modified according to the terms of
* the GNU General Public License version 2. Note that NO WARRANTY is provided.
* See "LICENSE_GPLv2.txt" for details.
*
* @TAG(DATA61_GPL)
*/
#include <types.h>
#include <api/failures.h>
#include <object/structures.h>
/* functions to manage the circular buffer of
* sporadic budget replenishments (refills for short).
*
* The circular buffer always has at least one item in it.
*
* Items are appended at the tail (the back) and
* removed from the head (the front). Below is
* an example of a queue with 4 items (h = head, t = tail, x = item, [] = slot)
* and max size 8.
*
* [][h][x][x][t][][][]
*
* and another example of a queue with 5 items
*
* [x][t][][][][h][x][x]
*
* The queue has a minimum size of 1, so it is possible that h == t.
*
* The queue is implemented as head + tail rather than head + size as
* we cannot use the mod operator on all architectures without accessing
* the fpu or implementing divide.
*/
/* return the index of the next item in the refill queue */
static inline word_t refill_next(sched_context_t *sc, word_t index)
{
return (index == sc->scRefillMax - 1u) ? (0) : index + 1u;
}
#ifdef CONFIG_PRINTING
/* for debugging */
UNUSED static inline void print_index(sched_context_t *sc, word_t index)
{
printf("index %lu, Amount: %llx, time %llx\n", index, REFILL_INDEX(sc, index).rAmount,
REFILL_INDEX(sc, index).rTime);
}
UNUSED static inline void refill_print(sched_context_t *sc)
{
printf("Head %lu tail %lu\n", sc->scRefillHead, sc->scRefillTail);
word_t current = sc->scRefillHead;
/* always print the head */
print_index(sc, current);
while (current != sc->scRefillTail) {
current = refill_next(sc, current);
print_index(sc, current);
}
}
#endif /* CONFIG_PRINTING */
#ifdef CONFIG_DEBUG_BUILD
/* check a refill queue is ordered correctly */
static UNUSED bool_t refill_ordered(sched_context_t *sc)
{
word_t current = sc->scRefillHead;
word_t next = refill_next(sc, sc->scRefillHead);
while (current != sc->scRefillTail) {
assert(REFILL_INDEX(sc, current).rTime <= REFILL_INDEX(sc, next).rTime);
current = next;
next = refill_next(sc, current);
}
}
#define REFILL_SANITY_START(sc) ticks_t _sum = refill_sum(sc); refill_ordered(sc);
#define REFILL_SANITY_CHECK(sc, budget) \
do { \
assert(refill_sum(sc) == budget); refill_ordered(sc); \
} while (0)
#define REFILL_SANITY_END(sc) \
do {\
REFILL_SANITY_CHECK(sc, _sum);\
} while (0)
#else
#define REFILL_SANITY_START(sc)
#define REFILL_SANITY_CHECK(sc, budget)
#define REFILL_SANITY_END(sc)
#endif /* CONFIG_DEBUG_BUILD */
/* compute the sum of a refill queue */
static ticks_t refill_sum(sched_context_t *sc)
{
ticks_t sum = REFILL_HEAD(sc).rAmount;
word_t current = sc->scRefillHead;
while (current != sc->scRefillTail) {
current = refill_next(sc, current);
sum += REFILL_INDEX(sc, current).rAmount;
}
return sum;
}
/* pop head of refill queue */
static inline refill_t refill_pop_head(sched_context_t *sc)
{
/* queues cannot be smaller than 1 */
assert(!refill_single(sc));
UNUSED word_t prev_size = refill_size(sc);
refill_t refill = REFILL_HEAD(sc);
sc->scRefillHead = refill_next(sc, sc->scRefillHead);
/* sanity */
assert(prev_size == (refill_size(sc) + 1));
assert(sc->scRefillHead < sc->scRefillMax);
return refill;
}
/* add item to tail of refill queue */
static inline void refill_add_tail(sched_context_t *sc, refill_t refill)
{
/* cannot add an empty refill */
assert(refill.rAmount != 0);
/* cannot add beyond queue size */
assert(refill_size(sc) < sc->scRefillMax);
word_t new_tail = refill_next(sc, sc->scRefillTail);
sc->scRefillTail = new_tail;
REFILL_TAIL(sc) = refill;
/* sanity */
assert(new_tail < sc->scRefillMax);
}
void refill_new(sched_context_t *sc, word_t max_refills, ticks_t budget, ticks_t period)
{
sc->scPeriod = period;
sc->scRefillHead = 0;
sc->scRefillTail = 0;
sc->scRefillMax = max_refills;
assert(budget > MIN_BUDGET);
/* full budget available */
REFILL_HEAD(sc).rAmount = budget;
/* budget can be used from now */
REFILL_HEAD(sc).rTime = NODE_STATE(ksCurTime);
REFILL_SANITY_CHECK(sc, budget);
}
void refill_update(sched_context_t *sc, ticks_t new_period, ticks_t new_budget, word_t new_max_refills)
{
/* refill must be initialised in order to be updated - otherwise refill_new should be used */
assert(sc->scRefillMax > 0);
/* figure out how much budget is available */
ticks_t total_budget = refill_sum(sc);
REFILL_SANITY_CHECK(sc, total_budget);
/* first deal with a difference in max refills - merge
* any refills that exceed the new max */
while (new_max_refills < refill_size(sc)) {
/* merge refills */
assert(!refill_single(sc));
refill_t refill = refill_pop_head(sc);
REFILL_HEAD(sc).rAmount += refill.rAmount;
}
REFILL_SANITY_CHECK(sc, total_budget);
/* move anything in the list that is beyond the old max */
if (sc->scRefillMax > new_max_refills) {
word_t curr = sc->scRefillHead;
for (curr = sc->scRefillHead; curr < sc->scRefillMax; curr++) {
word_t diff = sc->scRefillMax - new_max_refills;
REFILL_INDEX(sc, curr - diff) = REFILL_INDEX(sc, curr);
}
}
sc->scRefillMax = new_max_refills;
/* now deal with the period change - update each refill by the difference in period */
word_t current = refill_next(sc, sc->scRefillHead);
while (current != sc->scRefillTail) {
/* adjust the period of each refill by new one (except the head) */
REFILL_INDEX(sc, current).rTime += (new_period - sc->scPeriod);
current = refill_next(sc, current);
}
sc->scPeriod = new_period;
REFILL_SANITY_CHECK(sc, total_budget);
/* now deal with the new budget */
if (new_budget > total_budget) {
/* if the budget has increased, just add it to the last refill */
REFILL_TAIL(sc).rAmount += (new_budget - total_budget);
} else {
/* if the budget has decreased, iterate through from head to
* tail until the amount decreased has been removed from the refill
* buffer */
ticks_t remove = total_budget - new_budget;
while (remove >= REFILL_HEAD(sc).rAmount) {
assert(!refill_single(sc));
refill_t old_head = refill_pop_head(sc);
remove -= old_head.rAmount;
}
REFILL_HEAD(sc).rAmount -= remove;
if (REFILL_HEAD(sc).rAmount < MIN_BUDGET) {
assert(!refill_single(sc));
refill_t old_head = refill_pop_head(sc);
REFILL_HEAD(sc).rAmount += old_head.rAmount;
}
}
/* merge any overlapping refills */
refill_unblock_check(sc);
REFILL_SANITY_CHECK(sc, new_budget);
}
ticks_t refill_budget_check(sched_context_t *sc, ticks_t usage)
{
/* this function should only be called when the sc is out of budget */
assert(refill_capacity(sc, usage) == 0);
REFILL_SANITY_START(sc);
while (REFILL_HEAD(sc).rAmount <= usage) {
/* exhaust and schedule replenishment */
usage -= REFILL_HEAD(sc).rAmount;
if (refill_single(sc)) {
/* update in place */
REFILL_HEAD(sc).rTime += sc->scPeriod;
} else {
refill_t old_head = refill_pop_head(sc);
old_head.rTime = old_head.rTime + sc->scPeriod;
refill_add_tail(sc, old_head);
}
}
/* budget overrun */
if (usage > 0 && sc->scPeriod > 0) {
/* budget reduced when calculating capacity */
/* due to overrun delay next replenishment */
REFILL_HEAD(sc).rTime += usage;
/* merge front two replenishments if times overlap */
if (!refill_single(sc) &&
REFILL_HEAD(sc).rTime + REFILL_HEAD(sc).rAmount >=
REFILL_INDEX(sc, refill_next(sc, sc->scRefillHead)).rTime) {
refill_t refill = refill_pop_head(sc);
REFILL_HEAD(sc).rAmount += refill.rAmount;
}
}
REFILL_SANITY_END(sc);
/* return any usage we haven't dealt with */
return usage;
}
void refill_split_check(sched_context_t *sc, ticks_t usage)
{
/* invalid to call this on a NULL sc */
assert(sc != NULL);
/* something is seriously wrong if this is called and no
* time has been used */
assert(usage > 0);
assert(usage <= REFILL_HEAD(sc).rAmount);
REFILL_SANITY_START(sc);
/* first deal with the remaining budget of the current replenishment */
ticks_t remnant = REFILL_HEAD(sc).rAmount - usage;
if (remnant < MIN_BUDGET && refill_single(sc)) {
/* delay entire replenishment - can't merge, nothing to merge with */
REFILL_HEAD(sc).rTime += sc->scPeriod;
REFILL_SANITY_END(sc);
return;
}
if (refill_size(sc) == sc->scRefillMax || remnant < MIN_BUDGET) {
assert(!refill_single(sc));
/* merge remnant with next replenishment - either it's too small
* or we're out of space */
refill_pop_head(sc);
REFILL_HEAD(sc).rAmount += remnant;
} else {
assert(remnant >= MIN_BUDGET);
/* split the head refill */
REFILL_HEAD(sc).rAmount = remnant;
}
/* schedule the used amount */
refill_t new = (refill_t) {
.rAmount = usage, .rTime = REFILL_HEAD(sc).rTime + sc->scPeriod
};
refill_add_tail(sc, new);
REFILL_SANITY_END(sc);
}
void refill_unblock_check(sched_context_t *sc)
{
/* advance earliest activation time to now */
REFILL_SANITY_START(sc);
if (refill_ready(sc)) {
REFILL_HEAD(sc).rTime = NODE_STATE(ksCurTime);
/* merge available replenishments */
while (!refill_single(sc)) {
ticks_t amount = REFILL_HEAD(sc).rAmount;
if (REFILL_INDEX(sc, refill_next(sc, sc->scRefillHead)).rTime <= NODE_STATE(ksCurTime) + amount) {
refill_pop_head(sc);
REFILL_HEAD(sc).rAmount += amount;
REFILL_HEAD(sc).rTime = NODE_STATE(ksCurTime);
} else {
break;
}
}
/* it's possible that a refill is not bigger than min budget (if a task
* uses less than min budget, it will still be scheduled for refill), if
* so merge with the next refill, as it's not enough to schedule the task. */
if (!refill_sufficient(sc, 0)) {
assert(!refill_single(sc));
refill_t insufficient = refill_pop_head(sc);
REFILL_HEAD(sc).rAmount += insufficient.rAmount;
}
}
REFILL_SANITY_END(sc);
}

View file

@ -49,7 +49,8 @@ static inline bool_t PURE isBlocked(const tcb_t *thread)
static inline bool_t PURE isSchedulable(const tcb_t *thread)
{
return isRunnable(thread) &&
thread->tcbSchedContext != NULL;
thread->tcbSchedContext != NULL &&
!thread_state_get_tcbInReleaseQueue(thread->tcbState);
}
#else
#define isSchedulable isRunnable
@ -102,6 +103,9 @@ void suspend(tcb_t *target)
}
setThreadState(target, ThreadState_Inactive);
tcbSchedDequeue(target);
#ifdef CONFIG_KERNEL_MCS
tcbReleaseRemove(target);
#endif
}
void restart(tcb_t *target)
@ -294,10 +298,20 @@ static void switchSchedContext(void)
{
if (unlikely(NODE_STATE(ksCurSC) != NODE_STATE(ksCurThread)->tcbSchedContext)) {
NODE_STATE(ksReprogram) = true;
commitTime(ksCurSC);
commitTime();
refill_unblock_check(NODE_STATE(ksCurThread->tcbSchedContext));
assert(refill_ready(NODE_STATE(ksCurThread->tcbSchedContext)));
assert(refill_sufficient(NODE_STATE(ksCurThread->tcbSchedContext), 0));
} else {
rollbackTime();
}
/* if a thread doesn't have enough budget, it should not be in the scheduler */
if (!refill_ready(NODE_STATE(ksCurSC)) || !refill_sufficient(NODE_STATE(ksCurSC), 0)) {
assert(!thread_state_get_tcbQueued(NODE_STATE(ksCurSC)->scTcb->tcbState));
}
NODE_STATE(ksCurSC) = NODE_STATE(ksCurThread)->tcbSchedContext;
}
#endif
@ -312,6 +326,10 @@ static void scheduleChooseNewThread(void)
void schedule(void)
{
#ifdef CONFIG_KERNEL_MCS
awaken();
#endif
if (NODE_STATE(ksSchedulerAction) != SchedulerAction_ResumeCurrentThread) {
bool_t was_runnable;
if (isSchedulable(NODE_STATE(ksCurThread))) {
@ -386,7 +404,8 @@ void chooseThread(void)
assert(thread);
assert(isSchedulable(thread));
#ifdef CONFIG_KERNEL_MCS
assert(thread->tcbSchedContext->scRemaining > getKernelWcetTicks());
assert(refill_sufficient(thread->tcbSchedContext, 0));
assert(refill_ready(thread->tcbSchedContext));
#endif
switchToThread(thread);
} else {
@ -398,7 +417,9 @@ void switchToThread(tcb_t *thread)
{
#ifdef CONFIG_KERNEL_MCS
assert(thread->tcbSchedContext != NULL);
assert(thread->tcbSchedContext->scRemaining >= getKernelWcetTicks());
assert(!thread_state_get_tcbInReleaseQueue(thread->tcbState));
assert(refill_sufficient(thread->tcbSchedContext, 0));
assert(refill_ready(thread->tcbSchedContext));
#endif
#ifdef CONFIG_BENCHMARK_TRACK_UTILISATION
@ -463,7 +484,7 @@ void setPriority(tcb_t *tptr, prio_t prio)
void possibleSwitchTo(tcb_t *target)
{
#ifdef CONFIG_KERNEL_MCS
if (target->tcbSchedContext != NULL) {
if (target->tcbSchedContext != NULL && !thread_state_get_tcbInReleaseQueue(target->tcbState)) {
#endif
if (ksCurDomain != target->tcbDomain
SMP_COND_STATEMENT( || target->tcbAffinity != getCurrentCPUIndex())) {
@ -497,55 +518,40 @@ void scheduleTCB(tcb_t *tptr)
}
#ifdef CONFIG_KERNEL_MCS
static void recharge(sched_context_t *sc)
void postpone(sched_context_t *sc)
{
sc->scRemaining = sc->scBudget;
assert(sc->scBudget > 0);
tcbSchedDequeue(sc->scTcb);
tcbReleaseEnqueue(sc->scTcb);
NODE_STATE(ksReprogram) = true;
}
void setNextInterrupt(void)
{
time_t next_thread = NODE_STATE(ksCurTime) + NODE_STATE(ksCurThread)->tcbSchedContext->scRemaining;
time_t next_interrupt = NODE_STATE(ksCurTime) +
REFILL_HEAD(NODE_STATE(ksCurThread)->tcbSchedContext).rAmount;
if (CONFIG_NUM_DOMAINS > 1) {
time_t next_domain = ksCurTime + ksDomainTime;
setDeadline(MIN(next_thread, next_domain) - getTimerPrecision());
} else {
setDeadline(next_thread - getTimerPrecision());
next_interrupt = MIN(next_interrupt, NODE_STATE(ksCurTime) + ksDomainTime);
}
}
bool_t checkBudget(void)
{
if (unlikely(isCurThreadExpired())) {
commitTime(ksCurSC);
endTimeslice();
return false;
} else if (unlikely(isCurDomainExpired())) {
commitTime(ksCurSC);
rescheduleRequired();
return false;
} else {
return true;
if (NODE_STATE(ksReleaseHead) != NULL) {
next_interrupt = MIN(REFILL_HEAD(NODE_STATE(ksReleaseHead)->tcbSchedContext).rTime, next_interrupt);
}
}
bool_t checkBudgetRestart(void)
{
assert(isRunnable(NODE_STATE(ksCurThread)));
bool_t result = checkBudget();
if (!result) {
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
}
return result;
setDeadline(next_interrupt - getTimerPrecision());
}
void endTimeslice(void)
{
recharge(NODE_STATE(ksCurThread)->tcbSchedContext);
if (likely(thread_state_get_tsType(NODE_STATE(ksCurThread)->tcbState) ==
ThreadState_Running)) {
assert(isRunnable(NODE_STATE(ksCurSC->scTcb)));
if (refill_ready(NODE_STATE(ksCurSC)) && refill_sufficient(NODE_STATE(ksCurSC), 0)) {
/* apply round robin */
assert(refill_sufficient(NODE_STATE(ksCurSC), 0));
assert(!thread_state_get_tcbQueued(NODE_STATE(ksCurThread)->tcbState));
SCHED_APPEND_CURRENT_TCB;
} else {
/* postpone until ready */
postpone(NODE_STATE(ksCurSC));
}
rescheduleRequired();
}
@ -586,6 +592,10 @@ void rescheduleRequired(void)
&& isSchedulable(NODE_STATE(ksSchedulerAction))
#endif
) {
#ifdef CONFIG_KERNEL_MCS
assert(refill_sufficient(NODE_STATE(ksSchedulerAction)->tcbSchedContext, 0));
assert(refill_ready(NODE_STATE(ksSchedulerAction)->tcbSchedContext));
#endif
SCHED_ENQUEUE(NODE_STATE(ksSchedulerAction));
}
NODE_STATE(ksSchedulerAction) = SchedulerAction_ChooseNewThread;
@ -594,3 +604,20 @@ void rescheduleRequired(void)
#endif
}
#ifdef CONFIG_KERNEL_MCS
void awaken(void)
{
while (unlikely(NODE_STATE(ksReleaseHead) != NULL && refill_ready(NODE_STATE(ksReleaseHead)->tcbSchedContext))) {
tcb_t *awakened = tcbReleaseDequeue();
SMP_COND_STATEMENT(assert(awakened->tcbAffinity == getCurrentCPUIndex()));
refill_unblock_check(awakened->tcbSchedContext);
if (unlikely(!refill_ready(awakened->tcbSchedContext))) {
tcbReleaseEnqueue(awakened);
} else {
assert(refill_sufficient(awakened->tcbSchedContext, 0));
tcbSchedAppend(awakened);
possibleSwitchTo(awakened);
}
}
}
#endif

View file

@ -29,6 +29,10 @@ UP_STATE_DEFINE(tcb_queue_t, ksReadyQueues[NUM_READY_QUEUES]);
UP_STATE_DEFINE(word_t, ksReadyQueuesL1Bitmap[CONFIG_NUM_DOMAINS]);
UP_STATE_DEFINE(word_t, ksReadyQueuesL2Bitmap[CONFIG_NUM_DOMAINS][L2_BITMAP_SIZE]);
compile_assert(ksReadyQueuesL1BitmapBigEnough, (L2_BITMAP_SIZE - 1) <= wordBits)
#ifdef CONFIG_KERNEL_MCS
/* Head of the queue of threads waiting for their budget to be replenished */
UP_STATE_DEFINE(tcb_t *, ksReleaseHead);
#endif
/* Current thread TCB pointer */
UP_STATE_DEFINE(tcb_t *, ksCurThread);

View file

@ -103,6 +103,11 @@ void sendIPC(bool_t blocking, bool_t do_call, word_t badge,
}
}
#ifdef CONFIG_KERNEL_MCS
/* blocked threads should have enough budget to get out of the kernel */
assert(dest->tcbSchedContext == NULL || refill_sufficient(dest->tcbSchedContext, 0));
assert(dest->tcbSchedContext == NULL || refill_ready(dest->tcbSchedContext));
#endif
break;
}
}
@ -195,6 +200,9 @@ void receiveIPC(tcb_t *thread, cap_t cap, bool_t isBlocking)
} else {
setThreadState(sender, ThreadState_Running);
possibleSwitchTo(sender);
#ifdef CONFIG_KERNEL_MCS
assert(sender->tcbSchedContext == NULL || refill_sufficient(sender->tcbSchedContext, 0));
#endif
}
break;

View file

@ -219,10 +219,10 @@ void handleInterrupt(irq_t irq)
maskInterrupt(true, irq);
#endif
#ifdef CONFIG_KERNEL_MCS
/* Bill the current thread. We know it has enough budget, as otherwise we would be
* dealing with a timer interrupt not a signal interrupt */
commitTime(ksCurSC);
checkReschedule();
/* Bill the current thread. */
if (unlikely(checkBudget())) {
commitTime();
}
#endif
break;
}
@ -231,8 +231,10 @@ void handleInterrupt(irq_t irq)
#ifdef CONFIG_KERNEL_MCS
updateTimestamp();
ackDeadlineIRQ();
commitTime(ksCurSC);
checkBudget();
if (likely(checkBudget())) {
commitTime();
}
NODE_STATE(ksReprogram) = true;
#else
timerTick();
resetTimer();
@ -245,6 +247,11 @@ void handleInterrupt(irq_t irq)
updateTimestamp();
#endif
handleIPI(irq, true);
#ifdef CONFIG_KERNEL_MCS
if (unlikely(checkBudget())) {
commitTime();
}
#endif
break;
#endif /* ENABLE_SMP_SUPPORT */

View file

@ -112,14 +112,22 @@ exception_t decodeSchedContextInvocation(word_t label, cap_t cap, extra_caps_t e
void schedContext_resume(sched_context_t *sc)
{
assert(sc->scTcb != NULL);
assert(!sc || sc->scTcb != NULL);
if (likely(sc) && isSchedulable(sc->scTcb)) {
assert(sc->scTcb != NULL);
/* this should NOT be called when migration is possible */
#if CONFIG_MAX_NUM_NODES > 1
/* this should NOT be called when migration is possible */
assert(sc->scCore == getCurrentCPUIndex());
SMP_COND_STATEMENT(assert(sc->scCore == sc->scTcb->tcbAffinity));
SMP_COND_STATEMENT(assert(sc->scCore == getCurrentCPUIndex()));
#endif
if (isRunnable(sc->scTcb) && sc->scBudget > 0) {
recharge(sc);
possibleSwitchTo(sc->scTcb);
refill_unblock_check(sc);
if (isRunnable(sc->scTcb) && sc->scRefillMax > 0) {
if (!(refill_ready(sc) && refill_sufficient(sc, 0))) {
assert(!thread_state_get_tcbQueued(sc->scTcb->tcbState));
postpone(sc);
}
}
}
}
@ -147,15 +155,19 @@ void schedContext_unbindTCB(sched_context_t *sc, tcb_t *tcb)
assert(sc->scTcb == tcb);
tcbSchedDequeue(sc->scTcb);
tcbReleaseRemove(sc->scTcb);
sc->scTcb->tcbSchedContext = NULL;
if (sc->scTcb == NODE_STATE(ksCurThread)) {
sc->scTcb = NULL;
SMP_COND_STATEMENT(remoteTCBStall(tcb);)
if (tcb == NODE_STATE(ksCurThread)) {
rescheduleRequired();
} else {
SMP_COND_STATEMENT(remoteTCBStall(tcb));
}
sc->scTcb = NULL;
}
void schedContext_unbindAllTCBs(sched_context_t *sc)

View file

@ -14,22 +14,45 @@
#include <mode/api/ipc_buffer.h>
#include <object/schedcontext.h>
#include <object/schedcontrol.h>
#include <kernel/sporadic.h>
static exception_t invokeSchedControl_Configure(sched_context_t *target, ticks_t budget, word_t core)
static exception_t invokeSchedControl_Configure(sched_context_t *target, word_t core, ticks_t budget, ticks_t period,
word_t max_refills)
{
target->scBudget = budget;
target->scCore = core;
recharge(target);
/* don't modify parameters of tcb while it is in a sorted queue */
if (target->scTcb) {
tcbReleaseRemove(target->scTcb);
}
if (target->scTcb != NULL) {
/* target may no longer have budget for this core */
if (!isSchedulable(target->scTcb)) {
tcbSchedDequeue(target->scTcb);
} else {
possibleSwitchTo(target->scTcb);
if (budget == period) {
/* this is a cool hack: for round robin, we set the
* period to 0, which means that the budget will always be ready to be refilled
* and the code doesn't need special casing
*/
period = 0;
}
if (core == target->scCore && target->scRefillMax > 0 && target->scTcb && isRunnable(target->scTcb)) {
/* the scheduling context is active - it can be used, so
* we need to preserve the bandwidth */
refill_update(target, period, budget, max_refills);
} else {
/* the scheduling context isn't active - it's budget is not being used, so
* we can just populate the parameters from now */
refill_new(target, max_refills, budget, period);
if (core != target->scCore && target->scTcb) {
/* if the core changed and the SC has a tcb, the SC is getting
* budget - so migrate it */
target->scCore = core;
SMP_COND_STATEMENT(migrateTCB(target->scTcb));
}
}
if (target->scTcb && isRunnable(target->scTcb) && target->scRefillMax > 0) {
schedContext_resume(target);
}
return EXCEPTION_NONE;
}
@ -41,13 +64,15 @@ static exception_t decodeSchedControl_Configure(word_t length, cap_t cap, extra_
return EXCEPTION_SYSCALL_ERROR;
}
if (length < TIME_ARG_SIZE) {
if (length < (TIME_ARG_SIZE * 2) + 1) {
userError("SchedControl_configure: truncated message.");
current_syscall_error.type = seL4_TruncatedMessage;
return EXCEPTION_SYSCALL_ERROR;
}
time_t budget_us = mode_parseTimeArg(0, buffer);
time_t period_us = mode_parseTimeArg(TIME_ARG_SIZE, buffer);
word_t max_refills = MIN_REFILLS + getSyscallArg(TIME_ARG_SIZE * 2, buffer);
cap_t targetCap = extraCaps.excaprefs[0]->cap;
if (unlikely(cap_get_capType(targetCap) != cap_sched_context_cap)) {
@ -57,17 +82,44 @@ static exception_t decodeSchedControl_Configure(word_t length, cap_t cap, extra_
return EXCEPTION_SYSCALL_ERROR;
}
if (budget_us > getMaxUsToTicks() || budget_us < getKernelWcetUs()) {
if (budget_us > getMaxUsToTicks() || budget_us < MIN_BUDGET_US) {
userError("SchedControl_Configure: budget out of range.");
current_syscall_error.type = seL4_RangeError;
current_syscall_error.rangeErrorMin = getKernelWcetUs();
current_syscall_error.rangeErrorMin = MIN_BUDGET_US;
current_syscall_error.rangeErrorMax = getMaxUsToTicks();
return EXCEPTION_SYSCALL_ERROR;
}
if (period_us > getMaxUsToTicks() || period_us < MIN_BUDGET_US) {
userError("SchedControl_Configure: period out of range.");
current_syscall_error.type = seL4_RangeError;
current_syscall_error.rangeErrorMin = MIN_BUDGET_US;
current_syscall_error.rangeErrorMax = getMaxUsToTicks();
return EXCEPTION_SYSCALL_ERROR;
}
if (budget_us > period_us) {
userError("SchedControl_Configure: budget must be <= period");
current_syscall_error.type = seL4_RangeError;
current_syscall_error.rangeErrorMin = MIN_BUDGET_US;
current_syscall_error.rangeErrorMax = period_us;
return EXCEPTION_SYSCALL_ERROR;
}
if (max_refills > MAX_REFILLS) {
userError("Max refills invalid");
current_syscall_error.type = seL4_RangeError;
current_syscall_error.rangeErrorMin = 0;
current_syscall_error.rangeErrorMax = MAX_REFILLS - MIN_REFILLS - 1;
return EXCEPTION_SYSCALL_ERROR;
}
setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
return invokeSchedControl_Configure(SC_PTR(cap_sched_context_cap_get_capSCPtr(targetCap)),
usToTicks(budget_us), cap_sched_control_cap_get_core(cap));
cap_sched_control_cap_get_core(cap),
usToTicks(budget_us),
usToTicks(period_us),
max_refills + MIN_REFILLS);
}
exception_t decodeSchedControlInvocation(word_t label, cap_t cap, word_t length, extra_caps_t extraCaps,

View file

@ -87,7 +87,8 @@ void tcbSchedEnqueue(tcb_t *tcb)
{
#ifdef CONFIG_KERNEL_MCS
assert(isSchedulable(tcb));
assert(tcb->tcbSchedContext->scRemaining > getKernelWcetTicks());
assert(refill_sufficient(tcb->tcbSchedContext, 0));
assert(refill_ready(tcb->tcbSchedContext));
#endif
if (!thread_state_get_tcbQueued(tcb->tcbState)) {
@ -122,7 +123,8 @@ void tcbSchedAppend(tcb_t *tcb)
{
#ifdef CONFIG_KERNEL_MCS
assert(isSchedulable(tcb));
assert(tcb->tcbSchedContext->scRemaining > getKernelWcetTicks());
assert(refill_sufficient(tcb->tcbSchedContext, 0));
assert(refill_ready(tcb->tcbSchedContext));
#endif
if (!thread_state_get_tcbQueued(tcb->tcbState)) {
tcb_queue_t queue;
@ -253,6 +255,85 @@ tcb_queue_t tcbEPDequeue(tcb_t *tcb, tcb_queue_t queue)
return queue;
}
#ifdef CONFIG_KERNEL_MCS
void tcbReleaseRemove(tcb_t *tcb)
{
if (likely(thread_state_get_tcbInReleaseQueue(tcb->tcbState))) {
if (tcb->tcbSchedPrev) {
tcb->tcbSchedPrev->tcbSchedNext = tcb->tcbSchedNext;
} else {
NODE_STATE(ksReleaseHead) = tcb->tcbSchedNext;
/* the head has changed, we might need to set a new timeout */
NODE_STATE(ksReprogram) = true;
}
if (tcb->tcbSchedNext) {
tcb->tcbSchedNext->tcbSchedPrev = tcb->tcbSchedPrev;
}
tcb->tcbSchedNext = NULL;
tcb->tcbSchedPrev = NULL;
thread_state_ptr_set_tcbInReleaseQueue(&tcb->tcbState, false);
}
}
void tcbReleaseEnqueue(tcb_t *tcb)
{
assert(thread_state_get_tcbInReleaseQueue(tcb->tcbState) == false);
assert(thread_state_get_tcbQueued(tcb->tcbState) == false);
tcb_t *before = NULL;
tcb_t *after = NODE_STATE(ksReleaseHead);
/* find our place in the ordered queue */
while (after != NULL &&
REFILL_HEAD(tcb->tcbSchedContext).rTime >= REFILL_HEAD(after->tcbSchedContext).rTime) {
before = after;
after = after->tcbSchedNext;
}
if (before == NULL) {
/* insert at head */
NODE_STATE(ksReleaseHead) = tcb;
NODE_STATE(ksReprogram) = true;
} else {
before->tcbSchedNext = tcb;
}
if (after != NULL) {
after->tcbSchedPrev = tcb;
}
tcb->tcbSchedNext = after;
tcb->tcbSchedPrev = before;
thread_state_ptr_set_tcbInReleaseQueue(&tcb->tcbState, true);
}
tcb_t *tcbReleaseDequeue(void)
{
assert(NODE_STATE(ksReleaseHead) != NULL);
assert(NODE_STATE(ksReleaseHead)->tcbSchedPrev == NULL);
tcb_t *detached_head = NODE_STATE(ksReleaseHead);
NODE_STATE(ksReleaseHead) = NODE_STATE(ksReleaseHead)->tcbSchedNext;
if (NODE_STATE(ksReleaseHead)) {
NODE_STATE(ksReleaseHead)->tcbSchedPrev = NULL;
}
if (detached_head->tcbSchedNext) {
detached_head->tcbSchedNext->tcbSchedPrev = NULL;
detached_head->tcbSchedNext = NULL;
}
thread_state_ptr_set_tcbInReleaseQueue(&detached_head->tcbState, false);
NODE_STATE(ksReprogram) = true;
return detached_head;
}
#endif
cptr_t PURE getExtraCPtr(word_t *bufferPtr, word_t i)
{
return (cptr_t)bufferPtr[seL4_MsgMaxLength + 2 + i];