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_trace.h"
31 #include "tx_thread.h"
32 
33 
34 /**************************************************************************/
35 /*                                                                        */
36 /*  FUNCTION                                               RELEASE        */
37 /*                                                                        */
38 /*    _tx_thread_priority_change                         PORTABLE SMP     */
39 /*                                                           6.1          */
40 /*  AUTHOR                                                                */
41 /*                                                                        */
42 /*    William E. Lamie, Microsoft Corporation                             */
43 /*                                                                        */
44 /*  DESCRIPTION                                                           */
45 /*                                                                        */
46 /*    This function changes the priority of the specified thread.  It     */
47 /*    also returns the old priority and handles preemption if the calling */
48 /*    thread is currently executing and the priority change results in a  */
49 /*    higher priority thread ready for execution.                         */
50 /*                                                                        */
51 /*    Note: the preemption-threshold is automatically changed to the new  */
52 /*    priority.                                                           */
53 /*                                                                        */
54 /*  INPUT                                                                 */
55 /*                                                                        */
56 /*    thread_ptr                            Pointer to thread to suspend  */
57 /*    new_priority                          New thread priority           */
58 /*    old_priority                          Old thread priority           */
59 /*                                                                        */
60 /*  OUTPUT                                                                */
61 /*                                                                        */
62 /*    status                                Completion status             */
63 /*                                                                        */
64 /*  CALLS                                                                 */
65 /*                                                                        */
66 /*    _tx_thread_smp_rebalance_execute_list Rebalance the execution list  */
67 /*    _tx_thread_smp_simple_priority_change Change priority               */
68 /*    _tx_thread_system_resume              Resume thread                 */
69 /*    _tx_thread_system_ni_resume           Non-interruptable resume      */
70 /*    _tx_thread_system_suspend             Suspend thread                */
71 /*    _tx_thread_system_ni_suspend          Non-interruptable suspend     */
72 /*    _tx_thread_system_preempt_check       Check for preemption          */
73 /*                                                                        */
74 /*  CALLED BY                                                             */
75 /*                                                                        */
76 /*    Application Code                                                    */
77 /*                                                                        */
78 /*  RELEASE HISTORY                                                       */
79 /*                                                                        */
80 /*    DATE              NAME                      DESCRIPTION             */
81 /*                                                                        */
82 /*  09-30-2020     William E. Lamie         Initial Version 6.1           */
83 /*                                                                        */
84 /**************************************************************************/
_tx_thread_priority_change(TX_THREAD * thread_ptr,UINT new_priority,UINT * old_priority)85 UINT  _tx_thread_priority_change(TX_THREAD *thread_ptr, UINT new_priority, UINT *old_priority)
86 {
87 
88 TX_INTERRUPT_SAVE_AREA
89 
90 TX_THREAD       *execute_ptr;
91 UINT            core_index;
92 UINT            original_priority;
93 UINT            lowest_priority;
94 TX_THREAD       *original_pt_thread;
95 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
96 TX_THREAD       *new_pt_thread;
97 UINT            priority;
98 ULONG           priority_bit;
99 #if TX_MAX_PRIORITIES > 32
100 UINT            map_index;
101 #endif
102 #endif
103 UINT            status;
104 
105 
106     /* Default status to not done.  */
107     status =  TX_NOT_DONE;
108 
109     /* Lockout interrupts while the thread is being suspended.  */
110     TX_DISABLE
111 
112     /* If trace is enabled, insert this event into the trace buffer.  */
113     TX_TRACE_IN_LINE_INSERT(TX_TRACE_THREAD_PRIORITY_CHANGE, thread_ptr, new_priority, thread_ptr -> tx_thread_priority, thread_ptr -> tx_thread_state, TX_TRACE_THREAD_EVENTS)
114 
115     /* Log this kernel call.  */
116     TX_EL_THREAD_PRIORITY_CHANGE_INSERT
117 
118     /* Save the previous priority.  */
119     *old_priority =  thread_ptr -> tx_thread_user_priority;
120 
121     /* Determine if the new priority is the same as the last requested priority.  */
122     if ((thread_ptr -> tx_thread_user_priority == new_priority) &&
123         (thread_ptr -> tx_thread_user_preempt_threshold == new_priority))
124     {
125 
126         /* Nothing to do at this point, simply return.  */
127 
128         /* Restore interrupts.  */
129         TX_RESTORE
130 
131         /* Done, return success.  */
132         status =  TX_SUCCESS;
133     }
134 
135     /* Determine if the inherit priority is in effect and there is no preemption-threshold in force.  */
136     else if ((thread_ptr -> tx_thread_inherit_priority < new_priority) &&
137              (thread_ptr -> tx_thread_user_preempt_threshold == thread_ptr -> tx_thread_user_priority))
138     {
139 
140         /* In this case, simply setup the user priority and preemption-threshold and return.  */
141 
142         /* Setup the new priority for this thread.  */
143         thread_ptr -> tx_thread_user_priority =           new_priority;
144         thread_ptr -> tx_thread_user_preempt_threshold =  new_priority;
145 
146         /* Restore interrupts.  */
147         TX_RESTORE
148 
149         /* Done, return success.  */
150         status =  TX_SUCCESS;
151     }
152     else
153     {
154 
155         /* Default the execute pointer to NULL.  */
156         execute_ptr =  TX_NULL;
157 
158         /* Determine if this thread is currently ready.  */
159         if (thread_ptr -> tx_thread_state != TX_READY)
160         {
161 
162             /* Setup the user priority and threshold in the thread's control
163                block.  */
164             thread_ptr -> tx_thread_user_priority =               new_priority;
165             thread_ptr -> tx_thread_user_preempt_threshold =      new_priority;
166 
167             /* Determine if the actual thread priority should be setup, which is the
168                case if the new priority is higher than the priority inheritance.  */
169             if (new_priority < thread_ptr -> tx_thread_inherit_priority)
170             {
171 
172                 /* Change thread priority to the new user's priority.  */
173                 thread_ptr -> tx_thread_priority =           new_priority;
174                 thread_ptr -> tx_thread_preempt_threshold =  new_priority;
175             }
176             else
177             {
178 
179                 /* Change thread priority to the priority inheritance.  */
180                 thread_ptr -> tx_thread_priority =           thread_ptr -> tx_thread_inherit_priority;
181                 thread_ptr -> tx_thread_preempt_threshold =  thread_ptr -> tx_thread_inherit_priority;
182             }
183 
184             /* Restore interrupts.  */
185             TX_RESTORE
186 
187             /* Done, return success.  */
188             status =  TX_SUCCESS;
189         }
190         else
191         {
192 
193             /* Pickup the core index.  */
194             core_index =  thread_ptr -> tx_thread_smp_core_mapped;
195 
196             /* Save the original priority.  */
197             original_priority =  thread_ptr -> tx_thread_priority;
198 
199             /* Determine if this thread is the currently scheduled thread.  */
200             if (thread_ptr == _tx_thread_execute_ptr[core_index])
201             {
202 
203                 /* Yes, this thread is scheduled.  */
204 
205                 /* Remember this thread as the currently executing thread.  */
206                 execute_ptr =  thread_ptr;
207 
208                 /* Determine if the thread is being set to a higher-priority and it does't have
209                    preemption-threshold set.  */
210                 if ((new_priority < thread_ptr -> tx_thread_priority) &&
211                     (thread_ptr -> tx_thread_user_priority == thread_ptr -> tx_thread_user_preempt_threshold))
212                 {
213 
214                     /* Simple case, remove the thread from the current priority list and place in
215                        the higher priority list.   */
216                     _tx_thread_smp_simple_priority_change(thread_ptr, new_priority);
217 
218                     /* Setup the new priority for this thread.  */
219                     thread_ptr -> tx_thread_user_priority =           new_priority;
220                     thread_ptr -> tx_thread_user_preempt_threshold =  new_priority;
221 
222                     /* Restore interrupts.   */
223                     TX_RESTORE
224 
225                     /* Return a successful completion.  */
226                     status =  TX_SUCCESS;
227                 }
228             }
229             else
230             {
231 
232                 /* Thread is not currently executing, so it can just be moved to the lower priority in the list.  */
233 
234                 /* Determine if the thread is being set to a lower-priority and it does't have
235                    preemption-threshold set.  */
236                 if ((new_priority > thread_ptr -> tx_thread_priority) &&
237                     (thread_ptr -> tx_thread_user_priority == thread_ptr -> tx_thread_user_preempt_threshold))
238                 {
239 
240                     /* Simple case, remove the thread from the current priority list and place in
241                        the lower priority list.   */
242                     _tx_thread_smp_simple_priority_change(thread_ptr, new_priority);
243 
244                     /* Setup the new priority for this thread.  */
245                     thread_ptr -> tx_thread_user_priority =           new_priority;
246                     thread_ptr -> tx_thread_user_preempt_threshold =  new_priority;
247 
248                     /* Restore interrupts.   */
249                     TX_RESTORE
250 
251                     /* Return a successful completion.  */
252                     status =  TX_SUCCESS;
253                 }
254             }
255 
256             /* Determine if we are done.  */
257             if (status == TX_NOT_DONE)
258             {
259 
260                 /* Yes, more to do.  */
261 
262                 /* Default the status to success.  */
263                 status =  TX_SUCCESS;
264 
265                 /* Save the original preemption-threshold thread.  */
266                 original_pt_thread =  _tx_thread_preemption__threshold_scheduled;
267 
268 #ifdef TX_NOT_INTERRUPTABLE
269 
270                 /* Increment the preempt disable flag.  */
271                 _tx_thread_preempt_disable++;
272 
273                 /* Set the state to priority change.  */
274                 thread_ptr -> tx_thread_state =    TX_PRIORITY_CHANGE;
275 
276                 /* Call actual non-interruptable thread suspension routine.  */
277                 _tx_thread_system_ni_suspend(thread_ptr, ((ULONG) 0));
278 
279                 /* At this point, the preempt disable flag is still set, so we still have
280                    protection against all preemption.  */
281 
282                 /* Setup the new priority for this thread.  */
283                 thread_ptr -> tx_thread_user_priority =           new_priority;
284                 thread_ptr -> tx_thread_user_preempt_threshold =  new_priority;
285 
286                 /* Determine if the actual thread priority should be setup, which is the
287                    case if the new priority is higher than the priority inheritance.  */
288                 if (new_priority < thread_ptr -> tx_thread_inherit_priority)
289                 {
290 
291                     /* Change thread priority to the new user's priority.  */
292                     thread_ptr -> tx_thread_priority =           new_priority;
293                     thread_ptr -> tx_thread_preempt_threshold =  new_priority;
294                 }
295                 else
296                 {
297 
298                     /* Change thread priority to the priority inheritance.  */
299                     thread_ptr -> tx_thread_priority =           thread_ptr -> tx_thread_inherit_priority;
300                     thread_ptr -> tx_thread_preempt_threshold =  thread_ptr -> tx_thread_inherit_priority;
301                 }
302 
303                 /* Resume the thread with the new priority.  */
304                 _tx_thread_system_ni_resume(thread_ptr);
305 
306                 /* Decrement the preempt disable flag.  */
307                 _tx_thread_preempt_disable--;
308 #else
309 
310                 /* Increment the preempt disable flag.  */
311                 _tx_thread_preempt_disable =  _tx_thread_preempt_disable + ((UINT) 3);
312 
313                 /* Set the state to priority change.  */
314                 thread_ptr -> tx_thread_state =    TX_PRIORITY_CHANGE;
315 
316                 /* Set the suspending flag. */
317                 thread_ptr -> tx_thread_suspending =  TX_TRUE;
318 
319                 /* Setup the timeout period.  */
320                 thread_ptr -> tx_thread_timer.tx_timer_internal_remaining_ticks =  ((ULONG) 0);
321 
322                 /* Restore interrupts.  */
323                 TX_RESTORE
324 
325                 /* The thread is ready and must first be removed from the list.  Call the
326                    system suspend function to accomplish this.  */
327                 _tx_thread_system_suspend(thread_ptr);
328 
329                 /* Lockout interrupts again.  */
330                 TX_DISABLE
331 
332                 /* At this point, the preempt disable flag is still set, so we still have
333                    protection against all preemption.  */
334 
335                 /* Setup the new priority for this thread.  */
336                 thread_ptr -> tx_thread_user_priority =           new_priority;
337                 thread_ptr -> tx_thread_user_preempt_threshold =  new_priority;
338 
339                 /* Determine if the actual thread priority should be setup, which is the
340                    case if the new priority is higher than the priority inheritance.  */
341                 if (new_priority < thread_ptr -> tx_thread_inherit_priority)
342                 {
343 
344                     /* Change thread priority to the new user's priority.  */
345                     thread_ptr -> tx_thread_priority =           new_priority;
346                     thread_ptr -> tx_thread_preempt_threshold =  new_priority;
347                 }
348                 else
349                 {
350 
351                     /* Change thread priority to the priority inheritance.  */
352                     thread_ptr -> tx_thread_priority =           thread_ptr -> tx_thread_inherit_priority;
353                     thread_ptr -> tx_thread_preempt_threshold =  thread_ptr -> tx_thread_inherit_priority;
354                 }
355 
356                 /* Restore interrupts.  */
357                 TX_RESTORE
358 
359                 /* Resume the thread with the new priority.  */
360                 _tx_thread_system_resume(thread_ptr);
361 #endif
362 
363                 /* Optional processing extension.  */
364                 TX_THREAD_PRIORITY_CHANGE_EXTENSION
365 
366 #ifndef TX_NOT_INTERRUPTABLE
367 
368                 /* Disable interrupts.  */
369                 TX_DISABLE
370 
371                 /* Decrement the preemption-threshold flag.  */
372                 _tx_thread_preempt_disable--;
373 #endif
374 
375                 /* Determine if the thread was previously executing.  */
376                 if (thread_ptr == execute_ptr)
377                 {
378 
379                     /* Make sure the thread is still ready.  */
380                     if (thread_ptr -> tx_thread_state == TX_READY)
381                     {
382 
383 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
384                         /* Determine if preemption-threshold is in force at the new priority level.  */
385                         if (_tx_thread_preemption_threshold_list[thread_ptr -> tx_thread_priority] == TX_NULL)
386                         {
387 
388                             /* Ensure that this thread is placed at the front of the priority list.  */
389                             _tx_thread_priority_list[thread_ptr -> tx_thread_priority] =  thread_ptr;
390                         }
391 #else
392 
393                         /* Ensure that this thread is placed at the front of the priority list.  */
394                         _tx_thread_priority_list[thread_ptr -> tx_thread_priority] =  thread_ptr;
395 #endif
396                     }
397                 }
398 
399                 /* Pickup the core index.  */
400                 core_index =  thread_ptr -> tx_thread_smp_core_mapped;
401 
402 #ifndef TX_THREAD_SMP_DYNAMIC_CORE_MAX
403 
404                 /* Pickup the next thread to execute.   */
405                 if (core_index < ((UINT) TX_THREAD_SMP_MAX_CORES))
406 #else
407 
408                 /* Pickup the next thread to execute.   */
409                 if (core_index < _tx_thread_smp_max_cores)
410 #endif
411                 {
412 
413                     /* Determine if this thread is not the next thread to execute.  */
414                     if (thread_ptr != _tx_thread_execute_ptr[core_index])
415                     {
416 
417                         /* Now determine if this thread was previously executing thread.  */
418                         if (thread_ptr == execute_ptr)
419                         {
420 
421                             /* Determine if we moved to a lower priority. If so, move the thread to the front of the priority list.  */
422                             if (original_priority < new_priority)
423                             {
424 
425                                 /* Make sure the thread is still ready.  */
426                                 if (thread_ptr -> tx_thread_state == TX_READY)
427                                 {
428 
429                                     /* Determine the lowest priority scheduled thread.  */
430                                     lowest_priority =  _tx_thread_smp_lowest_priority_get();
431 
432                                     /* Determine if this thread has a higher or same priority as the lowest priority
433                                        in the list.  */
434                                     if (thread_ptr -> tx_thread_priority <= lowest_priority)
435                                     {
436 
437                                         /* Yes, we need to rebalance to make it possible for this thread to execute.  */
438 
439                                         /* Determine if the thread with preemption-threshold thread has changed... and is
440                                            not the scheduled thread.   */
441                                         if ((original_pt_thread != _tx_thread_preemption__threshold_scheduled) &&
442                                             (original_pt_thread != thread_ptr))
443                                         {
444 
445                                             /* Yes, preemption-threshold has changed.  Determine if it can or should
446                                                be reversed.  */
447 
448 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
449 
450                                             /* Pickup the preemption-threshold thread.  */
451                                             new_pt_thread =  _tx_thread_preemption__threshold_scheduled;
452 #endif
453 
454                                             /* Restore the original preemption-threshold thread.  */
455                                             _tx_thread_preemption__threshold_scheduled =  original_pt_thread;
456 
457 
458 #ifndef TX_DISABLE_PREEMPTION_THRESHOLD
459 
460                                             /* Determine if there is a new preemption-threshold thread to reverse.  */
461                                             if (new_pt_thread != TX_NULL)
462                                             {
463 
464                                                 /* Clear the information associated with the new preemption-threshold thread.  */
465 
466                                                 /* Pickup the priority.  */
467                                                 priority =  new_pt_thread -> tx_thread_priority;
468 
469                                                 /* Clear the preempted list entry.  */
470                                                 _tx_thread_preemption_threshold_list[priority] =  TX_NULL;
471 
472 #if TX_MAX_PRIORITIES > 32
473                                                 /* Calculate the bit map array index.  */
474                                                 map_index =  new_priority/((UINT) 32);
475 #endif
476 
477                                                 /* Ensure that this thread's priority is clear in the preempt map.  */
478                                                 TX_MOD32_BIT_SET(priority, priority_bit)
479                                                 _tx_thread_preempted_maps[MAP_INDEX] =  _tx_thread_preempted_maps[MAP_INDEX] & (~(priority_bit));
480 #if TX_MAX_PRIORITIES > 32
481 
482                                                 /* Determine if there are any other bits set in this preempt map.  */
483                                                 if (_tx_thread_preempted_maps[MAP_INDEX] == ((ULONG) 0))
484                                                 {
485 
486                                                     /* No, clear the active bit to signify this preempted map has nothing set.  */
487                                                     TX_DIV32_BIT_SET(priority, priority_bit)
488                                                     _tx_thread_preempted_map_active =  _tx_thread_preempted_map_active & (~(priority_bit));
489                                                 }
490 #endif
491                                             }
492 #endif
493                                         }
494 
495                                         /* Pickup the index.  */
496                                         core_index =  TX_SMP_CORE_ID;
497 
498                                         /* Call the rebalance routine. This routine maps cores and ready threads.  */
499                                         _tx_thread_smp_rebalance_execute_list(core_index);
500                                     }
501                                 }
502                             }
503                         }
504                     }
505                 }
506 
507                 /* Restore interrupts.  */
508                 TX_RESTORE
509 
510                 /* Check for preemption.  */
511                 _tx_thread_system_preempt_check();
512             }
513         }
514     }
515 
516     /* Return completion status.  */
517     return(status);
518 }
519 
520