Skip to content

Commit

Permalink
Kernel: Do disk cache writebacks in a worker thread
Browse files Browse the repository at this point in the history
  • Loading branch information
byteduck committed Apr 14, 2024
1 parent 516270a commit 40eeb35
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 3 deletions.
55 changes: 52 additions & 3 deletions kernel/device/DiskDevice.cpp
Expand Up @@ -26,6 +26,7 @@
size_t DiskDevice::s_used_cache_memory = 0;
kstd::vector<DiskDevice*> DiskDevice::s_disk_devices;
Mutex DiskDevice::s_disk_devices_lock("DiskDevices");
BooleanBlocker DiskDevice::s_writeback_blocker;

DiskDevice::DiskDevice(unsigned int major, unsigned int minor): BlockDevice(major, minor) {
s_disk_devices.push_back(this);
Expand Down Expand Up @@ -55,19 +56,31 @@ Result DiskDevice::read_blocks(uint32_t start_block, uint32_t count, uint8_t* bu
}

Result DiskDevice::write_blocks(uint32_t start_block, uint32_t count, const uint8_t* buffer) {
auto invalidate_cache_region = [this] (const kstd::Arc<BlockCacheRegion>& region) {
LOCK(_dirty_regions_lock);
if (_dirty_regions.contains(region->start_block))
return;
_dirty_regions.push_back(region->start_block);
};

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))
if(!cache_region || !cache_region->has_block(block)) {
if (cache_region)
invalidate_cache_region(cache_region);
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);
invalidate_cache_region(cache_region);
s_writeback_blocker.set_ready(true);

return Result::Success;
}

size_t DiskDevice::used_cache_memory() {
Expand Down Expand Up @@ -146,6 +159,42 @@ kstd::Arc<DiskDevice::BlockCacheRegion> DiskDevice::get_cache_region(size_t bloc
return reg;
}

void DiskDevice::cache_writeback_task_entry() {
s_writeback_blocker.set_ready(false);
static constexpr bool writeback_debug = false;
while (true) {
TaskManager::current_thread()->block(s_writeback_blocker);
s_writeback_blocker.set_ready(false);

KLog::dbg_if<writeback_debug>("DiskDevice", "Writing back caches...");
for (auto device : s_disk_devices) {
while (true) {
size_t region_loc;
{
LOCK(device->_dirty_regions_lock);
if (device->_dirty_regions.empty())
break;
region_loc = device->_dirty_regions.pop_front();
}
kstd::Arc<BlockCacheRegion> region;
{
LOCK(device->_cache_lock);
auto region_opt = device->_cache_regions.get(region_loc);
if (!region_opt) {
KLog::warn("DiskDevice", "Was going to write back cache region, but couldn't find it!");
return;
}
region = region_opt.value();
}
LOCK(region->lock);
device->write_uncached_blocks(region->start_block, region->num_blocks(), (uint8_t*) region->region->start());
region->dirty = false;
}
}
KLog::dbg_if<writeback_debug>("DiskDevice", "Done writing caches!");
}
}

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) {}

Expand Down
5 changes: 5 additions & 0 deletions kernel/device/DiskDevice.h
Expand Up @@ -39,6 +39,8 @@ class DiskDevice: public BlockDevice {
/** 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);

static void cache_writeback_task_entry();

private:
class BlockCacheRegion {
public:
Expand All @@ -61,11 +63,14 @@ class DiskDevice: public BlockDevice {
static Mutex s_disk_devices_lock;
static size_t s_used_cache_memory;
static kstd::vector<DiskDevice*> s_disk_devices;
static BooleanBlocker s_writeback_blocker;

kstd::LRUCache<size_t, kstd::Arc<BlockCacheRegion>> _cache_regions;
kstd::queue<size_t> _dirty_regions; // We really need a set type.
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()); }
Mutex _cache_lock {"DiskDeviceCache"};
Mutex _dirty_regions_lock { "DiskDeviceDirty" };
};

14 changes: 14 additions & 0 deletions kernel/kstd/queue.hpp
Expand Up @@ -144,6 +144,20 @@ namespace kstd {
return _size == 0;
}

bool contains(const T& value) const {
if (empty())
return false;

size_t i = _front;
do {
if (_storage[i] == value)
return true;
i = (i + 1) % _capacity;
} while (i != ((_back + 1) % _size));

return false;
}

size_t size() const {
return _size;
}
Expand Down
2 changes: 2 additions & 0 deletions kernel/tasking/TaskManager.cpp
Expand Up @@ -28,6 +28,7 @@
#include <kernel/Processor.h>
#include <kernel/kstd/KLog.h>
#include <kernel/net/NetworkManager.h>
#include "../device/DiskDevice.h"

TSS TaskManager::tss;
Mutex TaskManager::g_tasking_lock {"Tasking"};
Expand Down Expand Up @@ -177,6 +178,7 @@ void TaskManager::init(){
//Create kernel threads
kernel_process->spawn_kernel_thread(kreaper_entry);
kernel_process->spawn_kernel_thread(NetworkManager::task_entry);
kernel_process->spawn_kernel_thread(DiskDevice::cache_writeback_task_entry);

//Preempt
cur_thread = kernel_process->get_thread(kernel_process->pid());
Expand Down

0 comments on commit 40eeb35

Please sign in to comment.