Changes in / [54dcab1:8fc652e0]
- File:
-
- 1 edited
-
libcfa/src/concurrency/preemption.cfa (modified) (4 diffs)
Legend:
- Unmodified
- Added
- Removed
-
libcfa/src/concurrency/preemption.cfa
r54dcab1 r8fc652e0 10 10 // Created On : Mon Jun 5 14:20:42 2017 11 11 // Last Modified By : Peter A. Buhr 12 // Last Modified On : Fri Nov 6 07:42:13 202013 // Update Count : 5 412 // Last Modified On : Wed Aug 26 16:46:03 2020 13 // Update Count : 53 14 14 // 15 15 … … 163 163 // Kernel Signal Tools 164 164 //============================================================================================= 165 // In a user-level threading system, there are handful of thread-local variables where this problem occurs on the ARM.166 //167 // For each kernel thread running user-level threads, there is a flag variable to indicate if interrupts are168 // enabled/disabled for that kernel thread. Therefore, this variable is made thread local.169 //170 // For example, this code fragment sets the state of the "interrupt" variable in thread-local memory.171 //172 // _Thread_local volatile int interrupts;173 // int main() {174 // interrupts = 0; // disable interrupts }175 //176 // which generates the following code on the ARM177 //178 // (gdb) disassemble main179 // Dump of assembler code for function main:180 // 0x0000000000000610 <+0>: mrs x1, tpidr_el0181 // 0x0000000000000614 <+4>: mov w0, #0x0 // #0182 // 0x0000000000000618 <+8>: add x1, x1, #0x0, lsl #12183 // 0x000000000000061c <+12>: add x1, x1, #0x10184 // 0x0000000000000620 <+16>: str wzr, [x1]185 // 0x0000000000000624 <+20>: ret186 //187 // The mrs moves a pointer from coprocessor register tpidr_el0 into register x1. Register w0 is set to 0. The two adds188 // increase the TLS pointer with the displacement (offset) 0x10, which is the location in the TSL of variable189 // "interrupts". Finally, 0 is stored into "interrupts" through the pointer in register x1 that points into the190 // TSL. Now once x1 has the pointer to the location of the TSL for kernel thread N, it can be be preempted at a191 // user-level and the user thread is put on the user-level ready-queue. When the preempted thread gets to the front of192 // the user-level ready-queue it is run on kernel thread M. It now stores 0 into "interrupts" back on kernel thread N,193 // turning off interrupt on the wrong kernel thread.194 //195 // On the x86, the following code is generated for the same code fragment.196 //197 // (gdb) disassemble main198 // Dump of assembler code for function main:199 // 0x0000000000400420 <+0>: movl $0x0,%fs:0xfffffffffffffffc200 // 0x000000000040042c <+12>: xor %eax,%eax201 // 0x000000000040042e <+14>: retq202 //203 // and there is base-displacement addressing used to atomically reset variable "interrupts" off of the TSL pointer in204 // register "fs".205 //206 // Hence, the ARM has base-displacement address for the general purpose registers, BUT not to the coprocessor207 // registers. As a result, generating the address for the write into variable "interrupts" is no longer atomic.208 //209 // Note this problem does NOT occur when just using multiple kernel threads because the preemption ALWAYS restarts the210 // thread on the same kernel thread.211 //212 // The obvious question is why does ARM use a coprocessor register to store the TSL pointer given that coprocessor213 // registers are second-class registers with respect to the instruction set. One possible answer is that they did not214 // want to dedicate one of the general registers to hold the TLS pointer and there was a free coprocessor register215 // available.216 165 217 166 //---------- … … 247 196 return val; 248 197 } 198 199 // //---------- 200 // // Write data to the TLS block 201 // // sadly it looses the type information and can only write 1 word at a time 202 // // use with __builtin_offsetof 203 // void __cfatls_set(uintptr_t offset, void * value) __attribute__((__noinline__)); 204 // void __cfatls_set(uintptr_t offset, void * value) { 205 // // create a assembler label before 206 // // marked as clobber all to avoid movement 207 // asm volatile("__cfaasm_set_before:":::"memory"); 208 209 // // access tls as normal (except for type information) 210 // *(void**)(offset + (uintptr_t)&my_tls) = value; 211 212 // // create a assembler label after 213 // // marked as clobber all to avoid movement 214 // asm volatile("__cfaasm_set_after:":::"memory"); 215 // } 216 217 // //---------- 218 // #include <stdio.h> 219 // int main() { 220 // // Get the information 221 // // Must use inline assembly to get access to label 222 // // C is annoying here because this could easily be a static const but "initializer element is not a compile-time constant" 223 // // The big advantage of this approach is that there is 0 overhead for the read and writes function 224 // void * __cfaasm_addr_get_before = ({ void * value; asm("movq $__cfaasm_get_before, %[v]\n\t" : [v]"=r"(value) ); value; }); 225 // void * __cfaasm_addr_get_after = ({ void * value; asm("movq $__cfaasm_get_after , %[v]\n\t" : [v]"=r"(value) ); value; }); 226 // void * __cfaasm_addr_set_before = ({ void * value; asm("movq $__cfaasm_set_before, %[v]\n\t" : [v]"=r"(value) ); value; }); 227 // void * __cfaasm_addr_set_after = ({ void * value; asm("movq $__cfaasm_set_after , %[v]\n\t" : [v]"=r"(value) ); value; }); 228 229 // printf("%p to %p\n", __cfaasm_addr_get_before, __cfaasm_addr_get_after); 230 // printf("%p to %p\n", __cfaasm_addr_set_before, __cfaasm_addr_set_after); 231 // return 0; 232 // } 233 234 __cfaabi_dbg_debug_do( static thread_local void * last_interrupt = 0; ) 249 235 250 236 extern "C" { … … 508 494 #endif 509 495 510 __cfaabi_dbg_debug_do( static thread_local void * last_interrupt = 0; )511 512 496 // Context switch signal handler 513 497 // Receives SIGUSR1 signal and causes the current thread to yield
Note:
See TracChangeset
for help on using the changeset viewer.