diff options
author | Tom <tomut@yahoo.com> | 2021-01-26 14:16:07 -0700 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-01-27 21:12:24 +0100 |
commit | 21d288a10e5a1a44e8852b70dd3bdfb619483180 (patch) | |
tree | 4f9329b50ba4cd625305296c5686bc9d0c787d74 /Kernel/Arch/i386/CPU.h | |
parent | f88a8b16d7391ccdb828f659e40b892fe8fdbc83 (diff) | |
download | serenity-21d288a10e5a1a44e8852b70dd3bdfb619483180.zip |
Kernel: Make Thread::current smp-safe
Change Thread::current to be a static function and read using the fs
register, which eliminates a window between Processor::current()
returning and calling a function on it, which can trigger preemption
and a move to a different processor, which then causes operating
on the wrong object.
Diffstat (limited to 'Kernel/Arch/i386/CPU.h')
-rw-r--r-- | Kernel/Arch/i386/CPU.h | 23 |
1 files changed, 14 insertions, 9 deletions
diff --git a/Kernel/Arch/i386/CPU.h b/Kernel/Arch/i386/CPU.h index bdc75034dd..3c0e1ab5db 100644 --- a/Kernel/Arch/i386/CPU.h +++ b/Kernel/Arch/i386/CPU.h @@ -700,7 +700,7 @@ class Processor { AK_MAKE_NONCOPYABLE(Processor); AK_MAKE_NONMOVABLE(Processor); - Processor* m_self; // must be first field (%fs offset 0x0) + Processor* m_self; DescriptorTablePointer m_gdtr; Descriptor m_gdt[256]; @@ -812,12 +812,12 @@ public: ALWAYS_INLINE static Processor& current() { - return *(Processor*)read_fs_u32(0); + return *(Processor*)read_fs_u32(__builtin_offsetof(Processor, m_self)); } ALWAYS_INLINE static bool is_initialized() { - return get_fs() == GDT_SELECTOR_PROC && read_fs_u32(0) != 0; + return get_fs() == GDT_SELECTOR_PROC && read_fs_u32(__builtin_offsetof(Processor, m_self)) != 0; } ALWAYS_INLINE void set_scheduler_data(SchedulerPerProcessorData& scheduler_data) @@ -850,16 +850,21 @@ public: m_idle_thread = &idle_thread; } - ALWAYS_INLINE Thread* current_thread() const + ALWAYS_INLINE static Thread* current_thread() { - // NOTE: NOT safe to call from another processor! - ASSERT(&Processor::current() == this); - return m_current_thread; + // If we were to use Processor::current here, we'd have to + // disable interrupts to prevent a race where we may get pre-empted + // right after getting the Processor structure and then get moved + // to another processor, which would lead us to get the wrong thread. + // To avoid having to disable interrupts, we can just read the field + // directly in an atomic fashion, similar to Processor::current. + return (Thread*)read_fs_u32(__builtin_offsetof(Processor, m_current_thread)); } - ALWAYS_INLINE void set_current_thread(Thread& current_thread) + ALWAYS_INLINE static void set_current_thread(Thread& current_thread) { - m_current_thread = ¤t_thread; + // See comment in Processor::current_thread + write_fs_u32(__builtin_offsetof(Processor, m_current_thread), FlatPtr(¤t_thread)); } ALWAYS_INLINE u32 id() |