diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index db4a48f289..a81bb6b8b5 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -228,6 +228,7 @@ set(KERNEL_SOURCES VM/Space.cpp VM/VMObject.cpp WaitQueue.cpp + WorkQueue.cpp init.cpp kprintf.cpp ) diff --git a/Kernel/Devices/AsyncDeviceRequest.h b/Kernel/Devices/AsyncDeviceRequest.h index 3d6704e87b..8084f5b971 100644 --- a/Kernel/Devices/AsyncDeviceRequest.h +++ b/Kernel/Devices/AsyncDeviceRequest.h @@ -37,6 +37,8 @@ namespace Kernel { class Device; +extern WorkQueue* g_io_work; + class AsyncDeviceRequest : public RefCounted { AK_MAKE_NONCOPYABLE(AsyncDeviceRequest); AK_MAKE_NONMOVABLE(AsyncDeviceRequest); diff --git a/Kernel/Forward.h b/Kernel/Forward.h index e5a3de0c27..3bd2b6e36b 100644 --- a/Kernel/Forward.h +++ b/Kernel/Forward.h @@ -76,6 +76,7 @@ class UserOrKernelBuffer; class VFS; class VMObject; class WaitQueue; +class WorkQueue; template class KResultOr; diff --git a/Kernel/Storage/IDEChannel.cpp b/Kernel/Storage/IDEChannel.cpp index 1b1213fa69..1f3dea0712 100644 --- a/Kernel/Storage/IDEChannel.cpp +++ b/Kernel/Storage/IDEChannel.cpp @@ -35,6 +35,7 @@ #include #include #include +#include namespace Kernel { @@ -122,7 +123,7 @@ void IDEChannel::complete_current_request(AsyncDeviceRequest::RequestResult resu // This is important so that we can safely write the buffer back, // which could cause page faults. Note that this may be called immediately // before Processor::deferred_call_queue returns! - Processor::deferred_call_queue([this, result]() { + g_io_work->queue([this, result]() { dbgln_if(PATA_DEBUG, "IDEChannel::complete_current_request result: {}", (int)result); ScopedSpinLock lock(m_request_lock); VERIFY(m_current_request); diff --git a/Kernel/WorkQueue.cpp b/Kernel/WorkQueue.cpp new file mode 100644 index 0000000000..7078408abf --- /dev/null +++ b/Kernel/WorkQueue.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2021, The SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include + +namespace Kernel { + +WorkQueue* g_io_work; + +void WorkQueue::initialize() +{ + g_io_work = new WorkQueue("IO WorkQueue"); +} + +WorkQueue::WorkQueue(const char* name) + : m_name(name) +{ + RefPtr thread; + Process::create_kernel_process(thread, name, [this] { + for (;;) { + WorkItem* item; + bool have_more; + { + ScopedSpinLock lock(m_lock); + item = m_items.take_first(); + have_more = !m_items.is_empty(); + } + if (item) { + item->function(item->data); + if (item->free_data) + item->free_data(item->data); + delete item; + + if (have_more) + continue; + } + [[maybe_unused]] auto result = m_wait_queue.wait_on({}); + } + }); + // If we can't create the thread we're in trouble... + m_thread = thread.release_nonnull(); +} + +void WorkQueue::do_queue(WorkItem* item) +{ + { + ScopedSpinLock lock(m_lock); + m_items.append(*item); + } + m_wait_queue.wake_one(); +} + +} diff --git a/Kernel/WorkQueue.h b/Kernel/WorkQueue.h new file mode 100644 index 0000000000..a37a41accf --- /dev/null +++ b/Kernel/WorkQueue.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2021, The SerenityOS developers. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include +#include + +namespace Kernel { + +extern WorkQueue* g_io_work; + +class WorkQueue { + AK_MAKE_NONCOPYABLE(WorkQueue); + AK_MAKE_NONMOVABLE(WorkQueue); + +public: + static void initialize(); + + WorkQueue(const char*); + + void queue(void (*function)(void*), void* data = nullptr, void (*free_data)(void*) = nullptr) + { + auto* item = new WorkItem; // TODO: use a pool + item->function = function; + item->data = data; + item->free_data = free_data; + do_queue(item); + } + + template + void queue(Function function) + { + auto* item = new WorkItem; // TODO: use a pool + item->function = [](void* f) { + (*reinterpret_cast(f))(); + }; + if constexpr (sizeof(Function) <= sizeof(item->inline_data)) { + item->data = new (item->inline_data) Function(move(function)); + item->free_data = [](void* f) { + reinterpret_cast(f)->~Function(); + }; + + } else { + item->data = new Function(move(function)); + item->free_data = [](void* f) { + delete reinterpret_cast(f); + }; + } + do_queue(item); + } + +private: + struct WorkItem { + IntrusiveListNode m_node; + void (*function)(void*); + void* data; + void (*free_data)(void*); + u8 inline_data[4 * sizeof(void*)]; + }; + + void do_queue(WorkItem*); + + const char* const m_name; + RefPtr m_thread; + WaitQueue m_wait_queue; + IntrusiveList m_items; + SpinLock m_lock; +}; + +} diff --git a/Kernel/init.cpp b/Kernel/init.cpp index 71615fe84d..847bf7ad34 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -72,6 +72,7 @@ #include #include #include +#include #include // Defined in the linker script @@ -189,6 +190,8 @@ extern "C" UNMAP_AFTER_INIT [[noreturn]] void init() Process::initialize(); Scheduler::initialize(); + WorkQueue::initialize(); + { RefPtr init_stage2_thread; Process::create_kernel_process(init_stage2_thread, "init_stage2", init_stage2, nullptr);