summaryrefslogtreecommitdiff
path: root/Kernel/Arch/i386/CPU.h
diff options
context:
space:
mode:
authorTom <tomut@yahoo.com>2021-01-26 14:16:07 -0700
committerAndreas Kling <kling@serenityos.org>2021-01-27 21:12:24 +0100
commit21d288a10e5a1a44e8852b70dd3bdfb619483180 (patch)
tree4f9329b50ba4cd625305296c5686bc9d0c787d74 /Kernel/Arch/i386/CPU.h
parentf88a8b16d7391ccdb828f659e40b892fe8fdbc83 (diff)
downloadserenity-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.h23
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 = &current_thread;
+ // See comment in Processor::current_thread
+ write_fs_u32(__builtin_offsetof(Processor, m_current_thread), FlatPtr(&current_thread));
}
ALWAYS_INLINE u32 id()