Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Replace block-level cache with Inode cache #59

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions kernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
132 changes: 1 addition & 131 deletions kernel/device/DiskDevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,136 +17,6 @@
Copyright (c) Byteduck 2016-2021. All rights reserved.
*/

#include <kernel/memory/PageDirectory.h>
#include <kernel/kstd/cstring.h>
#include <kernel/memory/MemoryManager.h>
#include "DiskDevice.h"
#include "kernel/kstd/KLog.h"

size_t DiskDevice::s_used_cache_memory = 0;
kstd::vector<DiskDevice*> 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<BlockCacheRegion> 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<BlockCacheRegion> 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<BlockCacheRegion> 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::BlockCacheRegion> 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<BlockCacheRegion>::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) {}
40 changes: 0 additions & 40 deletions kernel/device/DiskDevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<VMRegion> 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<DiskDevice*> s_disk_devices;

kstd::LRUCache<size_t, kstd::Arc<BlockCacheRegion>> _cache_regions;
kstd::Arc<BlockCacheRegion> 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;
};

4 changes: 2 additions & 2 deletions kernel/device/PATADevice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions kernel/device/PATADevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
41 changes: 41 additions & 0 deletions kernel/filesystem/CachedFilesystemInode.cpp
Original file line number Diff line number Diff line change
@@ -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<uint8_t> 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<uint8_t> 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<kstd::Arc<VMRegion>> 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<VMRegion>(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);
}
17 changes: 17 additions & 0 deletions kernel/filesystem/CachedFilesystemInode.h
Original file line number Diff line number Diff line change
@@ -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<uint8_t> buffer, FileDescriptor* fd) override;
ssize_t write_cached(size_t start, size_t length, SafePointer<uint8_t> buffer, FileDescriptor* fd) override;

private:
ResultRet<kstd::Arc<VMRegion>> map_inode(size_t start, size_t& length);
};
8 changes: 8 additions & 0 deletions kernel/filesystem/Inode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,11 @@ kstd::Arc<InodeVMObject> Inode::shared_vm_object() {

return ret;
}

ssize_t Inode::read_cached(size_t start, size_t length, SafePointer<uint8_t> buffer, FileDescriptor* fd) {
return read(start, length, buffer, fd);
}

ssize_t Inode::write_cached(size_t start, size_t length, SafePointer<uint8_t> buffer, FileDescriptor* fd) {
return write(start, length, buffer, fd);
}
2 changes: 2 additions & 0 deletions kernel/filesystem/Inode.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ class Inode: public kstd::ArcSelf<Inode> {
virtual InodeMetadata metadata();

kstd::Arc<InodeVMObject> shared_vm_object();
virtual ssize_t read_cached(size_t start, size_t length, SafePointer<uint8_t> buffer, FileDescriptor* fd);
virtual ssize_t write_cached(size_t start, size_t length, SafePointer<uint8_t> buffer, FileDescriptor* fd);

protected:
InodeMetadata _metadata;
Expand Down
13 changes: 8 additions & 5 deletions kernel/filesystem/InodeFile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,21 @@ kstd::Arc<Inode> InodeFile::inode() {
}

ssize_t InodeFile::read(FileDescriptor &fd, size_t offset, SafePointer<uint8_t> 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<DirectoryEntry> 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<uint8_t> 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) {
Expand Down
4 changes: 2 additions & 2 deletions kernel/filesystem/ext2/Ext2Inode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#include <kernel/filesystem/DirectoryEntry.h>
#include <kernel/kstd/KLog.h>

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());

Expand All @@ -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<uint32_t>& 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<uint32_t>& block_pointers, ino_t parent): CachedFilesystemInode(filesystem, i), block_pointers(block_pointers), raw(raw) {
create_metadata();
if(IS_DIR(raw.mode)) {
kstd::vector<DirectoryEntry> entries;
Expand Down
4 changes: 2 additions & 2 deletions kernel/filesystem/ext2/Ext2Inode.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@

#pragma once

#include <kernel/filesystem/Inode.h>
#include <kernel/filesystem/CachedFilesystemInode.h>
#include <kernel/kstd/vector.hpp>

class Ext2Filesystem;
class Ext2Inode: public Inode {
class Ext2Inode: public CachedFilesystemInode {
public:
typedef struct __attribute__((packed)) Raw {
uint16_t mode = 0;
Expand Down
5 changes: 0 additions & 5 deletions kernel/filesystem/procfs/ProcFSInode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,6 @@ ssize_t ProcFSInode::read(size_t start, size_t length, SafePointer<uint8_t> 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())
Expand Down
7 changes: 7 additions & 0 deletions kernel/memory/InodeVMObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,10 @@ ResultRet<bool> InodeVMObject::read_page_if_needed(size_t index) {

return true;
}

ResultRet<kstd::Arc<VMRegion>> 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});
}
7 changes: 7 additions & 0 deletions kernel/memory/InodeVMObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ class InodeVMObject: public VMObject {
*/
ResultRet<bool> 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<kstd::Arc<VMRegion>> map_pages_in_kernel(PageIndex start_page, size_t num_pages);

kstd::Arc<Inode> inode() const { return m_inode; }
SpinLock& lock() { return m_page_lock; }
Type type() const { return m_type; }
Expand Down
Loading