Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2018 Intel Corporation
3 : : *
4 : : * SPDX-License-Identifier: Apache-2.0
5 : : */
6 : : #include <kernel.h>
7 : : #include <ksched.h>
8 : : #include <spinlock.h>
9 : : #include <kernel/sched_priq.h>
10 : : #include <wait_q.h>
11 : : #include <kswap.h>
12 : : #include <kernel_arch_func.h>
13 : : #include <syscall_handler.h>
14 : : #include <drivers/timer/system_timer.h>
15 : : #include <stdbool.h>
16 : : #include <kernel_internal.h>
17 : : #include <logging/log.h>
18 : : #include <sys/atomic.h>
19 : : #include <sys/math_extras.h>
20 : : #include <timing/timing.h>
21 : :
22 : : LOG_MODULE_DECLARE(os, CONFIG_KERNEL_LOG_LEVEL);
23 : :
24 : : #if defined(CONFIG_SCHED_DUMB)
25 : : #define _priq_run_add z_priq_dumb_add
26 : : #define _priq_run_remove z_priq_dumb_remove
27 : : # if defined(CONFIG_SCHED_CPU_MASK)
28 : : # define _priq_run_best _priq_dumb_mask_best
29 : : # else
30 : : # define _priq_run_best z_priq_dumb_best
31 : : # endif
32 : : #elif defined(CONFIG_SCHED_SCALABLE)
33 : : #define _priq_run_add z_priq_rb_add
34 : : #define _priq_run_remove z_priq_rb_remove
35 : : #define _priq_run_best z_priq_rb_best
36 : : #elif defined(CONFIG_SCHED_MULTIQ)
37 : : #define _priq_run_add z_priq_mq_add
38 : : #define _priq_run_remove z_priq_mq_remove
39 : : #define _priq_run_best z_priq_mq_best
40 : : static ALWAYS_INLINE void z_priq_mq_add(struct _priq_mq *pq,
41 : : struct k_thread *thread);
42 : : static ALWAYS_INLINE void z_priq_mq_remove(struct _priq_mq *pq,
43 : : struct k_thread *thread);
44 : : #endif
45 : :
46 : : #if defined(CONFIG_WAITQ_SCALABLE)
47 : : #define z_priq_wait_add z_priq_rb_add
48 : : #define _priq_wait_remove z_priq_rb_remove
49 : : #define _priq_wait_best z_priq_rb_best
50 : : #elif defined(CONFIG_WAITQ_DUMB)
51 : : #define z_priq_wait_add z_priq_dumb_add
52 : : #define _priq_wait_remove z_priq_dumb_remove
53 : : #define _priq_wait_best z_priq_dumb_best
54 : : #endif
55 : :
56 : : struct k_spinlock sched_spinlock;
57 : :
58 : : static void update_cache(int preempt_ok);
59 : : static void end_thread(struct k_thread *thread);
60 : :
61 : :
62 : 2 : static inline int is_preempt(struct k_thread *thread)
63 : : {
64 : : /* explanation in kernel_struct.h */
65 : 2 : return thread->base.preempt <= _PREEMPT_THRESHOLD;
66 : : }
67 : :
68 : 0 : static inline int is_metairq(struct k_thread *thread)
69 : : {
70 : : #if CONFIG_NUM_METAIRQ_PRIORITIES > 0
71 : : return (thread->base.prio - K_HIGHEST_THREAD_PRIO)
72 : : < CONFIG_NUM_METAIRQ_PRIORITIES;
73 : : #else
74 : 0 : return 0;
75 : : #endif
76 : : }
77 : :
78 : : #if CONFIG_ASSERT
79 : 0 : static inline bool is_thread_dummy(struct k_thread *thread)
80 : : {
81 : 0 : return (thread->base.thread_state & _THREAD_DUMMY) != 0U;
82 : : }
83 : : #endif
84 : :
85 : : /*
86 : : * Return value same as e.g. memcmp
87 : : * > 0 -> thread 1 priority > thread 2 priority
88 : : * = 0 -> thread 1 priority == thread 2 priority
89 : : * < 0 -> thread 1 priority < thread 2 priority
90 : : * Do not rely on the actual value returned aside from the above.
91 : : * (Again, like memcmp.)
92 : : */
93 : 1 : int32_t z_sched_prio_cmp(struct k_thread *thread_1,
94 : : struct k_thread *thread_2)
95 : : {
96 : : /* `prio` is <32b, so the below cannot overflow. */
97 : 1 : int32_t b1 = thread_1->base.prio;
98 : 1 : int32_t b2 = thread_2->base.prio;
99 : :
100 [ + - ]: 1 : if (b1 != b2) {
101 : 1 : return b2 - b1;
102 : : }
103 : :
104 : : #ifdef CONFIG_SCHED_DEADLINE
105 : : /* If we assume all deadlines live within the same "half" of
106 : : * the 32 bit modulus space (this is a documented API rule),
107 : : * then the latest deadline in the queue minus the earliest is
108 : : * guaranteed to be (2's complement) non-negative. We can
109 : : * leverage that to compare the values without having to check
110 : : * the current time.
111 : : */
112 : : uint32_t d1 = thread_1->base.prio_deadline;
113 : : uint32_t d2 = thread_2->base.prio_deadline;
114 : :
115 : : if (d1 != d2) {
116 : : /* Sooner deadline means higher effective priority.
117 : : * Doing the calculation with unsigned types and casting
118 : : * to signed isn't perfect, but at least reduces this
119 : : * from UB on overflow to impdef.
120 : : */
121 : : return (int32_t) (d2 - d1);
122 : : }
123 : : #endif
124 : 0 : return 0;
125 : : }
126 : :
127 : 4 : static ALWAYS_INLINE bool should_preempt(struct k_thread *thread,
128 : : int preempt_ok)
129 : : {
130 : : /* Preemption is OK if it's being explicitly allowed by
131 : : * software state (e.g. the thread called k_yield())
132 : : */
133 [ + + ]: 4 : if (preempt_ok != 0) {
134 : 1 : return true;
135 : : }
136 : :
137 [ - + ]: 3 : __ASSERT(_current != NULL, "");
138 : :
139 : : /* Or if we're pended/suspended/dummy (duh) */
140 [ + + ]: 3 : if (z_is_thread_prevented_from_running(_current)) {
141 : 1 : return true;
142 : : }
143 : :
144 : : /* Edge case on ARM where a thread can be pended out of an
145 : : * interrupt handler before the "synchronous" swap starts
146 : : * context switching. Platforms with atomic swap can never
147 : : * hit this.
148 : : */
149 [ - + ]: 2 : if (IS_ENABLED(CONFIG_SWAP_NONATOMIC)
150 : 2 : && z_is_thread_timeout_active(thread)) {
151 : 0 : return true;
152 : : }
153 : :
154 : : /* Otherwise we have to be running a preemptible thread or
155 : : * switching to a metairq
156 : : */
157 [ - + - - ]: 2 : if (is_preempt(_current) || is_metairq(thread)) {
158 : 2 : return true;
159 : : }
160 : :
161 : 0 : return false;
162 : : }
163 : :
164 : : #ifdef CONFIG_SCHED_CPU_MASK
165 : : static ALWAYS_INLINE struct k_thread *_priq_dumb_mask_best(sys_dlist_t *pq)
166 : : {
167 : : /* With masks enabled we need to be prepared to walk the list
168 : : * looking for one we can run
169 : : */
170 : : struct k_thread *thread;
171 : :
172 : : SYS_DLIST_FOR_EACH_CONTAINER(pq, thread, base.qnode_dlist) {
173 : : if ((thread->base.cpu_mask & BIT(_current_cpu->id)) != 0) {
174 : : return thread;
175 : : }
176 : : }
177 : : return NULL;
178 : : }
179 : : #endif
180 : :
181 : 2 : static ALWAYS_INLINE void z_priq_dumb_add(sys_dlist_t *pq,
182 : : struct k_thread *thread)
183 : : {
184 : : struct k_thread *t;
185 : :
186 [ - + ]: 2 : __ASSERT_NO_MSG(!z_is_idle_thread_object(thread));
187 : :
188 [ + + - - : 2 : SYS_DLIST_FOR_EACH_CONTAINER(pq, t, base.qnode_dlist) {
- - + + ]
189 [ + - ]: 1 : if (z_sched_prio_cmp(thread, t) > 0) {
190 : 1 : sys_dlist_insert(&t->base.qnode_dlist,
191 : : &thread->base.qnode_dlist);
192 : 1 : return;
193 : : }
194 : : }
195 : :
196 : 1 : sys_dlist_append(pq, &thread->base.qnode_dlist);
197 : : }
198 : :
199 : 3 : static ALWAYS_INLINE void *thread_runq(struct k_thread *thread)
200 : : {
201 : : #ifdef CONFIG_SCHED_CPU_MASK_PIN_ONLY
202 : : int cpu, m = thread->base.cpu_mask;
203 : :
204 : : /* Edge case: it's legal per the API to "make runnable" a
205 : : * thread with all CPUs masked off (i.e. one that isn't
206 : : * actually runnable!). Sort of a wart in the API and maybe
207 : : * we should address this in docs/assertions instead to avoid
208 : : * the extra test.
209 : : */
210 : : cpu = m == 0 ? 0 : u32_count_trailing_zeros(m);
211 : :
212 : : return &_kernel.cpus[cpu].ready_q.runq;
213 : : #else
214 : 3 : return &_kernel.ready_q.runq;
215 : : #endif
216 : : }
217 : :
218 : 4 : static ALWAYS_INLINE void *curr_cpu_runq(void)
219 : : {
220 : : #ifdef CONFIG_SCHED_CPU_MASK_PIN_ONLY
221 : : return &arch_curr_cpu()->ready_q.runq;
222 : : #else
223 : 4 : return &_kernel.ready_q.runq;
224 : : #endif
225 : : }
226 : :
227 : 2 : static ALWAYS_INLINE void runq_add(struct k_thread *thread)
228 : : {
229 : 2 : _priq_run_add(thread_runq(thread), thread);
230 : 2 : }
231 : :
232 : 1 : static ALWAYS_INLINE void runq_remove(struct k_thread *thread)
233 : : {
234 : 1 : _priq_run_remove(thread_runq(thread), thread);
235 : 1 : }
236 : :
237 : 4 : static ALWAYS_INLINE struct k_thread *runq_best(void)
238 : : {
239 : 4 : return _priq_run_best(curr_cpu_runq());
240 : : }
241 : :
242 : : /* _current is never in the run queue until context switch on
243 : : * SMP configurations, see z_requeue_current()
244 : : */
245 : 3 : static inline bool should_queue_thread(struct k_thread *th)
246 : : {
247 : 3 : return !IS_ENABLED(CONFIG_SMP) || th != _current;
248 : : }
249 : :
250 : 2 : static ALWAYS_INLINE void queue_thread(struct k_thread *thread)
251 : : {
252 : 2 : thread->base.thread_state |= _THREAD_QUEUED;
253 [ + - ]: 2 : if (should_queue_thread(thread)) {
254 : 2 : runq_add(thread);
255 : : }
256 : : #ifdef CONFIG_SMP
257 : : if (thread == _current) {
258 : : /* add current to end of queue means "yield" */
259 : : _current_cpu->swap_ok = true;
260 : : }
261 : : #endif
262 : 2 : }
263 : :
264 : 1 : static ALWAYS_INLINE void dequeue_thread(struct k_thread *thread)
265 : : {
266 : 1 : thread->base.thread_state &= ~_THREAD_QUEUED;
267 [ + - ]: 1 : if (should_queue_thread(thread)) {
268 : 1 : runq_remove(thread);
269 : : }
270 : 1 : }
271 : :
272 : : #ifdef CONFIG_SMP
273 : : /* Called out of z_swap() when CONFIG_SMP. The current thread can
274 : : * never live in the run queue until we are inexorably on the context
275 : : * switch path on SMP, otherwise there is a deadlock condition where a
276 : : * set of CPUs pick a cycle of threads to run and wait for them all to
277 : : * context switch forever.
278 : : */
279 : : void z_requeue_current(struct k_thread *curr)
280 : : {
281 : : if (z_is_thread_queued(curr)) {
282 : : runq_add(curr);
283 : : }
284 : : }
285 : :
286 : : static inline bool is_aborting(struct k_thread *thread)
287 : : {
288 : : return (thread->base.thread_state & _THREAD_ABORTING) != 0U;
289 : : }
290 : : #endif
291 : :
292 : 4 : static ALWAYS_INLINE struct k_thread *next_up(void)
293 : : {
294 : 4 : struct k_thread *thread = runq_best();
295 : :
296 : : #if (CONFIG_NUM_METAIRQ_PRIORITIES > 0) && (CONFIG_NUM_COOP_PRIORITIES > 0)
297 : : /* MetaIRQs must always attempt to return back to a
298 : : * cooperative thread they preempted and not whatever happens
299 : : * to be highest priority now. The cooperative thread was
300 : : * promised it wouldn't be preempted (by non-metairq threads)!
301 : : */
302 : : struct k_thread *mirqp = _current_cpu->metairq_preempted;
303 : :
304 : : if (mirqp != NULL && (thread == NULL || !is_metairq(thread))) {
305 : : if (!z_is_thread_prevented_from_running(mirqp)) {
306 : : thread = mirqp;
307 : : } else {
308 : : _current_cpu->metairq_preempted = NULL;
309 : : }
310 : : }
311 : : #endif
312 : :
313 : : #ifndef CONFIG_SMP
314 : : /* In uniprocessor mode, we can leave the current thread in
315 : : * the queue (actually we have to, otherwise the assembly
316 : : * context switch code for all architectures would be
317 : : * responsible for putting it back in z_swap and ISR return!),
318 : : * which makes this choice simple.
319 : : */
320 [ - + ]: 4 : return (thread != NULL) ? thread : _current_cpu->idle_thread;
321 : : #else
322 : : /* Under SMP, the "cache" mechanism for selecting the next
323 : : * thread doesn't work, so we have more work to do to test
324 : : * _current against the best choice from the queue. Here, the
325 : : * thread selected above represents "the best thread that is
326 : : * not current".
327 : : *
328 : : * Subtle note on "queued": in SMP mode, _current does not
329 : : * live in the queue, so this isn't exactly the same thing as
330 : : * "ready", it means "is _current already added back to the
331 : : * queue such that we don't want to re-add it".
332 : : */
333 : : if (is_aborting(_current)) {
334 : : end_thread(_current);
335 : : }
336 : :
337 : : int queued = z_is_thread_queued(_current);
338 : : int active = !z_is_thread_prevented_from_running(_current);
339 : :
340 : : if (thread == NULL) {
341 : : thread = _current_cpu->idle_thread;
342 : : }
343 : :
344 : : if (active) {
345 : : int32_t cmp = z_sched_prio_cmp(_current, thread);
346 : :
347 : : /* Ties only switch if state says we yielded */
348 : : if ((cmp > 0) || ((cmp == 0) && !_current_cpu->swap_ok)) {
349 : : thread = _current;
350 : : }
351 : :
352 : : if (!should_preempt(thread, _current_cpu->swap_ok)) {
353 : : thread = _current;
354 : : }
355 : : }
356 : :
357 : : /* Put _current back into the queue */
358 : : if (thread != _current && active &&
359 : : !z_is_idle_thread_object(_current) && !queued) {
360 : : queue_thread(_current);
361 : : }
362 : :
363 : : /* Take the new _current out of the queue */
364 : : if (z_is_thread_queued(thread)) {
365 : : dequeue_thread(thread);
366 : : }
367 : :
368 : : _current_cpu->swap_ok = false;
369 : : return thread;
370 : : #endif
371 : : }
372 : :
373 : 0 : static void move_thread_to_end_of_prio_q(struct k_thread *thread)
374 : : {
375 [ # # ]: 0 : if (z_is_thread_queued(thread)) {
376 : 0 : dequeue_thread(thread);
377 : : }
378 : 0 : queue_thread(thread);
379 : 0 : update_cache(thread == _current);
380 : 0 : }
381 : :
382 : : #ifdef CONFIG_TIMESLICING
383 : :
384 : : static int slice_ticks;
385 : : static int slice_max_prio;
386 : :
387 : 4 : static inline int slice_time(struct k_thread *curr)
388 : : {
389 : 4 : int ret = slice_ticks;
390 : :
391 : : #ifdef CONFIG_TIMESLICE_PER_THREAD
392 : : if (curr->base.slice_ticks != 0) {
393 : : ret = curr->base.slice_ticks;
394 : : }
395 : : #endif
396 : 4 : return ret;
397 : : }
398 : :
399 : : #ifdef CONFIG_SWAP_NONATOMIC
400 : : /* If z_swap() isn't atomic, then it's possible for a timer interrupt
401 : : * to try to timeslice away _current after it has already pended
402 : : * itself but before the corresponding context switch. Treat that as
403 : : * a noop condition in z_time_slice().
404 : : */
405 : : static struct k_thread *pending_current;
406 : : #endif
407 : :
408 : 4 : void z_reset_time_slice(struct k_thread *curr)
409 : : {
410 : : /* Add the elapsed time since the last announced tick to the
411 : : * slice count, as we'll see those "expired" ticks arrive in a
412 : : * FUTURE z_time_slice() call.
413 : : */
414 [ - + ]: 4 : if (slice_time(curr) != 0) {
415 : 0 : _current_cpu->slice_ticks = slice_time(curr) + sys_clock_elapsed();
416 : 0 : z_set_timeout_expiry(slice_time(curr), false);
417 : : }
418 : 4 : }
419 : :
420 : 1 : void k_sched_time_slice_set(int32_t slice, int prio)
421 : : {
422 [ + + ]: 2 : LOCKED(&sched_spinlock) {
423 : 1 : _current_cpu->slice_ticks = 0;
424 : 1 : slice_ticks = k_ms_to_ticks_ceil32(slice);
425 [ - + ]: 1 : if (IS_ENABLED(CONFIG_TICKLESS_KERNEL) && slice > 0) {
426 : : /* It's not possible to reliably set a 1-tick
427 : : * timeout if ticks aren't regular.
428 : : */
429 : 0 : slice_ticks = MAX(2, slice_ticks);
430 : : }
431 : 1 : slice_max_prio = prio;
432 : 1 : z_reset_time_slice(_current);
433 : : }
434 : 1 : }
435 : :
436 : : #ifdef CONFIG_TIMESLICE_PER_THREAD
437 : : void k_thread_time_slice_set(struct k_thread *th, int32_t slice_ticks,
438 : : k_thread_timeslice_fn_t expired, void *data)
439 : : {
440 : : LOCKED(&sched_spinlock) {
441 : : th->base.slice_ticks = slice_ticks;
442 : : th->base.slice_expired = expired;
443 : : th->base.slice_data = data;
444 : : }
445 : : }
446 : : #endif
447 : :
448 : 0 : static inline bool sliceable(struct k_thread *thread)
449 : : {
450 : 0 : bool ret = is_preempt(thread)
451 [ # # ]: 0 : && !z_is_thread_prevented_from_running(thread)
452 [ # # ]: 0 : && !z_is_prio_higher(thread->base.prio, slice_max_prio)
453 [ # # # # ]: 0 : && !z_is_idle_thread_object(thread);
454 : :
455 : : #ifdef CONFIG_TIMESLICE_PER_THREAD
456 : : ret |= thread->base.slice_ticks != 0;
457 : : #endif
458 : :
459 : 0 : return ret;
460 : : }
461 : :
462 : 0 : static k_spinlock_key_t slice_expired_locked(k_spinlock_key_t sched_lock_key)
463 : : {
464 : 0 : struct k_thread *curr = _current;
465 : :
466 : : #ifdef CONFIG_TIMESLICE_PER_THREAD
467 : : if (curr->base.slice_expired) {
468 : : k_spin_unlock(&sched_spinlock, sched_lock_key);
469 : : curr->base.slice_expired(curr, curr->base.slice_data);
470 : : sched_lock_key = k_spin_lock(&sched_spinlock);
471 : : }
472 : : #endif
473 [ # # ]: 0 : if (!z_is_thread_prevented_from_running(curr)) {
474 : 0 : move_thread_to_end_of_prio_q(curr);
475 : : }
476 : 0 : z_reset_time_slice(curr);
477 : :
478 : 0 : return sched_lock_key;
479 : : }
480 : :
481 : : /* Called out of each timer interrupt */
482 : 0 : void z_time_slice(int ticks)
483 : : {
484 : : /* Hold sched_spinlock, so that activity on another CPU
485 : : * (like a call to k_thread_abort() at just the wrong time)
486 : : * won't affect the correctness of the decisions made here.
487 : : * Also prevents any nested interrupts from changing
488 : : * thread state to avoid similar issues, since this would
489 : : * normally run with IRQs enabled.
490 : : */
491 : 0 : k_spinlock_key_t key = k_spin_lock(&sched_spinlock);
492 : :
493 : : #ifdef CONFIG_SWAP_NONATOMIC
494 [ # # ]: 0 : if (pending_current == _current) {
495 : 0 : z_reset_time_slice(_current);
496 : 0 : k_spin_unlock(&sched_spinlock, key);
497 : 0 : return;
498 : : }
499 : 0 : pending_current = NULL;
500 : : #endif
501 : :
502 [ # # # # ]: 0 : if (slice_time(_current) && sliceable(_current)) {
503 [ # # ]: 0 : if (ticks >= _current_cpu->slice_ticks) {
504 : : /* Note: this will (if so enabled) internally
505 : : * drop and reacquire the scheduler lock
506 : : * around the callback! Don't put anything
507 : : * after this line that requires
508 : : * synchronization.
509 : : */
510 : 0 : key = slice_expired_locked(key);
511 : : } else {
512 : 0 : _current_cpu->slice_ticks -= ticks;
513 : : }
514 : : } else {
515 : 0 : _current_cpu->slice_ticks = 0;
516 : : }
517 : 0 : k_spin_unlock(&sched_spinlock, key);
518 : : }
519 : : #endif
520 : :
521 : : /* Track cooperative threads preempted by metairqs so we can return to
522 : : * them specifically. Called at the moment a new thread has been
523 : : * selected to run.
524 : : */
525 : 4 : static void update_metairq_preempt(struct k_thread *thread)
526 : : {
527 : : #if (CONFIG_NUM_METAIRQ_PRIORITIES > 0) && (CONFIG_NUM_COOP_PRIORITIES > 0)
528 : : if (is_metairq(thread) && !is_metairq(_current) &&
529 : : !is_preempt(_current)) {
530 : : /* Record new preemption */
531 : : _current_cpu->metairq_preempted = _current;
532 : : } else if (!is_metairq(thread) && !z_is_idle_thread_object(thread)) {
533 : : /* Returning from existing preemption */
534 : : _current_cpu->metairq_preempted = NULL;
535 : : }
536 : : #endif
537 : 4 : }
538 : :
539 : 4 : static void update_cache(int preempt_ok)
540 : : {
541 : : #ifndef CONFIG_SMP
542 : 4 : struct k_thread *thread = next_up();
543 : :
544 [ + - ]: 4 : if (should_preempt(thread, preempt_ok)) {
545 : : #ifdef CONFIG_TIMESLICING
546 [ + + ]: 4 : if (thread != _current) {
547 : 3 : z_reset_time_slice(thread);
548 : : }
549 : : #endif
550 : 4 : update_metairq_preempt(thread);
551 : 4 : _kernel.ready_q.cache = thread;
552 : : } else {
553 : 0 : _kernel.ready_q.cache = _current;
554 : : }
555 : :
556 : : #else
557 : : /* The way this works is that the CPU record keeps its
558 : : * "cooperative swapping is OK" flag until the next reschedule
559 : : * call or context switch. It doesn't need to be tracked per
560 : : * thread because if the thread gets preempted for whatever
561 : : * reason the scheduler will make the same decision anyway.
562 : : */
563 : : _current_cpu->swap_ok = preempt_ok;
564 : : #endif
565 : 4 : }
566 : :
567 : 1 : static bool thread_active_elsewhere(struct k_thread *thread)
568 : : {
569 : : /* True if the thread is currently running on another CPU.
570 : : * There are more scalable designs to answer this question in
571 : : * constant time, but this is fine for now.
572 : : */
573 : : #ifdef CONFIG_SMP
574 : : int currcpu = _current_cpu->id;
575 : :
576 : : for (int i = 0; i < CONFIG_MP_NUM_CPUS; i++) {
577 : : if ((i != currcpu) &&
578 : : (_kernel.cpus[i].current == thread)) {
579 : : return true;
580 : : }
581 : : }
582 : : #endif
583 : 1 : return false;
584 : : }
585 : :
586 : 2 : static void ready_thread(struct k_thread *thread)
587 : : {
588 : : #ifdef CONFIG_KERNEL_COHERENCE
589 : : __ASSERT_NO_MSG(arch_mem_coherent(thread));
590 : : #endif
591 : :
592 : : /* If thread is queued already, do not try and added it to the
593 : : * run queue again
594 : : */
595 [ + - + - ]: 2 : if (!z_is_thread_queued(thread) && z_is_thread_ready(thread)) {
596 : : SYS_PORT_TRACING_OBJ_FUNC(k_thread, sched_ready, thread);
597 : :
598 : 2 : queue_thread(thread);
599 : 2 : update_cache(0);
600 : : #if defined(CONFIG_SMP) && defined(CONFIG_SCHED_IPI_SUPPORTED)
601 : : arch_sched_ipi();
602 : : #endif
603 : : }
604 : 2 : }
605 : :
606 : 1 : void z_ready_thread(struct k_thread *thread)
607 : : {
608 [ + + ]: 2 : LOCKED(&sched_spinlock) {
609 [ + - ]: 1 : if (!thread_active_elsewhere(thread)) {
610 : 1 : ready_thread(thread);
611 : : }
612 : : }
613 : 1 : }
614 : :
615 : 0 : void z_move_thread_to_end_of_prio_q(struct k_thread *thread)
616 : : {
617 [ # # ]: 0 : LOCKED(&sched_spinlock) {
618 : 0 : move_thread_to_end_of_prio_q(thread);
619 : : }
620 : 0 : }
621 : :
622 : 1 : void z_sched_start(struct k_thread *thread)
623 : : {
624 : 1 : k_spinlock_key_t key = k_spin_lock(&sched_spinlock);
625 : :
626 [ - + ]: 1 : if (z_has_thread_started(thread)) {
627 : 0 : k_spin_unlock(&sched_spinlock, key);
628 : 0 : return;
629 : : }
630 : :
631 : 1 : z_mark_thread_as_started(thread);
632 : 1 : ready_thread(thread);
633 : 1 : z_reschedule(&sched_spinlock, key);
634 : : }
635 : :
636 : 0 : void z_impl_k_thread_suspend(struct k_thread *thread)
637 : : {
638 : : SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_thread, suspend, thread);
639 : :
640 : 0 : (void)z_abort_thread_timeout(thread);
641 : :
642 [ # # ]: 0 : LOCKED(&sched_spinlock) {
643 [ # # ]: 0 : if (z_is_thread_queued(thread)) {
644 : 0 : dequeue_thread(thread);
645 : : }
646 : 0 : z_mark_thread_as_suspended(thread);
647 : 0 : update_cache(thread == _current);
648 : : }
649 : :
650 [ # # ]: 0 : if (thread == _current) {
651 : 0 : z_reschedule_unlocked();
652 : : }
653 : :
654 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_thread, suspend, thread);
655 : 0 : }
656 : :
657 : : #ifdef CONFIG_USERSPACE
658 : : static inline void z_vrfy_k_thread_suspend(struct k_thread *thread)
659 : : {
660 : : Z_OOPS(Z_SYSCALL_OBJ(thread, K_OBJ_THREAD));
661 : : z_impl_k_thread_suspend(thread);
662 : : }
663 : : #include <syscalls/k_thread_suspend_mrsh.c>
664 : : #endif
665 : :
666 : 0 : void z_impl_k_thread_resume(struct k_thread *thread)
667 : : {
668 : : SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_thread, resume, thread);
669 : :
670 : 0 : k_spinlock_key_t key = k_spin_lock(&sched_spinlock);
671 : :
672 : : /* Do not try to resume a thread that was not suspended */
673 [ # # ]: 0 : if (!z_is_thread_suspended(thread)) {
674 : 0 : k_spin_unlock(&sched_spinlock, key);
675 : 0 : return;
676 : : }
677 : :
678 : 0 : z_mark_thread_as_not_suspended(thread);
679 : 0 : ready_thread(thread);
680 : :
681 : 0 : z_reschedule(&sched_spinlock, key);
682 : :
683 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_thread, resume, thread);
684 : : }
685 : :
686 : : #ifdef CONFIG_USERSPACE
687 : : static inline void z_vrfy_k_thread_resume(struct k_thread *thread)
688 : : {
689 : : Z_OOPS(Z_SYSCALL_OBJ(thread, K_OBJ_THREAD));
690 : : z_impl_k_thread_resume(thread);
691 : : }
692 : : #include <syscalls/k_thread_resume_mrsh.c>
693 : : #endif
694 : :
695 : 0 : static _wait_q_t *pended_on_thread(struct k_thread *thread)
696 : : {
697 [ # # ]: 0 : __ASSERT_NO_MSG(thread->base.pended_on);
698 : :
699 : 0 : return thread->base.pended_on;
700 : : }
701 : :
702 : 0 : static void unready_thread(struct k_thread *thread)
703 : : {
704 [ # # ]: 0 : if (z_is_thread_queued(thread)) {
705 : 0 : dequeue_thread(thread);
706 : : }
707 : 0 : update_cache(thread == _current);
708 : 0 : }
709 : :
710 : : /* sched_spinlock must be held */
711 : 0 : static void add_to_waitq_locked(struct k_thread *thread, _wait_q_t *wait_q)
712 : : {
713 : 0 : unready_thread(thread);
714 : 0 : z_mark_thread_as_pending(thread);
715 : :
716 : : SYS_PORT_TRACING_FUNC(k_thread, sched_pend, thread);
717 : :
718 [ # # ]: 0 : if (wait_q != NULL) {
719 : 0 : thread->base.pended_on = wait_q;
720 : 0 : z_priq_wait_add(&wait_q->waitq, thread);
721 : : }
722 : 0 : }
723 : :
724 : 0 : static void add_thread_timeout(struct k_thread *thread, k_timeout_t timeout)
725 : : {
726 [ # # ]: 0 : if (!K_TIMEOUT_EQ(timeout, K_FOREVER)) {
727 : 0 : z_add_thread_timeout(thread, timeout);
728 : : }
729 : 0 : }
730 : :
731 : 0 : static void pend(struct k_thread *thread, _wait_q_t *wait_q,
732 : : k_timeout_t timeout)
733 : : {
734 : : #ifdef CONFIG_KERNEL_COHERENCE
735 : : __ASSERT_NO_MSG(wait_q == NULL || arch_mem_coherent(wait_q));
736 : : #endif
737 : :
738 [ # # ]: 0 : LOCKED(&sched_spinlock) {
739 : 0 : add_to_waitq_locked(thread, wait_q);
740 : : }
741 : :
742 : 0 : add_thread_timeout(thread, timeout);
743 : 0 : }
744 : :
745 : 0 : void z_pend_thread(struct k_thread *thread, _wait_q_t *wait_q,
746 : : k_timeout_t timeout)
747 : : {
748 [ # # # # ]: 0 : __ASSERT_NO_MSG(thread == _current || is_thread_dummy(thread));
749 : 0 : pend(thread, wait_q, timeout);
750 : 0 : }
751 : :
752 : 0 : static inline void unpend_thread_no_timeout(struct k_thread *thread)
753 : : {
754 : 0 : _priq_wait_remove(&pended_on_thread(thread)->waitq, thread);
755 : 0 : z_mark_thread_as_not_pending(thread);
756 : 0 : thread->base.pended_on = NULL;
757 : 0 : }
758 : :
759 : 0 : ALWAYS_INLINE void z_unpend_thread_no_timeout(struct k_thread *thread)
760 : : {
761 [ # # ]: 0 : LOCKED(&sched_spinlock) {
762 : 0 : unpend_thread_no_timeout(thread);
763 : : }
764 : 0 : }
765 : :
766 : : #ifdef CONFIG_SYS_CLOCK_EXISTS
767 : : /* Timeout handler for *_thread_timeout() APIs */
768 : 0 : void z_thread_timeout(struct _timeout *timeout)
769 : : {
770 : 0 : struct k_thread *thread = CONTAINER_OF(timeout,
771 : : struct k_thread, base.timeout);
772 : :
773 [ # # ]: 0 : LOCKED(&sched_spinlock) {
774 [ # # ]: 0 : bool killed = ((thread->base.thread_state & _THREAD_DEAD) ||
775 [ # # ]: 0 : (thread->base.thread_state & _THREAD_ABORTING));
776 : :
777 [ # # ]: 0 : if (!killed) {
778 [ # # ]: 0 : if (thread->base.pended_on != NULL) {
779 : 0 : unpend_thread_no_timeout(thread);
780 : : }
781 : 0 : z_mark_thread_as_started(thread);
782 : 0 : z_mark_thread_as_not_suspended(thread);
783 : 0 : ready_thread(thread);
784 : : }
785 : : }
786 : 0 : }
787 : : #endif
788 : :
789 : 0 : int z_pend_curr_irqlock(uint32_t key, _wait_q_t *wait_q, k_timeout_t timeout)
790 : : {
791 : 0 : pend(_current, wait_q, timeout);
792 : :
793 : : #if defined(CONFIG_TIMESLICING) && defined(CONFIG_SWAP_NONATOMIC)
794 : 0 : pending_current = _current;
795 : :
796 : 0 : int ret = z_swap_irqlock(key);
797 [ # # ]: 0 : LOCKED(&sched_spinlock) {
798 [ # # ]: 0 : if (pending_current == _current) {
799 : 0 : pending_current = NULL;
800 : : }
801 : : }
802 : 0 : return ret;
803 : : #else
804 : : return z_swap_irqlock(key);
805 : : #endif
806 : : }
807 : :
808 : 0 : int z_pend_curr(struct k_spinlock *lock, k_spinlock_key_t key,
809 : : _wait_q_t *wait_q, k_timeout_t timeout)
810 : : {
811 : : #if defined(CONFIG_TIMESLICING) && defined(CONFIG_SWAP_NONATOMIC)
812 : 0 : pending_current = _current;
813 : : #endif
814 : 0 : pend(_current, wait_q, timeout);
815 : 0 : return z_swap(lock, key);
816 : : }
817 : :
818 : 0 : struct k_thread *z_unpend1_no_timeout(_wait_q_t *wait_q)
819 : : {
820 : 0 : struct k_thread *thread = NULL;
821 : :
822 [ # # ]: 0 : LOCKED(&sched_spinlock) {
823 : 0 : thread = _priq_wait_best(&wait_q->waitq);
824 : :
825 [ # # ]: 0 : if (thread != NULL) {
826 : 0 : unpend_thread_no_timeout(thread);
827 : : }
828 : : }
829 : :
830 : 0 : return thread;
831 : : }
832 : :
833 : 0 : struct k_thread *z_unpend_first_thread(_wait_q_t *wait_q)
834 : : {
835 : 0 : struct k_thread *thread = NULL;
836 : :
837 [ # # ]: 0 : LOCKED(&sched_spinlock) {
838 : 0 : thread = _priq_wait_best(&wait_q->waitq);
839 : :
840 [ # # ]: 0 : if (thread != NULL) {
841 : 0 : unpend_thread_no_timeout(thread);
842 : 0 : (void)z_abort_thread_timeout(thread);
843 : : }
844 : : }
845 : :
846 : 0 : return thread;
847 : : }
848 : :
849 : 0 : void z_unpend_thread(struct k_thread *thread)
850 : : {
851 : 0 : z_unpend_thread_no_timeout(thread);
852 : 0 : (void)z_abort_thread_timeout(thread);
853 : 0 : }
854 : :
855 : : /* Priority set utility that does no rescheduling, it just changes the
856 : : * run queue state, returning true if a reschedule is needed later.
857 : : */
858 : 0 : bool z_set_prio(struct k_thread *thread, int prio)
859 : : {
860 : 0 : bool need_sched = 0;
861 : :
862 [ # # ]: 0 : LOCKED(&sched_spinlock) {
863 : 0 : need_sched = z_is_thread_ready(thread);
864 : :
865 [ # # ]: 0 : if (need_sched) {
866 : : /* Don't requeue on SMP if it's the running thread */
867 : : if (!IS_ENABLED(CONFIG_SMP) || z_is_thread_queued(thread)) {
868 : 0 : dequeue_thread(thread);
869 : 0 : thread->base.prio = prio;
870 : 0 : queue_thread(thread);
871 : : } else {
872 : : thread->base.prio = prio;
873 : : }
874 : 0 : update_cache(1);
875 : : } else {
876 : 0 : thread->base.prio = prio;
877 : : }
878 : : }
879 : :
880 : : SYS_PORT_TRACING_OBJ_FUNC(k_thread, sched_priority_set, thread, prio);
881 : :
882 : 0 : return need_sched;
883 : : }
884 : :
885 : 0 : void z_thread_priority_set(struct k_thread *thread, int prio)
886 : : {
887 : 0 : bool need_sched = z_set_prio(thread, prio);
888 : :
889 : : #if defined(CONFIG_SMP) && defined(CONFIG_SCHED_IPI_SUPPORTED)
890 : : arch_sched_ipi();
891 : : #endif
892 : :
893 [ # # # # ]: 0 : if (need_sched && _current->base.sched_locked == 0U) {
894 : 0 : z_reschedule_unlocked();
895 : : }
896 : 0 : }
897 : :
898 : 2 : static inline bool resched(uint32_t key)
899 : : {
900 : : #ifdef CONFIG_SMP
901 : : _current_cpu->swap_ok = 0;
902 : : #endif
903 : :
904 [ + - + - ]: 2 : return arch_irq_unlocked(key) && !arch_is_in_isr();
905 : : }
906 : :
907 : : /*
908 : : * Check if the next ready thread is the same as the current thread
909 : : * and save the trip if true.
910 : : */
911 : 1 : static inline bool need_swap(void)
912 : : {
913 : : /* the SMP case will be handled in C based z_swap() */
914 : : #ifdef CONFIG_SMP
915 : : return true;
916 : : #else
917 : : struct k_thread *new_thread;
918 : :
919 : : /* Check if the next ready thread is the same as the current thread */
920 : 1 : new_thread = _kernel.ready_q.cache;
921 : 1 : return new_thread != _current;
922 : : #endif
923 : : }
924 : :
925 : 1 : void z_reschedule(struct k_spinlock *lock, k_spinlock_key_t key)
926 : : {
927 [ + - + - ]: 1 : if (resched(key.key) && need_swap()) {
928 : 1 : z_swap(lock, key);
929 : : } else {
930 : 0 : k_spin_unlock(lock, key);
931 : : }
932 : 1 : }
933 : :
934 : 1 : void z_reschedule_irqlock(uint32_t key)
935 : : {
936 [ + - ]: 1 : if (resched(key)) {
937 : 1 : z_swap_irqlock(key);
938 : : } else {
939 : 0 : irq_unlock(key);
940 : : }
941 : 1 : }
942 : :
943 : 2 : void k_sched_lock(void)
944 : : {
945 [ + + ]: 4 : LOCKED(&sched_spinlock) {
946 : : SYS_PORT_TRACING_FUNC(k_thread, sched_lock);
947 : :
948 : 2 : z_sched_lock();
949 : : }
950 : 2 : }
951 : :
952 : 1 : void k_sched_unlock(void)
953 : : {
954 [ + + ]: 2 : LOCKED(&sched_spinlock) {
955 [ - + ]: 1 : __ASSERT(_current->base.sched_locked != 0U, "");
956 [ - + ]: 1 : __ASSERT(!arch_is_in_isr(), "");
957 : :
958 : 1 : ++_current->base.sched_locked;
959 : 1 : update_cache(0);
960 : : }
961 : :
962 [ + - ]: 1 : LOG_DBG("scheduler unlocked (%p:%d)",
963 : : _current, _current->base.sched_locked);
964 : :
965 : : SYS_PORT_TRACING_FUNC(k_thread, sched_unlock);
966 : :
967 : 1 : z_reschedule_unlocked();
968 : 1 : }
969 : :
970 : 0 : struct k_thread *z_swap_next_thread(void)
971 : : {
972 : : #ifdef CONFIG_SMP
973 : : return next_up();
974 : : #else
975 : 0 : return _kernel.ready_q.cache;
976 : : #endif
977 : : }
978 : :
979 : : #ifdef CONFIG_USE_SWITCH
980 : : /* Just a wrapper around _current = xxx with tracing */
981 : : static inline void set_current(struct k_thread *new_thread)
982 : : {
983 : : z_thread_mark_switched_out();
984 : : _current_cpu->current = new_thread;
985 : : }
986 : :
987 : : /**
988 : : * @brief Determine next thread to execute upon completion of an interrupt
989 : : *
990 : : * Thread preemption is performed by context switching after the completion
991 : : * of a non-recursed interrupt. This function determines which thread to
992 : : * switch to if any. This function accepts as @p interrupted either:
993 : : *
994 : : * - The handle for the interrupted thread in which case the thread's context
995 : : * must already be fully saved and ready to be picked up by a different CPU.
996 : : *
997 : : * - NULL if more work is required to fully save the thread's state after
998 : : * it is known that a new thread is to be scheduled. It is up to the caller
999 : : * to store the handle resulting from the thread that is being switched out
1000 : : * in that thread's "switch_handle" field after its
1001 : : * context has fully been saved, following the same requirements as with
1002 : : * the @ref arch_switch() function.
1003 : : *
1004 : : * If a new thread needs to be scheduled then its handle is returned.
1005 : : * Otherwise the same value provided as @p interrupted is returned back.
1006 : : * Those handles are the same opaque types used by the @ref arch_switch()
1007 : : * function.
1008 : : *
1009 : : * @warning
1010 : : * The @ref _current value may have changed after this call and not refer
1011 : : * to the interrupted thread anymore. It might be necessary to make a local
1012 : : * copy before calling this function.
1013 : : *
1014 : : * @param interrupted Handle for the thread that was interrupted or NULL.
1015 : : * @retval Handle for the next thread to execute, or @p interrupted when
1016 : : * no new thread is to be scheduled.
1017 : : */
1018 : : void *z_get_next_switch_handle(void *interrupted)
1019 : : {
1020 : : z_check_stack_sentinel();
1021 : :
1022 : : #ifdef CONFIG_SMP
1023 : : void *ret = NULL;
1024 : :
1025 : : LOCKED(&sched_spinlock) {
1026 : : struct k_thread *old_thread = _current, *new_thread;
1027 : :
1028 : : if (IS_ENABLED(CONFIG_SMP)) {
1029 : : old_thread->switch_handle = NULL;
1030 : : }
1031 : : new_thread = next_up();
1032 : :
1033 : : z_sched_usage_switch(new_thread);
1034 : :
1035 : : if (old_thread != new_thread) {
1036 : : update_metairq_preempt(new_thread);
1037 : : wait_for_switch(new_thread);
1038 : : arch_cohere_stacks(old_thread, interrupted, new_thread);
1039 : :
1040 : : _current_cpu->swap_ok = 0;
1041 : : set_current(new_thread);
1042 : :
1043 : : #ifdef CONFIG_TIMESLICING
1044 : : z_reset_time_slice(new_thread);
1045 : : #endif
1046 : :
1047 : : #ifdef CONFIG_SPIN_VALIDATE
1048 : : /* Changed _current! Update the spinlock
1049 : : * bookkeeping so the validation doesn't get
1050 : : * confused when the "wrong" thread tries to
1051 : : * release the lock.
1052 : : */
1053 : : z_spin_lock_set_owner(&sched_spinlock);
1054 : : #endif
1055 : :
1056 : : /* A queued (runnable) old/current thread
1057 : : * needs to be added back to the run queue
1058 : : * here, and atomically with its switch handle
1059 : : * being set below. This is safe now, as we
1060 : : * will not return into it.
1061 : : */
1062 : : if (z_is_thread_queued(old_thread)) {
1063 : : runq_add(old_thread);
1064 : : }
1065 : : }
1066 : : old_thread->switch_handle = interrupted;
1067 : : ret = new_thread->switch_handle;
1068 : : if (IS_ENABLED(CONFIG_SMP)) {
1069 : : /* Active threads MUST have a null here */
1070 : : new_thread->switch_handle = NULL;
1071 : : }
1072 : : }
1073 : : return ret;
1074 : : #else
1075 : : z_sched_usage_switch(_kernel.ready_q.cache);
1076 : : _current->switch_handle = interrupted;
1077 : : set_current(_kernel.ready_q.cache);
1078 : : return _current->switch_handle;
1079 : : #endif
1080 : : }
1081 : : #endif
1082 : :
1083 : 1 : void z_priq_dumb_remove(sys_dlist_t *pq, struct k_thread *thread)
1084 : : {
1085 [ - + ]: 1 : __ASSERT_NO_MSG(!z_is_idle_thread_object(thread));
1086 : :
1087 : 1 : sys_dlist_remove(&thread->base.qnode_dlist);
1088 : 1 : }
1089 : :
1090 : 4 : struct k_thread *z_priq_dumb_best(sys_dlist_t *pq)
1091 : : {
1092 : 4 : struct k_thread *thread = NULL;
1093 : 4 : sys_dnode_t *n = sys_dlist_peek_head(pq);
1094 : :
1095 [ + - ]: 4 : if (n != NULL) {
1096 : 4 : thread = CONTAINER_OF(n, struct k_thread, base.qnode_dlist);
1097 : : }
1098 : 4 : return thread;
1099 : : }
1100 : :
1101 : 0 : bool z_priq_rb_lessthan(struct rbnode *a, struct rbnode *b)
1102 : : {
1103 : : struct k_thread *thread_a, *thread_b;
1104 : : int32_t cmp;
1105 : :
1106 : 0 : thread_a = CONTAINER_OF(a, struct k_thread, base.qnode_rb);
1107 : 0 : thread_b = CONTAINER_OF(b, struct k_thread, base.qnode_rb);
1108 : :
1109 : 0 : cmp = z_sched_prio_cmp(thread_a, thread_b);
1110 : :
1111 [ # # ]: 0 : if (cmp > 0) {
1112 : 0 : return true;
1113 [ # # ]: 0 : } else if (cmp < 0) {
1114 : 0 : return false;
1115 : : } else {
1116 : 0 : return thread_a->base.order_key < thread_b->base.order_key
1117 : 0 : ? 1 : 0;
1118 : : }
1119 : : }
1120 : :
1121 : 0 : void z_priq_rb_add(struct _priq_rb *pq, struct k_thread *thread)
1122 : : {
1123 : : struct k_thread *t;
1124 : :
1125 [ # # ]: 0 : __ASSERT_NO_MSG(!z_is_idle_thread_object(thread));
1126 : :
1127 : 0 : thread->base.order_key = pq->next_order_key++;
1128 : :
1129 : : /* Renumber at wraparound. This is tiny code, and in practice
1130 : : * will almost never be hit on real systems. BUT on very
1131 : : * long-running systems where a priq never completely empties
1132 : : * AND that contains very large numbers of threads, it can be
1133 : : * a latency glitch to loop over all the threads like this.
1134 : : */
1135 [ # # ]: 0 : if (!pq->next_order_key) {
1136 [ # # ]: 0 : RB_FOR_EACH_CONTAINER(&pq->tree, t, base.qnode_rb) {
1137 : 0 : t->base.order_key = pq->next_order_key++;
1138 : : }
1139 : : }
1140 : :
1141 : 0 : rb_insert(&pq->tree, &thread->base.qnode_rb);
1142 : 0 : }
1143 : :
1144 : 0 : void z_priq_rb_remove(struct _priq_rb *pq, struct k_thread *thread)
1145 : : {
1146 [ # # ]: 0 : __ASSERT_NO_MSG(!z_is_idle_thread_object(thread));
1147 : :
1148 : 0 : rb_remove(&pq->tree, &thread->base.qnode_rb);
1149 : :
1150 [ # # ]: 0 : if (!pq->tree.root) {
1151 : 0 : pq->next_order_key = 0;
1152 : : }
1153 : 0 : }
1154 : :
1155 : 0 : struct k_thread *z_priq_rb_best(struct _priq_rb *pq)
1156 : : {
1157 : 0 : struct k_thread *thread = NULL;
1158 : 0 : struct rbnode *n = rb_get_min(&pq->tree);
1159 : :
1160 [ # # ]: 0 : if (n != NULL) {
1161 : 0 : thread = CONTAINER_OF(n, struct k_thread, base.qnode_rb);
1162 : : }
1163 : 0 : return thread;
1164 : : }
1165 : :
1166 : : #ifdef CONFIG_SCHED_MULTIQ
1167 : : # if (K_LOWEST_THREAD_PRIO - K_HIGHEST_THREAD_PRIO) > 31
1168 : : # error Too many priorities for multiqueue scheduler (max 32)
1169 : : # endif
1170 : :
1171 : : static ALWAYS_INLINE void z_priq_mq_add(struct _priq_mq *pq,
1172 : : struct k_thread *thread)
1173 : : {
1174 : : int priority_bit = thread->base.prio - K_HIGHEST_THREAD_PRIO;
1175 : :
1176 : : sys_dlist_append(&pq->queues[priority_bit], &thread->base.qnode_dlist);
1177 : : pq->bitmask |= BIT(priority_bit);
1178 : : }
1179 : :
1180 : : static ALWAYS_INLINE void z_priq_mq_remove(struct _priq_mq *pq,
1181 : : struct k_thread *thread)
1182 : : {
1183 : : int priority_bit = thread->base.prio - K_HIGHEST_THREAD_PRIO;
1184 : :
1185 : : sys_dlist_remove(&thread->base.qnode_dlist);
1186 : : if (sys_dlist_is_empty(&pq->queues[priority_bit])) {
1187 : : pq->bitmask &= ~BIT(priority_bit);
1188 : : }
1189 : : }
1190 : : #endif
1191 : :
1192 : 0 : struct k_thread *z_priq_mq_best(struct _priq_mq *pq)
1193 : : {
1194 [ # # ]: 0 : if (!pq->bitmask) {
1195 : 0 : return NULL;
1196 : : }
1197 : :
1198 : 0 : struct k_thread *thread = NULL;
1199 : 0 : sys_dlist_t *l = &pq->queues[__builtin_ctz(pq->bitmask)];
1200 : 0 : sys_dnode_t *n = sys_dlist_peek_head(l);
1201 : :
1202 [ # # ]: 0 : if (n != NULL) {
1203 : 0 : thread = CONTAINER_OF(n, struct k_thread, base.qnode_dlist);
1204 : : }
1205 : 0 : return thread;
1206 : : }
1207 : :
1208 : 3 : int z_unpend_all(_wait_q_t *wait_q)
1209 : : {
1210 : 3 : int need_sched = 0;
1211 : : struct k_thread *thread;
1212 : :
1213 [ - + ]: 3 : while ((thread = z_waitq_head(wait_q)) != NULL) {
1214 : 0 : z_unpend_thread(thread);
1215 : 0 : z_ready_thread(thread);
1216 : 0 : need_sched = 1;
1217 : : }
1218 : :
1219 : 3 : return need_sched;
1220 : : }
1221 : :
1222 : 1 : void init_ready_q(struct _ready_q *rq)
1223 : : {
1224 : : #if defined(CONFIG_SCHED_SCALABLE)
1225 : : rq->runq = (struct _priq_rb) {
1226 : : .tree = {
1227 : : .lessthan_fn = z_priq_rb_lessthan,
1228 : : }
1229 : : };
1230 : : #elif defined(CONFIG_SCHED_MULTIQ)
1231 : : for (int i = 0; i < ARRAY_SIZE(_kernel.ready_q.runq.queues); i++) {
1232 : : sys_dlist_init(&rq->runq.queues[i]);
1233 : : }
1234 : : #else
1235 : 1 : sys_dlist_init(&rq->runq);
1236 : : #endif
1237 : 1 : }
1238 : :
1239 : 1 : void z_sched_init(void)
1240 : : {
1241 : : #ifdef CONFIG_SCHED_CPU_MASK_PIN_ONLY
1242 : : for (int i = 0; i < CONFIG_MP_NUM_CPUS; i++) {
1243 : : init_ready_q(&_kernel.cpus[i].ready_q);
1244 : : }
1245 : : #else
1246 : 1 : init_ready_q(&_kernel.ready_q);
1247 : : #endif
1248 : :
1249 : : #ifdef CONFIG_TIMESLICING
1250 : 1 : k_sched_time_slice_set(CONFIG_TIMESLICE_SIZE,
1251 : : CONFIG_TIMESLICE_PRIORITY);
1252 : : #endif
1253 : 1 : }
1254 : :
1255 : 0 : int z_impl_k_thread_priority_get(k_tid_t thread)
1256 : : {
1257 : 0 : return thread->base.prio;
1258 : : }
1259 : :
1260 : : #ifdef CONFIG_USERSPACE
1261 : : static inline int z_vrfy_k_thread_priority_get(k_tid_t thread)
1262 : : {
1263 : : Z_OOPS(Z_SYSCALL_OBJ(thread, K_OBJ_THREAD));
1264 : : return z_impl_k_thread_priority_get(thread);
1265 : : }
1266 : : #include <syscalls/k_thread_priority_get_mrsh.c>
1267 : : #endif
1268 : :
1269 : 0 : void z_impl_k_thread_priority_set(k_tid_t thread, int prio)
1270 : : {
1271 : : /*
1272 : : * Use NULL, since we cannot know what the entry point is (we do not
1273 : : * keep track of it) and idle cannot change its priority.
1274 : : */
1275 [ # # # # : 0 : Z_ASSERT_VALID_PRIO(prio, NULL);
# # # # ]
1276 [ # # ]: 0 : __ASSERT(!arch_is_in_isr(), "");
1277 : :
1278 : 0 : struct k_thread *th = (struct k_thread *)thread;
1279 : :
1280 : 0 : z_thread_priority_set(th, prio);
1281 : 0 : }
1282 : :
1283 : : #ifdef CONFIG_USERSPACE
1284 : : static inline void z_vrfy_k_thread_priority_set(k_tid_t thread, int prio)
1285 : : {
1286 : : Z_OOPS(Z_SYSCALL_OBJ(thread, K_OBJ_THREAD));
1287 : : Z_OOPS(Z_SYSCALL_VERIFY_MSG(_is_valid_prio(prio, NULL),
1288 : : "invalid thread priority %d", prio));
1289 : : Z_OOPS(Z_SYSCALL_VERIFY_MSG((int8_t)prio >= thread->base.prio,
1290 : : "thread priority may only be downgraded (%d < %d)",
1291 : : prio, thread->base.prio));
1292 : :
1293 : : z_impl_k_thread_priority_set(thread, prio);
1294 : : }
1295 : : #include <syscalls/k_thread_priority_set_mrsh.c>
1296 : : #endif
1297 : :
1298 : : #ifdef CONFIG_SCHED_DEADLINE
1299 : : void z_impl_k_thread_deadline_set(k_tid_t tid, int deadline)
1300 : : {
1301 : : struct k_thread *thread = tid;
1302 : :
1303 : : LOCKED(&sched_spinlock) {
1304 : : thread->base.prio_deadline = k_cycle_get_32() + deadline;
1305 : : if (z_is_thread_queued(thread)) {
1306 : : dequeue_thread(thread);
1307 : : queue_thread(thread);
1308 : : }
1309 : : }
1310 : : }
1311 : :
1312 : : #ifdef CONFIG_USERSPACE
1313 : : static inline void z_vrfy_k_thread_deadline_set(k_tid_t tid, int deadline)
1314 : : {
1315 : : struct k_thread *thread = tid;
1316 : :
1317 : : Z_OOPS(Z_SYSCALL_OBJ(thread, K_OBJ_THREAD));
1318 : : Z_OOPS(Z_SYSCALL_VERIFY_MSG(deadline > 0,
1319 : : "invalid thread deadline %d",
1320 : : (int)deadline));
1321 : :
1322 : : z_impl_k_thread_deadline_set((k_tid_t)thread, deadline);
1323 : : }
1324 : : #include <syscalls/k_thread_deadline_set_mrsh.c>
1325 : : #endif
1326 : : #endif
1327 : :
1328 : 0 : void z_impl_k_yield(void)
1329 : : {
1330 [ # # ]: 0 : __ASSERT(!arch_is_in_isr(), "");
1331 : :
1332 : : SYS_PORT_TRACING_FUNC(k_thread, yield);
1333 : :
1334 : 0 : k_spinlock_key_t key = k_spin_lock(&sched_spinlock);
1335 : :
1336 : : if (!IS_ENABLED(CONFIG_SMP) ||
1337 : : z_is_thread_queued(_current)) {
1338 : 0 : dequeue_thread(_current);
1339 : : }
1340 : 0 : queue_thread(_current);
1341 : 0 : update_cache(1);
1342 : 0 : z_swap(&sched_spinlock, key);
1343 : 0 : }
1344 : :
1345 : : #ifdef CONFIG_USERSPACE
1346 : : static inline void z_vrfy_k_yield(void)
1347 : : {
1348 : : z_impl_k_yield();
1349 : : }
1350 : : #include <syscalls/k_yield_mrsh.c>
1351 : : #endif
1352 : :
1353 : 0 : static int32_t z_tick_sleep(k_ticks_t ticks)
1354 : : {
1355 : : #ifdef CONFIG_MULTITHREADING
1356 : : uint32_t expected_wakeup_ticks;
1357 : :
1358 [ # # ]: 0 : __ASSERT(!arch_is_in_isr(), "");
1359 : :
1360 : : #ifndef CONFIG_TIMEOUT_64BIT
1361 : : /* LOG subsys does not handle 64-bit values
1362 : : * https://github.com/zephyrproject-rtos/zephyr/issues/26246
1363 : : */
1364 : : LOG_DBG("thread %p for %u ticks", _current, ticks);
1365 : : #endif
1366 : :
1367 : : /* wait of 0 ms is treated as a 'yield' */
1368 [ # # ]: 0 : if (ticks == 0) {
1369 : 0 : k_yield();
1370 : 0 : return 0;
1371 : : }
1372 : :
1373 : 0 : k_timeout_t timeout = Z_TIMEOUT_TICKS(ticks);
1374 [ # # ]: 0 : if (Z_TICK_ABS(ticks) <= 0) {
1375 : 0 : expected_wakeup_ticks = ticks + sys_clock_tick_get_32();
1376 : : } else {
1377 : 0 : expected_wakeup_ticks = Z_TICK_ABS(ticks);
1378 : : }
1379 : :
1380 : 0 : k_spinlock_key_t key = k_spin_lock(&sched_spinlock);
1381 : :
1382 : : #if defined(CONFIG_TIMESLICING) && defined(CONFIG_SWAP_NONATOMIC)
1383 : 0 : pending_current = _current;
1384 : : #endif
1385 : 0 : unready_thread(_current);
1386 : 0 : z_add_thread_timeout(_current, timeout);
1387 : 0 : z_mark_thread_as_suspended(_current);
1388 : :
1389 : 0 : (void)z_swap(&sched_spinlock, key);
1390 : :
1391 [ # # ]: 0 : __ASSERT(!z_is_thread_state_set(_current, _THREAD_SUSPENDED), "");
1392 : :
1393 : 0 : ticks = (k_ticks_t)expected_wakeup_ticks - sys_clock_tick_get_32();
1394 [ # # ]: 0 : if (ticks > 0) {
1395 : 0 : return ticks;
1396 : : }
1397 : : #endif
1398 : :
1399 : 0 : return 0;
1400 : : }
1401 : :
1402 : 0 : int32_t z_impl_k_sleep(k_timeout_t timeout)
1403 : : {
1404 : : k_ticks_t ticks;
1405 : :
1406 [ # # ]: 0 : __ASSERT(!arch_is_in_isr(), "");
1407 : :
1408 : : SYS_PORT_TRACING_FUNC_ENTER(k_thread, sleep, timeout);
1409 : :
1410 : : /* in case of K_FOREVER, we suspend */
1411 [ # # ]: 0 : if (K_TIMEOUT_EQ(timeout, K_FOREVER)) {
1412 : 0 : k_thread_suspend(_current);
1413 : :
1414 : : SYS_PORT_TRACING_FUNC_EXIT(k_thread, sleep, timeout, (int32_t) K_TICKS_FOREVER);
1415 : :
1416 : 0 : return (int32_t) K_TICKS_FOREVER;
1417 : : }
1418 : :
1419 : 0 : ticks = timeout.ticks;
1420 : :
1421 : 0 : ticks = z_tick_sleep(ticks);
1422 : :
1423 : 0 : int32_t ret = k_ticks_to_ms_floor64(ticks);
1424 : :
1425 : : SYS_PORT_TRACING_FUNC_EXIT(k_thread, sleep, timeout, ret);
1426 : :
1427 : 0 : return ret;
1428 : : }
1429 : :
1430 : : #ifdef CONFIG_USERSPACE
1431 : : static inline int32_t z_vrfy_k_sleep(k_timeout_t timeout)
1432 : : {
1433 : : return z_impl_k_sleep(timeout);
1434 : : }
1435 : : #include <syscalls/k_sleep_mrsh.c>
1436 : : #endif
1437 : :
1438 : 0 : int32_t z_impl_k_usleep(int us)
1439 : : {
1440 : : int32_t ticks;
1441 : :
1442 : : SYS_PORT_TRACING_FUNC_ENTER(k_thread, usleep, us);
1443 : :
1444 : 0 : ticks = k_us_to_ticks_ceil64(us);
1445 : 0 : ticks = z_tick_sleep(ticks);
1446 : :
1447 : : SYS_PORT_TRACING_FUNC_EXIT(k_thread, usleep, us, k_ticks_to_us_floor64(ticks));
1448 : :
1449 : 0 : return k_ticks_to_us_floor64(ticks);
1450 : : }
1451 : :
1452 : : #ifdef CONFIG_USERSPACE
1453 : : static inline int32_t z_vrfy_k_usleep(int us)
1454 : : {
1455 : : return z_impl_k_usleep(us);
1456 : : }
1457 : : #include <syscalls/k_usleep_mrsh.c>
1458 : : #endif
1459 : :
1460 : 0 : void z_impl_k_wakeup(k_tid_t thread)
1461 : : {
1462 : : SYS_PORT_TRACING_OBJ_FUNC(k_thread, wakeup, thread);
1463 : :
1464 [ # # ]: 0 : if (z_is_thread_pending(thread)) {
1465 : 0 : return;
1466 : : }
1467 : :
1468 [ # # ]: 0 : if (z_abort_thread_timeout(thread) < 0) {
1469 : : /* Might have just been sleeping forever */
1470 [ # # ]: 0 : if (thread->base.thread_state != _THREAD_SUSPENDED) {
1471 : 0 : return;
1472 : : }
1473 : : }
1474 : :
1475 : 0 : z_mark_thread_as_not_suspended(thread);
1476 : 0 : z_ready_thread(thread);
1477 : :
1478 : : #if defined(CONFIG_SMP) && defined(CONFIG_SCHED_IPI_SUPPORTED)
1479 : : arch_sched_ipi();
1480 : : #endif
1481 : :
1482 [ # # ]: 0 : if (!arch_is_in_isr()) {
1483 : 0 : z_reschedule_unlocked();
1484 : : }
1485 : : }
1486 : :
1487 : : #ifdef CONFIG_TRACE_SCHED_IPI
1488 : : extern void z_trace_sched_ipi(void);
1489 : : #endif
1490 : :
1491 : : #ifdef CONFIG_SMP
1492 : : void z_sched_ipi(void)
1493 : : {
1494 : : /* NOTE: When adding code to this, make sure this is called
1495 : : * at appropriate location when !CONFIG_SCHED_IPI_SUPPORTED.
1496 : : */
1497 : : #ifdef CONFIG_TRACE_SCHED_IPI
1498 : : z_trace_sched_ipi();
1499 : : #endif
1500 : : }
1501 : : #endif
1502 : :
1503 : : #ifdef CONFIG_USERSPACE
1504 : : static inline void z_vrfy_k_wakeup(k_tid_t thread)
1505 : : {
1506 : : Z_OOPS(Z_SYSCALL_OBJ(thread, K_OBJ_THREAD));
1507 : : z_impl_k_wakeup(thread);
1508 : : }
1509 : : #include <syscalls/k_wakeup_mrsh.c>
1510 : : #endif
1511 : :
1512 : 1 : k_tid_t z_impl_z_current_get(void)
1513 : : {
1514 : : #ifdef CONFIG_SMP
1515 : : /* In SMP, _current is a field read from _current_cpu, which
1516 : : * can race with preemption before it is read. We must lock
1517 : : * local interrupts when reading it.
1518 : : */
1519 : : unsigned int k = arch_irq_lock();
1520 : : #endif
1521 : :
1522 : 1 : k_tid_t ret = _current_cpu->current;
1523 : :
1524 : : #ifdef CONFIG_SMP
1525 : : arch_irq_unlock(k);
1526 : : #endif
1527 : 1 : return ret;
1528 : : }
1529 : :
1530 : : #ifdef CONFIG_USERSPACE
1531 : : static inline k_tid_t z_vrfy_z_current_get(void)
1532 : : {
1533 : : return z_impl_z_current_get();
1534 : : }
1535 : : #include <syscalls/z_current_get_mrsh.c>
1536 : : #endif
1537 : :
1538 : 0 : int z_impl_k_is_preempt_thread(void)
1539 : : {
1540 [ # # # # ]: 0 : return !arch_is_in_isr() && is_preempt(_current);
1541 : : }
1542 : :
1543 : : #ifdef CONFIG_USERSPACE
1544 : : static inline int z_vrfy_k_is_preempt_thread(void)
1545 : : {
1546 : : return z_impl_k_is_preempt_thread();
1547 : : }
1548 : : #include <syscalls/k_is_preempt_thread_mrsh.c>
1549 : : #endif
1550 : :
1551 : : #ifdef CONFIG_SCHED_CPU_MASK
1552 : : # ifdef CONFIG_SMP
1553 : : /* Right now we use a single byte for this mask */
1554 : : BUILD_ASSERT(CONFIG_MP_NUM_CPUS <= 8, "Too many CPUs for mask word");
1555 : : # endif
1556 : :
1557 : :
1558 : : static int cpu_mask_mod(k_tid_t thread, uint32_t enable_mask, uint32_t disable_mask)
1559 : : {
1560 : : int ret = 0;
1561 : :
1562 : : LOCKED(&sched_spinlock) {
1563 : : if (z_is_thread_prevented_from_running(thread)) {
1564 : : thread->base.cpu_mask |= enable_mask;
1565 : : thread->base.cpu_mask &= ~disable_mask;
1566 : : } else {
1567 : : ret = -EINVAL;
1568 : : }
1569 : : }
1570 : :
1571 : : #if defined(CONFIG_ASSERT) && defined(CONFIG_SCHED_CPU_MASK_PIN_ONLY)
1572 : : int m = thread->base.cpu_mask;
1573 : :
1574 : : __ASSERT((m == 0) || ((m & (m - 1)) == 0),
1575 : : "Only one CPU allowed in mask when PIN_ONLY");
1576 : : #endif
1577 : :
1578 : : return ret;
1579 : : }
1580 : :
1581 : : int k_thread_cpu_mask_clear(k_tid_t thread)
1582 : : {
1583 : : return cpu_mask_mod(thread, 0, 0xffffffff);
1584 : : }
1585 : :
1586 : : int k_thread_cpu_mask_enable_all(k_tid_t thread)
1587 : : {
1588 : : return cpu_mask_mod(thread, 0xffffffff, 0);
1589 : : }
1590 : :
1591 : : int k_thread_cpu_mask_enable(k_tid_t thread, int cpu)
1592 : : {
1593 : : return cpu_mask_mod(thread, BIT(cpu), 0);
1594 : : }
1595 : :
1596 : : int k_thread_cpu_mask_disable(k_tid_t thread, int cpu)
1597 : : {
1598 : : return cpu_mask_mod(thread, 0, BIT(cpu));
1599 : : }
1600 : :
1601 : : int k_thread_cpu_pin(k_tid_t thread, int cpu)
1602 : : {
1603 : : int ret;
1604 : :
1605 : : ret = k_thread_cpu_mask_clear(thread);
1606 : : if (ret == 0) {
1607 : : return k_thread_cpu_mask_enable(thread, cpu);
1608 : : }
1609 : : return ret;
1610 : : }
1611 : :
1612 : : #endif /* CONFIG_SCHED_CPU_MASK */
1613 : :
1614 : 1 : static inline void unpend_all(_wait_q_t *wait_q)
1615 : : {
1616 : : struct k_thread *thread;
1617 : :
1618 [ - + ]: 1 : while ((thread = z_waitq_head(wait_q)) != NULL) {
1619 : 0 : unpend_thread_no_timeout(thread);
1620 : 0 : (void)z_abort_thread_timeout(thread);
1621 : 0 : arch_thread_return_value_set(thread, 0);
1622 : 0 : ready_thread(thread);
1623 : : }
1624 : 1 : }
1625 : :
1626 : : #ifdef CONFIG_CMSIS_RTOS_V1
1627 : : extern void z_thread_cmsis_status_mask_clear(struct k_thread *thread);
1628 : : #endif
1629 : :
1630 : 1 : static void end_thread(struct k_thread *thread)
1631 : : {
1632 : : /* We hold the lock, and the thread is known not to be running
1633 : : * anywhere.
1634 : : */
1635 [ + - ]: 1 : if ((thread->base.thread_state & _THREAD_DEAD) == 0U) {
1636 : 1 : thread->base.thread_state |= _THREAD_DEAD;
1637 : 1 : thread->base.thread_state &= ~_THREAD_ABORTING;
1638 [ + - ]: 1 : if (z_is_thread_queued(thread)) {
1639 : 1 : dequeue_thread(thread);
1640 : : }
1641 [ - + ]: 1 : if (thread->base.pended_on != NULL) {
1642 : 0 : unpend_thread_no_timeout(thread);
1643 : : }
1644 : 1 : (void)z_abort_thread_timeout(thread);
1645 : 1 : unpend_all(&thread->join_queue);
1646 : 1 : update_cache(1);
1647 : :
1648 : : SYS_PORT_TRACING_FUNC(k_thread, sched_abort, thread);
1649 : :
1650 : : z_thread_monitor_exit(thread);
1651 : :
1652 : : #ifdef CONFIG_CMSIS_RTOS_V1
1653 : : z_thread_cmsis_status_mask_clear(thread);
1654 : : #endif
1655 : :
1656 : : #ifdef CONFIG_USERSPACE
1657 : : z_mem_domain_exit_thread(thread);
1658 : : z_thread_perms_all_clear(thread);
1659 : : z_object_uninit(thread->stack_obj);
1660 : : z_object_uninit(thread);
1661 : : #endif
1662 : : }
1663 : 1 : }
1664 : :
1665 : 2 : void z_thread_abort(struct k_thread *thread)
1666 : : {
1667 : 2 : k_spinlock_key_t key = k_spin_lock(&sched_spinlock);
1668 : :
1669 [ + + ]: 2 : if ((thread->base.thread_state & _THREAD_DEAD) != 0U) {
1670 : 1 : k_spin_unlock(&sched_spinlock, key);
1671 : 1 : return;
1672 : : }
1673 : :
1674 : : #ifdef CONFIG_SMP
1675 : : if (is_aborting(thread) && thread == _current && arch_is_in_isr()) {
1676 : : /* Another CPU is spinning for us, don't deadlock */
1677 : : end_thread(thread);
1678 : : }
1679 : :
1680 : : bool active = thread_active_elsewhere(thread);
1681 : :
1682 : : if (active) {
1683 : : /* It's running somewhere else, flag and poke */
1684 : : thread->base.thread_state |= _THREAD_ABORTING;
1685 : :
1686 : : #ifdef CONFIG_SCHED_IPI_SUPPORTED
1687 : : arch_sched_ipi();
1688 : : #endif
1689 : : }
1690 : :
1691 : : if (is_aborting(thread) && thread != _current) {
1692 : : if (arch_is_in_isr()) {
1693 : : /* ISRs can only spin waiting another CPU */
1694 : : k_spin_unlock(&sched_spinlock, key);
1695 : : while (is_aborting(thread)) {
1696 : : }
1697 : : } else if (active) {
1698 : : /* Threads can join */
1699 : : add_to_waitq_locked(_current, &thread->join_queue);
1700 : : z_swap(&sched_spinlock, key);
1701 : : }
1702 : : return; /* lock has been released */
1703 : : }
1704 : : #endif
1705 : 1 : end_thread(thread);
1706 [ + - + - ]: 1 : if (thread == _current && !arch_is_in_isr()) {
1707 : 1 : z_swap(&sched_spinlock, key);
1708 : 0 : __ASSERT(false, "aborted _current back from dead");
1709 : : }
1710 : 0 : k_spin_unlock(&sched_spinlock, key);
1711 : : }
1712 : :
1713 : : #if !defined(CONFIG_ARCH_HAS_THREAD_ABORT)
1714 : : void z_impl_k_thread_abort(struct k_thread *thread)
1715 : : {
1716 : : SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_thread, abort, thread);
1717 : :
1718 : : z_thread_abort(thread);
1719 : :
1720 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_thread, abort, thread);
1721 : : }
1722 : : #endif
1723 : :
1724 : 1 : int z_impl_k_thread_join(struct k_thread *thread, k_timeout_t timeout)
1725 : : {
1726 : 1 : k_spinlock_key_t key = k_spin_lock(&sched_spinlock);
1727 : 1 : int ret = 0;
1728 : :
1729 : : SYS_PORT_TRACING_OBJ_FUNC_ENTER(k_thread, join, thread, timeout);
1730 : :
1731 [ + - ]: 1 : if ((thread->base.thread_state & _THREAD_DEAD) != 0U) {
1732 : 1 : ret = 0;
1733 [ # # ]: 0 : } else if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
1734 : 0 : ret = -EBUSY;
1735 [ # # ]: 0 : } else if ((thread == _current) ||
1736 [ # # ]: 0 : (thread->base.pended_on == &_current->join_queue)) {
1737 : 0 : ret = -EDEADLK;
1738 : : } else {
1739 [ # # ]: 0 : __ASSERT(!arch_is_in_isr(), "cannot join in ISR");
1740 : 0 : add_to_waitq_locked(_current, &thread->join_queue);
1741 : 0 : add_thread_timeout(_current, timeout);
1742 : :
1743 : : SYS_PORT_TRACING_OBJ_FUNC_BLOCKING(k_thread, join, thread, timeout);
1744 : 0 : ret = z_swap(&sched_spinlock, key);
1745 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_thread, join, thread, timeout, ret);
1746 : :
1747 : 0 : return ret;
1748 : : }
1749 : :
1750 : : SYS_PORT_TRACING_OBJ_FUNC_EXIT(k_thread, join, thread, timeout, ret);
1751 : :
1752 : 1 : k_spin_unlock(&sched_spinlock, key);
1753 : 1 : return ret;
1754 : : }
1755 : :
1756 : : #ifdef CONFIG_USERSPACE
1757 : : /* Special case: don't oops if the thread is uninitialized. This is because
1758 : : * the initialization bit does double-duty for thread objects; if false, means
1759 : : * the thread object is truly uninitialized, or the thread ran and exited for
1760 : : * some reason.
1761 : : *
1762 : : * Return true in this case indicating we should just do nothing and return
1763 : : * success to the caller.
1764 : : */
1765 : : static bool thread_obj_validate(struct k_thread *thread)
1766 : : {
1767 : : struct z_object *ko = z_object_find(thread);
1768 : : int ret = z_object_validate(ko, K_OBJ_THREAD, _OBJ_INIT_TRUE);
1769 : :
1770 : : switch (ret) {
1771 : : case 0:
1772 : : return false;
1773 : : case -EINVAL:
1774 : : return true;
1775 : : default:
1776 : : #ifdef CONFIG_LOG
1777 : : z_dump_object_error(ret, thread, ko, K_OBJ_THREAD);
1778 : : #endif
1779 : : Z_OOPS(Z_SYSCALL_VERIFY_MSG(ret, "access denied"));
1780 : : }
1781 : : CODE_UNREACHABLE; /* LCOV_EXCL_LINE */
1782 : : }
1783 : :
1784 : : static inline int z_vrfy_k_thread_join(struct k_thread *thread,
1785 : : k_timeout_t timeout)
1786 : : {
1787 : : if (thread_obj_validate(thread)) {
1788 : : return 0;
1789 : : }
1790 : :
1791 : : return z_impl_k_thread_join(thread, timeout);
1792 : : }
1793 : : #include <syscalls/k_thread_join_mrsh.c>
1794 : :
1795 : : static inline void z_vrfy_k_thread_abort(k_tid_t thread)
1796 : : {
1797 : : if (thread_obj_validate(thread)) {
1798 : : return;
1799 : : }
1800 : :
1801 : : Z_OOPS(Z_SYSCALL_VERIFY_MSG(!(thread->base.user_options & K_ESSENTIAL),
1802 : : "aborting essential thread %p", thread));
1803 : :
1804 : : z_impl_k_thread_abort((struct k_thread *)thread);
1805 : : }
1806 : : #include <syscalls/k_thread_abort_mrsh.c>
1807 : : #endif /* CONFIG_USERSPACE */
1808 : :
1809 : : /*
1810 : : * future scheduler.h API implementations
1811 : : */
1812 : 0 : bool z_sched_wake(_wait_q_t *wait_q, int swap_retval, void *swap_data)
1813 : : {
1814 : : struct k_thread *thread;
1815 : 0 : bool ret = false;
1816 : :
1817 [ # # ]: 0 : LOCKED(&sched_spinlock) {
1818 : 0 : thread = _priq_wait_best(&wait_q->waitq);
1819 : :
1820 [ # # ]: 0 : if (thread != NULL) {
1821 : 0 : z_thread_return_value_set_with_data(thread,
1822 : : swap_retval,
1823 : : swap_data);
1824 : 0 : unpend_thread_no_timeout(thread);
1825 : 0 : (void)z_abort_thread_timeout(thread);
1826 : 0 : ready_thread(thread);
1827 : 0 : ret = true;
1828 : : }
1829 : : }
1830 : :
1831 : 0 : return ret;
1832 : : }
1833 : :
1834 : 0 : int z_sched_wait(struct k_spinlock *lock, k_spinlock_key_t key,
1835 : : _wait_q_t *wait_q, k_timeout_t timeout, void **data)
1836 : : {
1837 : 0 : int ret = z_pend_curr(lock, key, wait_q, timeout);
1838 : :
1839 [ # # ]: 0 : if (data != NULL) {
1840 : 0 : *data = _current->base.swap_data;
1841 : : }
1842 : 0 : return ret;
1843 : : }
|