1 /*
2  * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3  *
4  * SPDX-License-Identifier: GPL-2.0-only
5  */
6 
7 #pragma once
8 /* This header presents the interface for sporadic servers,
9  * implemented according to Stankcovich et. al in
10  * "Defects of the POSIX Spoardic Server and How to correct them",
11  * although without the priority management.
12  *
13  * Briefly, a sporadic server is a period and a queue of refills. Each
14  * refill consists of an amount, and a period. No thread is allowed to consume
15  * more than amount ticks per period.
16  *
17  * The sum of all refill amounts in the refill queue is always the budget of the scheduling context -
18  * that is it should never change, unless it is being updated / configured.
19  *
20  * Every time budget is consumed, that amount of budget is scheduled
21  * for reuse in period time. If the refill queue is full (the queue's
22  * minimum size is 2, and can be configured by the user per scheduling context
23  * above this) the next refill is merged.
24  */
25 #include <config.h>
26 #include <types.h>
27 #include <util.h>
28 #include <object/structures.h>
29 #include <machine/timer.h>
30 #include <model/statedata.h>
31 
32 /* To do an operation in the kernel, the thread must have
33  * at least this much budget - see comment on refill_sufficient */
34 #define MIN_BUDGET_US (2u * getKernelWcetUs() * CONFIG_KERNEL_WCET_SCALE)
35 #define MIN_BUDGET    (2u * getKernelWcetTicks() * CONFIG_KERNEL_WCET_SCALE)
36 #if (CONFIG_KERNEL_STATIC_MAX_PERIOD_US) != 0
37 #define MAX_PERIOD_US (CONFIG_KERNEL_STATIC_MAX_PERIOD_US)
38 #else
39 /* The maximum period determines the point at which the scheduling logic
40  * will no longer function correctly (UINT64_MAX - 5 * MAX_PERIOD), so
41  * we keep the maximum period relatively small to ensure that the system
42  * can function for a reasonably long time.
43  *
44  * Anything below getMaxUsToTicks() / 8 would ensure that time up to
45  * 2^63 would still be be valid as 5 * (getMaxUsToTicks()) must be less
46  * than 2^62. */
47 #define MAX_PERIOD_US (getMaxUsToTicks() / 8)
48 #endif /* CONFIG_KERNEL_STATIC_MAX_PERIOD_US != 0 */
49 #define MAX_RELEASE_TIME (UINT64_MAX - 5 * usToTicks(MAX_PERIOD_US))
50 
51 /* Short hand for accessing refill queue items */
refill_index(sched_context_t * sc,word_t index)52 static inline refill_t *refill_index(sched_context_t *sc, word_t index)
53 {
54     return ((refill_t *)(SC_REF(sc) + sizeof(sched_context_t))) + index;
55 }
refill_head(sched_context_t * sc)56 static inline refill_t *refill_head(sched_context_t *sc)
57 {
58     return refill_index(sc, sc->scRefillHead);
59 }
refill_tail(sched_context_t * sc)60 static inline refill_t *refill_tail(sched_context_t *sc)
61 {
62     return refill_index(sc, sc->scRefillTail);
63 }
64 
65 
66 /* Scheduling context objects consist of a sched_context_t at the start, followed by a
67  * circular buffer of refills. As scheduling context objects are of variable size, the
68  * amount of refill_ts that can fit into a scheduling context object is also variable.
69  *
70  * @return the maximum number of refill_t data structures that can fit into this
71  * specific scheduling context object.
72  */
refill_absolute_max(cap_t sc_cap)73 static inline word_t refill_absolute_max(cap_t sc_cap)
74 {
75     return (BIT(cap_sched_context_cap_get_capSCSizeBits(sc_cap)) - sizeof(sched_context_t)) / sizeof(refill_t);
76 }
77 
78 /* @return the current amount of empty slots in the refill buffer */
refill_size(sched_context_t * sc)79 static inline word_t refill_size(sched_context_t *sc)
80 {
81     if (sc->scRefillHead <= sc->scRefillTail) {
82         return (sc->scRefillTail - sc->scRefillHead + 1u);
83     }
84     return sc->scRefillTail + 1u + (sc->scRefillMax - sc->scRefillHead);
85 }
86 
87 /* @return true if the circular buffer of refills is current full (all slots in the
88  * buffer are currently being used */
refill_full(sched_context_t * sc)89 static inline bool_t refill_full(sched_context_t *sc)
90 {
91     return refill_size(sc) == sc->scRefillMax;
92 }
93 
94 /* @return true if the ciruclar buffer only contains 1 used slot */
refill_single(sched_context_t * sc)95 static inline bool_t refill_single(sched_context_t *sc)
96 {
97     return sc->scRefillHead == sc->scRefillTail;
98 }
99 
100 /* Return the amount of budget this scheduling context
101  * has available if usage is charged to it. */
refill_capacity(sched_context_t * sc,ticks_t usage)102 static inline ticks_t refill_capacity(sched_context_t *sc, ticks_t usage)
103 {
104     if (unlikely(usage > refill_head(sc)->rAmount)) {
105         return 0;
106     }
107 
108     return refill_head(sc)->rAmount - usage;
109 }
110 
111 /*
112  * Return true if the head refill has sufficient capacity
113  * to enter and exit the kernel after usage is charged to it.
114  */
refill_sufficient(sched_context_t * sc,ticks_t usage)115 static inline bool_t refill_sufficient(sched_context_t *sc, ticks_t usage)
116 {
117     return refill_capacity(sc, usage) >= MIN_BUDGET;
118 }
119 
120 /*
121  * Return true if the head refill is eligible to be used.
122  * This indicates if the thread bound to the sc can be placed
123  * into the scheduler, otherwise it needs to go into the release queue
124  * to wait.
125  */
refill_ready(sched_context_t * sc)126 static inline bool_t refill_ready(sched_context_t *sc)
127 {
128     return refill_head(sc)->rTime <= (NODE_STATE_ON_CORE(ksCurTime, sc->scCore) + getKernelWcetTicks());
129 }
130 
131 /*
132  * Return true if an SC has been successfully configured with parameters
133  * that allow for a thread to run.
134  */
sc_active(sched_context_t * sc)135 static inline bool_t sc_active(sched_context_t *sc)
136 {
137     return sc->scRefillMax > 0;
138 }
139 
140 /*
141  * Return true if a SC has been 'released', if its head refill is
142  * sufficient and is in the past.
143  */
sc_released(sched_context_t * sc)144 static inline bool_t sc_released(sched_context_t *sc)
145 {
146     if (sc_active(sc)) {
147         /* All refills must all be greater than MIN_BUDGET so this
148          * should be true for all active SCs */
149         assert(refill_sufficient(sc, 0));
150         return refill_ready(sc);
151     } else {
152         return false;
153     }
154 }
155 
156 /*
157  * Return true if a SC's available refills should be delayed at the
158  * point the associated thread becomes runnable (sporadic server).
159  */
sc_sporadic(sched_context_t * sc)160 static inline bool_t sc_sporadic(sched_context_t *sc)
161 {
162     return sc != NULL && sc_active(sc) && sc->scSporadic;
163 }
164 
165 /*
166  * Return true if a SC's available refills should be delayed at the
167  * point the associated thread becomes the current thread (constant
168  * bandwidth).
169  */
sc_constant_bandwidth(sched_context_t * sc)170 static inline bool_t sc_constant_bandwidth(sched_context_t *sc)
171 {
172     return !sc->scSporadic;
173 }
174 
175 /* Create a new refill in a non-active sc */
176 #ifdef ENABLE_SMP_SUPPORT
177 void refill_new(sched_context_t *sc, word_t max_refills, ticks_t budget, ticks_t period, word_t core);
178 #define REFILL_NEW(sc, max_refills, budget, period, core) refill_new(sc, max_refills, budget, period, core)
179 #else
180 void refill_new(sched_context_t *sc, word_t max_refills, ticks_t budget, ticks_t period);
181 #define REFILL_NEW(sc, max_refills, budget, period, core) refill_new(sc, max_refills, budget, period)
182 #endif
183 
184 /* Update refills in an active sc without violating bandwidth constraints */
185 void refill_update(sched_context_t *sc, ticks_t new_period, ticks_t new_budget, word_t new_max_refills);
186 
187 
188 /* Charge `usage` to the current scheduling context.
189  * This function should only be called only when charging `used` will deplete
190  * the head refill, resulting in refill_sufficient failing.
191  *
192  * @param usage the amount of time to charge.
193  */
194 void refill_budget_check(ticks_t used);
195 
196 /*
197  * This is called when a thread is eligible to start running: it
198  * iterates through the refills queue and merges any
199  * refills that overlap.
200  */
201 void refill_unblock_check(sched_context_t *sc);
202 
203