Changeset 54dcab1


Ignore:
Timestamp:
Nov 6, 2020, 11:25:30 AM (4 years ago)
Author:
Thierry Delisle <tdelisle@…>
Branches:
ADT, arm-eh, ast-experimental, enum, forall-pointer-decay, jacob/cs343-translation, master, new-ast-unique-expr, pthread-emulation, qualifiedEnum
Children:
82a2fed, 836c9925
Parents:
8fc652e0 (diff), 231b18f (diff)
Note: this is a merge changeset, the changes displayed below correspond to the merge itself.
Use the (diff) links above to see all the changes relative to each parent.
Message:

Merge branch 'master' of plg.uwaterloo.ca:software/cfa/cfa-cc

File:
1 edited

Legend:

Unmodified
Added
Removed
  • libcfa/src/concurrency/preemption.cfa

    r8fc652e0 r54dcab1  
    1010// Created On       : Mon Jun 5 14:20:42 2017
    1111// Last Modified By : Peter A. Buhr
    12 // Last Modified On : Wed Aug 26 16:46:03 2020
    13 // Update Count     : 53
     12// Last Modified On : Fri Nov  6 07:42:13 2020
     13// Update Count     : 54
    1414//
    1515
     
    163163// Kernel Signal Tools
    164164//=============================================================================================
     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 are
     168// 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 ARM
     177//
     178// (gdb) disassemble main
     179// Dump of assembler code for function main:
     180//    0x0000000000000610 <+0>:  mrs     x1, tpidr_el0
     181//    0x0000000000000614 <+4>:  mov     w0, #0x0                        // #0
     182//    0x0000000000000618 <+8>:  add     x1, x1, #0x0, lsl #12
     183//    0x000000000000061c <+12>: add     x1, x1, #0x10
     184//    0x0000000000000620 <+16>: str     wzr, [x1]
     185//    0x0000000000000624 <+20>: ret
     186//
     187// The mrs moves a pointer from coprocessor register tpidr_el0 into register x1.  Register w0 is set to 0. The two adds
     188// increase the TLS pointer with the displacement (offset) 0x10, which is the location in the TSL of variable
     189// "interrupts".  Finally, 0 is stored into "interrupts" through the pointer in register x1 that points into the
     190// TSL. Now once x1 has the pointer to the location of the TSL for kernel thread N, it can be be preempted at a
     191// user-level and the user thread is put on the user-level ready-queue. When the preempted thread gets to the front of
     192// 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 main
     198// Dump of assembler code for function main:
     199//    0x0000000000400420 <+0>:  movl   $0x0,%fs:0xfffffffffffffffc
     200//    0x000000000040042c <+12>: xor    %eax,%eax
     201//    0x000000000040042e <+14>: retq
     202//
     203// and there is base-displacement addressing used to atomically reset variable "interrupts" off of the TSL pointer in
     204// register "fs".
     205//
     206// Hence, the ARM has base-displacement address for the general purpose registers, BUT not to the coprocessor
     207// 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 the
     210// 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 coprocessor
     213// registers are second-class registers with respect to the instruction set. One possible answer is that they did not
     214// want to dedicate one of the general registers to hold the TLS pointer and there was a free coprocessor register
     215// available.
    165216
    166217//----------
     
    196247        return val;
    197248}
    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; )
    235249
    236250extern "C" {
     
    494508#endif
    495509
     510__cfaabi_dbg_debug_do( static thread_local void * last_interrupt = 0; )
     511
    496512// Context switch signal handler
    497513// Receives SIGUSR1 signal and causes the current thread to yield
Note: See TracChangeset for help on using the changeset viewer.