Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2018, 2024 Intel Corporation
3 : : *
4 : : * SPDX-License-Identifier: Apache-2.0
5 : : */
6 : : #include <zephyr/kernel.h>
7 : : #include <kswap.h>
8 : : #include <ksched.h>
9 : : #include <ipi.h>
10 : :
11 : : static int slice_ticks = DIV_ROUND_UP(CONFIG_TIMESLICE_SIZE * Z_HZ_ticks, Z_HZ_ms);
12 : : static int slice_max_prio = CONFIG_TIMESLICE_PRIORITY;
13 : : static struct _timeout slice_timeouts[CONFIG_MP_MAX_NUM_CPUS];
14 : : static bool slice_expired[CONFIG_MP_MAX_NUM_CPUS];
15 : :
16 : : #ifdef CONFIG_SWAP_NONATOMIC
17 : : /* If z_swap() isn't atomic, then it's possible for a timer interrupt
18 : : * to try to timeslice away _current after it has already pended
19 : : * itself but before the corresponding context switch. Treat that as
20 : : * a noop condition in z_time_slice().
21 : : */
22 : : struct k_thread *pending_current;
23 : : #endif
24 : :
25 : 105 : static inline int slice_time(struct k_thread *thread)
26 : : {
27 : 105 : int ret = slice_ticks;
28 : :
29 : : #ifdef CONFIG_TIMESLICE_PER_THREAD
30 : : if (thread->base.slice_ticks != 0) {
31 : : ret = thread->base.slice_ticks;
32 : : }
33 : : #else
34 : 105 : ARG_UNUSED(thread);
35 : : #endif
36 : 105 : return ret;
37 : : }
38 : :
39 : 105 : static int z_time_slice_size(struct k_thread *thread)
40 : : {
41 [ + - ]: 105 : if (z_is_thread_prevented_from_running(thread) ||
42 [ + - ]: 105 : z_is_idle_thread_object(thread) ||
43 [ + - ]: 105 : (slice_time(thread) == 0)) {
44 : : return 0;
45 : : }
46 : :
47 : : #ifdef CONFIG_TIMESLICE_PER_THREAD
48 : : if (thread->base.slice_ticks != 0) {
49 : : return thread->base.slice_ticks;
50 : : }
51 : : #endif
52 : :
53 [ + + ]: 105 : if (thread_is_preemptible(thread) &&
54 [ + - ]: 55 : !z_is_prio_higher(thread->base.prio, slice_max_prio)) {
55 : 55 : return slice_ticks;
56 : : }
57 : :
58 : : return 0;
59 : : }
60 : :
61 : 0 : static void slice_timeout(struct _timeout *timeout)
62 : : {
63 [ # # # # : 0 : int cpu = ARRAY_INDEX(slice_timeouts, timeout);
# # # # ]
64 : :
65 : 0 : slice_expired[cpu] = true;
66 : :
67 : : /* We need an IPI if we just handled a timeslice expiration
68 : : * for a different CPU.
69 : : */
70 : 0 : if (cpu != _current_cpu->id) {
71 : 0 : flag_ipi(IPI_CPU_MASK(cpu));
72 : : }
73 : 0 : }
74 : :
75 : 105 : void z_reset_time_slice(struct k_thread *thread)
76 : : {
77 : 105 : int cpu = _current_cpu->id;
78 : 105 : int slice_size = z_time_slice_size(thread);
79 : :
80 : 105 : z_abort_timeout(&slice_timeouts[cpu]);
81 : 105 : slice_expired[cpu] = false;
82 [ + + ]: 105 : if (slice_size != 0) {
83 : 55 : z_add_timeout(&slice_timeouts[cpu], slice_timeout,
84 : 55 : K_TICKS(slice_size - 1));
85 : : }
86 : 105 : }
87 : :
88 : 0 : static ALWAYS_INLINE bool thread_defines_time_slice_size(struct k_thread *thread)
89 : : {
90 : : #ifdef CONFIG_TIMESLICE_PER_THREAD
91 : : return (thread->base.slice_ticks != 0);
92 : : #else /* !CONFIG_TIMESLICE_PER_THREAD */
93 : 0 : return false;
94 : : #endif /* !CONFIG_TIMESLICE_PER_THREAD */
95 : : }
96 : :
97 : 0 : void k_sched_time_slice_set(int32_t slice, int prio)
98 : : {
99 : 0 : k_spinlock_key_t key = k_spin_lock(&_sched_spinlock);
100 : :
101 [ # # ]: 0 : slice_ticks = k_ms_to_ticks_ceil32(slice);
102 : 0 : slice_max_prio = prio;
103 : :
104 : : /*
105 : : * Threads that define their own time slice size should not have
106 : : * their time slices reset here as a thread-specific time slice size
107 : : * take precedence over the global time slice size.
108 : : */
109 : :
110 [ # # ]: 0 : if (!thread_defines_time_slice_size(_current)) {
111 : 0 : z_reset_time_slice(_current);
112 : : }
113 : :
114 : 0 : k_spin_unlock(&_sched_spinlock, key);
115 : 0 : }
116 : :
117 : : #ifdef CONFIG_TIMESLICE_PER_THREAD
118 : : void k_thread_time_slice_set(struct k_thread *thread, int32_t thread_slice_ticks,
119 : : k_thread_timeslice_fn_t expired, void *data)
120 : : {
121 : : K_SPINLOCK(&_sched_spinlock) {
122 : : thread->base.slice_ticks = thread_slice_ticks;
123 : : thread->base.slice_expired = expired;
124 : : thread->base.slice_data = data;
125 : : z_reset_time_slice(thread);
126 : : }
127 : : }
128 : : #endif
129 : :
130 : : /* Called out of each timer and IPI interrupt */
131 : 0 : void z_time_slice(void)
132 : : {
133 : 0 : k_spinlock_key_t key = k_spin_lock(&_sched_spinlock);
134 : 0 : struct k_thread *curr = _current;
135 : :
136 : : #ifdef CONFIG_SWAP_NONATOMIC
137 : : if (pending_current == curr) {
138 : : z_reset_time_slice(curr);
139 : : k_spin_unlock(&_sched_spinlock, key);
140 : : return;
141 : : }
142 : : pending_current = NULL;
143 : : #endif
144 : :
145 [ # # # # ]: 0 : if (slice_expired[_current_cpu->id] && (z_time_slice_size(curr) != 0)) {
146 : : #ifdef CONFIG_TIMESLICE_PER_THREAD
147 : : k_thread_timeslice_fn_t handler = curr->base.slice_expired;
148 : :
149 : : if (handler != NULL) {
150 : : k_spin_unlock(&_sched_spinlock, key);
151 : : handler(curr, curr->base.slice_data);
152 : : key = k_spin_lock(&_sched_spinlock);
153 : : }
154 : : #endif
155 [ # # ]: 0 : if (!z_is_thread_prevented_from_running(curr)) {
156 : 0 : move_current_to_end_of_prio_q();
157 : : }
158 : 0 : z_reset_time_slice(curr);
159 : : }
160 : 0 : k_spin_unlock(&_sched_spinlock, key);
161 : 0 : }
|