Kernel: Add indirect block cache to ext2 inode
This reduces indirect block lookup by a lot :)
This commit is contained in:
@@ -55,7 +55,7 @@ namespace Kernel
|
|||||||
BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_no_lock(BAN::StringView);
|
BAN::ErrorOr<BAN::RefPtr<Inode>> find_inode_no_lock(BAN::StringView);
|
||||||
|
|
||||||
/* needs write end of the lock when allocate is true*/
|
/* needs write end of the lock when allocate is true*/
|
||||||
BAN::ErrorOr<BAN::Optional<uint32_t>> block_from_indirect_block_no_lock(uint32_t& block, uint32_t index, uint32_t depth, bool allocate);
|
BAN::ErrorOr<BAN::Optional<uint32_t>> block_from_indirect_block_no_lock(uint32_t block, uint32_t index, uint32_t depth, bool allocate);
|
||||||
BAN::ErrorOr<BAN::Optional<uint32_t>> fs_block_of_data_block_index_no_lock(uint32_t data_block_index, bool allocate);
|
BAN::ErrorOr<BAN::Optional<uint32_t>> fs_block_of_data_block_index_no_lock(uint32_t data_block_index, bool allocate);
|
||||||
|
|
||||||
/* needs write end of the lock */
|
/* needs write end of the lock */
|
||||||
@@ -71,6 +71,10 @@ namespace Kernel
|
|||||||
private:
|
private:
|
||||||
Ext2Inode(Ext2FS& fs, Ext2::Inode inode, uint32_t ino);
|
Ext2Inode(Ext2FS& fs, Ext2::Inode inode, uint32_t ino);
|
||||||
|
|
||||||
|
BAN::Optional<uint32_t> block_cache_find(uint32_t block, uint32_t index) const;
|
||||||
|
void block_cache_remove(uint32_t block, uint32_t index);
|
||||||
|
void block_cache_add(uint32_t block, uint32_t index, uint32_t target);
|
||||||
|
|
||||||
BAN::RefPtr<Inode> dir_cache_find(BAN::StringView) const;
|
BAN::RefPtr<Inode> dir_cache_find(BAN::StringView) const;
|
||||||
void dir_cache_remove(BAN::StringView);
|
void dir_cache_remove(BAN::StringView);
|
||||||
void dir_cache_add(BAN::StringView, BAN::RefPtr<Inode>);
|
void dir_cache_add(BAN::StringView, BAN::RefPtr<Inode>);
|
||||||
@@ -109,6 +113,16 @@ namespace Kernel
|
|||||||
const uint32_t m_og_faddr;
|
const uint32_t m_og_faddr;
|
||||||
const Ext2::Osd2 m_og_osd2;
|
const Ext2::Osd2 m_og_osd2;
|
||||||
|
|
||||||
|
struct BlockCacheEntry
|
||||||
|
{
|
||||||
|
mutable uint32_t freq;
|
||||||
|
uint32_t block;
|
||||||
|
uint32_t index;
|
||||||
|
uint32_t target;
|
||||||
|
};
|
||||||
|
mutable SpinLock m_block_cache_lock;
|
||||||
|
BAN::Array<BlockCacheEntry, 8> m_block_cache;
|
||||||
|
|
||||||
struct DirCacheEntry
|
struct DirCacheEntry
|
||||||
{
|
{
|
||||||
mutable size_t freq { 0 };
|
mutable size_t freq { 0 };
|
||||||
|
|||||||
@@ -71,57 +71,53 @@ namespace Kernel
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::Optional<uint32_t>> Ext2Inode::block_from_indirect_block_no_lock(uint32_t& block, uint32_t index, uint32_t depth, bool allocate)
|
BAN::ErrorOr<BAN::Optional<uint32_t>> Ext2Inode::block_from_indirect_block_no_lock(uint32_t block, uint32_t index, uint32_t depth, bool allocate)
|
||||||
{
|
{
|
||||||
const uint32_t indices_per_fs_block = blksize() / sizeof(uint32_t);
|
const uint32_t indices_per_fs_block = blksize() / sizeof(uint32_t);
|
||||||
|
|
||||||
if (block == 0 && !allocate)
|
ASSERT(block != 0);
|
||||||
return BAN::Optional<uint32_t>();
|
|
||||||
|
|
||||||
if (depth == 0)
|
if (depth == 0)
|
||||||
{
|
|
||||||
if (block == 0)
|
|
||||||
{
|
|
||||||
block = TRY(m_fs.reserve_free_block(block_group()));
|
|
||||||
m_blocks++;
|
|
||||||
|
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
return BAN::Optional<uint32_t>(block);
|
return BAN::Optional<uint32_t>(block);
|
||||||
}
|
|
||||||
|
|
||||||
auto block_buffer = TRY(m_fs.get_block_buffer());
|
uint32_t local_index = index;
|
||||||
|
for (uint32_t i = 1; i < depth; i++)
|
||||||
|
local_index /= indices_per_fs_block;
|
||||||
|
local_index %= indices_per_fs_block;
|
||||||
|
|
||||||
bool needs_write = false;
|
uint32_t next_block = 0;
|
||||||
|
|
||||||
if (block != 0)
|
if (auto cached = block_cache_find(block, local_index); cached.has_value())
|
||||||
TRY(m_fs.read_block(block, block_buffer));
|
next_block = cached.value();
|
||||||
else
|
|
||||||
|
if (next_block == 0)
|
||||||
{
|
{
|
||||||
block = TRY(m_fs.reserve_free_block(block_group()));
|
auto block_buffer = TRY(m_fs.get_block_buffer());
|
||||||
|
TRY(m_fs.read_block(block, block_buffer));
|
||||||
|
auto block_span = block_buffer.span().as_span<uint32_t>();
|
||||||
|
|
||||||
|
next_block = block_span[local_index];
|
||||||
|
|
||||||
|
if (next_block == 0)
|
||||||
|
{
|
||||||
|
if (!allocate)
|
||||||
|
return BAN::Optional<uint32_t>();
|
||||||
|
|
||||||
|
auto zero_buffer = TRY(m_fs.get_block_buffer());
|
||||||
|
memset(zero_buffer.data(), 0, zero_buffer.size());
|
||||||
|
|
||||||
|
next_block = TRY(m_fs.reserve_free_block(block_group()));
|
||||||
|
TRY(m_fs.write_block(next_block, zero_buffer));
|
||||||
m_blocks++;
|
m_blocks++;
|
||||||
|
|
||||||
memset(block_buffer.data(), 0, block_buffer.size());
|
block_span[local_index] = next_block;
|
||||||
|
TRY(m_fs.write_block(block, block_buffer));
|
||||||
needs_write = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t divisor = 1;
|
block_cache_add(block, local_index, next_block);
|
||||||
for (uint32_t i = 1; i < depth; i++)
|
}
|
||||||
divisor *= indices_per_fs_block;
|
|
||||||
|
|
||||||
uint32_t& new_block = block_buffer.span().as_span<uint32_t>()[(index / divisor) % indices_per_fs_block];
|
return block_from_indirect_block_no_lock(next_block, index, depth - 1, allocate);
|
||||||
const auto old_block = new_block;
|
|
||||||
|
|
||||||
const auto result = TRY(block_from_indirect_block_no_lock(new_block, index, depth - 1, allocate));
|
|
||||||
|
|
||||||
if (needs_write || old_block != new_block)
|
|
||||||
TRY(m_fs.write_block(block, block_buffer));
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BAN::ErrorOr<BAN::Optional<uint32_t>> Ext2Inode::fs_block_of_data_block_index_no_lock(uint32_t data_block_index, bool allocate)
|
BAN::ErrorOr<BAN::Optional<uint32_t>> Ext2Inode::fs_block_of_data_block_index_no_lock(uint32_t data_block_index, bool allocate)
|
||||||
@@ -148,16 +144,29 @@ namespace Kernel
|
|||||||
}
|
}
|
||||||
data_block_index -= 12;
|
data_block_index -= 12;
|
||||||
|
|
||||||
if (data_block_index < indices_per_block)
|
uint32_t depth_block_count = indices_per_block;
|
||||||
return block_from_indirect_block_no_lock(m_ext2_blocks.block[12], data_block_index, 1, allocate);
|
for (size_t i = 0; i < 3; i++)
|
||||||
|
{
|
||||||
|
if (data_block_index < depth_block_count)
|
||||||
|
{
|
||||||
|
auto& block = m_ext2_blocks.block[12 + i];
|
||||||
|
if (block == 0)
|
||||||
|
{
|
||||||
|
if (!allocate)
|
||||||
|
return BAN::Optional<uint32_t>();
|
||||||
|
|
||||||
|
auto zero_buffer = TRY(m_fs.get_block_buffer());
|
||||||
|
memset(zero_buffer.data(), 0, zero_buffer.size());
|
||||||
|
|
||||||
|
block = TRY(m_fs.reserve_free_block(block_group()));
|
||||||
|
TRY(m_fs.write_block(block, zero_buffer));
|
||||||
|
m_blocks++;
|
||||||
|
}
|
||||||
|
return block_from_indirect_block_no_lock(block, data_block_index, i + 1, allocate);
|
||||||
|
}
|
||||||
data_block_index -= indices_per_block;
|
data_block_index -= indices_per_block;
|
||||||
|
depth_block_count *= indices_per_block;
|
||||||
if (data_block_index < indices_per_block * indices_per_block)
|
}
|
||||||
return block_from_indirect_block_no_lock(m_ext2_blocks.block[13], data_block_index, 2, allocate);
|
|
||||||
data_block_index -= indices_per_block * indices_per_block;
|
|
||||||
|
|
||||||
if (data_block_index < indices_per_block * indices_per_block * indices_per_block)
|
|
||||||
return block_from_indirect_block_no_lock(m_ext2_blocks.block[14], data_block_index, 3, allocate);
|
|
||||||
|
|
||||||
ASSERT_NOT_REACHED();
|
ASSERT_NOT_REACHED();
|
||||||
}
|
}
|
||||||
@@ -958,6 +967,61 @@ needs_new_block:
|
|||||||
return BAN::Error::from_errno(ENOENT);
|
return BAN::Error::from_errno(ENOENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BAN::Optional<uint32_t> Ext2Inode::block_cache_find(uint32_t block, uint32_t index) const
|
||||||
|
{
|
||||||
|
SpinLockGuard _(m_block_cache_lock);
|
||||||
|
for (const auto& cache : m_block_cache)
|
||||||
|
{
|
||||||
|
if (cache.block != block || cache.index != index)
|
||||||
|
continue;
|
||||||
|
cache.freq++;
|
||||||
|
return cache.target;
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ext2Inode::block_cache_remove(uint32_t block, uint32_t index)
|
||||||
|
{
|
||||||
|
SpinLockGuard _(m_block_cache_lock);
|
||||||
|
for (auto& cache : m_block_cache)
|
||||||
|
{
|
||||||
|
if (cache.block != block || cache.index != index)
|
||||||
|
continue;
|
||||||
|
cache = {};
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ext2Inode::block_cache_add(uint32_t block, uint32_t index, uint32_t target)
|
||||||
|
{
|
||||||
|
SpinLockGuard _(m_block_cache_lock);
|
||||||
|
|
||||||
|
size_t min_freq = BAN::numeric_limits<size_t>::max();
|
||||||
|
size_t min_index = 0;
|
||||||
|
for (size_t i = 0; i < m_block_cache.size(); i++)
|
||||||
|
{
|
||||||
|
const auto& cache = m_block_cache[i];
|
||||||
|
if (cache.block == block && cache.index == index)
|
||||||
|
{
|
||||||
|
ASSERT(cache.target == target);
|
||||||
|
cache.freq++;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (cache.freq < min_freq)
|
||||||
|
{
|
||||||
|
min_freq = cache.freq;
|
||||||
|
min_index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_block_cache[min_index] = {
|
||||||
|
.freq = 1,
|
||||||
|
.block = block,
|
||||||
|
.index = index,
|
||||||
|
.target = target,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
BAN::RefPtr<Inode> Ext2Inode::dir_cache_find(BAN::StringView name) const
|
BAN::RefPtr<Inode> Ext2Inode::dir_cache_find(BAN::StringView name) const
|
||||||
{
|
{
|
||||||
RWLockRDGuard _(m_dir_cache_lock);
|
RWLockRDGuard _(m_dir_cache_lock);
|
||||||
|
|||||||
Reference in New Issue
Block a user