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