1 /**************************************************************************/
2 /*                                                                        */
3 /*       Copyright (c) Microsoft Corporation. All rights reserved.        */
4 /*                                                                        */
5 /*       This software is licensed under the Microsoft Software License   */
6 /*       Terms for Microsoft Azure RTOS. Full text of the license can be  */
7 /*       found in the LICENSE file at https://aka.ms/AzureRTOS_EULA       */
8 /*       and in the root directory of this software.                      */
9 /*                                                                        */
10 /**************************************************************************/
11 
12 
13 /**************************************************************************/
14 /**************************************************************************/
15 /**                                                                       */
16 /** ThreadX Component                                                     */
17 /**                                                                       */
18 /**   Thread                                                              */
19 /**                                                                       */
20 /**************************************************************************/
21 /**************************************************************************/
22 
23 #define TX_SOURCE_CODE
24 #define TX_THREAD_SMP_SOURCE_CODE
25 
26 
27 /* Include necessary system files.  */
28 
29 #include "tx_api.h"
30 #include "tx_timer.h"
31 #include "tx_thread.h"
32 #include "tx_trace.h"
33 
34 
35 /**************************************************************************/
36 /*                                                                        */
37 /*  FUNCTION                                               RELEASE        */
38 /*                                                                        */
39 /*    _tx_thread_time_slice                              PORTABLE SMP     */
40 /*                                                           6.1          */
41 /*  AUTHOR                                                                */
42 /*                                                                        */
43 /*    William E. Lamie, Microsoft Corporation                             */
44 /*                                                                        */
45 /*  DESCRIPTION                                                           */
46 /*                                                                        */
47 /*    This function moves the currently executing thread to the end of    */
48 /*    the threads ready at the same priority level as a result of a       */
49 /*    time-slice interrupt.  If no other thread of the same priority is   */
50 /*    ready, this function simply returns.                                */
51 /*                                                                        */
52 /*  INPUT                                                                 */
53 /*                                                                        */
54 /*    None                                                                */
55 /*                                                                        */
56 /*  OUTPUT                                                                */
57 /*                                                                        */
58 /*    None                                                                */
59 /*                                                                        */
60 /*  CALLS                                                                 */
61 /*                                                                        */
62 /*    _tx_thread_smp_rebalance_execute_list Rebalance the execution list  */
63 /*                                                                        */
64 /*  CALLED BY                                                             */
65 /*                                                                        */
66 /*    _tx_timer_interrupt                   Timer interrupt handling      */
67 /*                                                                        */
68 /*  RELEASE HISTORY                                                       */
69 /*                                                                        */
70 /*    DATE              NAME                      DESCRIPTION             */
71 /*                                                                        */
72 /*  09-30-2020     William E. Lamie         Initial Version 6.1           */
73 /*                                                                        */
74 /**************************************************************************/
_tx_thread_time_slice(VOID)75 VOID  _tx_thread_time_slice(VOID)
76 {
77 
78 ULONG           core_index, current_core;
79 UINT            priority;
80 TX_THREAD       *thread_ptr;
81 TX_THREAD       *next_thread;
82 TX_THREAD       *previous_thread;
83 TX_THREAD       *head_ptr;
84 TX_THREAD       *tail_ptr;
85 UINT            loop_finished;
86 UINT            rebalance;
87 ULONG           excluded;
88 #ifdef TX_ENABLE_EVENT_TRACE
89 ULONG           system_state;
90 UINT            preempt_disable;
91 #endif
92 
93 
94     /* Quick check for expiration.  */
95 #if TX_THREAD_SMP_MAX_CORES == 1
96     if (_tx_timer_time_slice[0] != 0)
97     {
98 #endif
99 #if TX_THREAD_SMP_MAX_CORES == 2
100     if ((_tx_timer_time_slice[0] != ((ULONG) 0)) || (_tx_timer_time_slice[1] != ((ULONG) 0)))
101     {
102 #endif
103 #if TX_THREAD_SMP_MAX_CORES == 3
104     if ((_tx_timer_time_slice[0] != ((ULONG) 0)) || (_tx_timer_time_slice[1] != ((ULONG) 0)) || (_tx_timer_time_slice[2] != ((ULONG) 0)))
105     {
106 #endif
107 #if TX_THREAD_SMP_MAX_CORES == 4
108     if ((_tx_timer_time_slice[0] != ((ULONG) 0)) || (_tx_timer_time_slice[1] != ((ULONG) 0)) || (_tx_timer_time_slice[2] != ((ULONG) 0)) || (_tx_timer_time_slice[3] != ((ULONG) 0)))
109     {
110 #endif
111 
112         /* Initialize the rebalance flag to false.  */
113         rebalance =  TX_FALSE;
114 
115         /* Get the core index.  */
116         current_core =  TX_SMP_CORE_ID;
117 
118         /* Loop to process all time-slices.  */
119 
120 #ifndef TX_THREAD_SMP_DYNAMIC_CORE_MAX
121 
122         for (core_index = ((ULONG) 0); core_index < ((ULONG) TX_THREAD_SMP_MAX_CORES); core_index++)
123 #else
124 
125         for (core_index = ((ULONG) 0); core_index < _tx_thread_smp_max_cores; core_index++)
126 #endif
127         {
128 
129             /* Determine if there is a time-slice active on this core.  */
130             if (_tx_timer_time_slice[core_index] != ((ULONG) 0))
131             {
132 
133                 /* Time-slice is active, decrement it for this core.  */
134                 _tx_timer_time_slice[core_index]--;
135 
136                 /* Has the time-slice expired?  */
137                 if (_tx_timer_time_slice[core_index] == ((ULONG) 0))
138                 {
139 
140                     /* Yes, time-slice on this core has expired.  */
141 
142                     /* Pickup the current thread pointer.  */
143                     thread_ptr =  _tx_thread_current_ptr[core_index];
144 
145                     /* Make sure the thread is still active, i.e. not suspended.  */
146                     if ((thread_ptr != TX_NULL) && (thread_ptr -> tx_thread_state == TX_READY) && (thread_ptr -> tx_thread_time_slice != ((ULONG) 0)))
147                     {
148 
149                         /* Yes, thread is still active and time-slice has expired.  */
150 
151                         /* Setup a fresh time-slice for the thread.  */
152                         thread_ptr -> tx_thread_time_slice =  thread_ptr -> tx_thread_new_time_slice;
153 
154                         /* Reset the actual time-slice variable.  */
155                         _tx_timer_time_slice[core_index] =  thread_ptr -> tx_thread_time_slice;
156 
157 #ifdef TX_THREAD_SMP_DEBUG_ENABLE
158 
159                         /* Debug entry.  */
160                         _tx_thread_smp_debug_entry_insert(10, 0, thread_ptr);
161 #endif
162 
163 #ifdef TX_ENABLE_STACK_CHECKING
164 
165                         /* Check this thread's stack.  */
166                         TX_THREAD_STACK_CHECK(thread_ptr)
167 #endif
168 
169 #ifdef TX_THREAD_ENABLE_PERFORMANCE_INFO
170 
171                         /* Increment the thread's time-slice counter.  */
172                         thread_ptr -> tx_thread_performance_time_slice_count++;
173 
174                         /* Increment the total number of thread time-slice operations.  */
175                         _tx_thread_performance_time_slice_count++;
176 #endif
177 
178                         /* Setup the priority.  */
179                         priority =  thread_ptr -> tx_thread_priority;
180 
181                         /* Determine if preemption-threshold is set. If so, don't time-slice.  */
182                         if (priority == thread_ptr -> tx_thread_preempt_threshold)
183                         {
184 
185                             /* Preemption-threshold is not set.  */
186 
187                             /* Pickup the next thread.  */
188                             next_thread =  thread_ptr -> tx_thread_ready_next;
189 
190                             /* Pickup the head of the list.  */
191                             head_ptr =  _tx_thread_priority_list[priority];
192 
193                             /* Pickup the list tail.  */
194                             tail_ptr =  head_ptr -> tx_thread_ready_previous;
195 
196                             /* Determine if this thread is not the tail pointer.  */
197                             if (thread_ptr != tail_ptr)
198                             {
199 
200                                 /* Not the tail pointer, this thread must be moved to the end of the ready list.  */
201 
202                                 /* Determine if this thread is at the head of the list.  */
203                                 if (head_ptr == thread_ptr)
204                                 {
205 
206                                     /* Simply move the head pointer to put this thread at the end of the ready list at this priority.  */
207                                     _tx_thread_priority_list[priority] =  next_thread;
208                                 }
209                                 else
210                                 {
211 
212                                     /* Now we need to remove this thread from its current position and place it at the end of the list.   */
213 
214                                     /* Pickup the previous thread pointer.  */
215                                     previous_thread =  thread_ptr -> tx_thread_ready_previous;
216 
217                                     /* Remove the thread from the ready list.  */
218                                     next_thread -> tx_thread_ready_previous =    previous_thread;
219                                     previous_thread -> tx_thread_ready_next =    next_thread;
220 
221                                     /* Insert the thread at the end of the list.  */
222                                     tail_ptr -> tx_thread_ready_next =         thread_ptr;
223                                     head_ptr -> tx_thread_ready_previous =     thread_ptr;
224                                     thread_ptr -> tx_thread_ready_previous =   tail_ptr;
225                                     thread_ptr -> tx_thread_ready_next =       head_ptr;
226                                 }
227 
228                                 /* Make sure the current core execute pointer is still this thread.  If not, a higher priority thread has already
229                                    preempted it either from another ISR or from the timer processing.  */
230                                 if (_tx_thread_execute_ptr[core_index] != thread_ptr)
231                                 {
232 
233                                     /* Set the rebalance flag.  */
234                                     rebalance =  TX_TRUE;
235                                 }
236 
237                                 /* Determine if the rebalance flag has been set already.  If so, don't bother trying to update the
238                                    execute list from this routine.  */
239                                 if (rebalance == TX_FALSE)
240                                 {
241 
242                                     /* Set the loop finished flag to false.  */
243                                     loop_finished =  TX_FALSE;
244 
245                                     /* Determine if there is a thread at the same priority that isn't currently executing.  */
246                                     do
247                                     {
248 
249                                         /* Isolate the exclusion for this core.  */
250                                         excluded =  (next_thread -> tx_thread_smp_cores_excluded >> core_index) & ((ULONG) 1);
251 
252                                         /* Determine if the next thread has preemption-threshold set.  */
253                                         if (next_thread -> tx_thread_preempt_threshold < next_thread -> tx_thread_priority)
254                                         {
255 
256                                             /* Set the rebalance flag.  */
257                                             rebalance =  TX_TRUE;
258 
259                                             /* Get out of the loop.  */
260                                             loop_finished =  TX_TRUE;
261                                         }
262 
263                                         /* Determine if the next thread is excluded from running on this core.  */
264                                         else if (excluded == ((ULONG) 1))
265                                         {
266 
267                                             /* Set the rebalance flag.  */
268                                             rebalance =  TX_TRUE;
269 
270                                             /* Get out of the loop.  */
271                                             loop_finished =  TX_TRUE;
272                                         }
273                                         else
274                                         {
275 
276                                             /* Is the next thread not scheduled  */
277                                             if (next_thread != _tx_thread_execute_ptr[next_thread -> tx_thread_smp_core_mapped])
278                                             {
279 
280                                                 /* Remember this index in the thread control block.  */
281                                                 next_thread -> tx_thread_smp_core_mapped =  core_index;
282 
283                                                 /* Setup the entry in the execution list.  */
284                                                 _tx_thread_execute_ptr[core_index] =  next_thread;
285 
286 #ifdef TX_THREAD_SMP_INTER_CORE_INTERRUPT
287 
288                                                 /* Determine if we need to preempt the core.  */
289                                                 if (core_index != current_core)
290                                                 {
291 
292                                                     /* Preempt the mapped thread.  */
293                                                     _tx_thread_smp_core_preempt(core_index);
294                                                 }
295 #endif
296                                                 /* Get out of the loop.  */
297                                                 loop_finished =  TX_TRUE;
298                                             }
299                                         }
300 
301                                         /* Is the loop fininshed?  */
302                                         if (loop_finished == TX_TRUE)
303                                         {
304 
305                                             /* Yes, break out of the loop.  */
306                                             break;
307                                         }
308 
309                                         /* Move to the next thread at this priority.  */
310                                         next_thread =  next_thread -> tx_thread_ready_next;
311 
312                                     } while (next_thread != thread_ptr);
313                                 }
314                             }
315 
316 #ifdef TX_THREAD_SMP_DEBUG_ENABLE
317 
318                             /* Debug entry.  */
319                             _tx_thread_smp_debug_entry_insert(11, 0, thread_ptr);
320 #endif
321                         }
322                     }
323                 }
324             }
325         }
326 
327         /* Determine if rebalance was set.  */
328         if (rebalance == TX_TRUE)
329         {
330 
331             /* Call the rebalance routine. This routine maps cores and ready threads.  */
332             _tx_thread_smp_rebalance_execute_list(current_core);
333         }
334 
335 #ifdef TX_ENABLE_EVENT_TRACE
336 
337         /* Pickup the volatile information.  */
338         system_state =  TX_THREAD_GET_SYSTEM_STATE();
339         preempt_disable =  _tx_thread_preempt_disable;
340 
341         /* Insert this event into the trace buffer.  */
342         TX_TRACE_IN_LINE_INSERT(TX_TRACE_TIME_SLICE, _tx_thread_execute_ptr[0], system_state, preempt_disable, TX_POINTER_TO_ULONG_CONVERT(&thread_ptr), TX_TRACE_INTERNAL_EVENTS)
343 #endif
344 
345 #if TX_THREAD_SMP_MAX_CORES == 1
346     }
347 #endif
348 #if TX_THREAD_SMP_MAX_CORES == 2
349     }
350 #endif
351 #if TX_THREAD_SMP_MAX_CORES == 3
352     }
353 #endif
354 #if TX_THREAD_SMP_MAX_CORES == 4
355     }
356 #endif
357 }
358 
359