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:
parent
1a26a318a4
commit
8236598f9d
|
@ -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*);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
Loading…
Reference in New Issue