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 */ 18 /** */ 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_timer.h" 32 33 34 /**************************************************************************/ 35 /* */ 36 /* FUNCTION RELEASE */ 37 /* */ 38 /* _tx_thread_smp_protect SMP/Linux/GCC */ 39 /* 6.1 */ 40 /* AUTHOR */ 41 /* */ 42 /* William E. Lamie, Microsoft Corporation */ 43 /* */ 44 /* DESCRIPTION */ 45 /* */ 46 /* This function gets protection for running inside the ThreadX */ 47 /* source. This is acomplished by a combination of a test-and-set */ 48 /* flag and periodically disabling interrupts. */ 49 /* */ 50 /* INPUT */ 51 /* */ 52 /* None */ 53 /* */ 54 /* OUTPUT */ 55 /* */ 56 /* Previous Status Register */ 57 /* */ 58 /* CALLS */ 59 /* */ 60 /* pthread_self Get Linux thread ID */ 61 /* GetThreadPriority Get current thread priority */ 62 /* _tx_thread_smp_core_get Get the current core ID */ 63 /* */ 64 /* CALLED BY */ 65 /* */ 66 /* ThreadX Source */ 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_smp_protect(void)75UINT _tx_thread_smp_protect(void) 76 { 77 78 pthread_t current_thread_id; 79 int exit_code = 0; 80 struct sched_param sp; 81 UINT core; 82 UINT interrupt_posture; 83 TX_THREAD *current_thread; 84 UINT current_state; 85 86 /* Loop to attempt to get the protection. */ 87 do 88 { 89 90 /* First, get the critical section. */ 91 do 92 { 93 94 95 /* Lock Linux mutex. */ 96 _tx_linux_mutex_obtain(&_tx_linux_mutex); 97 98 /* Pickup the current thread ID. */ 99 current_thread_id = pthread_self(); 100 101 /* Pickup the current core. */ 102 core = _tx_thread_smp_core_get(); 103 104 /* Pickup the current thread pointer. */ 105 current_thread = _tx_thread_current_ptr[core]; 106 107 /* Determine if this is a thread (THREAD_PRIORITY_LOWEST) and it does not 108 match the current thread pointer. */ 109 if ((_tx_linux_threadx_thread) && 110 ((!current_thread) || (current_thread -> tx_thread_linux_thread_id != current_thread_id))) 111 { 112 113 /* This indicates the Linux thread was actually terminated by ThreadX is only 114 being allowed to run in order to cleanup its resources. */ 115 _tx_linux_mutex_release_all(&_tx_linux_mutex); 116 117 /* Exit this thread. */ 118 pthread_exit((void *)&exit_code); 119 } 120 121 /* Determine if this is not actually a thread. */ 122 if (!_tx_linux_threadx_thread) 123 break; 124 125 /* Now check for terminated or completed state... and preempt disable is not set! */ 126 if ((current_thread) && (_tx_thread_preempt_disable == 0)) 127 { 128 129 /* Pickup current state. */ 130 current_state = current_thread -> tx_thread_state; 131 132 /* Now check for terminated or completed state. */ 133 if ((current_state == TX_TERMINATED) || (current_state == TX_COMPLETED)) 134 { 135 136 /* Clear the preemption flag. */ 137 current_thread -> tx_thread_linux_deferred_preempt = TX_FALSE; 138 139 /* Indicate that this thread was suspended asynchronously. */ 140 current_thread -> tx_thread_linux_suspension_type = 1; 141 142 /* Save the remaining time-slice and disable it. */ 143 if (_tx_timer_time_slice[core]) 144 { 145 146 current_thread -> tx_thread_time_slice = _tx_timer_time_slice[core]; 147 _tx_timer_time_slice[core] = 0; 148 } 149 150 /* Clear the current thread pointer. */ 151 _tx_thread_current_ptr[core] = TX_NULL; 152 153 /* Clear this mapping entry. */ 154 _tx_linux_virtual_cores[core].tx_thread_smp_core_mapping_thread = TX_NULL; 155 _tx_linux_virtual_cores[core].tx_thread_smp_core_mapping_linux_thread_id = 0; 156 157 /* Indicate that this thread is now ready for scheduling again by another core. */ 158 current_thread -> tx_thread_smp_core_control = 1; 159 160 /* Debug entry. */ 161 _tx_linux_debug_entry_insert("SCHEDULE-thread_terminate_preempt_complete", __FILE__, __LINE__); 162 163 /* Release the scheduler's semaphore to immediately try again. */ 164 tx_linux_sem_post(&_tx_linux_scheduler_semaphore); 165 166 /* This indicates the Linux thread was actually terminated by ThreadX is only 167 being allowed to run in order to cleanup its resources. */ 168 _tx_linux_mutex_release_all(&_tx_linux_mutex); 169 170 /* Exit this thread. */ 171 pthread_exit((void *)&exit_code); 172 } 173 } 174 175 /* Determine if the deferred preempt flag is set. */ 176 if ((current_thread) && (current_thread -> tx_thread_linux_deferred_preempt)) 177 { 178 179 /* Release the scheduler's semaphore to immediately try again. */ 180 tx_linux_sem_post(&_tx_linux_scheduler_semaphore); 181 182 /* Release the protection that is nested. */ 183 _tx_linux_mutex_release_all(&_tx_linux_mutex); 184 185 /* Sleep just to let other threads run. */ 186 _tx_linux_thread_sleep(1000000); 187 } 188 else 189 { 190 191 /* Get out of the protection loop. */ 192 break; 193 } 194 } while (1); 195 196 /* Setup the returned interrupt posture. */ 197 interrupt_posture = _tx_linux_global_int_disabled_flag; 198 199 /* Determine if the protection is already active for this core. */ 200 if (_tx_thread_smp_protection.tx_thread_smp_protect_core == core) 201 { 202 203 /* Yes, we have the protection already. */ 204 205 /* Increment the protection count. */ 206 _tx_thread_smp_protection.tx_thread_smp_protect_count++; 207 208 /* Set the global interrupt disable value. */ 209 _tx_linux_global_int_disabled_flag = TX_TRUE; 210 211 /* Debug entry. */ 212 _tx_linux_debug_entry_insert("PROTECT-obtained-nested", __FILE__, __LINE__); 213 214 /* Get out of the retry loop. */ 215 break; 216 } 217 /* Determine if the protection is available. */ 218 else if (_tx_thread_smp_protection.tx_thread_smp_protect_core == 0xFFFFFFFF) 219 { 220 221 /* At this point we have the protection. Setup the protection structure. */ 222 _tx_thread_smp_protection.tx_thread_smp_protect_in_force = TX_TRUE; 223 _tx_thread_smp_protection.tx_thread_smp_protect_thread = current_thread; 224 _tx_thread_smp_protection.tx_thread_smp_protect_core = core; 225 _tx_thread_smp_protection.tx_thread_smp_protect_count = 1; 226 _tx_thread_smp_protection.tx_thread_smp_protect_linux_thread_id = current_thread_id; 227 228 /* Set the global interrupt disable value. */ 229 _tx_linux_global_int_disabled_flag = TX_TRUE; 230 231 /* Debug entry. */ 232 _tx_linux_debug_entry_insert("PROTECT-obtained", __FILE__, __LINE__); 233 234 /* Get out of the retry loop. */ 235 break; 236 } 237 else 238 { 239 240 /* Protection is owned by another core. */ 241 242 /* Release the protection and start over. */ 243 _tx_linux_mutex_release(&_tx_linux_mutex); 244 } 245 } while (1); 246 247 /* Set the global interrupt disable value. */ 248 _tx_linux_global_int_disabled_flag = TX_TRUE; 249 250 /* Return the interrupt posture. */ 251 return(interrupt_posture); 252 } 253 254 255