@@ -30,16 +30,30 @@ void z_smp_release_global_lock(struct k_thread *thread);
30
30
/* context switching and scheduling-related routines */
31
31
#ifdef CONFIG_USE_SWITCH
32
32
33
- /* There is an unavoidable SMP race when threads swap -- their thread
34
- * record is in the queue (and visible to other CPUs) before
35
- * arch_switch() finishes saving state. We must spin for the switch
36
- * handle before entering a new thread. See docs on arch_switch().
33
+ /* Spin, with the scheduler lock held (!), on a thread that is known
34
+ * (!!) to have released the lock and be on a path where it will
35
+ * deterministically (!!!) reach arch_switch() in very small constant
36
+ * time.
37
+ *
38
+ * This exists to treat an unavoidable SMP race when threads swap --
39
+ * their thread record is in the queue (and visible to other CPUs)
40
+ * before arch_switch() finishes saving state. We must spin for the
41
+ * switch handle before entering a new thread. See docs on
42
+ * arch_switch().
43
+ *
44
+ * Stated differently: there's a chicken and egg bug with the question
45
+ * of "is a thread running or not?". The thread needs to mark itself
46
+ * "not running" from its own context, but at that moment it obviously
47
+ * is still running until it reaches arch_switch()! Locking can't
48
+ * treat this because the scheduler lock can't be released by the
49
+ * switched-to thread, which is going to (obviously) be running its
50
+ * own code and doesn't know it was switched out.
37
51
*
38
52
* Note: future SMP architectures may need a fence/barrier or cache
39
53
* invalidation here. Current ones don't, and sadly Zephyr doesn't
40
54
* have a framework for that yet.
41
55
*/
42
- static inline void wait_for_switch (struct k_thread * thread )
56
+ static inline void z_sched_switch_spin (struct k_thread * thread )
43
57
{
44
58
#ifdef CONFIG_SMP
45
59
volatile void * * shp = (void * )& thread -> switch_handle ;
@@ -117,7 +131,7 @@ static ALWAYS_INLINE unsigned int do_swap(unsigned int key,
117
131
}
118
132
#endif
119
133
z_thread_mark_switched_out ();
120
- wait_for_switch (new_thread );
134
+ z_sched_switch_spin (new_thread );
121
135
_current_cpu -> current = new_thread ;
122
136
123
137
#ifdef CONFIG_TIMESLICING
@@ -131,10 +145,9 @@ static ALWAYS_INLINE unsigned int do_swap(unsigned int key,
131
145
arch_cohere_stacks (old_thread , NULL , new_thread );
132
146
133
147
#ifdef CONFIG_SMP
134
- /* Add _current back to the run queue HERE. After
135
- * wait_for_switch() we are guaranteed to reach the
136
- * context switch in finite time, avoiding a potential
137
- * deadlock.
148
+ /* Now add _current back to the run queue, once we are
149
+ * guaranteed to reach the context switch in finite
150
+ * time. See z_sched_switch_spin().
138
151
*/
139
152
z_requeue_current (old_thread );
140
153
#endif
@@ -178,6 +191,11 @@ static inline void z_swap_unlocked(void)
178
191
179
192
extern int arch_swap (unsigned int key );
180
193
194
+ static inline void z_sched_switch_spin (struct k_thread * thread )
195
+ {
196
+ ARG_UNUSED (thread );
197
+ }
198
+
181
199
static inline int z_swap_irqlock (unsigned int key )
182
200
{
183
201
int ret ;
0 commit comments