Kernel: Optimize Ext2 disk reads

We used to read whole file until we reached the asked offset.
Now we can calculate the appropriate block and read just the asked data.
This commit is contained in:
Bananymous 2023-03-18 03:47:59 +02:00
parent 1a26a318a4
commit 8236598f9d
2 changed files with 102 additions and 128 deletions

View File

@ -136,6 +136,8 @@ namespace Kernel
virtual BAN::ErrorOr<BAN::RefPtr<Inode>> directory_find(BAN::StringView) override; virtual BAN::ErrorOr<BAN::RefPtr<Inode>> directory_find(BAN::StringView) override;
private: private:
BAN::ErrorOr<uint32_t> data_block_index(uint32_t);
using block_callback_t = BAN::ErrorOr<bool>(*)(const BAN::Vector<uint8_t>&, void*); using block_callback_t = BAN::ErrorOr<bool>(*)(const BAN::Vector<uint8_t>&, void*);
BAN::ErrorOr<void> for_each_block(block_callback_t, void*); BAN::ErrorOr<void> for_each_block(block_callback_t, void*);

View File

@ -138,91 +138,89 @@ namespace Kernel
} }
BAN::ErrorOr<void> Ext2Inode::for_each_block(block_callback_t callback, void* callback_data) BAN::ErrorOr<uint32_t> Ext2Inode::data_block_index(uint32_t asked_data_block)
{ {
uint32_t data_blocks_left = m_inode.blocks / (2 << m_fs->superblock().log_block_size); uint32_t data_blocks_count = m_inode.blocks / (2 << m_fs->superblock().log_block_size);
uint32_t block_array_block_count = (1024 << m_fs->superblock().log_block_size) / sizeof(uint32_t); uint32_t blocks_per_array = (1024 << m_fs->superblock().log_block_size) / sizeof(uint32_t);
// Direct blocks if (asked_data_block >= data_blocks_count)
for (uint32_t i = 0; i < 12; i++) return BAN::Error::from_c_string("Ext2: no such block");
// Direct block
if (asked_data_block < 12)
{ {
if (m_inode.block[i] == 0) uint32_t block = m_inode.block[asked_data_block];
continue; if (block == 0)
return BAN::Error::from_errno(EIO);
auto block_data = TRY(m_fs->read_block(m_inode.block[i])); return block;
if (!TRY(callback(block_data, callback_data)))
return {};
if (--data_blocks_left == 0)
return {};
} }
asked_data_block -= 12;
// Singly indirect block // Singly indirect block
if (m_inode.block[12]) if (asked_data_block < blocks_per_array)
{ {
if (m_inode.block[12] == 0)
return BAN::Error::from_errno(EIO);
auto block_array = TRY(m_fs->read_block(m_inode.block[12])); auto block_array = TRY(m_fs->read_block(m_inode.block[12]));
for (uint32_t i = 0; i < block_array_block_count; i++) uint32_t block = ((uint32_t*)block_array.data())[asked_data_block];
{
uint32_t block = ((uint32_t*)block_array.data())[i];
if (block == 0) if (block == 0)
continue; return BAN::Error::from_errno(EIO);
auto block_data = TRY(m_fs->read_block(block)); return block;
if (!TRY(callback(block_data, callback_data)))
return {};
if (--data_blocks_left == 0)
return {};
}
} }
asked_data_block -= blocks_per_array;
// Doubly indirect blocks // Doubly indirect blocks
if (m_inode.block[13]) if (asked_data_block < blocks_per_array * blocks_per_array)
{ {
auto singly_indirect_array = TRY(m_fs->read_block(m_inode.block[13])); auto singly_indirect_array = TRY(m_fs->read_block(m_inode.block[13]));
for (uint32_t i = 0; i < block_array_block_count; i++) uint32_t direct_block = ((uint32_t*)singly_indirect_array.data())[asked_data_block / blocks_per_array];
{ if (direct_block == 0)
uint32_t singly_indirect_block = ((uint32_t*)singly_indirect_array.data())[i]; return BAN::Error::from_errno(EIO);
auto block_array = TRY(m_fs->read_block(singly_indirect_block)); auto block_array = TRY(m_fs->read_block(direct_block));
for (uint32_t j = 0; j < block_array_block_count; j++) uint32_t block = ((uint32_t*)block_array.data())[asked_data_block % blocks_per_array];
{
uint32_t block = ((uint32_t*)block_array.data())[j];
if (block == 0) if (block == 0)
continue; return BAN::Error::from_errno(EIO);
auto block_data = TRY(m_fs->read_block(block)); return block;
if (!TRY(callback(block_data, callback_data)))
return {};
if (--data_blocks_left == 0)
return {};
}
}
} }
asked_data_block -= blocks_per_array * blocks_per_array;
// Triply indirect blocks // Triply indirect blocks
if (m_inode.block[14]) if (asked_data_block < blocks_per_array * blocks_per_array * blocks_per_array)
{ {
auto doubly_indirect_array = TRY(m_fs->read_block(m_inode.block[14])); auto doubly_indirect_array = TRY(m_fs->read_block(m_inode.block[14]));
for (uint32_t i = 0; i < block_array_block_count; i++) uint32_t singly_indirect_block = ((uint32_t*)doubly_indirect_array.data())[asked_data_block / (blocks_per_array * blocks_per_array)];
{ if (singly_indirect_block == 0)
uint32_t doubly_indirect_block = ((uint32_t*)doubly_indirect_array.data())[i]; return BAN::Error::from_errno(EIO);
auto singly_indirect_array = TRY(m_fs->read_block(doubly_indirect_block)); auto singly_indirect_array = TRY(m_fs->read_block(singly_indirect_block));
for (uint32_t j = 0; j < block_array_block_count; j++) uint32_t direct_block = ((uint32_t*)singly_indirect_array.data())[(asked_data_block / blocks_per_array) % blocks_per_array];
{ if (direct_block == 0)
uint32_t singly_indirect_block = ((uint32_t*)singly_indirect_array.data())[j]; return BAN::Error::from_errno(EIO);
auto block_array = TRY(m_fs->read_block(singly_indirect_block)); auto block_array = TRY(m_fs->read_block(direct_block));
for (uint32_t k = 0; k < block_array_block_count; k++) uint32_t block = ((uint32_t*)block_array.data())[asked_data_block % blocks_per_array];
{
uint32_t block = ((uint32_t*)block_array.data())[k];
if (block == 0) if (block == 0)
continue; return BAN::Error::from_errno(EIO);
auto block_data = TRY(m_fs->read_block(block)); return block;
if (!TRY(callback(block_data, callback_data)))
return {};
if (--data_blocks_left == 0)
return {};
}
}
}
} }
return BAN::Error::from_c_string("Inode did not contain enough blocks"); ASSERT(false);
}
BAN::ErrorOr<void> Ext2Inode::for_each_block(block_callback_t callback, void* callback_data)
{
uint32_t data_block_count = m_inode.blocks / (2 << m_fs->superblock().log_block_size);
for (uint32_t i = 0; i < data_block_count; i++)
{
uint32_t data_block_index = TRY(this->data_block_index(i));
auto block_data = TRY(m_fs->read_block(data_block_index));
if (!TRY(callback(block_data, callback_data)))
return {};
}
return {};
} }
BAN::ErrorOr<size_t> Ext2Inode::read(size_t offset, void* buffer, size_t count) BAN::ErrorOr<size_t> Ext2Inode::read(size_t offset, void* buffer, size_t count)
@ -230,58 +228,32 @@ namespace Kernel
if (ifdir()) if (ifdir())
return BAN::Error::from_errno(EISDIR); return BAN::Error::from_errno(EISDIR);
struct read_info if (offset >= m_inode.size)
return 0;
if (offset + count > m_inode.size)
count = m_inode.size - offset;
const uint32_t block_size = 1024 << m_fs->superblock().log_block_size;
ASSERT(offset % block_size == 0);
const uint32_t first_block = offset / block_size;
const uint32_t last_block = BAN::Math::div_round_up<uint32_t>(offset + count, block_size);
size_t n_read = 0;
for (uint32_t block = first_block; block < last_block; block++)
{ {
size_t file_current_offset; uint32_t data_block = TRY(data_block_index(block));
size_t file_start_offset; auto block_data = TRY(m_fs->read_block(data_block));
size_t file_bytes_left;
uint8_t* out_byte_buffer;
size_t out_bytes_read;
size_t out_byte_buffer_size;
};
read_info info; uint32_t to_copy = BAN::Math::min<uint32_t>(block_data.size(), count - n_read);
info.file_current_offset = 0;
info.file_start_offset = offset;
info.file_bytes_left = m_inode.size;
info.out_byte_buffer = (uint8_t*)buffer;
info.out_bytes_read = 0;
info.out_byte_buffer_size = count;
block_callback_t read_func = memcpy((uint8_t*)buffer + n_read, block_data.data(), to_copy);
[](const BAN::Vector<uint8_t>& block_data, void* info_) -> BAN::ErrorOr<bool> n_read += to_copy;
{
read_info& info = *(read_info*)info_;
size_t block_size = BAN::Math::min<size_t>(block_data.size(), info.file_bytes_left);
// Skip blocks before 'offset'
if (info.file_current_offset + block_size <= info.file_start_offset)
{
info.file_current_offset += block_size;
info.file_bytes_left -= block_size;
return info.file_bytes_left > 0;
} }
size_t read_offset = 0; return count;
if (info.file_current_offset < info.file_start_offset)
read_offset = info.file_start_offset - info.file_current_offset;
size_t to_read = BAN::Math::min<size_t>(block_size - read_offset, info.out_byte_buffer_size - info.out_bytes_read);
memcpy(info.out_byte_buffer + info.out_bytes_read, block_data.data() + read_offset, to_read);
info.out_bytes_read += to_read;
if (info.out_bytes_read >= info.out_byte_buffer_size)
return false;
info.file_current_offset += block_size;
info.file_bytes_left -= block_size;
return info.file_bytes_left > 0;
};
TRY(for_each_block(read_func, &info));
return info.out_bytes_read;
} }
BAN::ErrorOr<BAN::RefPtr<Inode>> Ext2Inode::directory_find(BAN::StringView file_name) BAN::ErrorOr<BAN::RefPtr<Inode>> Ext2Inode::directory_find(BAN::StringView file_name)
@ -530,7 +502,7 @@ namespace Kernel
const Ext2::Inode& Ext2FS::ext2_root_inode() const const Ext2::Inode& Ext2FS::ext2_root_inode() const
{ {
return reinterpret_cast<const Ext2Inode*>(m_root_inode.operator->())->m_inode; return reinterpret_cast<const Ext2Inode*>(m_root_inode.ptr())->m_inode;
} }
} }