source: rtems/cpukit/include/rtems/score/userextimpl.h @ fcb11510

5
Last change on this file since fcb11510 was fcb11510, checked in by Sebastian Huber <sebastian.huber@…>, on 02/26/20 at 09:02:37

score: Fix context switch extensions (SMP)

In uniprocessor and SMP configurations, the context switch extensions
were called during _Thread_Do_dispatch():

void _Thread_Do_dispatch( Per_CPU_Control *cpu_self, ISR_Level level )
{

Thread_Control *executing;

executing = cpu_self->executing;

...
do {

Thread_Control *heir;

heir = _Thread_Get_heir_and_make_it_executing( cpu_self );
...
_User_extensions_Thread_switch( executing, heir );
...
_Context_Switch( &executing->Registers, &heir->Registers );
...

} while ( cpu_self->dispatch_necessary );
...

}

In uniprocessor configurations, this is fine and the context switch
extensions are called for all thread switches except the very first
thread switch to the initialization thread. However, in SMP
configurations, the context switch may be invalidated and updated in the
low-level _Context_Switch() routine. See:

https://docs.rtems.org/branches/master/c-user/symmetric_multiprocessing_services.html#thread-dispatch-details

In case such an update happens, a thread will execute on the processor
which was not seen in the previous call of the context switch
extensions. This can confuse for example event record consumers which
use events generated by a context switch extension.

Fixing this is not straight forward. The context switch extensions call
must move after the low-level context switch. The problem here is that
we may end up in _Thread_Handler(). Adding the context switch
extensions call to _Thread_Handler() covers now also the thread switch
to the initialization thread. We also have to save the last executing
thread (ancestor) of the processor. Registers or the stack cannot be
used for this purpose. We have to add it to the per-processor
information. Existing extensions may be affected, since now context
switch extensions use the stack of the heir thread. The stack checker
is affected by this.

Calling the thread switch extensions in the low-level context switch is
difficult since at this point an intermediate stack is used which is
only large enough to enable servicing of interrupts.

Update #3885.

  • Property mode set to 100644
File size: 12.2 KB
Line 
1/**
2 * @file
3 *
4 * @ingroup RTEMSScoreUserExt
5 *
6 * @brief User Extension Handler API
7 */
8
9/*
10 *  COPYRIGHT (c) 1989-2009.
11 *  On-Line Applications Research Corporation (OAR).
12 *
13 *  The license and distribution terms for this file may be
14 *  found in the file LICENSE in this distribution or at
15 *  http://www.rtems.org/license/LICENSE.
16 */
17
18#ifndef _RTEMS_SCORE_USEREXTIMPL_H
19#define _RTEMS_SCORE_USEREXTIMPL_H
20
21#include <rtems/score/userextdata.h>
22#include <rtems/score/chainimpl.h>
23#include <rtems/score/isrlock.h>
24#include <rtems/score/thread.h>
25#include <rtems/score/percpu.h>
26
27#ifdef __cplusplus
28extern "C" {
29#endif
30
31/**
32 * @addtogroup RTEMSScoreUserExt
33 *
34 * @{
35 */
36
37/**
38 * @brief Chain iterator for dynamic user extensions.
39 *
40 * Since user extensions may delete or restart the executing thread, we must
41 * clean up registered iterators.
42 *
43 * @see _User_extensions_Iterate(), _User_extensions_Destroy_iterators() and
44 *   Thread_Control::last_user_extensions_iterator.
45 */
46typedef struct User_extensions_Iterator {
47  Chain_Iterator                   Iterator;
48  struct User_extensions_Iterator *previous;
49} User_extensions_Iterator;
50
51typedef struct {
52  /**
53   * @brief Active dynamically added user extensions.
54   */
55  Chain_Control Active;
56
57  /**
58   * @brief Chain iterator registration.
59   */
60  Chain_Iterator_registry Iterators;
61
62  /**
63   * @brief Lock to protect User_extensions_List::Active and
64   * User_extensions_List::Iterators.
65   */
66  ISR_LOCK_MEMBER( Lock )
67} User_extensions_List;
68
69/**
70 * @brief List of active extensions.
71 */
72extern User_extensions_List _User_extensions_List;
73
74/**
75 * @brief List of active task switch extensions.
76 */
77extern Chain_Control _User_extensions_Switches_list;
78
79/**
80 * @name Extension Maintainance
81 *
82 * @{
83 */
84
85/**
86 * @brief Initializes the user extensions handler.
87 */
88void _User_extensions_Handler_initialization( void );
89
90/**
91 * @brief Adds a user extension.
92 *
93 * @param extension The user extension to add.
94 */
95void _User_extensions_Add_set(
96  User_extensions_Control *extension
97);
98
99/**
100 * @brief Adds a user extension.
101 *
102 * @param extension The user extension to add.
103 */
104RTEMS_INLINE_ROUTINE void _User_extensions_Add_API_set(
105  User_extensions_Control *extension
106)
107{
108  _User_extensions_Add_set( extension );
109}
110
111/**
112 * @brief Adds a user extension with the given extension table as callouts.
113 *
114 * @param[in, out] extension The user extension to add.
115 * @param extension_table Is set as callouts for @a extension.
116 */
117RTEMS_INLINE_ROUTINE void _User_extensions_Add_set_with_table(
118  User_extensions_Control     *extension,
119  const User_extensions_Table *extension_table
120)
121{
122  extension->Callouts = *extension_table;
123
124  _User_extensions_Add_set( extension );
125}
126
127/**
128 * @brief Removes a user extension.
129 *
130 * @param extension The user extension to remove.
131 */
132void _User_extensions_Remove_set(
133  User_extensions_Control *extension
134);
135
136/**
137 * @brief User extension visitor.
138 *
139 * @param[in, out] executing The currently executing thread.
140 * @param[in, out] arg The argument passed to _User_extensions_Iterate().
141 * @param[in] callouts The current callouts.
142 */
143typedef void (*User_extensions_Visitor)(
144  Thread_Control              *executing,
145  void                        *arg,
146  const User_extensions_Table *callouts
147);
148
149typedef struct {
150  Thread_Control *created;
151  bool            ok;
152} User_extensions_Thread_create_context;
153
154/**
155 * @brief Creates a visitor.
156 *
157 * @param executing The currently executing thread.
158 * @param[in, out] arg Is used as the thread create context for the operation.
159 * @param callouts The user extension table for the operation.
160 */
161void _User_extensions_Thread_create_visitor(
162  Thread_Control              *executing,
163  void                        *arg,
164  const User_extensions_Table *callouts
165);
166
167/**
168 * @brief Deletes a visitor.
169 *
170 * @param executing The currently executing thread.
171 * @param[in, out] arg Parameter for the callout.
172 * @param callouts The user extension table for the operation.
173 */
174void _User_extensions_Thread_delete_visitor(
175  Thread_Control              *executing,
176  void                        *arg,
177  const User_extensions_Table *callouts
178);
179
180/**
181 * @brief Starts a visitor.
182 *
183 * @param executing The currently executing thread.
184 * @param arg Parameter for the callout.
185 * @param callouts The user extension table for the operation.
186 */
187void _User_extensions_Thread_start_visitor(
188  Thread_Control              *executing,
189  void                        *arg,
190  const User_extensions_Table *callouts
191);
192
193/**
194 * @brief Restarts a visitor.
195 *
196 * @param executing The currently executing thread.
197 * @param arg Parameter for the callout.
198 * @param callouts The user extension table for the operation.
199 */
200void _User_extensions_Thread_restart_visitor(
201  Thread_Control              *executing,
202  void                        *arg,
203  const User_extensions_Table *callouts
204);
205
206/**
207 * @brief Calls the begin function of the thread callout for the visitor.
208 *
209 * @param executing The currently executing thread.
210 * @param arg This parameter is unused.
211 * @param callouts The user extension table for the operation.
212 */
213void _User_extensions_Thread_begin_visitor(
214  Thread_Control              *executing,
215  void                        *arg,
216  const User_extensions_Table *callouts
217);
218
219/**
220 * @brief Calls the exitted function of the thread callout for the visitor.
221 *
222 * @param executing The currently executing thread.
223 * @param arg This parameter is unused.
224 * @param callouts The user extension table for the operation.
225 */
226void _User_extensions_Thread_exitted_visitor(
227  Thread_Control              *executing,
228  void                        *arg,
229  const User_extensions_Table *callouts
230);
231
232typedef struct {
233  Internal_errors_Source source;
234  Internal_errors_t      error;
235} User_extensions_Fatal_context;
236
237/**
238 * @brief Calls the fatal function of the thread callout for the visitor.
239 *
240 * @param executing The currently executing thread.
241 * @param arg Is used as the user extension fatal context.
242 * @param callouts The user extension table for the operation.
243 */
244void _User_extensions_Fatal_visitor(
245  Thread_Control              *executing,
246  void                        *arg,
247  const User_extensions_Table *callouts
248);
249
250/**
251 * @brief Terminates a visitor.
252 *
253 * @param executing The currently executing thread.
254 * @param arg This parameter is unused.
255 * @param callouts The user extension table for the operation.
256 */
257void _User_extensions_Thread_terminate_visitor(
258  Thread_Control              *executing,
259  void                        *arg,
260  const User_extensions_Table *callouts
261);
262
263/**
264 * @brief Iterates through all user extensions and calls the visitor for each.
265 *
266 * @param[in, out] arg The argument passed to the visitor.
267 * @param visitor The visitor for each extension.
268 * @param direction The iteration direction for dynamic extensions.
269 */
270void _User_extensions_Iterate(
271  void                     *arg,
272  User_extensions_Visitor   visitor,
273  Chain_Iterator_direction  direction
274);
275
276/** @} */
277
278/**
279 * @name Extension Callout Dispatcher
280 */
281/** @{ **/
282
283/**
284 * @brief Creates a thread.
285 *
286 * @param[out] created The thread to create.
287 *
288 * @retval true The operation succeeded.
289 * @retval false The operation failed.
290 */
291static inline bool _User_extensions_Thread_create( Thread_Control *created )
292{
293  User_extensions_Thread_create_context ctx = { created, true };
294
295  _User_extensions_Iterate(
296    &ctx,
297    _User_extensions_Thread_create_visitor,
298    CHAIN_ITERATOR_FORWARD
299  );
300
301  return ctx.ok;
302}
303
304/**
305 * @brief Deletes a thread.
306 *
307 * @param[out] created The thread to delete.
308 */
309static inline void _User_extensions_Thread_delete( Thread_Control *deleted )
310{
311  _User_extensions_Iterate(
312    deleted,
313    _User_extensions_Thread_delete_visitor,
314    CHAIN_ITERATOR_BACKWARD
315  );
316}
317
318/**
319 * @brief Starts a thread.
320 *
321 * @param created The thread to start.
322 */
323static inline void _User_extensions_Thread_start( Thread_Control *started )
324{
325  _User_extensions_Iterate(
326    started,
327    _User_extensions_Thread_start_visitor,
328    CHAIN_ITERATOR_FORWARD
329  );
330}
331
332/**
333 * @brief Restarts a thread.
334 *
335 * @param created The thread to restart.
336 */
337static inline void _User_extensions_Thread_restart( Thread_Control *restarted )
338{
339  _User_extensions_Iterate(
340    restarted,
341    _User_extensions_Thread_restart_visitor,
342    CHAIN_ITERATOR_FORWARD
343  );
344}
345
346/**
347 * @brief Begins a thread.
348 *
349 * @param created The thread to begin.
350 */
351static inline void _User_extensions_Thread_begin( Thread_Control *executing )
352{
353  _User_extensions_Iterate(
354    executing,
355    _User_extensions_Thread_begin_visitor,
356    CHAIN_ITERATOR_FORWARD
357  );
358}
359
360/**
361 * @brief Switches the thread from the executing to the heir.
362 *
363 * @param executing The currently executing thread.
364 * @param heir The thread that will switch with @a executing.
365 */
366static inline void _User_extensions_Thread_switch(
367  Thread_Control *executing,
368  Thread_Control *heir
369)
370{
371  const Chain_Control *chain;
372  const Chain_Node    *tail;
373  const Chain_Node    *node;
374
375  chain = &_User_extensions_Switches_list;
376  tail = _Chain_Immutable_tail( chain );
377  node = _Chain_Immutable_first( chain );
378
379  if ( node != tail ) {
380#if defined(RTEMS_SMP)
381    ISR_lock_Context  lock_context;
382    Per_CPU_Control  *cpu_self;
383
384    cpu_self = _Per_CPU_Get();
385
386    _ISR_lock_ISR_disable( &lock_context );
387    _Per_CPU_Acquire( cpu_self, &lock_context );
388
389    executing = cpu_self->ancestor;
390    cpu_self->ancestor = heir;
391    node = _Chain_Immutable_first( chain );
392
393    /*
394     * An executing thread equal to the heir thread may happen in two
395     * situations.  Firstly, in case context switch extensions are created after
396     * system initialization.  Secondly, during a thread self restart.
397     */
398    if ( executing != heir ) {
399#endif
400
401    while ( node != tail ) {
402      const User_extensions_Switch_control *extension;
403
404      extension = (const User_extensions_Switch_control *) node;
405      node = _Chain_Immutable_next( node );
406      (*extension->thread_switch)( executing, heir );
407    }
408
409#if defined(RTEMS_SMP)
410    }
411
412    _Per_CPU_Release( cpu_self, &lock_context );
413    _ISR_lock_ISR_enable( &lock_context );
414#endif
415  }
416}
417
418/**
419 * @brief A user extension thread exitted.
420 *
421 * @param created The thread.
422 */
423static inline void _User_extensions_Thread_exitted( Thread_Control *executing )
424{
425  _User_extensions_Iterate(
426    executing,
427    _User_extensions_Thread_exitted_visitor,
428    CHAIN_ITERATOR_FORWARD
429  );
430}
431
432/**
433 * @brief Forwards all visitors that there was a fatal failure.
434 *
435 * @param source The error source.
436 * @param error The error.
437 */
438static inline void _User_extensions_Fatal(
439  Internal_errors_Source source,
440  Internal_errors_t      error
441)
442{
443  User_extensions_Fatal_context ctx = { source, error };
444
445  _User_extensions_Iterate(
446    &ctx,
447    _User_extensions_Fatal_visitor,
448    CHAIN_ITERATOR_FORWARD
449  );
450}
451
452/**
453 * @brief Terminates the executing thread.
454 *
455 * @param executing The currently executing thread.
456 */
457static inline void _User_extensions_Thread_terminate(
458  Thread_Control *executing
459)
460{
461  _User_extensions_Iterate(
462    executing,
463    _User_extensions_Thread_terminate_visitor,
464    CHAIN_ITERATOR_BACKWARD
465  );
466}
467
468/**
469 * @brief Disables interrupts and acquires the lock context.
470 *
471 * @param lock_context The lock context to acquire.
472 */
473static inline void _User_extensions_Acquire( ISR_lock_Context *lock_context )
474{
475  _ISR_lock_ISR_disable_and_acquire(
476    &_User_extensions_List.Lock,
477    lock_context
478  );
479}
480
481/**
482 * @brief Releases the lock context and enables interrupts.
483 *
484 * @param lock_context The lock context to release.
485 */
486static inline void _User_extensions_Release( ISR_lock_Context *lock_context )
487{
488  _ISR_lock_Release_and_ISR_enable(
489    &_User_extensions_List.Lock,
490    lock_context
491  );
492}
493
494/**
495 * @brief Destroys all user extension iterators of a thread.
496 *
497 * @param[in, out] the_thread The thread to destroy all user extension
498 *      iterators of.
499 */
500static inline void _User_extensions_Destroy_iterators(
501  Thread_Control *the_thread
502)
503{
504  ISR_lock_Context          lock_context;
505  User_extensions_Iterator *iter;
506
507  _User_extensions_Acquire( &lock_context );
508
509  iter = the_thread->last_user_extensions_iterator;
510
511  while ( iter != NULL ) {
512    _Chain_Iterator_destroy( &iter->Iterator );
513    iter = iter->previous;
514  }
515
516  _User_extensions_Release( &lock_context );
517}
518
519/** @} */
520
521/** @} */
522
523#ifdef __cplusplus
524}
525#endif
526
527#endif
528/* end of include file */
Note: See TracBrowser for help on using the repository browser.