Serenity Operating System
at portability 247 lines 7.9 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <Kernel/Arch/i386/CPU.h> 28#include <Kernel/Devices/BlockDevice.h> 29#include <Kernel/FileSystem/DiskBackedFileSystem.h> 30#include <Kernel/FileSystem/FileDescription.h> 31#include <Kernel/KBuffer.h> 32#include <Kernel/Process.h> 33 34//#define DBFS_DEBUG 35 36namespace Kernel { 37 38struct CacheEntry { 39 time_t timestamp { 0 }; 40 u32 block_index { 0 }; 41 u8* data { nullptr }; 42 bool has_data { false }; 43 bool is_dirty { false }; 44}; 45 46class DiskCache { 47public: 48 explicit DiskCache(DiskBackedFS& fs) 49 : m_fs(fs) 50 , m_cached_block_data(KBuffer::create_with_size(m_entry_count * m_fs.block_size())) 51 , m_entries(KBuffer::create_with_size(m_entry_count * sizeof(CacheEntry))) 52 { 53 for (size_t i = 0; i < m_entry_count; ++i) { 54 entries()[i].data = m_cached_block_data.data() + i * m_fs.block_size(); 55 } 56 } 57 58 ~DiskCache() {} 59 60 bool is_dirty() const { return m_dirty; } 61 void set_dirty(bool b) { m_dirty = b; } 62 63 CacheEntry& get(u32 block_index) const 64 { 65 auto now = kgettimeofday().tv_sec; 66 67 CacheEntry* oldest_clean_entry = nullptr; 68 for (size_t i = 0; i < m_entry_count; ++i) { 69 auto& entry = const_cast<CacheEntry&>(entries()[i]); 70 if (entry.block_index == block_index) { 71 entry.timestamp = now; 72 return entry; 73 } 74 if (!entry.is_dirty) { 75 if (!oldest_clean_entry) 76 oldest_clean_entry = &entry; 77 else if (entry.timestamp < oldest_clean_entry->timestamp) 78 oldest_clean_entry = &entry; 79 } 80 } 81 if (!oldest_clean_entry) { 82 // Not a single clean entry! Flush writes and try again. 83 // NOTE: We want to make sure we only call DiskBackedFS flush here, 84 // not some DiskBackedFS subclass flush! 85 m_fs.flush_writes_impl(); 86 return get(block_index); 87 } 88 89 // Replace the oldest clean entry. 90 auto& new_entry = *oldest_clean_entry; 91 new_entry.timestamp = now; 92 new_entry.block_index = block_index; 93 new_entry.has_data = false; 94 new_entry.is_dirty = false; 95 return new_entry; 96 } 97 98 const CacheEntry* entries() const { return (const CacheEntry*)m_entries.data(); } 99 CacheEntry* entries() { return (CacheEntry*)m_entries.data(); } 100 101 template<typename Callback> 102 void for_each_entry(Callback callback) 103 { 104 for (size_t i = 0; i < m_entry_count; ++i) 105 callback(entries()[i]); 106 } 107 108private: 109 DiskBackedFS& m_fs; 110 size_t m_entry_count { 10000 }; 111 KBuffer m_cached_block_data; 112 KBuffer m_entries; 113 bool m_dirty { false }; 114}; 115 116DiskBackedFS::DiskBackedFS(BlockDevice& device) 117 : m_device(device) 118{ 119} 120 121DiskBackedFS::~DiskBackedFS() 122{ 123} 124 125bool DiskBackedFS::write_block(unsigned index, const u8* data, FileDescription* description) 126{ 127#ifdef DBFS_DEBUG 128 kprintf("DiskBackedFileSystem::write_block %u, size=%u\n", index, data.size()); 129#endif 130 131 bool allow_cache = !description || !description->is_direct(); 132 133 if (!allow_cache) { 134 flush_specific_block_if_needed(index); 135 u32 base_offset = static_cast<u32>(index) * static_cast<u32>(block_size()); 136 device().write_raw(base_offset, block_size(), data); 137 return true; 138 } 139 140 auto& entry = cache().get(index); 141 memcpy(entry.data, data, block_size()); 142 entry.is_dirty = true; 143 entry.has_data = true; 144 145 cache().set_dirty(true); 146 return true; 147} 148 149bool DiskBackedFS::write_blocks(unsigned index, unsigned count, const u8* data, FileDescription* description) 150{ 151#ifdef DBFS_DEBUG 152 kprintf("DiskBackedFileSystem::write_blocks %u x%u\n", index, count); 153#endif 154 for (unsigned i = 0; i < count; ++i) 155 write_block(index + i, data + i * block_size(), description); 156 return true; 157} 158 159bool DiskBackedFS::read_block(unsigned index, u8* buffer, FileDescription* description) const 160{ 161#ifdef DBFS_DEBUG 162 kprintf("DiskBackedFileSystem::read_block %u\n", index); 163#endif 164 165 bool allow_cache = !description || !description->is_direct(); 166 167 if (!allow_cache) { 168 const_cast<DiskBackedFS*>(this)->flush_specific_block_if_needed(index); 169 u32 base_offset = static_cast<u32>(index) * static_cast<u32>(block_size()); 170 bool success = device().read_raw(base_offset, block_size(), buffer); 171 ASSERT(success); 172 return true; 173 } 174 175 auto& entry = cache().get(index); 176 if (!entry.has_data) { 177 u32 base_offset = static_cast<u32>(index) * static_cast<u32>(block_size()); 178 bool success = device().read_raw(base_offset, block_size(), entry.data); 179 entry.has_data = true; 180 ASSERT(success); 181 } 182 memcpy(buffer, entry.data, block_size()); 183 return true; 184} 185 186bool DiskBackedFS::read_blocks(unsigned index, unsigned count, u8* buffer, FileDescription* description) const 187{ 188 if (!count) 189 return false; 190 if (count == 1) 191 return read_block(index, buffer, description); 192 u8* out = buffer; 193 194 for (unsigned i = 0; i < count; ++i) { 195 if (!read_block(index + i, out, description)) 196 return false; 197 out += block_size(); 198 } 199 200 return true; 201} 202 203void DiskBackedFS::flush_specific_block_if_needed(unsigned index) 204{ 205 LOCKER(m_lock); 206 if (!cache().is_dirty()) 207 return; 208 cache().for_each_entry([&](CacheEntry& entry) { 209 if (entry.is_dirty && entry.block_index == index) { 210 u32 base_offset = static_cast<u32>(entry.block_index) * static_cast<u32>(block_size()); 211 device().write_raw(base_offset, block_size(), entry.data); 212 entry.is_dirty = false; 213 } 214 }); 215} 216 217void DiskBackedFS::flush_writes_impl() 218{ 219 LOCKER(m_lock); 220 if (!cache().is_dirty()) 221 return; 222 u32 count = 0; 223 cache().for_each_entry([&](CacheEntry& entry) { 224 if (!entry.is_dirty) 225 return; 226 u32 base_offset = static_cast<u32>(entry.block_index) * static_cast<u32>(block_size()); 227 device().write_raw(base_offset, block_size(), entry.data); 228 ++count; 229 entry.is_dirty = false; 230 }); 231 cache().set_dirty(false); 232 dbg() << class_name() << ": Flushed " << count << " blocks to disk"; 233} 234 235void DiskBackedFS::flush_writes() 236{ 237 flush_writes_impl(); 238} 239 240DiskCache& DiskBackedFS::cache() const 241{ 242 if (!m_cache) 243 m_cache = make<DiskCache>(const_cast<DiskBackedFS&>(*this)); 244 return *m_cache; 245} 246 247}