1 /*
2 * Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
3 *
4 * SPDX-License-Identifier: GPL-2.0-only
5 */
6
7 #include <machine/timer.h>
8 #include <mode/api/ipc_buffer.h>
9 #include <object/schedcontext.h>
10 #include <object/schedcontrol.h>
11 #include <kernel/sporadic.h>
12
invokeSchedControl_ConfigureFlags(sched_context_t * target,word_t core,ticks_t budget,ticks_t period,word_t max_refills,word_t badge,word_t flags)13 static exception_t invokeSchedControl_ConfigureFlags(sched_context_t *target, word_t core, ticks_t budget,
14 ticks_t period, word_t max_refills, word_t badge, word_t flags)
15 {
16
17 target->scBadge = badge;
18 target->scSporadic = (flags & seL4_SchedContext_Sporadic) != 0;
19
20 /* don't modify parameters of tcb while it is in a sorted queue */
21 if (target->scTcb) {
22 /* possibly stall a remote core */
23 SMP_COND_STATEMENT(remoteTCBStall(target->scTcb));
24 /* remove from scheduler */
25 tcbReleaseRemove(target->scTcb);
26 tcbSchedDequeue(target->scTcb);
27 /* bill the current consumed amount before adjusting the params */
28 if (NODE_STATE_ON_CORE(ksCurSC, target->scCore) == target) {
29 #ifdef ENABLE_SMP_SUPPORT
30 if (target->scCore == getCurrentCPUIndex()) {
31 #endif /* ENABLE_SMP_SUPPORT */
32 /* This could potentially mutate state but if it returns
33 * true no state was modified, thus removing it should
34 * be the same. */
35 assert(checkBudget());
36 commitTime();
37 #ifdef ENABLE_SMP_SUPPORT
38 } else {
39 chargeBudget(NODE_STATE_ON_CORE(ksConsumed, target->scCore), false, target->scCore, false);
40 doReschedule(target->scCore);
41 }
42 #endif /* ENABLE_SMP_SUPPORT */
43 }
44 }
45
46 if (budget == period) {
47 /* this is a cool hack: for round robin, we set the
48 * period to 0, which means that the budget will always be ready to be refilled
49 * and avoids some special casing.
50 */
51 REFILL_NEW(target, MIN_REFILLS, budget, 0, core);
52 } else if (SMP_COND_STATEMENT(core == target->scCore &&) target->scRefillMax > 0 && target->scTcb
53 && isRunnable(target->scTcb)) {
54 /* the scheduling context is active - it can be used, so
55 * we need to preserve the bandwidth */
56 refill_update(target, period, budget, max_refills);
57 } else {
58 /* the scheduling context isn't active - it's budget is not being used, so
59 * we can just populate the parameters from now */
60 REFILL_NEW(target, max_refills, budget, period, core);
61 }
62
63 #ifdef ENABLE_SMP_SUPPORT
64 target->scCore = core;
65 if (target->scTcb) {
66 migrateTCB(target->scTcb, target->scCore);
67 }
68 #endif /* ENABLE_SMP_SUPPORT */
69
70 assert(target->scRefillMax > 0);
71 if (target->scTcb) {
72 schedContext_resume(target);
73 if (SMP_TERNARY(core == CURRENT_CPU_INDEX(), true)) {
74 if (isRunnable(target->scTcb) && target->scTcb != NODE_STATE(ksCurThread)) {
75 possibleSwitchTo(target->scTcb);
76 }
77 } else if (isRunnable(target->scTcb)) {
78 SCHED_ENQUEUE(target->scTcb);
79 }
80 if (target->scTcb == NODE_STATE(ksCurThread)) {
81 rescheduleRequired();
82 }
83 }
84
85 return EXCEPTION_NONE;
86 }
87
decodeSchedControl_ConfigureFlags(word_t length,cap_t cap,word_t * buffer)88 static exception_t decodeSchedControl_ConfigureFlags(word_t length, cap_t cap, word_t *buffer)
89 {
90 if (current_extra_caps.excaprefs[0] == NULL) {
91 userError("SchedControl_ConfigureFlags: Truncated message.");
92 current_syscall_error.type = seL4_TruncatedMessage;
93 return EXCEPTION_SYSCALL_ERROR;
94 }
95
96 if (length < (TIME_ARG_SIZE * 2) + 3) {
97 userError("SchedControl_configureFlags: truncated message.");
98 current_syscall_error.type = seL4_TruncatedMessage;
99 return EXCEPTION_SYSCALL_ERROR;
100 }
101
102 time_t budget_us = mode_parseTimeArg(0, buffer);
103 ticks_t budget_ticks = usToTicks(budget_us);
104 time_t period_us = mode_parseTimeArg(TIME_ARG_SIZE, buffer);
105 ticks_t period_ticks = usToTicks(period_us);
106 word_t extra_refills = getSyscallArg(TIME_ARG_SIZE * 2, buffer);
107 word_t badge = getSyscallArg(TIME_ARG_SIZE * 2 + 1, buffer);
108 word_t flags = getSyscallArg(TIME_ARG_SIZE * 2 + 2, buffer);
109
110 cap_t targetCap = current_extra_caps.excaprefs[0]->cap;
111 if (unlikely(cap_get_capType(targetCap) != cap_sched_context_cap)) {
112 userError("SchedControl_ConfigureFlags: target cap not a scheduling context cap");
113 current_syscall_error.type = seL4_InvalidCapability;
114 current_syscall_error.invalidCapNumber = 1;
115 return EXCEPTION_SYSCALL_ERROR;
116 }
117
118 if (budget_us > MAX_PERIOD_US || budget_ticks < MIN_BUDGET) {
119 userError("SchedControl_ConfigureFlags: budget out of range.");
120 current_syscall_error.type = seL4_RangeError;
121 current_syscall_error.rangeErrorMin = MIN_BUDGET_US;
122 current_syscall_error.rangeErrorMax = MAX_PERIOD_US;
123 return EXCEPTION_SYSCALL_ERROR;
124 }
125
126 if (period_us > MAX_PERIOD_US || period_ticks < MIN_BUDGET) {
127 userError("SchedControl_ConfigureFlags: period out of range.");
128 current_syscall_error.type = seL4_RangeError;
129 current_syscall_error.rangeErrorMin = MIN_BUDGET_US;
130 current_syscall_error.rangeErrorMax = MAX_PERIOD_US;
131 return EXCEPTION_SYSCALL_ERROR;
132 }
133
134 if (budget_ticks > period_ticks) {
135 userError("SchedControl_ConfigureFlags: budget must be <= period");
136 current_syscall_error.type = seL4_RangeError;
137 current_syscall_error.rangeErrorMin = MIN_BUDGET_US;
138 current_syscall_error.rangeErrorMax = period_us;
139 return EXCEPTION_SYSCALL_ERROR;
140 }
141
142 if (extra_refills + MIN_REFILLS > refill_absolute_max(targetCap)) {
143 current_syscall_error.type = seL4_RangeError;
144 current_syscall_error.rangeErrorMin = 0;
145 current_syscall_error.rangeErrorMax = refill_absolute_max(targetCap) - MIN_REFILLS;
146 userError("Max refills invalid, got %lu, max %lu",
147 extra_refills,
148 current_syscall_error.rangeErrorMax);
149 return EXCEPTION_SYSCALL_ERROR;
150 }
151
152 setThreadState(NODE_STATE(ksCurThread), ThreadState_Restart);
153 return invokeSchedControl_ConfigureFlags(SC_PTR(cap_sched_context_cap_get_capSCPtr(targetCap)),
154 cap_sched_control_cap_get_core(cap),
155 budget_ticks,
156 period_ticks,
157 extra_refills + MIN_REFILLS,
158 badge,
159 flags);
160 }
161
decodeSchedControlInvocation(word_t label,cap_t cap,word_t length,word_t * buffer)162 exception_t decodeSchedControlInvocation(word_t label, cap_t cap, word_t length, word_t *buffer)
163 {
164 switch (label) {
165 case SchedControlConfigureFlags:
166 return decodeSchedControl_ConfigureFlags(length, cap, buffer);
167 default:
168 userError("SchedControl invocation: Illegal operation attempted.");
169 current_syscall_error.type = seL4_IllegalOperation;
170 return EXCEPTION_SYSCALL_ERROR;
171 }
172 }
173