mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 09:00:22 +00:00
Kernel: Fix broken disk cache behavior with O_DIRECT
When writing into a block via an O_DIRECT open file description, we would first flush every dirty block *except* the one we're about to write into. The purpose of flushing is to ensure coherency when mixing direct and indirect accesses to the same file. This patch fixes the issue by only flushing the affected block. Our behavior was totally backwards and could end up doing a lot of unnecessary work while avoiding the work that actually mattered.
This commit is contained in:
parent
95df924e0e
commit
a7b70c62d7
Notes:
sideshowbarker
2024-07-18 08:59:31 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/a7b70c62d77
1 changed files with 21 additions and 18 deletions
|
@ -35,6 +35,7 @@ public:
|
|||
~DiskCache() = default;
|
||||
|
||||
bool is_dirty() const { return !m_dirty_list.is_empty(); }
|
||||
bool entry_is_dirty(CacheEntry const& entry) const { return m_dirty_list.contains(entry); }
|
||||
|
||||
void mark_all_clean()
|
||||
{
|
||||
|
@ -52,13 +53,20 @@ public:
|
|||
m_clean_list.prepend(entry);
|
||||
}
|
||||
|
||||
CacheEntry* get(BlockBasedFileSystem::BlockIndex block_index) const
|
||||
{
|
||||
auto it = m_hash.find(block_index);
|
||||
if (it == m_hash.end())
|
||||
return nullptr;
|
||||
auto& entry = const_cast<CacheEntry&>(*it->value);
|
||||
VERIFY(entry.block_index == block_index);
|
||||
return &entry;
|
||||
}
|
||||
|
||||
CacheEntry& ensure(BlockBasedFileSystem::BlockIndex block_index) const
|
||||
{
|
||||
if (auto it = m_hash.find(block_index); it != m_hash.end()) {
|
||||
auto& entry = const_cast<CacheEntry&>(*it->value);
|
||||
VERIFY(entry.block_index == block_index);
|
||||
return entry;
|
||||
}
|
||||
if (auto* entry = get(block_index))
|
||||
return *entry;
|
||||
|
||||
if (m_clean_list.is_empty()) {
|
||||
// Not a single clean entry! Flush writes and try again.
|
||||
|
@ -261,19 +269,14 @@ void BlockBasedFileSystem::flush_specific_block_if_needed(BlockIndex index)
|
|||
m_cache.with_exclusive([&](auto& cache) {
|
||||
if (!cache->is_dirty())
|
||||
return;
|
||||
Vector<CacheEntry*, 32> cleaned_entries;
|
||||
cache->for_each_dirty_entry([&](CacheEntry& entry) {
|
||||
if (entry.block_index != index) {
|
||||
size_t base_offset = entry.block_index.value() * block_size();
|
||||
auto entry_data_buffer = UserOrKernelBuffer::for_kernel_buffer(entry.data);
|
||||
[[maybe_unused]] auto rc = file_description().write(base_offset, entry_data_buffer, block_size());
|
||||
cleaned_entries.append(&entry);
|
||||
}
|
||||
});
|
||||
// NOTE: We make a separate pass to mark entries clean since marking them clean
|
||||
// moves them out of the dirty list which would disturb the iteration above.
|
||||
for (auto* entry : cleaned_entries)
|
||||
cache->mark_clean(*entry);
|
||||
auto* entry = cache->get(index);
|
||||
if (!entry)
|
||||
return;
|
||||
if (!cache->entry_is_dirty(*entry))
|
||||
return;
|
||||
size_t base_offset = entry->block_index.value() * block_size();
|
||||
auto entry_data_buffer = UserOrKernelBuffer::for_kernel_buffer(entry->data);
|
||||
(void)file_description().write(base_offset, entry_data_buffer, block_size());
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue