diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index e6c7997231..6112a857a9 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -14,6 +14,7 @@ set(KERNEL_SOURCES CMOS.cpp CommandLine.cpp Console.cpp + Devices/AsyncDeviceRequest.cpp Devices/BXVGADevice.cpp Devices/BlockDevice.cpp Devices/CharacterDevice.cpp diff --git a/Kernel/Devices/AsyncDeviceRequest.cpp b/Kernel/Devices/AsyncDeviceRequest.cpp new file mode 100644 index 0000000000..a114584085 --- /dev/null +++ b/Kernel/Devices/AsyncDeviceRequest.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2020, 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 + +namespace Kernel { + +AsyncDeviceRequest::AsyncDeviceRequest(Device& device) + : m_device(device) + , m_process(*Process::current()) +{ +} + +AsyncDeviceRequest::~AsyncDeviceRequest() +{ + { + ScopedSpinLock lock(m_lock); + ASSERT(is_completed_result(m_result)); + ASSERT(m_sub_requests_pending.is_empty()); + } + + // We should not need any locking here anymore. The destructor should + // only be called until either wait() or cancel() (once implemented) returned. + // At that point no sub-request should be adding more requests and all + // sub-requests should be completed (either succeeded, failed, or cancelled). + // Which means there should be no more pending sub-requests and the + // entire AsyncDeviceRequest hirarchy should be immutable. + for (auto& sub_request : m_sub_requests_complete) { + ASSERT(is_completed_result(sub_request.m_result)); // Shouldn't need any locking anymore + ASSERT(sub_request.m_parent_request == this); + sub_request.m_parent_request = nullptr; + } +} + +void AsyncDeviceRequest::request_finished() +{ + if (m_parent_request) + m_parent_request->sub_request_finished(*this); + + // Trigger processing the next request + m_device.process_next_queued_request({}, *this); + + // Wake anyone who may be waiting + m_queue.wake_all(); +} + +auto AsyncDeviceRequest::wait(timeval* timeout) -> RequestWaitResult +{ + ASSERT(!m_parent_request); + auto request_result = get_request_result(); + if (is_completed_result(request_result)) + return { request_result, Thread::BlockResult::NotBlocked }; + auto wait_result = Thread::current()->wait_on(m_queue, name(), timeout); + return { get_request_result(), wait_result }; +} + +auto AsyncDeviceRequest::get_request_result() const -> RequestResult +{ + ScopedSpinLock lock(m_lock); + return m_result; +} + +void AsyncDeviceRequest::add_sub_request(NonnullRefPtr sub_request) +{ + // Sub-requests cannot be for the same device + ASSERT(&m_device != &sub_request->m_device); + ASSERT(sub_request->m_parent_request == nullptr); + sub_request->m_parent_request = this; + + bool should_start; + { + ScopedSpinLock lock(m_lock); + ASSERT(!is_completed_result(m_result)); + m_sub_requests_pending.append(sub_request); + should_start = (m_result == Started); + } + if (should_start) + sub_request->do_start(); +} + +void AsyncDeviceRequest::sub_request_finished(AsyncDeviceRequest& sub_request) +{ + bool all_completed; + { + ScopedSpinLock lock(m_lock); + ASSERT(m_result == Started); + size_t index; + for (index = 0; index < m_sub_requests_pending.size(); index++) { + if (&m_sub_requests_pending[index] == &sub_request) { + NonnullRefPtr request(m_sub_requests_pending[index]); + m_sub_requests_pending.remove(index); + m_sub_requests_complete.append(move(request)); + break; + } + } + ASSERT(index < m_sub_requests_pending.size()); + all_completed = m_sub_requests_pending.is_empty(); + if (all_completed) { + // Aggregate any errors + bool any_failures = false; + bool any_memory_faults = false; + for (index = 0; index < m_sub_requests_complete.size(); index++) { + auto& sub_request = m_sub_requests_complete[index]; + auto sub_result = sub_request.get_request_result(); + ASSERT(is_completed_result(sub_result)); + switch (sub_result) { + case Failure: + any_failures = true; + break; + case MemoryFault: + any_memory_faults = true; + break; + default: + break; + } + if (any_failures && any_memory_faults) + break; // Stop checking if all error conditions were found + } + if (any_failures) + m_result = Failure; + else if (any_memory_faults) + m_result = MemoryFault; + else + m_result = Success; + } + } + if (all_completed) + request_finished(); +} + +void AsyncDeviceRequest::complete(RequestResult result) +{ + ASSERT(result == Success || result == Failure || result == MemoryFault); + ScopedCritical critical; + { + ScopedSpinLock lock(m_lock); + ASSERT(m_result == Started); + m_result = result; + } + if (Processor::current().in_irq()) { + ref(); // Make sure we don't get freed + Processor::deferred_call_queue([this]() { + request_finished(); + unref(); + }); + } else { + request_finished(); + } +} + +} diff --git a/Kernel/Devices/AsyncDeviceRequest.h b/Kernel/Devices/AsyncDeviceRequest.h new file mode 100644 index 0000000000..7d8010bd33 --- /dev/null +++ b/Kernel/Devices/AsyncDeviceRequest.h @@ -0,0 +1,175 @@ +/* + * Copyright (c) 2020, 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 +#include +#include +#include +#include + +namespace Kernel { + +class Device; + +class AsyncDeviceRequest : public RefCounted { + AK_MAKE_NONCOPYABLE(AsyncDeviceRequest); + AK_MAKE_NONMOVABLE(AsyncDeviceRequest); + +public: + enum RequestResult { + Pending = 0, + Started, + Success, + Failure, + MemoryFault, + Cancelled + }; + + class RequestWaitResult { + friend class AsyncDeviceRequest; + + public: + RequestResult request_result() const { return m_request_result; } + Thread::BlockResult wait_result() const { return m_wait_result; } + + private: + RequestWaitResult(RequestResult request_result, Thread::BlockResult wait_result) + : m_request_result(request_result) + , m_wait_result(wait_result) + { + } + + RequestResult m_request_result; + Thread::BlockResult m_wait_result; + }; + + virtual ~AsyncDeviceRequest(); + + virtual const char* name() const = 0; + virtual void start() = 0; + + void add_sub_request(NonnullRefPtr); + + [[nodiscard]] RequestWaitResult wait(timeval* = nullptr); + + void do_start(Badge) + { + do_start(); + } + + void complete(RequestResult result); + + void set_private(void* priv) + { + ASSERT(!m_private || !priv); + m_private = priv; + } + void* get_private() const { return m_private; } + + template + [[nodiscard]] bool write_to_buffer(UserOrKernelBuffer& buffer, Args... args) + { + if (in_target_context(buffer)) + return buffer.write(forward(args)...); + ProcessPagingScope paging_scope(m_process); + return buffer.write(forward(args)...); + } + + template + [[nodiscard]] bool write_to_buffer_buffered(UserOrKernelBuffer& buffer, Args... args) + { + if (in_target_context(buffer)) + return buffer.write_buffered(forward(args)...); + ProcessPagingScope paging_scope(m_process); + return buffer.write_buffered(forward(args)...); + } + + template + [[nodiscard]] bool read_from_buffer(const UserOrKernelBuffer& buffer, Args... args) + { + if (in_target_context(buffer)) + return buffer.read(forward(args)...); + ProcessPagingScope paging_scope(m_process); + return buffer.read(forward(args)...); + } + + template + [[nodiscard]] bool read_from_buffer_buffered(const UserOrKernelBuffer& buffer, Args... args) + { + if (in_target_context(buffer)) + return buffer.read_buffered(forward(args)...); + ProcessPagingScope paging_scope(m_process); + return buffer.read_buffered(forward(args)...); + } + +protected: + AsyncDeviceRequest(Device&); + + RequestResult get_request_result() const; + +private: + void sub_request_finished(AsyncDeviceRequest&); + void request_finished(); + + void do_start() + { + { + ScopedSpinLock lock(m_lock); + if (is_completed_result(m_result)) + return; + m_result = Started; + } + start(); + } + + bool in_target_context(const UserOrKernelBuffer& buffer) const + { + if (buffer.is_kernel_buffer()) + return true; + return m_process == Process::current(); + } + + static bool is_completed_result(RequestResult result) + { + return result > Started; + } + + Device& m_device; + + AsyncDeviceRequest* m_parent_request { nullptr }; + RequestResult m_result { Pending }; + NonnullRefPtrVector m_sub_requests_pending; + NonnullRefPtrVector m_sub_requests_complete; + WaitQueue m_queue; + NonnullRefPtr m_process; + void* m_private { nullptr }; + mutable SpinLock m_lock; +}; + +} diff --git a/Kernel/Devices/BXVGADevice.h b/Kernel/Devices/BXVGADevice.h index 6b98db525b..de3f4bfd72 100644 --- a/Kernel/Devices/BXVGADevice.h +++ b/Kernel/Devices/BXVGADevice.h @@ -48,10 +48,9 @@ private: virtual const char* class_name() const override { return "BXVGA"; } virtual bool can_read(const FileDescription&, size_t) const override { return true; } virtual bool can_write(const FileDescription&, size_t) const override { return true; } + virtual void start_request(AsyncBlockDeviceRequest& request) override { request.complete(AsyncDeviceRequest::Failure); } virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override { return -EINVAL; } virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override { return -EINVAL; } - virtual bool read_blocks(unsigned, u16, UserOrKernelBuffer&) override { return false; } - virtual bool write_blocks(unsigned, u16, const UserOrKernelBuffer&) override { return false; } void set_safe_resolution(); diff --git a/Kernel/Devices/BlockDevice.cpp b/Kernel/Devices/BlockDevice.cpp index d1f70a14dd..9e24f2dba8 100644 --- a/Kernel/Devices/BlockDevice.cpp +++ b/Kernel/Devices/BlockDevice.cpp @@ -28,18 +28,66 @@ namespace Kernel { +AsyncBlockDeviceRequest::AsyncBlockDeviceRequest(Device& block_device, RequestType request_type, u32 block_index, u32 block_count, const UserOrKernelBuffer& buffer, size_t buffer_size) + : AsyncDeviceRequest(block_device) + , m_block_device(static_cast(block_device)) + , m_request_type(request_type) + , m_block_index(block_index) + , m_block_count(block_count) + , m_buffer(buffer) + , m_buffer_size(buffer_size) +{ +} + +void AsyncBlockDeviceRequest::start() +{ + m_block_device.start_request(*this); +} + BlockDevice::~BlockDevice() { } -bool BlockDevice::read_block(unsigned index, UserOrKernelBuffer& buffer) const +bool BlockDevice::read_block(unsigned index, UserOrKernelBuffer& buffer) { - return const_cast(this)->read_blocks(index, 1, buffer); + auto read_request = make_request(AsyncBlockDeviceRequest::Read, index, 1, buffer, 512); + switch (read_request->wait().request_result()) { + case AsyncDeviceRequest::Success: + return true; + case AsyncDeviceRequest::Failure: + dbg() << "BlockDevice::read_block(" << index << ") IO error"; + break; + case AsyncDeviceRequest::MemoryFault: + dbg() << "BlockDevice::read_block(" << index << ") EFAULT"; + break; + case AsyncDeviceRequest::Cancelled: + dbg() << "BlockDevice::read_block(" << index << ") cancelled"; + break; + default: + ASSERT_NOT_REACHED(); + } + return false; } -bool BlockDevice::write_block(unsigned index, const UserOrKernelBuffer& data) +bool BlockDevice::write_block(unsigned index, const UserOrKernelBuffer& buffer) { - return write_blocks(index, 1, data); + auto write_request = make_request(AsyncBlockDeviceRequest::Write, index, 1, buffer, 512); + switch (write_request->wait().request_result()) { + case AsyncDeviceRequest::Success: + return true; + case AsyncDeviceRequest::Failure: + dbg() << "BlockDevice::write_block(" << index << ") IO error"; + break; + case AsyncDeviceRequest::MemoryFault: + dbg() << "BlockDevice::write_block(" << index << ") EFAULT"; + break; + case AsyncDeviceRequest::Cancelled: + dbg() << "BlockDevice::write_block(" << index << ") cancelled"; + break; + default: + ASSERT_NOT_REACHED(); + } + return false; } } diff --git a/Kernel/Devices/BlockDevice.h b/Kernel/Devices/BlockDevice.h index ee9a5bbead..975f46b7a8 100644 --- a/Kernel/Devices/BlockDevice.h +++ b/Kernel/Devices/BlockDevice.h @@ -30,6 +30,46 @@ namespace Kernel { +class BlockDevice; + +class AsyncBlockDeviceRequest : public AsyncDeviceRequest { +public: + enum RequestType { + Read, + Write + }; + AsyncBlockDeviceRequest(Device& block_device, RequestType request_type, + u32 block_index, u32 block_count, const UserOrKernelBuffer& buffer, size_t buffer_size); + + RequestType request_type() const { return m_request_type; } + u32 block_index() const { return m_block_index; } + u32 block_count() const { return m_block_count; } + UserOrKernelBuffer& buffer() { return m_buffer; } + const UserOrKernelBuffer& buffer() const { return m_buffer; } + size_t buffer_size() const { return m_buffer_size; } + + virtual void start() override; + virtual const char* name() const override + { + switch (m_request_type) { + case Read: + return "BlockDeviceRequest (read)"; + case Write: + return "BlockDeviceRequest (read)"; + default: + ASSERT_NOT_REACHED(); + } + } + +private: + BlockDevice& m_block_device; + const RequestType m_request_type; + const u32 m_block_index; + const u32 m_block_count; + UserOrKernelBuffer m_buffer; + const size_t m_buffer_size; +}; + class BlockDevice : public Device { public: virtual ~BlockDevice() override; @@ -37,11 +77,10 @@ public: size_t block_size() const { return m_block_size; } virtual bool is_seekable() const override { return true; } - bool read_block(unsigned index, UserOrKernelBuffer&) const; + bool read_block(unsigned index, UserOrKernelBuffer&); bool write_block(unsigned index, const UserOrKernelBuffer&); - virtual bool read_blocks(unsigned index, u16 count, UserOrKernelBuffer&) = 0; - virtual bool write_blocks(unsigned index, u16 count, const UserOrKernelBuffer&) = 0; + virtual void start_request(AsyncBlockDeviceRequest&) = 0; protected: BlockDevice(unsigned major, unsigned minor, size_t block_size = PAGE_SIZE) diff --git a/Kernel/Devices/Device.cpp b/Kernel/Devices/Device.cpp index 934c587ba6..eda56e1cfe 100644 --- a/Kernel/Devices/Device.cpp +++ b/Kernel/Devices/Device.cpp @@ -80,4 +80,21 @@ String Device::absolute_path(const FileDescription&) const return absolute_path(); } +void Device::process_next_queued_request(Badge, const AsyncDeviceRequest& completed_request) +{ + AsyncDeviceRequest* next_request = nullptr; + + { + ScopedSpinLock lock(m_requests_lock); + ASSERT(!m_requests.is_empty()); + ASSERT(m_requests.first().ptr() == &completed_request); + m_requests.remove(m_requests.begin()); + if (!m_requests.is_empty()) + next_request = m_requests.first().ptr(); + } + + if (next_request) + next_request->start(); +} + } diff --git a/Kernel/Devices/Device.h b/Kernel/Devices/Device.h index 8fa97a02ab..80a63e22a8 100644 --- a/Kernel/Devices/Device.h +++ b/Kernel/Devices/Device.h @@ -34,10 +34,12 @@ // There are two main subclasses: // - BlockDevice (random access) // - CharacterDevice (sequential) +#include #include #include -#include +#include #include +#include #include namespace Kernel { @@ -61,6 +63,23 @@ public: static void for_each(Function); static Device* get_device(unsigned major, unsigned minor); + void process_next_queued_request(Badge, const AsyncDeviceRequest&); + + template + NonnullRefPtr make_request(Args&&... args) + { + auto request = adopt(*new AsyncRequestType(*this, forward(args)...)); + bool was_empty; + { + ScopedSpinLock lock(m_requests_lock); + was_empty = m_requests.is_empty(); + m_requests.append(request); + } + if (was_empty) + request->do_start({}); + return request; + } + protected: Device(unsigned major, unsigned minor); void set_uid(uid_t uid) { m_uid = uid; } @@ -73,6 +92,9 @@ private: unsigned m_minor { 0 }; uid_t m_uid { 0 }; gid_t m_gid { 0 }; + + SpinLock m_requests_lock; + DoublyLinkedList> m_requests; }; } diff --git a/Kernel/Devices/DiskPartition.cpp b/Kernel/Devices/DiskPartition.cpp index d1ccb90d1b..d07961f74c 100644 --- a/Kernel/Devices/DiskPartition.cpp +++ b/Kernel/Devices/DiskPartition.cpp @@ -48,6 +48,12 @@ DiskPartition::~DiskPartition() { } +void DiskPartition::start_request(AsyncBlockDeviceRequest& request) +{ + request.add_sub_request(m_device->make_request(request.request_type(), + request.block_index() + m_block_offset, request.block_count(), request.buffer(), request.buffer_size())); +} + KResultOr DiskPartition::read(FileDescription& fd, size_t offset, UserOrKernelBuffer& outbuf, size_t len) { unsigned adjust = m_block_offset * block_size(); @@ -92,24 +98,6 @@ bool DiskPartition::can_write(const FileDescription& fd, size_t offset) const return m_device->can_write(fd, offset + adjust); } -bool DiskPartition::read_blocks(unsigned index, u16 count, UserOrKernelBuffer& out) -{ -#ifdef OFFD_DEBUG - klog() << "DiskPartition::read_blocks " << index << " (really: " << (m_block_offset + index) << ") count=" << count; -#endif - - return m_device->read_blocks(m_block_offset + index, count, out); -} - -bool DiskPartition::write_blocks(unsigned index, u16 count, const UserOrKernelBuffer& data) -{ -#ifdef OFFD_DEBUG - klog() << "DiskPartition::write_blocks " << index << " (really: " << (m_block_offset + index) << ") count=" << count; -#endif - - return m_device->write_blocks(m_block_offset + index, count, data); -} - const char* DiskPartition::class_name() const { return "DiskPartition"; diff --git a/Kernel/Devices/DiskPartition.h b/Kernel/Devices/DiskPartition.h index e9551b8097..7d37354960 100644 --- a/Kernel/Devices/DiskPartition.h +++ b/Kernel/Devices/DiskPartition.h @@ -36,8 +36,7 @@ public: static NonnullRefPtr create(BlockDevice&, unsigned block_offset, unsigned block_limit); virtual ~DiskPartition(); - virtual bool read_blocks(unsigned index, u16 count, UserOrKernelBuffer&) override; - virtual bool write_blocks(unsigned index, u16 count, const UserOrKernelBuffer&) override; + virtual void start_request(AsyncBlockDeviceRequest&) override; // ^BlockDevice virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; diff --git a/Kernel/Devices/EBRPartitionTable.cpp b/Kernel/Devices/EBRPartitionTable.cpp index bfaf999162..6cdd52fb8a 100644 --- a/Kernel/Devices/EBRPartitionTable.cpp +++ b/Kernel/Devices/EBRPartitionTable.cpp @@ -63,6 +63,9 @@ int EBRPartitionTable::index_of_ebr_container() const bool EBRPartitionTable::initialize() { + auto mbr_header_request = m_device->make_request(AsyncBlockDeviceRequest::Read, + 0, 1, UserOrKernelBuffer::for_kernel_buffer(m_cached_mbr_header), sizeof(m_cached_mbr_header)); + auto mbr_header_buffer = UserOrKernelBuffer::for_kernel_buffer(m_cached_mbr_header); if (!m_device->read_block(0, mbr_header_buffer)) { return false; diff --git a/Kernel/Devices/GPTPartitionTable.cpp b/Kernel/Devices/GPTPartitionTable.cpp index 9031269031..019f8994e1 100644 --- a/Kernel/Devices/GPTPartitionTable.cpp +++ b/Kernel/Devices/GPTPartitionTable.cpp @@ -84,7 +84,7 @@ RefPtr GPTPartitionTable::partition(unsigned index) GPTPartitionEntry entries[entries_per_sector]; auto entries_buffer = UserOrKernelBuffer::for_kernel_buffer((u8*)&entries); - this->m_device->read_blocks(lba, 1, entries_buffer); + this->m_device->read_block(lba, entries_buffer); GPTPartitionEntry& entry = entries[((index - 1) % entries_per_sector)]; #ifdef GPT_DEBUG diff --git a/Kernel/Devices/MBVGADevice.h b/Kernel/Devices/MBVGADevice.h index 09dda1f853..9ee9d762ae 100644 --- a/Kernel/Devices/MBVGADevice.h +++ b/Kernel/Devices/MBVGADevice.h @@ -49,8 +49,7 @@ private: virtual bool can_write(const FileDescription&, size_t) const override { return true; } virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override { return -EINVAL; } virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override { return -EINVAL; } - virtual bool read_blocks(unsigned, u16, UserOrKernelBuffer&) override { return false; } - virtual bool write_blocks(unsigned, u16, const UserOrKernelBuffer&) override { return false; } + virtual void start_request(AsyncBlockDeviceRequest& request) override { request.complete(AsyncDeviceRequest::Failure); } size_t framebuffer_size_in_bytes() const { return m_framebuffer_pitch * m_framebuffer_height; } diff --git a/Kernel/Devices/PATAChannel.cpp b/Kernel/Devices/PATAChannel.cpp index cc47ff532c..d4f32096c1 100644 --- a/Kernel/Devices/PATAChannel.cpp +++ b/Kernel/Devices/PATAChannel.cpp @@ -108,13 +108,6 @@ namespace Kernel { #define PCI_Mass_Storage_Class 0x1 #define PCI_IDE_Controller_Subclass 0x1 -static AK::Singleton s_pata_lock; - -static Lock& s_lock() -{ - return *s_pata_lock; -}; - OwnPtr PATAChannel::create(ChannelType type, bool force_pio) { PCI::Address pci_address; @@ -148,10 +141,59 @@ PATAChannel::~PATAChannel() { } -void PATAChannel::prepare_for_irq() +void PATAChannel::start_request(AsyncBlockDeviceRequest& request, bool use_dma, bool is_slave) { - cli(); - enable_irq(); + m_current_request = &request; + m_current_request_block_index = 0; + m_current_request_uses_dma = use_dma; + m_current_request_flushing_cache = false; + + if (request.request_type() == AsyncBlockDeviceRequest::Read) { + if (use_dma) + ata_read_sectors_with_dma(is_slave); + else + ata_read_sectors(is_slave); + } else { + if (use_dma) + ata_write_sectors_with_dma(is_slave); + else + ata_write_sectors(is_slave); + } +} + +void PATAChannel::complete_current_request(AsyncDeviceRequest::RequestResult result) +{ + // NOTE: this may be called from the interrupt handler! + ASSERT(m_current_request); + + // Now schedule reading back the buffer as soon as we leave the irq handler. + // 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]() { +#ifdef PATA_DEBUG + dbg() << "PATAChannel::complete_current_request result: " << result; +#endif + ASSERT(m_current_request); + auto& request = *m_current_request; + m_current_request = nullptr; + + if (m_current_request_uses_dma) { + if (result == AsyncDeviceRequest::Success) { + if (request.request_type() == AsyncBlockDeviceRequest::Read) { + if (!request.write_to_buffer(request.buffer(), m_dma_buffer_page->paddr().offset(0xc0000000).as_ptr(), 512 * request.block_count())) { + request.complete(AsyncDeviceRequest::MemoryFault); + return; + } + } + + // I read somewhere that this may trigger a cache flush so let's do it. + m_bus_master_base.offset(2).out(m_bus_master_base.offset(2).in() | 0x6); + } + } + + request.complete(result); + }); } void PATAChannel::initialize(bool force_pio) @@ -175,12 +217,6 @@ static void print_ide_status(u8 status) klog() << "PATAChannel: print_ide_status: DRQ=" << ((status & ATA_SR_DRQ) != 0) << " BSY=" << ((status & ATA_SR_BSY) != 0) << " DRDY=" << ((status & ATA_SR_DRDY) != 0) << " DSC=" << ((status & ATA_SR_DSC) != 0) << " DF=" << ((status & ATA_SR_DF) != 0) << " CORR=" << ((status & ATA_SR_CORR) != 0) << " IDX=" << ((status & ATA_SR_IDX) != 0) << " ERR=" << ((status & ATA_SR_ERR) != 0); } -void PATAChannel::wait_for_irq() -{ - Thread::current()->wait_on(m_irq_queue, "PATAChannel"); - disable_irq(); -} - void PATAChannel::handle_irq(const RegisterState&) { u8 status = m_io_base.offset(ATA_REG_STATUS).in(); @@ -196,16 +232,63 @@ void PATAChannel::handle_irq(const RegisterState&) return; } +#ifdef PATA_DEBUG + klog() << "PATAChannel: interrupt: DRQ=" << ((status & ATA_SR_DRQ) != 0) << " BSY=" << ((status & ATA_SR_BSY) != 0) << " DRDY=" << ((status & ATA_SR_DRDY) != 0); +#endif + + bool received_all_irqs = m_current_request_uses_dma || m_current_request_block_index + 1 >= m_current_request->block_count(); + + disable_irq(); + if (status & ATA_SR_ERR) { print_ide_status(status); m_device_error = m_io_base.offset(ATA_REG_ERROR).in(); klog() << "PATAChannel: Error " << String::format("%b", m_device_error) << "!"; - } else { - m_device_error = 0; + complete_current_request(AsyncDeviceRequest::Failure); + return; } -#ifdef PATA_DEBUG - klog() << "PATAChannel: interrupt: DRQ=" << ((status & ATA_SR_DRQ) != 0) << " BSY=" << ((status & ATA_SR_BSY) != 0) << " DRDY=" << ((status & ATA_SR_DRDY) != 0); -#endif + + m_device_error = 0; + if (received_all_irqs) { + complete_current_request(AsyncDeviceRequest::Success); + } else { + ASSERT(!m_current_request_uses_dma); + + // Now schedule reading/writing the buffer as soon as we leave the irq handler. + // This is important so that we can safely access the buffers, which could + // trigger page faults + Processor::deferred_call_queue([this]() { + if (m_current_request->request_type() == AsyncBlockDeviceRequest::Read) { + dbg() << "PATAChannel: Read block " << m_current_request_block_index << "/" << m_current_request->block_count(); + if (ata_do_read_sector()) { + if (++m_current_request_block_index >= m_current_request->block_count()) { + complete_current_request(AsyncDeviceRequest::Success); + return; + } + // Wait for the next block + enable_irq(); + } + } else { + if (!m_current_request_flushing_cache) { + dbg() << "PATAChannel: Wrote block " << m_current_request_block_index << "/" << m_current_request->block_count(); + if (++m_current_request_block_index >= m_current_request->block_count()) { + // We read the last block, flush cache + ASSERT(!m_current_request_flushing_cache); + m_current_request_flushing_cache = true; + enable_irq(); + m_io_base.offset(ATA_REG_COMMAND).out(ATA_CMD_CACHE_FLUSH); + } else { + // Read next block + enable_irq(); + ata_do_write_sector(); + } + } else { + complete_current_request(AsyncDeviceRequest::Success); + } + } + }); + } + m_irq_queue.wake_all(); } @@ -274,15 +357,16 @@ void PATAChannel::detect_disks() } } -bool PATAChannel::ata_read_sectors_with_dma(u32 lba, u16 count, UserOrKernelBuffer& outbuf, bool slave_request) +void PATAChannel::ata_read_sectors_with_dma(bool slave_request) { - LOCKER(s_lock()); + auto& request = *m_current_request; + u32 lba = request.block_index(); #ifdef PATA_DEBUG - dbg() << "PATAChannel::ata_read_sectors_with_dma (" << lba << " x" << count << ") -> " << outbuf.user_or_kernel_ptr(); + dbg() << "PATAChannel::ata_read_sectors_with_dma (" << lba << " x" << request.block_count() << ")"; #endif prdt().offset = m_dma_buffer_page->paddr(); - prdt().size = 512 * count; + prdt().size = 512 * request.block_count(); ASSERT(prdt().size <= PAGE_SIZE); @@ -312,7 +396,7 @@ bool PATAChannel::ata_read_sectors_with_dma(u32 lba, u16 count, UserOrKernelBuff m_io_base.offset(ATA_REG_LBA1).out(0); m_io_base.offset(ATA_REG_LBA2).out(0); - m_io_base.offset(ATA_REG_SECCOUNT0).out(count); + m_io_base.offset(ATA_REG_SECCOUNT0).out(request.block_count()); m_io_base.offset(ATA_REG_LBA0).out((lba & 0x000000ff) >> 0); m_io_base.offset(ATA_REG_LBA1).out((lba & 0x0000ff00) >> 8); m_io_base.offset(ATA_REG_LBA2).out((lba & 0x00ff0000) >> 16); @@ -326,35 +410,89 @@ bool PATAChannel::ata_read_sectors_with_dma(u32 lba, u16 count, UserOrKernelBuff m_io_base.offset(ATA_REG_COMMAND).out(ATA_CMD_READ_DMA_EXT); io_delay(); - prepare_for_irq(); + enable_irq(); // Start bus master m_bus_master_base.out(0x9); +} - wait_for_irq(); - - if (m_device_error) +bool PATAChannel::ata_do_read_sector() +{ + auto& request = *m_current_request; + auto out_buffer = request.buffer().offset(m_current_request_block_index * 512); + ssize_t nwritten = request.write_to_buffer_buffered<512>(out_buffer, 512, [&](u8* buffer, size_t buffer_bytes) { + for (size_t i = 0; i < buffer_bytes; i += sizeof(u16)) + *(u16*)&buffer[i] = IO::in16(m_io_base.offset(ATA_REG_DATA).get()); + return (ssize_t)buffer_bytes; + }); + if (nwritten < 0) { + // TODO: Do we need to abort the PATA read if this wasn't the last block? + complete_current_request(AsyncDeviceRequest::MemoryFault); return false; - - if (!outbuf.write(m_dma_buffer_page->paddr().offset(0xc0000000).as_ptr(), 512 * count)) - return false; // TODO: -EFAULT - - // I read somewhere that this may trigger a cache flush so let's do it. - m_bus_master_base.offset(2).out(m_bus_master_base.offset(2).in() | 0x6); + } return true; } -bool PATAChannel::ata_write_sectors_with_dma(u32 lba, u16 count, const UserOrKernelBuffer& inbuf, bool slave_request) +void PATAChannel::ata_read_sectors(bool slave_request) { - LOCKER(s_lock()); + auto& request = *m_current_request; + ASSERT(request.block_count() <= 256); #ifdef PATA_DEBUG - dbg() << "PATAChannel::ata_write_sectors_with_dma (" << lba << " x" << count << ") <- " << inbuf.user_or_kernel_ptr(); + dbg() << "PATAChannel::ata_read_sectors"; +#endif + + while (m_io_base.offset(ATA_REG_STATUS).in() & ATA_SR_BSY) + ; + + auto lba = request.block_index(); +#ifdef PATA_DEBUG + klog() << "PATAChannel: Reading " << request.block_count() << " sector(s) @ LBA " << lba; +#endif + + u8 devsel = 0xe0; + if (slave_request) + devsel |= 0x10; + + m_control_base.offset(ATA_CTL_CONTROL).out(0); + m_io_base.offset(ATA_REG_HDDEVSEL).out(devsel | (static_cast(slave_request) << 4) | 0x40); + io_delay(); + + m_io_base.offset(ATA_REG_FEATURES).out(0); + + m_io_base.offset(ATA_REG_SECCOUNT0).out(0); + m_io_base.offset(ATA_REG_LBA0).out(0); + m_io_base.offset(ATA_REG_LBA1).out(0); + m_io_base.offset(ATA_REG_LBA2).out(0); + + m_io_base.offset(ATA_REG_SECCOUNT0).out(request.block_count()); + m_io_base.offset(ATA_REG_LBA0).out((lba & 0x000000ff) >> 0); + m_io_base.offset(ATA_REG_LBA1).out((lba & 0x0000ff00) >> 8); + m_io_base.offset(ATA_REG_LBA2).out((lba & 0x00ff0000) >> 16); + + for (;;) { + auto status = m_io_base.offset(ATA_REG_STATUS).in(); + if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) + break; + } + + enable_irq(); + m_io_base.offset(ATA_REG_COMMAND).out(ATA_CMD_READ_PIO); +} + +void PATAChannel::ata_write_sectors_with_dma(bool slave_request) +{ + auto& request = *m_current_request; + u32 lba = request.block_index(); +#ifdef PATA_DEBUG + dbg() << "PATAChannel::ata_write_sectors_with_dma (" << lba << " x" << request.block_count() << ")"; #endif prdt().offset = m_dma_buffer_page->paddr(); - prdt().size = 512 * count; + prdt().size = 512 * request.block_count(); - if (!inbuf.read(m_dma_buffer_page->paddr().offset(0xc0000000).as_ptr(), 512 * count)) - return false; // TODO: -EFAULT + if (!request.read_from_buffer(request.buffer(), m_dma_buffer_page->paddr().offset(0xc0000000).as_ptr(), 512 * request.block_count())) { + complete_current_request(AsyncDeviceRequest::MemoryFault); + return; + } ASSERT(prdt().size <= PAGE_SIZE); @@ -381,7 +519,7 @@ bool PATAChannel::ata_write_sectors_with_dma(u32 lba, u16 count, const UserOrKer m_io_base.offset(ATA_REG_LBA1).out(0); m_io_base.offset(ATA_REG_LBA2).out(0); - m_io_base.offset(ATA_REG_SECCOUNT0).out(count); + m_io_base.offset(ATA_REG_SECCOUNT0).out(request.block_count()); m_io_base.offset(ATA_REG_LBA0).out((lba & 0x000000ff) >> 0); m_io_base.offset(ATA_REG_LBA1).out((lba & 0x0000ff00) >> 8); m_io_base.offset(ATA_REG_LBA2).out((lba & 0x00ff0000) >> 16); @@ -395,100 +533,42 @@ bool PATAChannel::ata_write_sectors_with_dma(u32 lba, u16 count, const UserOrKer m_io_base.offset(ATA_REG_COMMAND).out(ATA_CMD_WRITE_DMA_EXT); io_delay(); - prepare_for_irq(); + enable_irq(); // Start bus master m_bus_master_base.out(0x1); - wait_for_irq(); - - if (m_device_error) - return false; - - // I read somewhere that this may trigger a cache flush so let's do it. - m_bus_master_base.offset(2).out(m_bus_master_base.offset(2).in() | 0x6); - return true; } -bool PATAChannel::ata_read_sectors(u32 lba, u16 count, UserOrKernelBuffer& outbuf, bool slave_request) +void PATAChannel::ata_do_write_sector() { - ASSERT(count <= 256); - LOCKER(s_lock()); -#ifdef PATA_DEBUG - dbg() << "PATAChannel::ata_read_sectors request (" << count << " sector(s) @ " << lba << " into " << outbuf.user_or_kernel_ptr() << ")"; -#endif + auto& request = *m_current_request; - while (m_io_base.offset(ATA_REG_STATUS).in() & ATA_SR_BSY) + io_delay(); + while ((m_io_base.offset(ATA_REG_STATUS).in() & ATA_SR_BSY) || !(m_io_base.offset(ATA_REG_STATUS).in() & ATA_SR_DRQ)) ; + u8 status = m_io_base.offset(ATA_REG_STATUS).in(); + ASSERT(status & ATA_SR_DRQ); + + auto in_buffer = request.buffer().offset(m_current_request_block_index * 512); #ifdef PATA_DEBUG - klog() << "PATAChannel: Reading " << count << " sector(s) @ LBA " << lba; + dbg() << "PATAChannel: Writing 512 bytes (part " << m_current_request_block_index << ") (status=" << String::format("%b", status) << ")..."; #endif - - u8 devsel = 0xe0; - if (slave_request) - devsel |= 0x10; - - m_control_base.offset(ATA_CTL_CONTROL).out(0); - m_io_base.offset(ATA_REG_HDDEVSEL).out(devsel | (static_cast(slave_request) << 4) | 0x40); - io_delay(); - - m_io_base.offset(ATA_REG_FEATURES).out(0); - - m_io_base.offset(ATA_REG_SECCOUNT0).out(0); - m_io_base.offset(ATA_REG_LBA0).out(0); - m_io_base.offset(ATA_REG_LBA1).out(0); - m_io_base.offset(ATA_REG_LBA2).out(0); - - m_io_base.offset(ATA_REG_SECCOUNT0).out(count); - m_io_base.offset(ATA_REG_LBA0).out((lba & 0x000000ff) >> 0); - m_io_base.offset(ATA_REG_LBA1).out((lba & 0x0000ff00) >> 8); - m_io_base.offset(ATA_REG_LBA2).out((lba & 0x00ff0000) >> 16); - - for (;;) { - auto status = m_io_base.offset(ATA_REG_STATUS).in(); - if (!(status & ATA_SR_BSY) && (status & ATA_SR_DRDY)) - break; - } - - prepare_for_irq(); - m_io_base.offset(ATA_REG_COMMAND).out(ATA_CMD_READ_PIO); - - for (int i = 0; i < count; i++) { - if (i > 0) - prepare_for_irq(); - wait_for_irq(); - if (m_device_error) - return false; - - u8 status = m_control_base.offset(ATA_CTL_ALTSTATUS).in(); - ASSERT(!(status & ATA_SR_BSY)); - - auto out = outbuf.offset(i * 512); -#ifdef PATA_DEBUG - dbg() << "PATAChannel: Retrieving 512 bytes (part " << i << ") (status=" << String::format("%b", status) << "), outbuf=(" << out.user_or_kernel_ptr() << ")..."; -#endif - prepare_for_irq(); - - ssize_t nwritten = out.write_buffered<512>(512, [&](u8* buffer, size_t buffer_bytes) { - for (size_t i = 0; i < buffer_bytes; i += sizeof(u16)) - *(u16*)&buffer[i] = IO::in16(m_io_base.offset(ATA_REG_DATA).get()); - return (ssize_t)buffer_bytes; - }); - if (nwritten < 0) { - sti(); - disable_irq(); - return false; // TODO: -EFAULT - } - } - - sti(); - disable_irq(); - return true; + ssize_t nread = request.read_from_buffer_buffered<512>(in_buffer, 512, [&](const u8* buffer, size_t buffer_bytes) { + for (size_t i = 0; i < buffer_bytes; i += sizeof(u16)) + IO::out16(m_io_base.offset(ATA_REG_DATA).get(), *(const u16*)&buffer[i]); + return (ssize_t)buffer_bytes; + }); + if (nread < 0) + complete_current_request(AsyncDeviceRequest::MemoryFault); } -bool PATAChannel::ata_write_sectors(u32 start_sector, u16 count, const UserOrKernelBuffer& inbuf, bool slave_request) +void PATAChannel::ata_write_sectors(bool slave_request) { - ASSERT(count <= 256); - LOCKER(s_lock()); + auto& request = *m_current_request; + + ASSERT(request.block_count() <= 256); + u32 start_sector = request.block_index(); + u32 count = request.block_count(); #ifdef PATA_DEBUG klog() << "PATAChannel::ata_write_sectors request (" << count << " sector(s) @ " << start_sector << ")"; #endif @@ -516,37 +596,12 @@ bool PATAChannel::ata_write_sectors(u32 start_sector, u16 count, const UserOrKer m_io_base.offset(ATA_REG_COMMAND).out(ATA_CMD_WRITE_PIO); - for (int i = 0; i < count; i++) { - io_delay(); - while ((m_io_base.offset(ATA_REG_STATUS).in() & ATA_SR_BSY) || !(m_io_base.offset(ATA_REG_STATUS).in() & ATA_SR_DRQ)) - ; + io_delay(); + while ((m_io_base.offset(ATA_REG_STATUS).in() & ATA_SR_BSY) || !(m_io_base.offset(ATA_REG_STATUS).in() & ATA_SR_DRQ)) + ; - u8 status = m_io_base.offset(ATA_REG_STATUS).in(); - ASSERT(status & ATA_SR_DRQ); - - auto in = inbuf.offset(i * 512); -#ifdef PATA_DEBUG - dbg() << "PATAChannel: Writing 512 bytes (part " << i << ") (status=" << String::format("%b", status) << "), inbuf=(" << in.user_or_kernel_ptr() << ")..."; -#endif - prepare_for_irq(); - ssize_t nread = in.read_buffered<512>(512, [&](const u8* buffer, size_t buffer_bytes) { - for (size_t i = 0; i < buffer_bytes; i += sizeof(u16)) - IO::out16(m_io_base.offset(ATA_REG_DATA).get(), *(const u16*)&buffer[i]); - return (ssize_t)buffer_bytes; - }); - wait_for_irq(); - status = m_io_base.offset(ATA_REG_STATUS).in(); - ASSERT(!(status & ATA_SR_BSY)); - if (nread < 0) - return false; // TODO: -EFAULT - } - prepare_for_irq(); - m_io_base.offset(ATA_REG_COMMAND).out(ATA_CMD_CACHE_FLUSH); - wait_for_irq(); - u8 status = m_io_base.offset(ATA_REG_STATUS).in(); - ASSERT(!(status & ATA_SR_BSY)); - - return !m_device_error; + enable_irq(); + ata_do_write_sector(); } } diff --git a/Kernel/Devices/PATAChannel.h b/Kernel/Devices/PATAChannel.h index 40e331a4e8..033598ae5e 100644 --- a/Kernel/Devices/PATAChannel.h +++ b/Kernel/Devices/PATAChannel.h @@ -39,6 +39,7 @@ #include #include +#include #include #include #include @@ -50,6 +51,8 @@ namespace Kernel { +class AsyncBlockDeviceRequest; + struct PhysicalRegionDescriptor { PhysicalAddress offset; u16 size { 0 }; @@ -83,13 +86,15 @@ private: void initialize(bool force_pio); void detect_disks(); - void wait_for_irq(); - bool ata_read_sectors_with_dma(u32, u16, UserOrKernelBuffer&, bool); - bool ata_write_sectors_with_dma(u32, u16, const UserOrKernelBuffer&, bool); - bool ata_read_sectors(u32, u16, UserOrKernelBuffer&, bool); - bool ata_write_sectors(u32, u16, const UserOrKernelBuffer&, bool); + void start_request(AsyncBlockDeviceRequest&, bool, bool); + void complete_current_request(AsyncDeviceRequest::RequestResult); - inline void prepare_for_irq(); + void ata_read_sectors_with_dma(bool); + void ata_read_sectors(bool); + bool ata_do_read_sector(); + void ata_write_sectors_with_dma(bool); + void ata_write_sectors(bool); + void ata_do_write_sector(); // Data members u8 m_channel_number { 0 }; // Channel number. 0 = master, 1 = slave @@ -108,5 +113,10 @@ private: RefPtr m_master; RefPtr m_slave; + + AsyncBlockDeviceRequest* m_current_request { nullptr }; + u32 m_current_request_block_index { 0 }; + bool m_current_request_uses_dma { false }; + bool m_current_request_flushing_cache { false }; }; } diff --git a/Kernel/Devices/PATADiskDevice.cpp b/Kernel/Devices/PATADiskDevice.cpp index a81f7bb557..8b85783a5b 100644 --- a/Kernel/Devices/PATADiskDevice.cpp +++ b/Kernel/Devices/PATADiskDevice.cpp @@ -55,22 +55,10 @@ const char* PATADiskDevice::class_name() const return "PATADiskDevice"; } -bool PATADiskDevice::read_blocks(unsigned index, u16 count, UserOrKernelBuffer& out) +void PATADiskDevice::start_request(AsyncBlockDeviceRequest& request) { - if (!m_channel.m_bus_master_base.is_null() && m_channel.m_dma_enabled.resource()) - return read_sectors_with_dma(index, count, out); - return read_sectors(index, count, out); -} - -bool PATADiskDevice::write_blocks(unsigned index, u16 count, const UserOrKernelBuffer& data) -{ - if (!m_channel.m_bus_master_base.is_null() && m_channel.m_dma_enabled.resource()) - return write_sectors_with_dma(index, count, data); - for (unsigned i = 0; i < count; ++i) { - if (!write_sectors(index + i, 1, data.offset(i * 512))) - return false; - } - return true; + bool use_dma = !m_channel.m_bus_master_base.is_null() && m_channel.m_dma_enabled.resource(); + m_channel.start_request(request, use_dma, is_slave()); } void PATADiskDevice::set_drive_geometry(u16 cyls, u16 heads, u16 spt) @@ -100,8 +88,19 @@ KResultOr PATADiskDevice::read(FileDescription&, size_t offset, UserOrKe #endif if (whole_blocks > 0) { - if (!read_blocks(index, whole_blocks, outbuf)) - return -1; + auto read_request = make_request(AsyncBlockDeviceRequest::Read, index, whole_blocks, outbuf, whole_blocks * block_size()); + auto result = read_request->wait(); + if (result.wait_result().was_interrupted()) + return KResult(-EINTR); + switch (result.request_result()) { + case AsyncDeviceRequest::Failure: + case AsyncDeviceRequest::Cancelled: + return KResult(-EIO); + case AsyncDeviceRequest::MemoryFault: + return KResult(-EFAULT); + default: + break; + } } off_t pos = whole_blocks * block_size(); @@ -109,8 +108,21 @@ KResultOr PATADiskDevice::read(FileDescription&, size_t offset, UserOrKe if (remaining > 0) { auto data = ByteBuffer::create_uninitialized(block_size()); auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(data.data()); - if (!read_blocks(index + whole_blocks, 1, data_buffer)) + auto read_request = make_request(AsyncBlockDeviceRequest::Read, index + whole_blocks, 1, data_buffer, block_size()); + auto result = read_request->wait(); + if (result.wait_result().was_interrupted()) + return KResult(-EINTR); + switch (result.request_result()) { + case AsyncDeviceRequest::Failure: return pos; + case AsyncDeviceRequest::Cancelled: + return KResult(-EIO); + case AsyncDeviceRequest::MemoryFault: + // This should never happen, we're writing to a kernel buffer! + ASSERT_NOT_REACHED(); + default: + break; + } if (!outbuf.write(data.data(), pos, remaining)) return KResult(-EFAULT); } @@ -143,8 +155,19 @@ KResultOr PATADiskDevice::write(FileDescription&, size_t offset, const U #endif if (whole_blocks > 0) { - if (!write_blocks(index, whole_blocks, inbuf)) - return -1; + auto write_request = make_request(AsyncBlockDeviceRequest::Write, index, whole_blocks, inbuf, whole_blocks * block_size()); + auto result = write_request->wait(); + if (result.wait_result().was_interrupted()) + return KResult(-EINTR); + switch (result.request_result()) { + case AsyncDeviceRequest::Failure: + case AsyncDeviceRequest::Cancelled: + return KResult(-EIO); + case AsyncDeviceRequest::MemoryFault: + return KResult(-EFAULT); + default: + break; + } } off_t pos = whole_blocks * block_size(); @@ -155,12 +178,45 @@ KResultOr PATADiskDevice::write(FileDescription&, size_t offset, const U if (remaining > 0) { auto data = ByteBuffer::create_zeroed(block_size()); auto data_buffer = UserOrKernelBuffer::for_kernel_buffer(data.data()); - if (!read_blocks(index + whole_blocks, 1, data_buffer)) - return pos; + + { + auto read_request = make_request(AsyncBlockDeviceRequest::Read, index + whole_blocks, 1, data_buffer, block_size()); + auto result = read_request->wait(); + if (result.wait_result().was_interrupted()) + return KResult(-EINTR); + switch (result.request_result()) { + case AsyncDeviceRequest::Failure: + return pos; + case AsyncDeviceRequest::Cancelled: + return KResult(-EIO); + case AsyncDeviceRequest::MemoryFault: + // This should never happen, we're writing to a kernel buffer! + ASSERT_NOT_REACHED(); + default: + break; + } + } + if (!inbuf.read(data.data(), pos, remaining)) return KResult(-EFAULT); - if (!write_blocks(index + whole_blocks, 1, data_buffer)) - return pos; + + { + auto write_request = make_request(AsyncBlockDeviceRequest::Write, index + whole_blocks, 1, data_buffer, block_size()); + auto result = write_request->wait(); + if (result.wait_result().was_interrupted()) + return KResult(-EINTR); + switch (result.request_result()) { + case AsyncDeviceRequest::Failure: + return pos; + case AsyncDeviceRequest::Cancelled: + return KResult(-EIO); + case AsyncDeviceRequest::MemoryFault: + // This should never happen, we're writing to a kernel buffer! + ASSERT_NOT_REACHED(); + default: + break; + } + } } return pos + remaining; @@ -171,26 +227,6 @@ bool PATADiskDevice::can_write(const FileDescription&, size_t offset) const return offset < (m_cylinders * m_heads * m_sectors_per_track * block_size()); } -bool PATADiskDevice::read_sectors_with_dma(u32 lba, u16 count, UserOrKernelBuffer& outbuf) -{ - return m_channel.ata_read_sectors_with_dma(lba, count, outbuf, is_slave()); -} - -bool PATADiskDevice::read_sectors(u32 start_sector, u16 count, UserOrKernelBuffer& outbuf) -{ - return m_channel.ata_read_sectors(start_sector, count, outbuf, is_slave()); -} - -bool PATADiskDevice::write_sectors_with_dma(u32 lba, u16 count, const UserOrKernelBuffer& inbuf) -{ - return m_channel.ata_write_sectors_with_dma(lba, count, inbuf, is_slave()); -} - -bool PATADiskDevice::write_sectors(u32 start_sector, u16 count, const UserOrKernelBuffer& inbuf) -{ - return m_channel.ata_write_sectors(start_sector, count, inbuf, is_slave()); -} - bool PATADiskDevice::is_slave() const { return m_drive_type == DriveType::Slave; diff --git a/Kernel/Devices/PATADiskDevice.h b/Kernel/Devices/PATADiskDevice.h index eb9377e1bf..1036b06fd5 100644 --- a/Kernel/Devices/PATADiskDevice.h +++ b/Kernel/Devices/PATADiskDevice.h @@ -54,13 +54,10 @@ public: static NonnullRefPtr create(PATAChannel&, DriveType, int major, int minor); virtual ~PATADiskDevice() override; - // ^DiskDevice - virtual bool read_blocks(unsigned index, u16 count, UserOrKernelBuffer&) override; - virtual bool write_blocks(unsigned index, u16 count, const UserOrKernelBuffer&) override; - void set_drive_geometry(u16, u16, u16); // ^BlockDevice + virtual void start_request(AsyncBlockDeviceRequest&) override; virtual KResultOr read(FileDescription&, size_t, UserOrKernelBuffer&, size_t) override; virtual bool can_read(const FileDescription&, size_t) const override; virtual KResultOr write(FileDescription&, size_t, const UserOrKernelBuffer&, size_t) override; @@ -73,11 +70,6 @@ private: // ^DiskDevice virtual const char* class_name() const override; - bool wait_for_irq(); - bool read_sectors_with_dma(u32 lba, u16 count, UserOrKernelBuffer&); - bool write_sectors_with_dma(u32 lba, u16 count, const UserOrKernelBuffer&); - bool read_sectors(u32 lba, u16 count, UserOrKernelBuffer& buffer); - bool write_sectors(u32 lba, u16 count, const UserOrKernelBuffer& data); bool is_slave() const; Lock m_lock { "IDEDiskDevice" };