Ver Fonte

Kernel: Implement aligned `operator new` and use it

The compiler will use these to allocate objects that have alignment
requirements greater than that of our normal `operator new` (4/8 byte
aligned).

This means we can now use smart pointers for over-aligned types.

Fixes a FIXME.
Daniel Bertalan há 4 anos atrás
pai
commit
dd4ed4d22d
4 ficheiros alterados com 41 adições e 10 exclusões
  1. 27 0
      Kernel/Heap/kmalloc.cpp
  2. 8 0
      Kernel/Heap/kmalloc.h
  3. 4 8
      Kernel/Thread.cpp
  4. 2 2
      Kernel/Thread.h

+ 27 - 0
Kernel/Heap/kmalloc.cpp

@@ -301,6 +301,16 @@ size_t kmalloc_good_size(size_t size)
     return size;
 }
 
+[[gnu::malloc, gnu::alloc_size(1), gnu::alloc_align(2)]] static void* kmalloc_aligned_cxx(size_t size, size_t alignment)
+{
+    VERIFY(alignment <= 4096);
+    void* ptr = kmalloc(size + alignment + sizeof(ptrdiff_t));
+    size_t max_addr = (size_t)ptr + alignment;
+    void* aligned_ptr = (void*)(max_addr - (max_addr % alignment));
+    ((ptrdiff_t*)aligned_ptr)[-1] = (ptrdiff_t)((u8*)aligned_ptr - (u8*)ptr);
+    return aligned_ptr;
+}
+
 void* operator new(size_t size)
 {
     void* ptr = kmalloc(size);
@@ -313,6 +323,18 @@ void* operator new(size_t size, const std::nothrow_t&) noexcept
     return kmalloc(size);
 }
 
+void* operator new(size_t size, std::align_val_t al)
+{
+    void* ptr = kmalloc_aligned_cxx(size, (size_t)al);
+    VERIFY(ptr);
+    return ptr;
+}
+
+void* operator new(size_t size, std::align_val_t al, const std::nothrow_t&) noexcept
+{
+    return kmalloc_aligned_cxx(size, (size_t)al);
+}
+
 void* operator new[](size_t size)
 {
     void* ptr = kmalloc(size);
@@ -336,6 +358,11 @@ void operator delete(void* ptr, size_t size) noexcept
     return kfree_sized(ptr, size);
 }
 
+void operator delete(void* ptr, size_t, std::align_val_t) noexcept
+{
+    return kfree_aligned(ptr);
+}
+
 void operator delete[](void*) noexcept
 {
     // All deletes in kernel code should have a known size.

+ 8 - 0
Kernel/Heap/kmalloc.h

@@ -34,6 +34,8 @@ struct nothrow_t {
 };
 
 extern const nothrow_t nothrow;
+
+enum class align_val_t : size_t {};
 };
 
 void kmalloc_init();
@@ -59,10 +61,16 @@ inline void* operator new[](size_t, void* p) { return p; }
 
 [[nodiscard]] void* operator new(size_t size);
 [[nodiscard]] void* operator new(size_t size, const std::nothrow_t&) noexcept;
+[[nodiscard]] void* operator new(size_t size, std::align_val_t);
+[[nodiscard]] void* operator new(size_t size, std::align_val_t, const std::nothrow_t&) noexcept;
+
 void operator delete(void* ptr) noexcept;
 void operator delete(void* ptr, size_t) noexcept;
+void operator delete(void* ptr, size_t, std::align_val_t) noexcept;
+
 [[nodiscard]] void* operator new[](size_t size);
 [[nodiscard]] void* operator new[](size_t size, const std::nothrow_t&) noexcept;
+
 void operator delete[](void* ptrs) noexcept;
 void operator delete[](void* ptr, size_t) noexcept;
 

+ 4 - 8
Kernel/Thread.cpp

@@ -38,11 +38,9 @@ UNMAP_AFTER_INIT void Thread::initialize()
 
 KResultOr<NonnullRefPtr<Thread>> Thread::try_create(NonnullRefPtr<Process> process)
 {
-    // FIXME: Once we have aligned + nothrow operator new, we can avoid the manual kfree.
-    FPUState* fpu_state = (FPUState*)kmalloc_aligned<16>(sizeof(FPUState));
+    auto fpu_state = try_make<FPUState>();
     if (!fpu_state)
         return ENOMEM;
-    ArmedScopeGuard fpu_guard([fpu_state]() { kfree_aligned(fpu_state); });
 
     auto kernel_stack_region = MM.allocate_kernel_region(default_kernel_stack_size, {}, Region::Access::Read | Region::Access::Write, AllocationStrategy::AllocateNow);
     if (!kernel_stack_region)
@@ -53,18 +51,17 @@ KResultOr<NonnullRefPtr<Thread>> Thread::try_create(NonnullRefPtr<Process> proce
     if (!block_timer)
         return ENOMEM;
 
-    auto thread = adopt_ref_if_nonnull(new (nothrow) Thread(move(process), kernel_stack_region.release_nonnull(), block_timer.release_nonnull(), fpu_state));
+    auto thread = adopt_ref_if_nonnull(new (nothrow) Thread(move(process), kernel_stack_region.release_nonnull(), block_timer.release_nonnull(), fpu_state.release_nonnull()));
     if (!thread)
         return ENOMEM;
-    fpu_guard.disarm();
 
     return thread.release_nonnull();
 }
 
-Thread::Thread(NonnullRefPtr<Process> process, NonnullOwnPtr<Region> kernel_stack_region, NonnullRefPtr<Timer> block_timer, FPUState* fpu_state)
+Thread::Thread(NonnullRefPtr<Process> process, NonnullOwnPtr<Region> kernel_stack_region, NonnullRefPtr<Timer> block_timer, NonnullOwnPtr<FPUState> fpu_state)
     : m_process(move(process))
     , m_kernel_stack_region(move(kernel_stack_region))
-    , m_fpu_state(fpu_state)
+    , m_fpu_state(move(fpu_state))
     , m_name(m_process->name())
     , m_block_timer(block_timer)
     , m_global_procfs_inode_index(ProcFSComponentRegistry::the().allocate_inode_index())
@@ -533,7 +530,6 @@ void Thread::finalize()
     if (m_dump_backtrace_on_finalization)
         dbgln("{}", backtrace());
 
-    kfree_aligned(m_fpu_state);
     drop_thread_count(false);
 }
 

+ 2 - 2
Kernel/Thread.h

@@ -1192,7 +1192,7 @@ public:
     String backtrace();
 
 private:
-    Thread(NonnullRefPtr<Process>, NonnullOwnPtr<Region>, NonnullRefPtr<Timer>, FPUState*);
+    Thread(NonnullRefPtr<Process>, NonnullOwnPtr<Region>, NonnullRefPtr<Timer>, NonnullOwnPtr<FPUState>);
 
     IntrusiveListNode<Thread> m_process_thread_list_node;
     int m_runnable_priority { -1 };
@@ -1318,7 +1318,7 @@ private:
     unsigned m_ipv4_socket_read_bytes { 0 };
     unsigned m_ipv4_socket_write_bytes { 0 };
 
-    FPUState* m_fpu_state { nullptr };
+    OwnPtr<FPUState> m_fpu_state;
     State m_state { Invalid };
     String m_name;
     u32 m_priority { THREAD_PRIORITY_NORMAL };