diff --git a/kernel/CMakeLists.txt b/kernel/CMakeLists.txt index 4bfa8bf5..dbf681a3 100644 --- a/kernel/CMakeLists.txt +++ b/kernel/CMakeLists.txt @@ -54,6 +54,7 @@ SET(KERNEL_SRCS device/Device.cpp device/BlockDevice.cpp filesystem/Inode.cpp + filesystem/CachedFilesystemInode.cpp device/PartitionDevice.cpp filesystem/Filesystem.cpp filesystem/LinkedInode.cpp diff --git a/kernel/device/DiskDevice.cpp b/kernel/device/DiskDevice.cpp index 9944a996..781fdaa7 100644 --- a/kernel/device/DiskDevice.cpp +++ b/kernel/device/DiskDevice.cpp @@ -17,136 +17,6 @@ Copyright (c) Byteduck 2016-2021. All rights reserved. */ -#include -#include -#include #include "DiskDevice.h" -#include "kernel/kstd/KLog.h" -size_t DiskDevice::s_used_cache_memory = 0; -kstd::vector DiskDevice::s_disk_devices; -SpinLock DiskDevice::s_disk_devices_lock; - -DiskDevice::DiskDevice(unsigned int major, unsigned int minor): BlockDevice(major, minor) { - s_disk_devices.push_back(this); -} - -DiskDevice::~DiskDevice() { - LOCK(s_disk_devices_lock); - for(size_t i = 0; i < s_disk_devices.size(); i++) { - if(s_disk_devices[i] == this) { - s_disk_devices.erase(i); - break; - } - } -}; - -Result DiskDevice::read_blocks(uint32_t start_block, uint32_t count, uint8_t* buffer) { - kstd::Arc cache_region; - for(size_t i = 0; i < count; i++) { - size_t block = start_block + i; - if(!cache_region || !cache_region->has_block(block)) - cache_region = get_cache_region(block); - LOCK(cache_region->lock); - cache_region->last_used = Time::now(); - memcpy(buffer + i * block_size(), cache_region->block_data(block), block_size()); - } - return Result(SUCCESS); -} - -Result DiskDevice::write_blocks(uint32_t start_block, uint32_t count, const uint8_t* buffer) { - kstd::Arc cache_region; - for(size_t i = 0; i < count; i++) { - size_t block = start_block + i; - if(!cache_region || !cache_region->has_block(block)) - cache_region = get_cache_region(block); - LOCK(cache_region->lock); - cache_region->last_used = Time::now(); - cache_region->dirty = true; - memcpy(cache_region->block_data(block), buffer + i * block_size(), block_size()); - } - - //TODO: Flush cached writes to disk periodically instead of on every write - return write_uncached_blocks(start_block, count, buffer); -} - -size_t DiskDevice::used_cache_memory() { - return s_used_cache_memory; -} - -size_t DiskDevice::free_pages(size_t num_pages) { - size_t num_freed = 0; - LOCK(s_disk_devices_lock); - - while(num_freed < num_pages) { - DiskDevice* lru_device = nullptr; - Time lru_time = Time::distant_future(); - kstd::Arc lru_region; - - // Find the device with the least recently used cache region - for(size_t i = 0; i < s_disk_devices.size(); i++) { - auto device = s_disk_devices[i]; - LOCK_N(device->_cache_lock, device_lock); - if(device->_cache_regions.empty()) - continue; - auto device_lru = device->_cache_regions.lru_unsafe(); - auto time = device_lru.second->last_used; - if(time < lru_time) { - lru_time = time; - lru_device = device; - lru_region = device_lru.second; - } - } - - if(!lru_region) - break; - - // Flush it if necessary - if(lru_region->dirty) - lru_device->write_uncached_blocks(lru_region->start_block, lru_region->num_blocks(), (uint8_t*) lru_region->region->start()); - - // Free it - num_freed += lru_region->region->size() / PAGE_SIZE; - s_used_cache_memory -= lru_region->region->size(); - lru_region.reset(); - lru_device->_cache_regions.prune(1); - } - - if(num_freed != num_pages) - KLog::warn("DiskDevice", "Was asked to free %d pages, could only free %d...", num_pages, num_freed); - - return num_freed; -} - -kstd::Arc DiskDevice::get_cache_region(size_t block) { - _cache_lock.acquire(); - - //See if we already have the block - auto reg_opt = _cache_regions.get(block_cache_region_start(block)); - if(reg_opt) { - _cache_lock.release(); - return reg_opt.value(); - } - - //Create a new cache region - auto reg = kstd::Arc::make(block_cache_region_start(block), block_size()); - s_used_cache_memory += PAGE_SIZE; - reg->lock.acquire(); - _cache_regions.insert(block_cache_region_start(block), reg); - - //Read the blocks into it - read_uncached_blocks(reg->start_block, blocks_per_cache_region(), (uint8_t*) reg->region->start()); - - //TODO: Figure out how to read the block after releasing the cache lock so that other blocks can be used in the meantime - //(We cannot do this currently as that would result in acquiring / releasing locks in the wrong order) - reg->lock.release(); - _cache_lock.release(); - - //Return the requested region - return reg; -} - -DiskDevice::BlockCacheRegion::BlockCacheRegion(size_t start_block, size_t block_size): - region(MemoryManager::inst().alloc_kernel_region(PAGE_SIZE)), block_size(block_size), start_block(start_block) {} - -DiskDevice::BlockCacheRegion::~BlockCacheRegion() = default; +DiskDevice::DiskDevice(unsigned int major, unsigned int minor): BlockDevice(major, minor) {} diff --git a/kernel/device/DiskDevice.h b/kernel/device/DiskDevice.h index f3207f82..5863e1ea 100644 --- a/kernel/device/DiskDevice.h +++ b/kernel/device/DiskDevice.h @@ -27,45 +27,5 @@ class DiskDevice: public BlockDevice { public: DiskDevice(unsigned major, unsigned minor); - ~DiskDevice() override; - - Result read_blocks(uint32_t block, uint32_t count, uint8_t *buffer) override final; - Result write_blocks(uint32_t block, uint32_t count, const uint8_t *buffer) override final; - - virtual Result read_uncached_blocks(uint32_t block, uint32_t count, uint8_t *buffer) = 0; - virtual Result write_uncached_blocks(uint32_t block, uint32_t count, const uint8_t *buffer) = 0; - - static size_t used_cache_memory(); - /** Tries to free a number of pages from the cache. Returns the number of pages that could be freed. **/ - static size_t free_pages(size_t num_pages); - -private: - class BlockCacheRegion { - public: - explicit BlockCacheRegion(size_t start_block, size_t block_size); - ~BlockCacheRegion(); - - inline bool has_block(size_t block) const { return block >= start_block && block < start_block + num_blocks(); } - inline size_t num_blocks() const { return PAGE_SIZE / block_size; } - inline uint8_t* block_data(size_t block) const { return (uint8_t*) (region->start() + block_size * (block - start_block)); } - - kstd::Arc region; - size_t block_size; - size_t start_block; - Time last_used = Time::now(); - bool dirty = false; - SpinLock lock; - }; - - // Static - static SpinLock s_disk_devices_lock; - static size_t s_used_cache_memory; - static kstd::vector s_disk_devices; - - kstd::LRUCache> _cache_regions; - kstd::Arc get_cache_region(size_t block); - inline size_t blocks_per_cache_region() { return PAGE_SIZE / block_size(); } - inline size_t block_cache_region_start(size_t block) { return block - (block % blocks_per_cache_region()); } - SpinLock _cache_lock; }; diff --git a/kernel/device/PATADevice.cpp b/kernel/device/PATADevice.cpp index 33326e41..d3ced843 100644 --- a/kernel/device/PATADevice.cpp +++ b/kernel/device/PATADevice.cpp @@ -279,7 +279,7 @@ void PATADevice::access_drive(uint8_t command, uint32_t lba, uint8_t num_sectors IO::outb(_io_base + ATA_COMMAND, command); } -Result PATADevice::read_uncached_blocks(uint32_t block, uint32_t count, uint8_t *buffer) { +Result PATADevice::read_blocks(uint32_t block, uint32_t count, uint8_t *buffer) { if(!_use_pio) { //DMA mode size_t num_chunks = (count + ATA_MAX_SECTORS_AT_ONCE - 1) / ATA_MAX_SECTORS_AT_ONCE; @@ -302,7 +302,7 @@ Result PATADevice::read_uncached_blocks(uint32_t block, uint32_t count, uint8_t } } -Result PATADevice::write_uncached_blocks(uint32_t block, uint32_t count, const uint8_t *buffer) { +Result PATADevice::write_blocks(uint32_t block, uint32_t count, const uint8_t *buffer) { if(!_use_pio) { //DMA mode size_t num_chunks = (count + ATA_MAX_SECTORS_AT_ONCE - 1) / ATA_MAX_SECTORS_AT_ONCE; diff --git a/kernel/device/PATADevice.h b/kernel/device/PATADevice.h index cdcd8aa2..3bc8e7ec 100644 --- a/kernel/device/PATADevice.h +++ b/kernel/device/PATADevice.h @@ -49,8 +49,8 @@ class PATADevice: public IRQHandler, public DiskDevice { //BlockDevice - Result read_uncached_blocks(uint32_t block, uint32_t count, uint8_t *buffer) override; - Result write_uncached_blocks(uint32_t block, uint32_t count, const uint8_t *buffer) override; + Result read_blocks(uint32_t block, uint32_t count, uint8_t *buffer) override; + Result write_blocks(uint32_t block, uint32_t count, const uint8_t *buffer) override; size_t block_size() override; //File diff --git a/kernel/filesystem/CachedFilesystemInode.cpp b/kernel/filesystem/CachedFilesystemInode.cpp new file mode 100644 index 00000000..2cb30e93 --- /dev/null +++ b/kernel/filesystem/CachedFilesystemInode.cpp @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2023 Byteduck */ + +#include "CachedFilesystemInode.h" +#include "../memory/InodeVMObject.h" + +CachedFilesystemInode::CachedFilesystemInode(Filesystem& fs, ino_t id) : Inode(fs, id) {} + +ssize_t CachedFilesystemInode::read_cached(size_t start, size_t length, SafePointer buffer, FileDescriptor* fd) { + auto region_err = map_inode(start, length); + if(region_err.is_error()) + return region_err.code() < 0 ? region_err.code() : -region_err.code(); + if(!region_err.value()) + return 0; + buffer.write((uint8_t*) (region_err.value()->start() + (start % PAGE_SIZE)), length); + return length; +} + +ssize_t CachedFilesystemInode::write_cached(size_t start, size_t length, SafePointer buffer, FileDescriptor* fd) { + auto region_err = map_inode(start, length); + if(region_err.is_error()) + return region_err.code() < 0 ? region_err.code() : -region_err.code(); + buffer.read((uint8_t*) (region_err.value()->start() + (start % PAGE_SIZE)), length); + // TODO: Smarter way of scheduling writes + return write(start, length, buffer, fd); +} + +ResultRet> CachedFilesystemInode::map_inode(size_t start, size_t& length) { + auto meta_size = metadata().size; + if(start >= shared_vm_object()->size() || start >= meta_size) + return kstd::Arc(nullptr); + if(start + length >= shared_vm_object()->size()) + length = shared_vm_object()->size() - start; + if(start + length >= meta_size) + length = meta_size - start; + + auto object = shared_vm_object(); + auto start_page = start / PAGE_SIZE; + auto num_pages = (length + (start - (start_page * PAGE_SIZE)) + PAGE_SIZE - 1) / PAGE_SIZE; + return object->map_pages_in_kernel(start_page, num_pages); +} diff --git a/kernel/filesystem/CachedFilesystemInode.h b/kernel/filesystem/CachedFilesystemInode.h new file mode 100644 index 00000000..0bae10be --- /dev/null +++ b/kernel/filesystem/CachedFilesystemInode.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-3.0-or-later */ +/* Copyright © 2016-2023 Byteduck */ + +#pragma once + +#include "Inode.h" + +class CachedFilesystemInode: public Inode { +public: + CachedFilesystemInode(Filesystem& fs, ino_t id); + + ssize_t read_cached(size_t start, size_t length, SafePointer buffer, FileDescriptor* fd) override; + ssize_t write_cached(size_t start, size_t length, SafePointer buffer, FileDescriptor* fd) override; + +private: + ResultRet> map_inode(size_t start, size_t& length); +}; diff --git a/kernel/filesystem/Inode.cpp b/kernel/filesystem/Inode.cpp index f1c3933a..bae08e45 100644 --- a/kernel/filesystem/Inode.cpp +++ b/kernel/filesystem/Inode.cpp @@ -91,3 +91,11 @@ kstd::Arc Inode::shared_vm_object() { return ret; } + +ssize_t Inode::read_cached(size_t start, size_t length, SafePointer buffer, FileDescriptor* fd) { + return read(start, length, buffer, fd); +} + +ssize_t Inode::write_cached(size_t start, size_t length, SafePointer buffer, FileDescriptor* fd) { + return write(start, length, buffer, fd); +} diff --git a/kernel/filesystem/Inode.h b/kernel/filesystem/Inode.h index f5287ce5..e83a191e 100644 --- a/kernel/filesystem/Inode.h +++ b/kernel/filesystem/Inode.h @@ -63,6 +63,8 @@ class Inode: public kstd::ArcSelf { virtual InodeMetadata metadata(); kstd::Arc shared_vm_object(); + virtual ssize_t read_cached(size_t start, size_t length, SafePointer buffer, FileDescriptor* fd); + virtual ssize_t write_cached(size_t start, size_t length, SafePointer buffer, FileDescriptor* fd); protected: InodeMetadata _metadata; diff --git a/kernel/filesystem/InodeFile.cpp b/kernel/filesystem/InodeFile.cpp index 427bc267..1266b819 100644 --- a/kernel/filesystem/InodeFile.cpp +++ b/kernel/filesystem/InodeFile.cpp @@ -32,18 +32,21 @@ kstd::Arc InodeFile::inode() { } ssize_t InodeFile::read(FileDescriptor &fd, size_t offset, SafePointer buffer, size_t count) { - if(_inode->metadata().exists() && _inode->metadata().is_directory()) return -EISDIR; - return _inode->read(offset, count, buffer, &fd);; + if(_inode->metadata().exists() && _inode->metadata().is_directory()) + return -EISDIR; + return _inode->read_cached(offset, count, buffer, &fd);; } ssize_t InodeFile::read_dir_entry(FileDescriptor &fd, size_t offset, SafePointer buffer) { - if(_inode->metadata().exists() && !_inode->metadata().is_directory()) return -ENOTDIR; + if(_inode->metadata().exists() && !_inode->metadata().is_directory()) + return -ENOTDIR; return _inode->read_dir_entry(offset, buffer, &fd); } ssize_t InodeFile::write(FileDescriptor &fd, size_t offset, SafePointer buffer, size_t count) { - if(_inode->metadata().exists() && _inode->metadata().is_directory()) return -EISDIR; - return _inode->write(offset, count, buffer, &fd); + if(_inode->metadata().exists() && _inode->metadata().is_directory()) + return -EISDIR; + return _inode->write_cached(offset, count, buffer, &fd); } void InodeFile::open(FileDescriptor& fd, int options) { diff --git a/kernel/filesystem/ext2/Ext2Inode.cpp b/kernel/filesystem/ext2/Ext2Inode.cpp index 30689243..efb7c920 100644 --- a/kernel/filesystem/ext2/Ext2Inode.cpp +++ b/kernel/filesystem/ext2/Ext2Inode.cpp @@ -24,7 +24,7 @@ #include #include -Ext2Inode::Ext2Inode(Ext2Filesystem& filesystem, ino_t id): Inode(filesystem, id) { +Ext2Inode::Ext2Inode(Ext2Filesystem& filesystem, ino_t id): CachedFilesystemInode(filesystem, id) { //Get the block group Ext2BlockGroup* bg = ext2fs().get_block_group(block_group()); @@ -44,7 +44,7 @@ Ext2Inode::Ext2Inode(Ext2Filesystem& filesystem, ino_t id): Inode(filesystem, id read_block_pointers(); } -Ext2Inode::Ext2Inode(Ext2Filesystem& filesystem, ino_t i, const Raw &raw, kstd::vector& block_pointers, ino_t parent): Inode(filesystem, i), block_pointers(block_pointers), raw(raw) { +Ext2Inode::Ext2Inode(Ext2Filesystem& filesystem, ino_t i, const Raw &raw, kstd::vector& block_pointers, ino_t parent): CachedFilesystemInode(filesystem, i), block_pointers(block_pointers), raw(raw) { create_metadata(); if(IS_DIR(raw.mode)) { kstd::vector entries; diff --git a/kernel/filesystem/ext2/Ext2Inode.h b/kernel/filesystem/ext2/Ext2Inode.h index c7e6b6ac..9e028713 100644 --- a/kernel/filesystem/ext2/Ext2Inode.h +++ b/kernel/filesystem/ext2/Ext2Inode.h @@ -19,11 +19,11 @@ #pragma once -#include +#include #include class Ext2Filesystem; -class Ext2Inode: public Inode { +class Ext2Inode: public CachedFilesystemInode { public: typedef struct __attribute__((packed)) Raw { uint16_t mode = 0; diff --git a/kernel/filesystem/procfs/ProcFSInode.cpp b/kernel/filesystem/procfs/ProcFSInode.cpp index b4f3352f..1e12fc9a 100644 --- a/kernel/filesystem/procfs/ProcFSInode.cpp +++ b/kernel/filesystem/procfs/ProcFSInode.cpp @@ -129,11 +129,6 @@ ssize_t ProcFSInode::read(size_t start, size_t length, SafePointer buff itoa((int) MM.kernel_heap(), numbuf, 10); str += numbuf; - str += "\nkcache = "; - itoa((int) DiskDevice::used_cache_memory(), numbuf, 10); - str += numbuf; - str += "\n"; - if(start >= str.length()) return 0; if(start + length > str.length()) diff --git a/kernel/memory/InodeVMObject.cpp b/kernel/memory/InodeVMObject.cpp index 8f9f0d4f..ff90bcbb 100644 --- a/kernel/memory/InodeVMObject.cpp +++ b/kernel/memory/InodeVMObject.cpp @@ -44,3 +44,10 @@ ResultRet InodeVMObject::read_page_if_needed(size_t index) { return true; } + +ResultRet> InodeVMObject::map_pages_in_kernel(PageIndex start_page, size_t num_pages) { + bool read = false; + for(auto page = start_page; page < start_page + num_pages; page++) + read |= TRY(read_page_if_needed(page)); + return MM.map_object(self(), {start_page * PAGE_SIZE, num_pages * PAGE_SIZE}); +} diff --git a/kernel/memory/InodeVMObject.h b/kernel/memory/InodeVMObject.h index 7e665788..61f03d30 100644 --- a/kernel/memory/InodeVMObject.h +++ b/kernel/memory/InodeVMObject.h @@ -26,6 +26,13 @@ class InodeVMObject: public VMObject { */ ResultRet read_page_if_needed(size_t index); + /** + * Maps the given range of pages into the kernel, reading them in as needed. + * @param start_page The page to start the mapping at. + * @param num_pages The number of pages to map. + */ + ResultRet> map_pages_in_kernel(PageIndex start_page, size_t num_pages); + kstd::Arc inode() const { return m_inode; } SpinLock& lock() { return m_page_lock; } Type type() const { return m_type; } diff --git a/kernel/memory/MemoryManager.cpp b/kernel/memory/MemoryManager.cpp index 428105ab..14f94c36 100644 --- a/kernel/memory/MemoryManager.cpp +++ b/kernel/memory/MemoryManager.cpp @@ -287,19 +287,11 @@ ResultRet MemoryManager::alloc_physical_page() const { } } - // We couldn't allocate any physical pages. Try freeing four for good measure. - if(DiskDevice::free_pages(4) >= 1) - return alloc_physical_page(); - // No more pages. This is bad. PANIC("NO_MEM", "The system ran out of physical memory."); } ResultRet> MemoryManager::alloc_physical_pages(size_t num_pages) const { - // If we already know we won't have enough free memory, try freeing twice as many up in the disk cache first - if((usable_bytes_ram - used_pmem()) / PAGE_SIZE < num_pages) - DiskDevice::free_pages(num_pages * 2); - auto new_pages = kstd::vector(); new_pages.reserve(num_pages); while(num_pages--) diff --git a/libraries/libsys/Memory.cpp b/libraries/libsys/Memory.cpp index fa98cda2..34878167 100644 --- a/libraries/libsys/Memory.cpp +++ b/libraries/libsys/Memory.cpp @@ -41,7 +41,7 @@ ResultRet Mem::get_info(Duck::InputStream& file) { strtoul(cfg["kvirt"].c_str(), nullptr, 0), strtoul(cfg["kphys"].c_str(), nullptr, 0), strtoul(cfg["kheap"].c_str(), nullptr, 0), - strtoul(cfg["kcache"].c_str(), nullptr, 0) + 0 }; }