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 
slice_time(struct k_thread * thread)25 static inline int slice_time(struct k_thread *thread)
26 {
27 	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 	ARG_UNUSED(thread);
35 #endif
36 	return ret;
37 }
38 
thread_is_sliceable(struct k_thread * thread)39 bool thread_is_sliceable(struct k_thread *thread)
40 {
41 	bool ret = thread_is_preemptible(thread)
42 		&& slice_time(thread) != 0
43 		&& !z_is_prio_higher(thread->base.prio, slice_max_prio)
44 		&& !z_is_thread_prevented_from_running(thread)
45 		&& !z_is_idle_thread_object(thread);
46 
47 #ifdef CONFIG_TIMESLICE_PER_THREAD
48 	ret |= thread->base.slice_ticks != 0;
49 #endif
50 
51 	return ret;
52 }
53 
slice_timeout(struct _timeout * timeout)54 static void slice_timeout(struct _timeout *timeout)
55 {
56 	int cpu = ARRAY_INDEX(slice_timeouts, timeout);
57 
58 	slice_expired[cpu] = true;
59 
60 	/* We need an IPI if we just handled a timeslice expiration
61 	 * for a different CPU.
62 	 */
63 	if (cpu != _current_cpu->id) {
64 		flag_ipi(IPI_CPU_MASK(cpu));
65 	}
66 }
67 
z_reset_time_slice(struct k_thread * thread)68 void z_reset_time_slice(struct k_thread *thread)
69 {
70 	int cpu = _current_cpu->id;
71 
72 	z_abort_timeout(&slice_timeouts[cpu]);
73 	slice_expired[cpu] = false;
74 	if (thread_is_sliceable(thread)) {
75 		z_add_timeout(&slice_timeouts[cpu], slice_timeout,
76 			      K_TICKS(slice_time(thread) - 1));
77 	}
78 }
79 
k_sched_time_slice_set(int32_t slice,int prio)80 void k_sched_time_slice_set(int32_t slice, int prio)
81 {
82 	K_SPINLOCK(&_sched_spinlock) {
83 		slice_ticks = k_ms_to_ticks_ceil32(slice);
84 		slice_max_prio = prio;
85 		z_reset_time_slice(_current);
86 	}
87 }
88 
89 #ifdef CONFIG_TIMESLICE_PER_THREAD
k_thread_time_slice_set(struct k_thread * thread,int32_t thread_slice_ticks,k_thread_timeslice_fn_t expired,void * data)90 void k_thread_time_slice_set(struct k_thread *thread, int32_t thread_slice_ticks,
91 			     k_thread_timeslice_fn_t expired, void *data)
92 {
93 	K_SPINLOCK(&_sched_spinlock) {
94 		thread->base.slice_ticks = thread_slice_ticks;
95 		thread->base.slice_expired = expired;
96 		thread->base.slice_data = data;
97 		z_reset_time_slice(thread);
98 	}
99 }
100 #endif
101 
102 /* Called out of each timer interrupt */
z_time_slice(void)103 void z_time_slice(void)
104 {
105 	k_spinlock_key_t key = k_spin_lock(&_sched_spinlock);
106 	struct k_thread *curr = _current;
107 
108 #ifdef CONFIG_SWAP_NONATOMIC
109 	if (pending_current == curr) {
110 		z_reset_time_slice(curr);
111 		k_spin_unlock(&_sched_spinlock, key);
112 		return;
113 	}
114 	pending_current = NULL;
115 #endif
116 
117 	if (slice_expired[_current_cpu->id] && thread_is_sliceable(curr)) {
118 #ifdef CONFIG_TIMESLICE_PER_THREAD
119 		if (curr->base.slice_expired) {
120 			k_spin_unlock(&_sched_spinlock, key);
121 			curr->base.slice_expired(curr, curr->base.slice_data);
122 			key = k_spin_lock(&_sched_spinlock);
123 		}
124 #endif
125 		if (!z_is_thread_prevented_from_running(curr)) {
126 			move_thread_to_end_of_prio_q(curr);
127 		}
128 		z_reset_time_slice(curr);
129 	}
130 	k_spin_unlock(&_sched_spinlock, key);
131 }
132