1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 
12 /**************************************************************************/
13 /**************************************************************************/
14 /**                                                                       */
15 /** ThreadX Component                                                     */
16 /**                                                                       */
17 /**   Thread - High Level SMP Support                                     */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define TX_SOURCE_CODE
23 #define TX_THREAD_SMP_SOURCE_CODE
24 
25 
26 /* Include necessary system files.  */
27 
28 #include "tx_api.h"
29 #include "tx_initialize.h"
30 #include "tx_timer.h"
31 #include "tx_thread.h"
32 
33 
34 /**************************************************************************/
35 /*                                                                        */
36 /*  FUNCTION                                               RELEASE        */
37 /*                                                                        */
38 /*    _tx_thread_smp_rebalance_execute_list              PORTABLE SMP     */
39 /*                                                           6.1          */
40 /*  AUTHOR                                                                */
41 /*                                                                        */
42 /*    William E. Lamie, Microsoft Corporation                             */
43 /*                                                                        */
44 /*  DESCRIPTION                                                           */
45 /*                                                                        */
46 /*    This function is responsible for mapping ready ThreadX threads with */
47 /*    cores in the SMP . The basic idea is the standard ThreadX           */
48 /*    ready list is traversed to build the _tx_thread_execute_ptr list.   */
49 /*    Each index represents the and the corresponding entry in this       */
50 /*    array contains the thread that should be executed by that core. If  */
51 /*    the was previously running a different thread, it will be           */
52 /*    preempted and restarted so it can run the new thread.               */
53 /*                                                                        */
54 /*  INPUT                                                                 */
55 /*                                                                        */
56 /*    None                                                                */
57 /*                                                                        */
58 /*  OUTPUT                                                                */
59 /*                                                                        */
60 /*    None                                                                */
61 /*                                                                        */
62 /*  CALLS                                                                 */
63 /*                                                                        */
64 /*    _tx_thread_smp_execute_list_clear     Clear the thread execute list */
65 /*    _tx_thread_smp_execute_list_setup     Setup the thread execute list */
66 /*    _tx_thread_smp_next_priority_find     Find next priority with one   */
67 /*                                            or more ready threads       */
68 /*    _tx_thread_smp_remap_solution_find    Attempt to remap threads to   */
69 /*                                            schedule another thread     */
70 /*    _tx_thread_smp_schedule_list_clear    Clear the thread schedule list*/
71 /*                                                                        */
72 /*  CALLED BY                                                             */
73 /*                                                                        */
74 /*    _tx_mutex_priority_change             Mutex priority change         */
75 /*    _tx_thread_create                     Thread create                 */
76 /*    _tx_thread_preemption_change          Thread preemption change      */
77 /*    _tx_thread_priority_change            Thread priority change        */
78 /*    _tx_thread_relinquish                 Thread relinquish             */
79 /*    _tx_thread_resume                     Thread resume                 */
80 /*    _tx_thread_smp_core_exclude           Thread SMP core exclude       */
81 /*    _tx_thread_system_resume              Thread system resume          */
82 /*    _tx_thread_system_suspend             Thread suspend                */
83 /*    _tx_thread_time_slice                 Thread time-slice             */
84 /*                                                                        */
85 /*  RELEASE HISTORY                                                       */
86 /*                                                                        */
87 /*    DATE              NAME                      DESCRIPTION             */
88 /*                                                                        */
89 /*  09-30-2020     William E. Lamie         Initial Version 6.1           */
90 /*                                                                        */
91 /**************************************************************************/
_tx_thread_smp_rebalance_execute_list(UINT core_index)92 void  _tx_thread_smp_rebalance_execute_list(UINT core_index)
93 {
94 
95 UINT            i, j, core;
96 UINT            next_priority;
97 UINT            last_priority;
98 TX_THREAD       *schedule_thread;
99 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
100 TX_THREAD       *mapped_thread;
101 #endif
102 TX_THREAD       *preempted_thread;
103 ULONG           possible_cores;
104 ULONG           thread_possible_cores;
105 ULONG           available_cores;
106 ULONG           test_possible_cores;
107 ULONG           test_cores;
108 UINT            this_pass_complete;
109 UINT            loop_finished;
110 
111 #ifdef TX_THREAD_SMP_EQUAL_PRIORITY
112 TX_THREAD       *highest_priority_thread;
113 #endif
114 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
115 ULONG           priority_bit;
116 #if TX_MAX_PRIORITIES > 32
117 UINT            map_index;
118 #endif
119 #endif
120 
121 
122     /* It is assumed that the preempt disable flag is still set at this point.  */
123 
124     /* Pickup the last schedule thread with preemption-threshold enabled.  */
125     preempted_thread =  _tx_thread_preemption__threshold_scheduled;
126 
127     /* Clear the schedule list.  */
128     _tx_thread_smp_schedule_list_clear();
129 
130     /* Initialize the next priority to 0, the highest priority.  */
131     next_priority =  ((UINT) 0);
132 
133     /* Initialize the last priority.  */
134     last_priority =  ((UINT) 0);
135 
136 #ifndef TX_THREAD_SMP_DYNAMIC_CORE_MAX
137 
138     /* Set the possible cores bit map to all cores.  */
139     possible_cores =  ((ULONG) TX_THREAD_SMP_CORE_MASK);
140 #else
141 
142     /* Set the possible cores bit map to all cores.  */
143     possible_cores =  (((ULONG) 1) << _tx_thread_smp_max_cores) - 1;
144 #endif
145 
146     /* Setup the available cores bit map.  */
147     available_cores =  possible_cores;
148 
149     /* Clear the schedule thread pointer.  */
150     schedule_thread =  TX_NULL;
151 
152 #ifdef TX_THREAD_SMP_EQUAL_PRIORITY
153 
154     /* Set the highest priority thread to NULL.  */
155     highest_priority_thread =  TX_NULL;
156 #endif
157 
158     /* Loop to rebuild the schedule list.  */
159     i =  ((UINT) 0);
160     loop_finished =  TX_FALSE;
161     do
162     {
163 
164         /* Clear the pass complete flag, which is used to skip the remaining processing
165            of this loop on certain conditions.  */
166         this_pass_complete =  TX_FALSE;
167 
168         /* Determine if there is a thread to schedule.  */
169         if (schedule_thread == TX_NULL)
170         {
171 
172             /* Calculate the next ready priority.  */
173             next_priority =  _tx_thread_smp_next_priority_find(next_priority);
174 
175             /* Determine if there are no more threads to execute.  */
176             if (next_priority == ((UINT) TX_MAX_PRIORITIES))
177             {
178 
179                 /* Break out of loop.  */
180                 loop_finished =       TX_TRUE;
181                 this_pass_complete =  TX_TRUE;
182             }
183             else
184             {
185 
186                 /* Determine if a thread was executed with preemption-threshold set.  */
187                 if (preempted_thread != TX_NULL)
188                 {
189 
190                     /* Yes, a thread was previously preempted.  Let's first see if we reached the
191                        interrupted preemption-threshold level.  */
192                     if (next_priority >= preempted_thread -> tx_thread_preempt_threshold)
193                     {
194 
195                         /* Yes, now lets see if we are within the preemption-threshold level.  */
196                         if (next_priority <= preempted_thread -> tx_thread_priority)
197                         {
198 
199                             /* Yes, move the next priority to the preempted priority.  */
200                             next_priority =  preempted_thread -> tx_thread_priority;
201 
202                             /* Setup the schedule thread to the preempted thread.  */
203                             schedule_thread =  preempted_thread;
204 
205                             /* Start at the top of the loop.  */
206                             this_pass_complete =  TX_TRUE;
207                         }
208                         else
209                         {
210 
211                             /* Nothing else is allowed to execute after the preemption-threshold thread.  */
212                             next_priority =  ((UINT) TX_MAX_PRIORITIES);
213 
214                             /* Break out of loop.  */
215                             loop_finished =       TX_TRUE;
216                             this_pass_complete =  TX_TRUE;
217                         }
218                     }
219                 }
220             }
221 
222             /* Determine if this pass through the loop is already complete.  */
223             if (this_pass_complete == TX_FALSE)
224             {
225 
226                 /* Pickup the next thread to schedule.  */
227                 schedule_thread =  _tx_thread_priority_list[next_priority];
228             }
229         }
230 
231         /* Determine if this pass through the loop is already complete.  */
232         if (this_pass_complete == TX_FALSE)
233         {
234 
235             /* Determine what the possible cores are for this thread.  */
236             thread_possible_cores =  schedule_thread -> tx_thread_smp_cores_allowed;
237 
238             /* Apply the current possible cores.  */
239             thread_possible_cores =  thread_possible_cores & (available_cores | possible_cores);
240 
241             /* Determine if it is possible to schedule this thread.  */
242             if (thread_possible_cores == ((ULONG) 0))
243             {
244 
245                 /* No, this thread can't be scheduled.  */
246 
247                 /* Look at the next thread at the same priority level.  */
248                 schedule_thread =  schedule_thread -> tx_thread_ready_next;
249 
250                 /* Determine if this is the head of the list.  */
251                 if (schedule_thread == _tx_thread_priority_list[next_priority])
252                 {
253 
254                     /* Set the schedule thread to NULL to force examination of the next priority level.  */
255                     schedule_thread =  TX_NULL;
256 
257                     /* Move to the next priority level.  */
258                     next_priority++;
259 
260                     /* Determine if there are no more threads to execute.  */
261                     if (next_priority == ((UINT) TX_MAX_PRIORITIES))
262                     {
263 
264                         /* Break out of loop.  */
265                         loop_finished =       TX_TRUE;
266                     }
267                 }
268             }
269             else
270             {
271 
272                 /* It is possible to schedule this thread.  */
273 
274                 /* Determine if this thread has preemption-threshold set.  */
275                 if (schedule_thread -> tx_thread_preempt_threshold < schedule_thread -> tx_thread_priority)
276                 {
277 
278                     /* Yes, preemption-threshold is set.  */
279 
280                     /* Determine if the last priority is above the preemption-threshold.  If not, we can't
281                        schedule this thread with preemption-threshold set.  */
282                     if ((last_priority >= schedule_thread -> tx_thread_preempt_threshold) && (i != ((UINT) 0)))
283                     {
284 
285                         /* A thread was found that violates the next thread to be scheduled's preemption-threshold. We will simply
286                            skip this thread and see if there is anything else we can schedule. */
287 
288                         /* Look at the next thread at the same priority level.  */
289                         schedule_thread =  schedule_thread -> tx_thread_ready_next;
290 
291                         /* Determine if this is the head of the list.  */
292                         if (schedule_thread == _tx_thread_priority_list[next_priority])
293                         {
294 
295                             /* Set the schedule thread to NULL to force examination of the next priority level.  */
296                             schedule_thread =  TX_NULL;
297 
298                             /* Move to the next priority level.  */
299                             next_priority++;
300 
301                             /* Determine if there are no more threads to execute.  */
302                             if (next_priority == ((UINT) TX_MAX_PRIORITIES))
303                             {
304 
305                                 /* Break out of loop.  */
306                                 loop_finished =  TX_TRUE;
307                             }
308                         }
309 
310                         /* Restart the loop.  */
311                         this_pass_complete =  TX_TRUE;
312                     }
313                 }
314 
315                 /* Determine if this pass through the loop is already complete.  */
316                 if (this_pass_complete == TX_FALSE)
317                 {
318 
319 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
320 
321                     /* Initialize index to an invalid value.  */
322                     j =  ((UINT) TX_THREAD_SMP_MAX_CORES);
323 #endif
324 
325                     /* Determine if there is an available core for this thread to execute on.  */
326                     if ((thread_possible_cores & available_cores) != ((ULONG) 0))
327                     {
328 
329                         /* Pickup the last executed core for this thread.  */
330                         j =  schedule_thread -> tx_thread_smp_core_mapped;
331 
332                         /* Is this core valid and available?  */
333                         if ((thread_possible_cores & available_cores & (((ULONG) 1) << j)) == ((ULONG) 0))
334                         {
335 
336                             /* No, we must find the next core for this thread.  */
337                             test_cores =  (thread_possible_cores & available_cores);
338                             TX_LOWEST_SET_BIT_CALCULATE(test_cores, j)
339 
340                             /* Setup the last executed core for this thread.  */
341                             schedule_thread -> tx_thread_smp_core_mapped =  j;
342                         }
343 
344                         /* Place the this thread on this core.  */
345                         _tx_thread_smp_schedule_list[j] =  schedule_thread;
346 
347                         /* Clear the associated available cores bit.  */
348                         available_cores =  available_cores & ~(((ULONG) 1) << j);
349                     }
350                     else
351                     {
352 
353                         /* Note that we know that the thread must have at least one core excluded at this point,
354                            since we didn't find a match and we have available cores.  */
355 
356                         /* Now we need to see if one of the other threads in the non-excluded cores can be moved to make room
357                            for this thread.  */
358 
359                         /* Determine the possible core remapping attempt.  */
360                         test_possible_cores =  possible_cores & ~(thread_possible_cores);
361 
362                         /* Attempt to remap the cores in order to schedule this thread.  */
363                         core =  _tx_thread_smp_remap_solution_find(schedule_thread, available_cores, thread_possible_cores, test_possible_cores);
364 
365                         /* Determine if remapping was successful.  */
366                         if (core != ((UINT) TX_THREAD_SMP_MAX_CORES))
367                         {
368 
369                             /* Yes, remapping was successful. Update the available cores accordingly. */
370                             available_cores =  available_cores & ~(((ULONG) 1) << core);
371                         }
372                         else
373                         {
374 
375                             /* We couldn't assign the thread to any of the cores possible for the thread.  */
376 
377                             /* Check to see if the thread is the last thread preempted.  */
378                             if (schedule_thread == preempted_thread)
379                             {
380 
381                                 /* To honor the preemption-threshold, we cannot schedule any more threads.  */
382                                 loop_finished =  TX_TRUE;
383                             }
384                             else
385                             {
386 
387                                 /* update the available cores for the next pass so we don't waste time looking at them again!  */
388                                 possible_cores =  possible_cores & (~thread_possible_cores);
389 
390                                 /* No, we couldn't load the thread because none of the required cores were available.  Look at the next thread at the same priority level.  */
391                                 schedule_thread =  schedule_thread -> tx_thread_ready_next;
392 
393                                 /* Determine if this is the head of the list.  */
394                                 if (schedule_thread == _tx_thread_priority_list[next_priority])
395                                 {
396 
397                                     /* Set the schedule thread to NULL to force examination of the next priority level.  */
398                                     schedule_thread =  TX_NULL;
399 
400                                     /* Move to the next priority level.  */
401                                     next_priority++;
402 
403                                     /* Determine if there are no more threads to execute.  */
404                                     if (next_priority == ((UINT) TX_MAX_PRIORITIES))
405                                     {
406 
407                                         /* Break out of loop.  */
408                                         loop_finished =  TX_TRUE;
409                                     }
410                                 }
411                             }
412 
413                             /* Restart the loop.  */
414                             this_pass_complete =  TX_TRUE;
415                         }
416                     }
417 
418                     /* Determine if this pass through the loop is already complete.  */
419                     if (this_pass_complete == TX_FALSE)
420                     {
421 
422 #ifdef TX_THREAD_SMP_EQUAL_PRIORITY
423 
424                         /* Determine if this is the highest priority thread.  */
425                         if (highest_priority_thread == TX_NULL)
426                         {
427 
428                             /* No highest priority yet, remember this thread.  */
429                             highest_priority_thread =  schedule_thread;
430                         }
431 #endif
432 
433                         /* Increment the number of threads loaded. */
434                         i++;
435 
436 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
437 
438                         /* Determine if the thread was mapped.  */
439                         if (j != ((UINT) TX_THREAD_SMP_MAX_CORES))
440                         {
441 
442                             /* Pickup the currently mapped thread.  */
443                             mapped_thread =  _tx_thread_execute_ptr[j];
444 
445                             /* Determine if preemption is present.  */
446                             if ((mapped_thread != TX_NULL) && (schedule_thread != mapped_thread))
447                             {
448 
449                                 /* Determine if the previously mapped thread is still ready.  */
450                                 if (mapped_thread -> tx_thread_state == TX_READY)
451                                 {
452 
453                                     /* Determine if the caller is an interrupt or from a thread.  */
454                                     if (_tx_thread_system_state[core_index] == ((ULONG) 0))
455                                     {
456 
457                                         /* Caller is a thread, so this is a solicited preemption.  */
458                                         _tx_thread_performance_solicited_preemption_count++;
459 
460                                         /* Increment the thread's solicited preemption counter.  */
461                                         mapped_thread -> tx_thread_performance_solicited_preemption_count++;
462                                     }
463                                     else
464                                     {
465 
466                                         /* Is this an interrupt?  */
467                                         if (_tx_thread_system_state[core_index] < TX_INITIALIZE_IN_PROGRESS)
468                                         {
469 
470                                             /* Caller is an interrupt, so this is an interrupt preemption.  */
471                                             _tx_thread_performance_interrupt_preemption_count++;
472 
473                                             /* Increment the thread's interrupt preemption counter.  */
474                                             mapped_thread -> tx_thread_performance_interrupt_preemption_count++;
475                                         }
476                                     }
477                                 }
478                             }
479                         }
480 #endif
481 
482                         /* Determine if this thread has preemption-threshold set.  */
483                         if (schedule_thread -> tx_thread_preempt_threshold < schedule_thread -> tx_thread_priority)
484                         {
485 
486 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
487 
488                             /* mark the bit map to show that a thread with preemption-threshold has been executed.  */
489 #if TX_MAX_PRIORITIES > 32
490 
491                             /* Calculate the index into the bit map array.  */
492                             map_index =  (schedule_thread -> tx_thread_priority)/((UINT) 32);
493 
494                             /* Set the active bit to remember that the preempt map has something set.  */
495                             TX_DIV32_BIT_SET(schedule_thread -> tx_thread_priority, priority_bit)
496                             _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active | priority_bit;
497 #endif
498 
499                             /* Remember that this thread was executed with preemption-threshold set.  */
500                             TX_MOD32_BIT_SET(schedule_thread -> tx_thread_priority, priority_bit)
501                             _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] | priority_bit;
502 
503                             /* Place the thread in the preempted list indicating preemption-threshold is in force.  */
504                             _tx_thread_preemption_threshold_list[schedule_thread -> tx_thread_priority] =  schedule_thread;
505 #endif
506 
507                             /* Set the last thread with preemption-threshold enabled.  */
508                             _tx_thread_preemption__threshold_scheduled =  schedule_thread;
509 
510                             /* Now break out of the scheduling loop.  */
511                             loop_finished =  TX_TRUE;
512                         }
513                         else
514                         {
515 
516                             /* Remember the last priority.  */
517                             last_priority =  next_priority;
518 
519                             /* Pickup the next ready thread at the current priority level.  */
520                             schedule_thread =  schedule_thread -> tx_thread_ready_next;
521 
522                             /* Determine if this is the head of the list, which implies that we have exhausted this priority level.  */
523                             if (schedule_thread == _tx_thread_priority_list[next_priority])
524                             {
525 
526                                 /* Set the schedule thread to NULL to force examination of the next priority level.  */
527                                 schedule_thread =  TX_NULL;
528 
529                                 /* Move to the next priority level.  */
530                                 next_priority++;
531 
532 #ifdef TX_THREAD_SMP_EQUAL_PRIORITY
533 
534                                 /* Determine if there is a highest priority thread.  */
535                                 if (highest_priority_thread)
536                                 {
537 
538                                     /* Yes, break out of the loop, since only same priority threads can be
539                                        scheduled in this mode.  */
540                                     loop_finished =  TX_TRUE;
541                                 }
542 #endif
543 
544                                 /* Determine if there are no more threads to execute.  */
545                                 if (next_priority == ((UINT) TX_MAX_PRIORITIES))
546                                 {
547 
548                                     /* Break out of loop.  */
549                                     loop_finished =  TX_TRUE;
550                                 }
551                             }
552                         }
553                     }
554                 }
555             }
556         }
557 
558         /* Determine if the loop is finished.  */
559         if (loop_finished == TX_TRUE)
560         {
561 
562             /* Finished, break the loop.  */
563             break;
564         }
565 
566 #ifndef TX_THREAD_SMP_DYNAMIC_CORE_MAX
567 
568     } while (i < ((UINT) TX_THREAD_SMP_MAX_CORES));
569 #else
570 
571     } while (i < _tx_thread_smp_max_cores);
572 #endif
573 
574     /* Clear the execute list.  */
575     _tx_thread_smp_execute_list_clear();
576 
577     /* Setup the execute list based on the updated schedule list.  */
578     _tx_thread_smp_execute_list_setup(core_index);
579 }
580 
581