Oh no! Where's the JavaScript?
Your Web browser does not have JavaScript enabled or does not support JavaScript. Please enable JavaScript on your Web browser to properly view this Web site, or upgrade to a Web browser that does support JavaScript.

[PATCH] x86_64: a one-instruction thread pointer via the %gs block

Last updated on 16 hours ago
D
deadwoodAROS Dev
Posted 16 hours ago
Some comments from me:

Generally for such core system issues, it is best to post them here: https://github.com/aros-development-team/AROS/issues to have more AROS developers look at them. Kalamatee is probably not reading these forums and he would have to make a final call on this.

On the implementation itself - the problem I'm seeing with it is that it is not portable across architectures but also possibly (not checked) across targets withint architecutre. On x86_64 we have to targets - native (that you tested) and hosted - a linux application. Both of those targets have a different kernels (your changes are in x86_64-pc - so the native target).

The key point is however than a user application needs to work on both of them. So this fast route through gs would have to be accessible also to application running under linux-x86_64 (hosted). I don't know if Linux would allow that - or Windows - we also have/might have a Window hosted target.

There is a lesser issues of cross-architecture access, so that one source code compiles across different AROS architectures. I guess you could re-define FindTask() on x86_64 to use direct access and keep it using LVO call on other architectures.
C
coffeecatJunior Member
Posted 18 hours ago
Subject: [PATCH] x86_64: a one-instruction thread pointer via the %gs block

Hi all,

I've been looking at what it would take to run a language runtime with real thread-local storage on AROS x86_64, and ended up with a two-patch change to the kernel that I'd like some eyes on.

What it does. It makes FindTask(NULL) readable from userspace as a single instruction, and gives every task a private, writable word reachable the same way:


movq %gs:0x10, %rax ; the running task
movq %gs:0x18, %rax ; address of this task's private TLS word


Why this is small. The mechanism was already there and unused. arch/x86_64-pc/kernel/tls.h keeps a per-CPU tls_t behind %gs, and kernel_startup.c builds its GDT descriptor with dpl = 3, so userspace may read it. There's no swapgs in the kernel and no FS/GS base MSR write anywhere — it's a plain GDT descriptor set once at boot. I confirmed from a normal program that movq %gs:0, %rax returns SysBase before writing any patch. The block just held globals; the dispatcher knows the task it's about to run and never published it.

So patch 1 adds struct Task *ThisTask to tls_t plus one TLS_SET in cpu_Dispatch. The field goes before the __AROSEXEC_SMP__ member deliberately, so the offset is the same in UP and SMP builds — code outside the kernel needs one constant, not one per config.

Patch 2 adds APTR iet_TLSSlot to struct IntETask and publishes its address. IntETask is private and already allocated, so it costs no extra allocation and needs no new kernel.resource call. Tasks without TF_ETASK get NULL, and the slot is cleared on entry so they can't inherit the previous task's pointer.

I did consider tc_UserData for the storage and rejected it: it's documented "for use by the task; no restrictions!" and the USB classes, trackdisk and dbus genuinely use it, so a language runtime claiming it would collide with the libraries its own programs call.

Total is 38 lines across three files, three stores in the dispatcher, nothing else on the hot path.

Measured. A probe run from the Startup-Sequence on a live CD built from the patched tree — four pthreads plus main, each writing a private value through the pointer and re-checking it two million times:


thread 0: task=0x4be9e980 OK tls=0x4bedf348 val=0xc0de0000 OK drift=0 tlsbad=0
thread 1: task=0x4bedf6c0 OK tls=0x4bf20128 val=0xc0de0001 OK drift=0 tlsbad=0
thread 2: task=0x4bf204a0 OK tls=0x4bf60f98 val=0xc0de0002 OK drift=0 tlsbad=0
thread 3: task=0x4bf61310 OK tls=0x4bfa1e48 val=0xc0de0003 OK drift=0 tlsbad=0


Four distinct slots, every thread reading back its own value, zero corruption over eight million reads.

What I could not test, and where I'd like help. This was QEMU only, and — importantly — a UP kernel: %gs:16 read back as CPUNumber rather than a ScheduleData pointer, so __AROSEXEC_SMP__ was off. My argument that there's no migration hazard is that a single load already yields our own task, since at the instant it executes we are what's running on that core; a two-instruction load-then-deref could be preempted in between and read another core's task. That reasoning feels right to me but it is reasoning, not a measurement, and SMP is exactly where I can't check it. I also haven't run the nightly test suite against it.

If someone with an SMP build can try it, or tell me the placement of ThisTaskTLS is wrong, that's the feedback I'm after.

Who else this helps. Not just my use case: C and C++ would get real thread_local instead of a pthread_getspecific per access (GCC for AROS is built --disable-tls, so __thread currently goes through __emutls_get_address); the Rust target could set has-thread-local; and workbench/libs/mesa/tls.c could drop its list-walking GetFromTLS.

Patches, the probe and the full write-up:
https://github.com/tomaszstaniak/aros-tls

They're against deadwood2/AROS but tls.h there is byte-identical to mainline, so they apply to both.

Open to any comments and feedback, especially if I missed anything that already exists/was done by someone else. Thank you!
You can view all discussion threads in this forum.
You cannot start a new discussion thread in this forum.
You cannot reply in this discussion thread.
You cannot start on a poll in this forum.
You cannot upload attachments in this forum.
You cannot download attachments in this forum.
Users who participated in discussion: deadwood, coffeecat