Kernel: Don't crash if Ext2 filesystem doing too many fileops

I had a hardlimit of 10 block buffers and if they ran out, the kernel
would crash. this patchs increases the number of buffers to 16 and
removes the crash condition when they run out :D
This commit is contained in:
Bananymous 2025-08-28 15:52:55 +03:00
parent 948ef2c820
commit 391fc0c4c2
3 changed files with 79 additions and 48 deletions

View File

@ -26,18 +26,40 @@ namespace Kernel
class BlockBufferWrapper
{
BAN_NON_COPYABLE(BlockBufferWrapper);
BAN_NON_MOVABLE(BlockBufferWrapper);
public:
BlockBufferWrapper(BAN::Span<uint8_t> buffer, bool& used)
BlockBufferWrapper(BAN::Span<uint8_t> buffer, bool* used, Mutex* mutex, ThreadBlocker* blocker)
: m_buffer(buffer)
, m_used(used)
, m_mutex(mutex)
, m_blocker(blocker)
{
ASSERT(m_used);
ASSERT(m_used && *m_used);
}
BlockBufferWrapper(BlockBufferWrapper&& other) { *this = BAN::move(other); }
~BlockBufferWrapper()
{
m_used = false;
if (m_used == nullptr)
return;
m_mutex->lock();
*m_used = false;
m_blocker->unblock();
m_mutex->unlock();
}
BlockBufferWrapper& operator=(BlockBufferWrapper&& other)
{
this->m_buffer = other.m_buffer;
this->m_used = other.m_used;
this->m_mutex = other.m_mutex;
this->m_blocker = other.m_blocker;
other.m_buffer = {};
other.m_used = nullptr;
other.m_mutex = nullptr;
other.m_blocker = nullptr;
return *this;
}
size_t size() const { return m_buffer.size(); }
@ -53,7 +75,9 @@ namespace Kernel
private:
BAN::Span<uint8_t> m_buffer;
bool& m_used;
bool* m_used;
Mutex* m_mutex;
ThreadBlocker* m_blocker;
};
public:
@ -79,7 +103,7 @@ namespace Kernel
BAN::ErrorOr<void> sync_superblock();
BAN::ErrorOr<void> sync_block(uint32_t block);
BlockBufferWrapper get_block_buffer();
BAN::ErrorOr<BlockBufferWrapper> get_block_buffer();
BAN::ErrorOr<uint32_t> reserve_free_block(uint32_t primary_bgd);
BAN::ErrorOr<void> release_block(uint32_t block);
@ -102,7 +126,7 @@ namespace Kernel
{
public:
BlockBufferManager() = default;
BlockBufferWrapper get_buffer();
BAN::ErrorOr<BlockBufferWrapper> get_buffer();
BAN::ErrorOr<void> initialize(size_t block_size);
@ -114,7 +138,9 @@ namespace Kernel
};
private:
BAN::Array<BlockBuffer, 10> m_buffers;
Mutex m_buffer_mutex;
ThreadBlocker m_buffer_blocker;
BAN::Array<BlockBuffer, 16> m_buffers;
};
private:

View File

@ -125,7 +125,7 @@ namespace Kernel
TRY(m_buffer_manager.initialize(block_size()));
{
auto block_buffer = m_buffer_manager.get_buffer();
auto block_buffer = TRY(m_buffer_manager.get_buffer());
if (superblock().rev_level == Ext2::Enum::GOOD_OLD_REV)
{
@ -170,6 +170,9 @@ namespace Kernel
BAN::ErrorOr<uint32_t> Ext2FS::create_inode(const Ext2::Inode& ext2_inode)
{
auto bgd_buffer = TRY(m_buffer_manager.get_buffer());
auto inode_bitmap = TRY(m_buffer_manager.get_buffer());
LockGuard _(m_mutex);
ASSERT(ext2_inode.size == 0);
@ -179,9 +182,6 @@ namespace Kernel
const uint32_t block_size = this->block_size();
auto bgd_buffer = m_buffer_manager.get_buffer();
auto inode_bitmap = m_buffer_manager.get_buffer();
uint32_t current_group = -1;
BlockLocation bgd_location {};
Ext2::BlockGroupDescriptor* bgd = nullptr;
@ -248,15 +248,15 @@ namespace Kernel
BAN::ErrorOr<void> Ext2FS::delete_inode(uint32_t ino)
{
auto bgd_buffer = TRY(get_block_buffer());
auto bitmap_buffer = TRY(get_block_buffer());
auto inode_buffer = TRY(get_block_buffer());
LockGuard _(m_mutex);
ASSERT(ino >= superblock().first_ino);
ASSERT(ino <= superblock().inodes_count);
auto bgd_buffer = get_block_buffer();
auto bitmap_buffer = get_block_buffer();
auto inode_buffer = get_block_buffer();
const uint32_t inode_group = (ino - 1) / superblock().inodes_per_group;
const uint32_t inode_index = (ino - 1) % superblock().inodes_per_group;
@ -334,6 +334,8 @@ namespace Kernel
BAN::ErrorOr<void> Ext2FS::sync_superblock()
{
auto superblock_buffer = TRY(get_block_buffer());
LockGuard _(m_mutex);
const uint32_t sector_size = m_block_device->blksize();
@ -347,8 +349,6 @@ namespace Kernel
const uint32_t lba = 1024 / sector_size;
const uint32_t sector_count = BAN::Math::div_round_up<uint32_t>(superblock_bytes, sector_size);
auto superblock_buffer = get_block_buffer();
TRY(m_block_device->read_blocks(lba, sector_count, superblock_buffer.span()));
if (memcmp(superblock_buffer.data(), &m_superblock, superblock_bytes))
{
@ -370,22 +370,21 @@ namespace Kernel
return m_block_device->sync_blocks(block * sectors_per_block, sectors_per_block);
}
Ext2FS::BlockBufferWrapper Ext2FS::get_block_buffer()
BAN::ErrorOr<Ext2FS::BlockBufferWrapper> Ext2FS::get_block_buffer()
{
LockGuard _(m_mutex);
return m_buffer_manager.get_buffer();
}
BAN::ErrorOr<uint32_t> Ext2FS::reserve_free_block(uint32_t primary_bgd)
{
auto bgd_buffer = TRY(m_buffer_manager.get_buffer());
auto block_bitmap = TRY(m_buffer_manager.get_buffer());
LockGuard _(m_mutex);
if (m_superblock.r_blocks_count >= m_superblock.free_blocks_count)
return BAN::Error::from_errno(ENOSPC);
auto bgd_buffer = m_buffer_manager.get_buffer();
auto block_bitmap = m_buffer_manager.get_buffer();
auto check_block_group =
[&](uint32_t block_group) -> BAN::ErrorOr<uint32_t>
{
@ -439,6 +438,9 @@ namespace Kernel
BAN::ErrorOr<void> Ext2FS::release_block(uint32_t block)
{
auto bgd_buffer = TRY(get_block_buffer());
auto bitmap_buffer = TRY(get_block_buffer());
LockGuard _(m_mutex);
ASSERT(block >= m_superblock.first_data_block);
@ -447,9 +449,6 @@ namespace Kernel
const uint32_t block_group = (block - m_superblock.first_data_block) / m_superblock.blocks_per_group;
const uint32_t block_offset = (block - m_superblock.first_data_block) % m_superblock.blocks_per_group;
auto bgd_buffer = get_block_buffer();
auto bitmap_buffer = get_block_buffer();
auto bgd_location = locate_block_group_descriptior(block_group);
TRY(read_block(bgd_location.block, bgd_buffer));
@ -480,7 +479,7 @@ namespace Kernel
const uint32_t block_size = this->block_size();
auto bgd_buffer = m_buffer_manager.get_buffer();
auto bgd_buffer = TRY(m_buffer_manager.get_buffer());
const uint32_t inode_group = (ino - 1) / superblock().inodes_per_group;
const uint32_t inode_index = (ino - 1) % superblock().inodes_per_group;
@ -533,16 +532,22 @@ namespace Kernel
};
}
Ext2FS::BlockBufferWrapper Ext2FS::BlockBufferManager::get_buffer()
BAN::ErrorOr<Ext2FS::BlockBufferWrapper> Ext2FS::BlockBufferManager::get_buffer()
{
for (auto& buffer : m_buffers)
LockGuard _(m_buffer_mutex);
for (;;)
{
if (buffer.used)
continue;
buffer.used = true;
return Ext2FS::BlockBufferWrapper(buffer.buffer.span(), buffer.used);
for (auto& buffer : m_buffers)
{
if (buffer.used)
continue;
buffer.used = true;
return Ext2FS::BlockBufferWrapper(buffer.buffer.span(), &buffer.used, &m_buffer_mutex, &m_buffer_blocker);
}
TRY(Thread::current().block_or_eintr_indefinite(m_buffer_blocker, &m_buffer_mutex));
}
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<void> Ext2FS::BlockBufferManager::initialize(size_t block_size)

View File

@ -32,7 +32,7 @@ namespace Kernel
auto inode_location = TRY(fs.locate_inode(inode_ino));
auto block_buffer = fs.get_block_buffer();
auto block_buffer = TRY(fs.get_block_buffer());
TRY(fs.read_block(inode_location.block, block_buffer));
auto& inode = block_buffer.span().slice(inode_location.offset).as<Ext2::Inode>();
@ -61,7 +61,7 @@ namespace Kernel
return BAN::Optional<uint32_t>();
ASSERT(depth >= 1);
auto block_buffer = m_fs.get_block_buffer();
auto block_buffer = TRY(m_fs.get_block_buffer());
TRY(m_fs.read_block(block, block_buffer));
const uint32_t indices_per_block = blksize() / sizeof(uint32_t);
@ -152,7 +152,7 @@ namespace Kernel
const uint32_t block_size = blksize();
auto block_buffer = m_fs.get_block_buffer();
auto block_buffer = TRY(m_fs.get_block_buffer());
const uint32_t first_block = offset / block_size;
const uint32_t last_block = BAN::Math::div_round_up<uint32_t>(offset + count, block_size);
@ -192,7 +192,7 @@ namespace Kernel
const uint32_t block_size = blksize();
auto block_buffer = m_fs.get_block_buffer();
auto block_buffer = TRY(m_fs.get_block_buffer());
size_t written = 0;
size_t to_write = buffer.size();
@ -349,7 +349,7 @@ namespace Kernel
return {};
}
auto block_buffer = m_fs.get_block_buffer();
auto block_buffer = TRY(m_fs.get_block_buffer());
TRY(m_fs.read_block(block, block_buffer));
const uint32_t ids_per_block = blksize() / sizeof(uint32_t);
@ -409,7 +409,7 @@ done:
// FIXME: can we actually assume directories have all their blocks allocated
const uint32_t block_index = TRY(fs_block_of_data_block_index(offset)).value();
auto block_buffer = m_fs.get_block_buffer();
auto block_buffer = TRY(m_fs.get_block_buffer());
TRY(m_fs.read_block(block_index, block_buffer));
@ -602,7 +602,7 @@ done:
const uint32_t block_size = m_fs.block_size();
auto block_buffer = m_fs.get_block_buffer();
auto block_buffer = TRY(m_fs.get_block_buffer());
auto write_inode =
[&](uint32_t entry_offset, uint32_t entry_rec_len) -> BAN::ErrorOr<void>
@ -689,7 +689,7 @@ needs_new_block:
{
ASSERT(mode().ifdir());
auto block_buffer = m_fs.get_block_buffer();
auto block_buffer = TRY(m_fs.get_block_buffer());
// Confirm that this doesn't contain anything else than '.' or '..'
for (uint32_t i = 0; i < max_used_data_block_count(); i++)
@ -726,7 +726,7 @@ needs_new_block:
return BAN::Error::from_errno(ENOTSUP);
}
auto block_buffer = m_fs.get_block_buffer();
auto block_buffer = TRY(m_fs.get_block_buffer());
for (uint32_t i = 0; i < max_used_data_block_count(); i++)
{
@ -782,7 +782,7 @@ needs_new_block:
return BAN::Error::from_errno(ENOTSUP);
}
auto block_buffer = m_fs.get_block_buffer();
auto block_buffer = TRY(m_fs.get_block_buffer());
for (uint32_t i = 0; i < max_used_data_block_count(); i++)
{
@ -844,7 +844,7 @@ needs_new_block:
block = TRY(m_fs.reserve_free_block(block_group()));
m_inode.blocks += inode_blocks_per_fs_block;
auto block_buffer = m_fs.get_block_buffer();
auto block_buffer = TRY(m_fs.get_block_buffer());
memset(block_buffer.data(), 0x00, block_buffer.size());
TRY(m_fs.write_block(block, block_buffer));
}
@ -852,7 +852,7 @@ needs_new_block:
if (depth == 0)
return block;
auto block_buffer = m_fs.get_block_buffer();
auto block_buffer = TRY(m_fs.get_block_buffer());
TRY(m_fs.read_block(block, block_buffer));
uint32_t divisor = 1;
@ -901,7 +901,7 @@ needs_new_block:
BAN::ErrorOr<void> Ext2Inode::sync()
{
auto inode_location = TRY(m_fs.locate_inode(ino()));
auto block_buffer = m_fs.get_block_buffer();
auto block_buffer = TRY(m_fs.get_block_buffer());
TRY(m_fs.read_block(inode_location.block, block_buffer));
if (memcmp(block_buffer.data() + inode_location.offset, &m_inode, sizeof(Ext2::Inode)))
@ -917,7 +917,7 @@ needs_new_block:
{
ASSERT(mode().ifdir());
auto block_buffer = m_fs.get_block_buffer();
auto block_buffer = TRY(m_fs.get_block_buffer());
for (uint32_t i = 0; i < max_used_data_block_count(); i++)
{