DiskBackedFileSystem.cpp 4.6 KB

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