DiskBackedFileSystem.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. #include <Kernel/Arch/i386/CPU.h>
  2. #include <Kernel/FileSystem/DiskBackedFileSystem.h>
  3. #include <Kernel/FileSystem/FileDescription.h>
  4. #include <Kernel/KBuffer.h>
  5. #include <Kernel/Process.h>
  6. //#define DBFS_DEBUG
  7. struct CacheEntry {
  8. u32 timestamp { 0 };
  9. u32 block_index { 0 };
  10. u8* data { nullptr };
  11. bool has_data { false };
  12. bool is_dirty { false };
  13. };
  14. class DiskCache {
  15. public:
  16. explicit DiskCache(DiskBackedFS& fs)
  17. : m_fs(fs)
  18. , m_cached_block_data(KBuffer::create_with_size(m_entry_count * m_fs.block_size()))
  19. , m_entries(KBuffer::create_with_size(m_entry_count * sizeof(CacheEntry)))
  20. {
  21. for (size_t i = 0; i < m_entry_count; ++i) {
  22. entries()[i].data = m_cached_block_data.data() + i * m_fs.block_size();
  23. }
  24. }
  25. ~DiskCache() {}
  26. bool is_dirty() const { return m_dirty; }
  27. void set_dirty(bool b) { m_dirty = b; }
  28. CacheEntry& get(u32 block_index) const
  29. {
  30. auto now = kgettimeofday().tv_sec;
  31. CacheEntry* oldest_clean_entry = nullptr;
  32. for (size_t i = 0; i < m_entry_count; ++i) {
  33. auto& entry = const_cast<CacheEntry&>(entries()[i]);
  34. if (entry.block_index == block_index) {
  35. entry.timestamp = now;
  36. return entry;
  37. }
  38. if (!entry.is_dirty) {
  39. if (!oldest_clean_entry)
  40. oldest_clean_entry = &entry;
  41. else if (entry.timestamp < oldest_clean_entry->timestamp)
  42. oldest_clean_entry = &entry;
  43. }
  44. }
  45. if (!oldest_clean_entry) {
  46. // Not a single clean entry! Flush writes and try again.
  47. // NOTE: We want to make sure we only call DiskBackedFS flush here,
  48. // not some DiskBackedFS subclass flush!
  49. m_fs.flush_writes_impl();
  50. return get(block_index);
  51. }
  52. // Replace the oldest clean entry.
  53. auto& new_entry = *oldest_clean_entry;
  54. new_entry.timestamp = now;
  55. new_entry.block_index = block_index;
  56. new_entry.has_data = false;
  57. new_entry.is_dirty = false;
  58. return new_entry;
  59. }
  60. const CacheEntry* entries() const { return (const CacheEntry*)m_entries.data(); }
  61. CacheEntry* entries() { return (CacheEntry*)m_entries.data(); }
  62. template<typename Callback>
  63. void for_each_entry(Callback callback)
  64. {
  65. for (size_t i = 0; i < m_entry_count; ++i)
  66. callback(entries()[i]);
  67. }
  68. private:
  69. DiskBackedFS& m_fs;
  70. size_t m_entry_count { 10000 };
  71. KBuffer m_cached_block_data;
  72. KBuffer m_entries;
  73. bool m_dirty { false };
  74. };
  75. DiskBackedFS::DiskBackedFS(NonnullRefPtr<DiskDevice>&& device)
  76. : m_device(move(device))
  77. {
  78. }
  79. DiskBackedFS::~DiskBackedFS()
  80. {
  81. }
  82. bool DiskBackedFS::write_block(unsigned index, const u8* data, FileDescription* description)
  83. {
  84. #ifdef DBFS_DEBUG
  85. kprintf("DiskBackedFileSystem::write_block %u, size=%u\n", index, data.size());
  86. #endif
  87. bool allow_cache = !description || !description->is_direct();
  88. if (!allow_cache) {
  89. flush_specific_block_if_needed(index);
  90. DiskOffset base_offset = static_cast<DiskOffset>(index) * static_cast<DiskOffset>(block_size());
  91. device().write(base_offset, block_size(), data);
  92. return true;
  93. }
  94. auto& entry = cache().get(index);
  95. memcpy(entry.data, data, block_size());
  96. entry.is_dirty = true;
  97. entry.has_data = true;
  98. cache().set_dirty(true);
  99. return true;
  100. }
  101. bool DiskBackedFS::write_blocks(unsigned index, unsigned count, const u8* data, FileDescription* description)
  102. {
  103. #ifdef DBFS_DEBUG
  104. kprintf("DiskBackedFileSystem::write_blocks %u x%u\n", index, count);
  105. #endif
  106. for (unsigned i = 0; i < count; ++i)
  107. write_block(index + i, data + i * block_size(), description);
  108. return true;
  109. }
  110. bool DiskBackedFS::read_block(unsigned index, u8* buffer, FileDescription* description) const
  111. {
  112. #ifdef DBFS_DEBUG
  113. kprintf("DiskBackedFileSystem::read_block %u\n", index);
  114. #endif
  115. bool allow_cache = !description || !description->is_direct();
  116. if (!allow_cache) {
  117. const_cast<DiskBackedFS*>(this)->flush_specific_block_if_needed(index);
  118. DiskOffset base_offset = static_cast<DiskOffset>(index) * static_cast<DiskOffset>(block_size());
  119. bool success = device().read(base_offset, block_size(), buffer);
  120. ASSERT(success);
  121. return true;
  122. }
  123. auto& entry = cache().get(index);
  124. if (!entry.has_data) {
  125. DiskOffset base_offset = static_cast<DiskOffset>(index) * static_cast<DiskOffset>(block_size());
  126. bool success = device().read(base_offset, block_size(), entry.data);
  127. entry.has_data = true;
  128. ASSERT(success);
  129. }
  130. memcpy(buffer, entry.data, block_size());
  131. return true;
  132. }
  133. bool DiskBackedFS::read_blocks(unsigned index, unsigned count, u8* buffer, FileDescription* description) const
  134. {
  135. if (!count)
  136. return false;
  137. if (count == 1)
  138. return read_block(index, buffer, description);
  139. u8* out = buffer;
  140. for (unsigned i = 0; i < count; ++i) {
  141. if (!read_block(index + i, out, description))
  142. return false;
  143. out += block_size();
  144. }
  145. return true;
  146. }
  147. void DiskBackedFS::flush_specific_block_if_needed(unsigned index)
  148. {
  149. LOCKER(m_lock);
  150. if (!cache().is_dirty())
  151. return;
  152. cache().for_each_entry([&](CacheEntry& entry) {
  153. if (entry.is_dirty && entry.block_index == index) {
  154. DiskOffset base_offset = static_cast<DiskOffset>(entry.block_index) * static_cast<DiskOffset>(block_size());
  155. device().write(base_offset, block_size(), entry.data);
  156. entry.is_dirty = false;
  157. }
  158. });
  159. }
  160. void DiskBackedFS::flush_writes_impl()
  161. {
  162. LOCKER(m_lock);
  163. if (!cache().is_dirty())
  164. return;
  165. u32 count = 0;
  166. cache().for_each_entry([&](CacheEntry& entry) {
  167. if (!entry.is_dirty)
  168. return;
  169. DiskOffset base_offset = static_cast<DiskOffset>(entry.block_index) * static_cast<DiskOffset>(block_size());
  170. device().write(base_offset, block_size(), entry.data);
  171. ++count;
  172. entry.is_dirty = false;
  173. });
  174. cache().set_dirty(false);
  175. dbg() << class_name() << ": Flushed " << count << " blocks to disk";
  176. }
  177. void DiskBackedFS::flush_writes()
  178. {
  179. flush_writes_impl();
  180. }
  181. DiskCache& DiskBackedFS::cache() const
  182. {
  183. if (!m_cache)
  184. m_cache = make<DiskCache>(const_cast<DiskBackedFS&>(*this));
  185. return *m_cache;
  186. }