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