Kernel: Ext2 filesystem now holds 10 preallocated block buffers

Inodes can query blocks from this buffer. This allows allocation of
blocks to not fail during normal operations. Also less stress on
kmalloc.
This commit is contained in:
Bananymous 2023-09-25 14:22:32 +03:00
parent 7bdb428938
commit 8caba1e774
4 changed files with 158 additions and 102 deletions

View File

@ -9,6 +9,37 @@ namespace Kernel
class Ext2FS final : public FileSystem
{
public:
class BlockBufferWrapper
{
BAN_NON_COPYABLE(BlockBufferWrapper);
BAN_NON_MOVABLE(BlockBufferWrapper);
public:
BlockBufferWrapper(BAN::Span<uint8_t> buffer, bool& used)
: m_buffer(buffer)
, m_used(used)
{
ASSERT(m_used);
}
~BlockBufferWrapper()
{
m_used = false;
}
size_t size() const { return m_buffer.size(); }
uint8_t* data() { return m_buffer.data(); }
const uint8_t* data() const { return m_buffer.data(); }
uint8_t& operator[](size_t index) { return m_buffer[index]; }
uint8_t operator[](size_t index) const { return m_buffer[index]; }
private:
BAN::Span<uint8_t> m_buffer;
bool& m_used;
};
public:
static BAN::ErrorOr<Ext2FS*> create(Partition&);
@ -26,10 +57,12 @@ namespace Kernel
BAN::ErrorOr<void> delete_inode(uint32_t);
BAN::ErrorOr<void> resize_inode(uint32_t, size_t);
void read_block(uint32_t, BAN::Span<uint8_t>);
void write_block(uint32_t, BAN::Span<const uint8_t>);
void read_block(uint32_t, BlockBufferWrapper&);
void write_block(uint32_t, const BlockBufferWrapper&);
void sync_superblock();
BlockBufferWrapper get_block_buffer();
BAN::ErrorOr<uint32_t> reserve_free_block(uint32_t primary_bgd);
const Ext2::Superblock& superblock() const { return m_superblock; }
@ -39,11 +72,30 @@ namespace Kernel
uint32_t block;
uint32_t offset;
};
BAN::ErrorOr<BlockLocation> locate_inode(uint32_t);
BlockLocation locate_inode(uint32_t);
BlockLocation locate_block_group_descriptior(uint32_t);
uint32_t block_size() const { return 1024 << superblock().log_block_size; }
class BlockBufferManager
{
public:
BlockBufferManager() = default;
BlockBufferWrapper get_buffer();
BAN::ErrorOr<void> initialize(size_t block_size);
private:
struct BlockBuffer
{
BAN::Vector<uint8_t> buffer;
bool used { false };
};
private:
BAN::Array<BlockBuffer, 10> m_buffers;
};
private:
RecursiveSpinLock m_lock;
@ -52,6 +104,8 @@ namespace Kernel
BAN::RefPtr<Inode> m_root_inode;
BAN::Vector<uint32_t> m_superblock_backups;
BlockBufferManager m_buffer_manager;
Ext2::Superblock m_superblock;
friend class Ext2Inode;

View File

@ -39,7 +39,7 @@ namespace Kernel
virtual BAN::ErrorOr<void> truncate_impl(size_t) override;
private:
BAN::ErrorOr<uint32_t> fs_block_of_data_block_index(uint32_t data_block_index);
uint32_t fs_block_of_data_block_index(uint32_t data_block_index);
BAN::ErrorOr<uint32_t> allocate_new_block();
BAN::ErrorOr<void> sync();

View File

@ -90,16 +90,17 @@ namespace Kernel
dprintln(" inodes/group {}", m_superblock.inodes_per_group);
#endif
TRY(m_buffer_manager.initialize(block_size()));
{
BAN::Vector<uint8_t> block_buffer;
TRY(block_buffer.resize(block_size()));
auto block_buffer = m_buffer_manager.get_buffer();
if (superblock().rev_level == Ext2::Enum::GOOD_OLD_REV)
{
// In revision 0 all blockgroups contain superblock backup
TRY(m_superblock_backups.reserve(number_of_block_groups - 1));
for (uint32_t i = 1; i < number_of_block_groups; i++)
MUST(block_buffer.push_back(i));
MUST(m_superblock_backups.push_back(i));
}
else
{
@ -118,7 +119,7 @@ namespace Kernel
for (uint32_t bg : m_superblock_backups)
{
read_block(superblock().first_data_block + superblock().blocks_per_group * bg, block_buffer.span());
read_block(superblock().first_data_block + superblock().blocks_per_group * bg, block_buffer);
Ext2::Superblock& superblock_backup = *(Ext2::Superblock*)block_buffer.data();
if (superblock_backup.magic != Ext2::Enum::SUPER_MAGIC)
derrorln("superblock backup at block {} is invalid ({4H})", bg, superblock_backup.magic);
@ -145,11 +146,8 @@ namespace Kernel
const uint32_t block_size = this->block_size();
BAN::Vector<uint8_t> bgd_buffer;
TRY(bgd_buffer.resize(block_size));
BAN::Vector<uint8_t> inode_bitmap;
TRY(inode_bitmap.resize(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 {};
@ -165,7 +163,7 @@ namespace Kernel
current_group = ino_group;
bgd_location = locate_block_group_descriptior(current_group);
read_block(bgd_location.block, bgd_buffer.span());
read_block(bgd_location.block, bgd_buffer);
bgd = (Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset);
if (bgd->free_inodes_count == 0)
@ -174,7 +172,7 @@ namespace Kernel
continue;
}
read_block(bgd->inode_bitmap, inode_bitmap.span());
read_block(bgd->inode_bitmap, inode_bitmap);
}
const uint32_t ino_bitmap_byte = ino_index / 8;
@ -183,10 +181,10 @@ namespace Kernel
continue;
inode_bitmap[ino_bitmap_byte] |= 1 << ino_bitmap_bit;
write_block(bgd->inode_bitmap, inode_bitmap.span());
write_block(bgd->inode_bitmap, inode_bitmap);
bgd->free_inodes_count--;
write_block(bgd_location.block, bgd_buffer.span());
write_block(bgd_location.block, bgd_buffer);
const uint32_t inode_table_offset = ino_index * superblock().inode_size;
const BlockLocation inode_location
@ -198,11 +196,11 @@ namespace Kernel
// NOTE: we don't need inode bitmap anymore, so we can reuse it
auto& inode_buffer = inode_bitmap;
read_block(inode_location.block, inode_buffer.span());
read_block(inode_location.block, inode_buffer);
memcpy(inode_buffer.data() + inode_location.offset, &ext2_inode, sizeof(Ext2::Inode));
if (superblock().inode_size > sizeof(Ext2::Inode))
memset(inode_buffer.data() + inode_location.offset + sizeof(Ext2::Inode), 0, superblock().inode_size - sizeof(Ext2::Inode));
write_block(inode_location.block, inode_buffer.span());
write_block(inode_location.block, inode_buffer);
m_superblock.free_inodes_count--;
sync_superblock();
@ -214,7 +212,7 @@ namespace Kernel
return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted);
}
void Ext2FS::read_block(uint32_t block, BAN::Span<uint8_t> buffer)
void Ext2FS::read_block(uint32_t block, BlockBufferWrapper& buffer)
{
LockGuard _(m_lock);
@ -228,7 +226,7 @@ namespace Kernel
MUST(m_partition.read_sectors(sectors_before + (block - 2) * sectors_per_block, sectors_per_block, buffer.data()));
}
void Ext2FS::write_block(uint32_t block, BAN::Span<const uint8_t> buffer)
void Ext2FS::write_block(uint32_t block, const BlockBufferWrapper& buffer)
{
LockGuard _(m_lock);
@ -268,6 +266,13 @@ namespace Kernel
}
}
Ext2FS::BlockBufferWrapper Ext2FS::get_block_buffer()
{
LockGuard _(m_lock);
return m_buffer_manager.get_buffer();
}
BAN::ErrorOr<uint32_t> Ext2FS::reserve_free_block(uint32_t primary_bgd)
{
LockGuard _(m_lock);
@ -275,25 +280,20 @@ namespace Kernel
if (m_superblock.r_blocks_count >= m_superblock.free_blocks_count)
return BAN::Error::from_errno(ENOSPC);
const uint32_t block_size = this->block_size();
BAN::Vector<uint8_t> bgd_buffer;
TRY(bgd_buffer.resize(block_size));
BAN::Vector<uint8_t> block_bitmap;
TRY(block_bitmap.resize(block_size));
auto bgd_buffer = m_buffer_manager.get_buffer();
auto block_bitmap = m_buffer_manager.get_buffer();
auto check_block_group =
[&](uint32_t block_group) -> uint32_t
{
auto bgd_location = locate_block_group_descriptior(block_group);
read_block(bgd_location.block, bgd_buffer.span());
read_block(bgd_location.block, bgd_buffer);
auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset);
if (bgd.free_blocks_count == 0)
return 0;
read_block(bgd.block_bitmap, block_bitmap.span());
read_block(bgd.block_bitmap, block_bitmap);
for (uint32_t block_offset = 0; block_offset < m_superblock.blocks_per_group; block_offset++)
{
const uint32_t fs_block_index = m_superblock.first_data_block + m_superblock.blocks_per_group * block_group + block_offset;
@ -306,10 +306,10 @@ namespace Kernel
continue;
block_bitmap[byte] |= 1 << bit;
write_block(bgd.block_bitmap, block_bitmap.span());
write_block(bgd.block_bitmap, block_bitmap);
bgd.free_blocks_count--;
write_block(bgd_location.block, bgd_buffer.span());
write_block(bgd_location.block, bgd_buffer);
m_superblock.free_blocks_count--;
sync_superblock();
@ -334,7 +334,7 @@ namespace Kernel
return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted);
}
BAN::ErrorOr<Ext2FS::BlockLocation> Ext2FS::locate_inode(uint32_t ino)
Ext2FS::BlockLocation Ext2FS::locate_inode(uint32_t ino)
{
LockGuard _(m_lock);
@ -342,15 +342,14 @@ namespace Kernel
const uint32_t block_size = this->block_size();
BAN::Vector<uint8_t> bgd_buffer;
TRY(bgd_buffer.resize(block_size));
auto bgd_buffer = 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;
auto bgd_location = locate_block_group_descriptior(inode_group);
read_block(bgd_location.block, bgd_buffer.span());
read_block(bgd_location.block, bgd_buffer);
auto& bgd = *(Ext2::BlockGroupDescriptor*)(bgd_buffer.data() + bgd_location.offset);
@ -397,4 +396,26 @@ namespace Kernel
};
}
Ext2FS::BlockBufferWrapper Ext2FS::BlockBufferManager::get_buffer()
{
for (auto& buffer : m_buffers)
{
if (buffer.used)
continue;
buffer.used = true;
return Ext2FS::BlockBufferWrapper(buffer.buffer.span(), buffer.used);
}
ASSERT_NOT_REACHED();
}
BAN::ErrorOr<void> Ext2FS::BlockBufferManager::initialize(size_t block_size)
{
for (auto& buffer : m_buffers)
{
TRY(buffer.buffer.resize(block_size));
buffer.used = false;
}
return {};
}
}

View File

@ -23,12 +23,10 @@ namespace Kernel
BAN::ErrorOr<BAN::RefPtr<Inode>> Ext2Inode::create(Ext2FS& fs, uint32_t inode_ino)
{
auto inode_location = TRY(fs.locate_inode(inode_ino));
auto inode_location = fs.locate_inode(inode_ino);
BAN::Vector<uint8_t> block_buffer;
TRY(block_buffer.resize(fs.block_size()));
fs.read_block(inode_location.block, block_buffer.span());
auto block_buffer = fs.get_block_buffer();
fs.read_block(inode_location.block, block_buffer);
auto& inode = *(Ext2::Inode*)(block_buffer.data() + inode_location.offset);
@ -38,10 +36,10 @@ namespace Kernel
return BAN::RefPtr<Inode>::adopt(result);
}
#define VERIFY_AND_READ_BLOCK(expr) do { const uint32_t block_index = expr; ASSERT(block_index); m_fs.read_block(block_index, block_buffer.span()); } while (false)
#define VERIFY_AND_READ_BLOCK(expr) do { const uint32_t block_index = expr; ASSERT(block_index); m_fs.read_block(block_index, block_buffer); } while (false)
#define VERIFY_AND_RETURN(expr) ({ const uint32_t result = expr; ASSERT(result); return result; })
BAN::ErrorOr<uint32_t> Ext2Inode::fs_block_of_data_block_index(uint32_t data_block_index)
uint32_t Ext2Inode::fs_block_of_data_block_index(uint32_t data_block_index)
{
ASSERT(data_block_index < blocks());
@ -53,8 +51,7 @@ namespace Kernel
data_block_index -= 12;
BAN::Vector<uint8_t> block_buffer;
TRY(block_buffer.resize(blksize()));
auto block_buffer = m_fs.get_block_buffer();
// Singly indirect block
if (data_block_index < indices_per_block)
@ -115,8 +112,7 @@ namespace Kernel
const uint32_t block_size = blksize();
BAN::Vector<uint8_t> block_buffer;
TRY(block_buffer.resize(block_size));
auto block_buffer = 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);
@ -125,8 +121,8 @@ namespace Kernel
for (uint32_t data_block_index = first_block; data_block_index < last_block; data_block_index++)
{
uint32_t block_index = TRY(fs_block_of_data_block_index(data_block_index));
m_fs.read_block(block_index, block_buffer.span());
uint32_t block_index = fs_block_of_data_block_index(data_block_index);
m_fs.read_block(block_index, block_buffer);
uint32_t copy_offset = (offset + n_read) % block_size;
uint32_t to_copy = BAN::Math::min<uint32_t>(block_size - copy_offset, count - n_read);
@ -153,8 +149,7 @@ namespace Kernel
const uint32_t block_size = blksize();
BAN::Vector<uint8_t> block_buffer;
TRY(block_buffer.resize(block_size));
auto block_buffer = m_fs.get_block_buffer();
const uint8_t* u8buffer = (const uint8_t*)buffer;
@ -163,14 +158,14 @@ namespace Kernel
// Write partial block
if (offset % block_size)
{
uint32_t block_index = TRY(fs_block_of_data_block_index(offset / block_size));
uint32_t block_index = fs_block_of_data_block_index(offset / block_size);
uint32_t block_offset = offset % block_size;
uint32_t to_copy = BAN::Math::min<uint32_t>(block_size - block_offset, to_write);
m_fs.read_block(block_index, block_buffer.span());
m_fs.read_block(block_index, block_buffer);
memcpy(block_buffer.data() + block_offset, u8buffer, to_copy);
m_fs.write_block(block_index, block_buffer.span());
m_fs.write_block(block_index, block_buffer);
u8buffer += to_copy;
offset += to_copy;
@ -179,9 +174,10 @@ namespace Kernel
while (to_write >= block_size)
{
uint32_t block_index = TRY(fs_block_of_data_block_index(offset / block_size));
uint32_t block_index = fs_block_of_data_block_index(offset / block_size);
m_fs.write_block(block_index, BAN::Span<const uint8_t>(u8buffer, block_size));
memcpy(block_buffer.data(), u8buffer, block_buffer.size());
m_fs.write_block(block_index, block_buffer);
u8buffer += block_size;
offset += block_size;
@ -190,11 +186,11 @@ namespace Kernel
if (to_write > 0)
{
uint32_t block_index = TRY(fs_block_of_data_block_index(offset / block_size));
uint32_t block_index = fs_block_of_data_block_index(offset / block_size);
m_fs.read_block(block_index, block_buffer.span());
m_fs.read_block(block_index, block_buffer);
memcpy(block_buffer.data(), u8buffer, to_write);
m_fs.write_block(block_index, block_buffer.span());
m_fs.write_block(block_index, block_buffer);
}
return count;
@ -216,23 +212,22 @@ namespace Kernel
return {};
}
BAN::Vector<uint8_t> block_buffer;
TRY(block_buffer.resize(block_size));
auto block_buffer = m_fs.get_block_buffer();
if (uint32_t rem = m_inode.size % block_size)
{
uint32_t last_block_index = TRY(fs_block_of_data_block_index(current_data_blocks - 1));
uint32_t last_block_index = fs_block_of_data_block_index(current_data_blocks - 1);
m_fs.read_block(last_block_index, block_buffer.span());
m_fs.read_block(last_block_index, block_buffer);
memset(block_buffer.data() + rem, 0, block_size - rem);
m_fs.write_block(last_block_index, block_buffer.span());
m_fs.write_block(last_block_index, block_buffer);
}
memset(block_buffer.data(), 0, block_size);
while (blocks() < needed_data_blocks)
{
uint32_t block_index = TRY(allocate_new_block());
m_fs.write_block(block_index, block_buffer.span());
m_fs.write_block(block_index, block_buffer);
}
m_inode.size = new_size;
@ -254,12 +249,11 @@ namespace Kernel
}
const uint32_t block_size = blksize();
const uint32_t block_index = TRY(fs_block_of_data_block_index(offset));
const uint32_t block_index = fs_block_of_data_block_index(offset);
BAN::Vector<uint8_t> block_buffer;
TRY(block_buffer.resize(block_size));
auto block_buffer = m_fs.get_block_buffer();
m_fs.read_block(block_index, block_buffer.span());
m_fs.read_block(block_index, block_buffer);
// First determine if we have big enough list
{
@ -356,8 +350,8 @@ namespace Kernel
const uint32_t inode_index = TRY(m_fs.create_inode(ext2_inode));
const uint32_t block_size = m_fs.block_size();
BAN::Vector<uint8_t> block_buffer;
TRY(block_buffer.resize(block_size));
auto block_buffer = m_fs.get_block_buffer();
auto write_inode = [&](uint32_t entry_offset, uint32_t entry_rec_len)
{
@ -392,8 +386,8 @@ namespace Kernel
goto needs_new_block;
// Try to insert inode to last data block
block_index = TRY(fs_block_of_data_block_index(data_block_count - 1));
m_fs.read_block(block_index, block_buffer.span());
block_index = fs_block_of_data_block_index(data_block_count - 1);
m_fs.read_block(block_index, block_buffer);
while (entry_offset < block_size)
{
@ -406,7 +400,7 @@ namespace Kernel
if (entry.inode == 0 && needed_entry_len <= entry.rec_len)
{
write_inode(entry_offset, entry.rec_len);
m_fs.write_block(block_index, block_buffer.span());
m_fs.write_block(block_index, block_buffer);
return {};
}
else if (needed_entry_len <= entry.rec_len - entry_min_rec_len)
@ -415,7 +409,7 @@ namespace Kernel
entry.rec_len = entry_min_rec_len;
write_inode(entry_offset + entry.rec_len, new_rec_len);
m_fs.write_block(block_index, block_buffer.span());
m_fs.write_block(block_index, block_buffer);
return {};
}
@ -425,9 +419,9 @@ namespace Kernel
needs_new_block:
block_index = TRY(allocate_new_block());
m_fs.read_block(block_index, block_buffer.span());
m_fs.read_block(block_index, block_buffer);
write_inode(0, block_size);
m_fs.write_block(block_index, block_buffer.span());
m_fs.write_block(block_index, block_buffer);
return {};
}
@ -435,7 +429,7 @@ needs_new_block:
#define READ_OR_ALLOCATE_BASE_BLOCK(index_) \
do { \
if (m_inode.block[index_] != 0) \
m_fs.read_block(m_inode.block[index_], block_buffer.span()); \
m_fs.read_block(m_inode.block[index_], block_buffer); \
else \
{ \
m_inode.block[index_] = TRY(m_fs.reserve_free_block(block_group())); \
@ -446,13 +440,13 @@ needs_new_block:
#define READ_OR_ALLOCATE_INDIRECT_BLOCK(result_, buffer_index_, parent_block_) \
uint32_t result_ = ((uint32_t*)block_buffer.data())[buffer_index_]; \
if (result_ != 0) \
m_fs.read_block(result_, block_buffer.span()); \
m_fs.read_block(result_, block_buffer); \
else \
{ \
const uint32_t new_block_ = TRY(m_fs.reserve_free_block(block_group())); \
\
((uint32_t*)block_buffer.data())[buffer_index_] = new_block_; \
m_fs.write_block(parent_block_, block_buffer.span()); \
m_fs.write_block(parent_block_, block_buffer); \
\
result_ = new_block_; \
memset(block_buffer.data(), 0x00, block_buffer.size()); \
@ -465,7 +459,7 @@ needs_new_block:
\
ASSERT(((uint32_t*)block_buffer.data())[buffer_index_] == 0); \
((uint32_t*)block_buffer.data())[buffer_index_] = block_; \
m_fs.write_block(parent_block_, block_buffer.span()); \
m_fs.write_block(parent_block_, block_buffer); \
\
m_inode.blocks += blocks_per_fs_block; \
update_and_sync(); \
@ -503,8 +497,7 @@ needs_new_block:
block_array_index -= 12;
BAN::Vector<uint8_t> block_buffer;
TRY(block_buffer.resize(blksize()));
auto block_buffer = m_fs.get_block_buffer();
// singly indirect block
if (block_array_index < indices_per_fs_block)
@ -544,25 +537,14 @@ needs_new_block:
BAN::ErrorOr<void> Ext2Inode::sync()
{
auto inode_location_or_error = m_fs.locate_inode(ino());
if (inode_location_or_error.is_error())
{
dwarnln("Open inode not found from filesystem");
return BAN::Error::from_error_code(ErrorCode::Ext2_Corrupted);
}
auto inode_location = m_fs.locate_inode(ino());
auto block_buffer = m_fs.get_block_buffer();
auto inode_location = inode_location_or_error.release_value();
const uint32_t block_size = blksize();
BAN::Vector<uint8_t> block_buffer;
TRY(block_buffer.resize(block_size));
m_fs.read_block(inode_location.block, block_buffer.span());
m_fs.read_block(inode_location.block, block_buffer);
if (memcmp(block_buffer.data() + inode_location.offset, &m_inode, sizeof(Ext2::Inode)))
{
memcpy(block_buffer.data() + inode_location.offset, &m_inode, sizeof(Ext2::Inode));
m_fs.write_block(inode_location.block, block_buffer.span());
m_fs.write_block(inode_location.block, block_buffer);
}
return {};
@ -575,13 +557,12 @@ needs_new_block:
const uint32_t block_size = blksize();
const uint32_t data_block_count = blocks();
BAN::Vector<uint8_t> block_buffer;
TRY(block_buffer.resize(block_size));
auto block_buffer = m_fs.get_block_buffer();
for (uint32_t i = 0; i < data_block_count; i++)
{
const uint32_t block_index = TRY(fs_block_of_data_block_index(i));
m_fs.read_block(block_index, block_buffer.span());
const uint32_t block_index = fs_block_of_data_block_index(i);
m_fs.read_block(block_index, block_buffer);
const uint8_t* block_buffer_end = block_buffer.data() + block_size;
const uint8_t* entry_addr = block_buffer.data();