You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

295 lines
15 KiB

  1. // Copyright 2022 Nick Brassel (@tzarc)
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <numeric>
  4. #include "gtest/gtest.h"
  5. #include "gmock/gmock.h"
  6. #include "backing_mocks.hpp"
  7. class WearLeveling2ByteOptimizedWrites : public ::testing::Test {
  8. protected:
  9. void SetUp() override {
  10. MockBackingStore::Instance().reset_instance();
  11. wear_leveling_init();
  12. }
  13. };
  14. static std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> verify_data;
  15. static wear_leveling_status_t test_write(const uint32_t address, const void* value, size_t length) {
  16. memcpy(&verify_data[address], value, length);
  17. return wear_leveling_write(address, value, length);
  18. }
  19. /**
  20. * This test ensures the correct number of backing store writes occurs with a multibyte write, given the input buffer size.
  21. */
  22. TEST_F(WearLeveling2ByteOptimizedWrites, MultibyteBackingStoreWriteCounts) {
  23. auto& inst = MockBackingStore::Instance();
  24. for (std::size_t length = 1; length <= 5; ++length) {
  25. // Clear things out
  26. std::fill(verify_data.begin(), verify_data.end(), 0);
  27. inst.reset_instance();
  28. wear_leveling_init();
  29. // Generate a test block of data
  30. std::vector<std::uint8_t> testvalue(length);
  31. std::iota(testvalue.begin(), testvalue.end(), 0x20);
  32. // Write the data
  33. EXPECT_EQ(test_write(2000, testvalue.data(), testvalue.size()), WEAR_LEVELING_SUCCESS) << "Write failed with incorrect status";
  34. std::size_t expected;
  35. if (length > 3) {
  36. expected = 4;
  37. } else if (length > 1) {
  38. expected = 3;
  39. } else {
  40. expected = 2;
  41. }
  42. // Check that we got the expected number of write log entries
  43. EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), expected);
  44. }
  45. }
  46. /**
  47. * This test runs through writing U16 values of `0` or `1` over the entire logical address range, to even addresses only.
  48. * - Addresses <16384 will result in a single optimised backing write
  49. * - Higher addresses will result in a multibyte write of 3 backing writes
  50. */
  51. TEST_F(WearLeveling2ByteOptimizedWrites, WriteOneThenZeroToEvenAddresses) {
  52. auto& inst = MockBackingStore::Instance();
  53. // Only attempt writes for each address up to a limit that would NOT force a consolidated data write.
  54. std::size_t writes_per_loop = (MOCK_WRITE_LOG_MAX_ENTRIES::value / 6) - 1; // Worst case is 6 writes for each pair of writes of 0/1
  55. std::size_t final_address;
  56. for (uint32_t address = 0; address < WEAR_LEVELING_LOGICAL_SIZE; address += (writes_per_loop * 2)) {
  57. // Clear things out
  58. std::fill(verify_data.begin(), verify_data.end(), 0);
  59. inst.reset_instance();
  60. wear_leveling_init();
  61. // Loop through all the addresses in this range
  62. std::size_t expected = 0;
  63. for (uint32_t offset = 0; offset < (writes_per_loop * 2); offset += 2) {
  64. // If we're about to exceed the limit of the logical store, skip the writes
  65. if (address + offset + 2 > WEAR_LEVELING_LOGICAL_SIZE) {
  66. break;
  67. }
  68. // The default erased value of the wear-leveling cache is zero, so we write a one first, then a zero, to ensure a backing store write occurs.
  69. uint16_t val = 1;
  70. EXPECT_EQ(test_write(address + offset, &val, sizeof(val)), WEAR_LEVELING_SUCCESS) << "Write failed with incorrect status";
  71. val = 0;
  72. EXPECT_EQ(test_write(address + offset, &val, sizeof(val)), WEAR_LEVELING_SUCCESS) << "Write failed with incorrect status";
  73. std::size_t backing_store_writes_expected = 0;
  74. if (address + offset < 16384) {
  75. // A U16 value of 0/1 at an even address <16384 will result in 1 backing write each, so we need 2 backing writes for 2 logical writes
  76. backing_store_writes_expected = 2;
  77. } else {
  78. // All other addresses result in a multibyte write (3 backing store writes) to write two local bytes of data
  79. backing_store_writes_expected = 6;
  80. }
  81. // Keep track of the total number of expected writes to the backing store
  82. expected += backing_store_writes_expected;
  83. // Verify we're at the correct number of writes
  84. EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), expected) << "Write log doesn't match required number of backing store writes for address " << (address + offset);
  85. // Verify that the write log entries we expect are actually present
  86. std::size_t write_index = expected - backing_store_writes_expected;
  87. auto write_iter = inst.log_begin() + write_index;
  88. write_log_entry_t e;
  89. if (address + offset < 16384) {
  90. // A U16 value of 0/1 at an even address <16384 will result in 1 backing write each, so we need 2 backing writes for 2 logical writes
  91. for (std::size_t i = 0; i < 2; ++i) {
  92. e.raw16[0] = write_iter->value;
  93. EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_WORD_01) << "Invalid write log entry type at " << (address + offset);
  94. ++write_iter;
  95. }
  96. } else {
  97. // Multibyte write
  98. e.raw16[0] = write_iter->value;
  99. EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_MULTIBYTE) << "Invalid write log entry type at " << (address + offset);
  100. EXPECT_EQ(LOG_ENTRY_MULTIBYTE_GET_LENGTH(e), 2) << "Invalid write log entry length at " << (address + offset);
  101. ++write_iter;
  102. }
  103. // Keep track of the final address written, so we can verify the entire logical range was handled
  104. final_address = address + offset;
  105. }
  106. // Verify the number of writes that occurred to the backing store
  107. size_t backing_write_count = std::distance(inst.log_begin(), inst.log_end());
  108. EXPECT_EQ(backing_write_count, expected) << "Invalid write count at address " << address;
  109. // Verify the data is what we expected
  110. std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> readback;
  111. EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data";
  112. EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback for address " << address << " did not match";
  113. // Re-init and re-read, testing the reload capability
  114. EXPECT_NE(wear_leveling_init(), WEAR_LEVELING_FAILED) << "Re-initialisation failed";
  115. EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data";
  116. EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback for address " << address << " did not match";
  117. }
  118. // Verify the full range of the logical area got written
  119. EXPECT_EQ(final_address, WEAR_LEVELING_LOGICAL_SIZE - 2) << "Invalid final write address";
  120. }
  121. /**
  122. * This test runs through writing U16 values of `0` or `1` over the entire logical address range, to odd addresses only.
  123. * - Addresses <63 will result in 2 optimised backing writes
  124. * - Address 63 results in a single optimised backing write for the first logical byte, and a multibyte write of 2 backing writes for the second logical byte
  125. * - Higher addresses will result in a multibyte write of 3 backing writes
  126. */
  127. TEST_F(WearLeveling2ByteOptimizedWrites, WriteOneThenZeroToOddAddresses) {
  128. auto& inst = MockBackingStore::Instance();
  129. // Only attempt writes for each address up to a limit that would NOT force a consolidated data write.
  130. std::size_t writes_per_loop = (MOCK_WRITE_LOG_MAX_ENTRIES::value / 6) - 1; // Worst case is 6 writes for each pair of writes of 0/1
  131. std::size_t final_address;
  132. for (uint32_t address = 1; address < WEAR_LEVELING_LOGICAL_SIZE; address += (writes_per_loop * 2)) {
  133. // Clear things out
  134. std::fill(verify_data.begin(), verify_data.end(), 0);
  135. inst.reset_instance();
  136. wear_leveling_init();
  137. // Loop through all the addresses in this range
  138. std::size_t expected = 0;
  139. for (uint32_t offset = 0; offset < (writes_per_loop * 2); offset += 2) {
  140. // If we're about to exceed the limit of the logical store, skip the writes
  141. if (address + offset + 2 > WEAR_LEVELING_LOGICAL_SIZE) {
  142. break;
  143. }
  144. // The default erased value of the wear-leveling cache is zero, so we write a one first, then a zero, to ensure a backing store write occurs.
  145. uint16_t val = 1;
  146. EXPECT_EQ(test_write(address + offset, &val, sizeof(val)), WEAR_LEVELING_SUCCESS) << "Write failed with incorrect status";
  147. val = 0;
  148. EXPECT_EQ(test_write(address + offset, &val, sizeof(val)), WEAR_LEVELING_SUCCESS) << "Write failed with incorrect status";
  149. std::size_t backing_store_writes_expected = 0;
  150. if (address + offset < 63) {
  151. // A U16 value of 0/1 at an odd address <64 will result in 2 backing writes each, so we need 4 backing writes for 2 logical writes
  152. backing_store_writes_expected = 4;
  153. } else if (address + offset == 63) {
  154. // If we're straddling the boundary for optimised bytes (addr==64), then the first logical byte is written using the optimised write (1 backing
  155. // store write), and the second logical byte uses a multibyte write (2 backing store writes)
  156. backing_store_writes_expected = 2 // First logical bytes written using optimised log entries
  157. + 4; // Second logical bytes written using multibyte log entries
  158. } else {
  159. // All other addresses result in a multibyte write (3 backing store writes) to write two local bytes of data
  160. backing_store_writes_expected = 6;
  161. }
  162. // Keep track of the total number of expected writes to the backing store
  163. expected += backing_store_writes_expected;
  164. // Verify we're at the correct number of writes
  165. EXPECT_EQ(std::distance(inst.log_begin(), inst.log_end()), expected) << "Write log doesn't match required number of backing store writes for address " << (address + offset);
  166. // Verify that the write log entries we expect are actually present
  167. std::size_t write_index = expected - backing_store_writes_expected;
  168. auto write_iter = inst.log_begin() + write_index;
  169. write_log_entry_t e;
  170. if (address + offset < 63) {
  171. // A U16 value of 0/1 at an odd address <64 will result in 2 backing writes each, so we need 4 backing writes for 2 logical writes
  172. for (std::size_t i = 0; i < 4; ++i) {
  173. e.raw16[0] = write_iter->value;
  174. EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_OPTIMIZED_64) << "Invalid write log entry type";
  175. ++write_iter;
  176. }
  177. } else if (address + offset == 63) {
  178. // First log entry is the 64-addr optimised one
  179. e.raw16[0] = write_iter->value;
  180. EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_OPTIMIZED_64) << "Invalid write log entry type";
  181. ++write_iter;
  182. // Second log entry is the multibyte entry for the second logical byte
  183. e.raw16[0] = write_iter->value;
  184. EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_MULTIBYTE) << "Invalid write log entry type";
  185. EXPECT_EQ(LOG_ENTRY_MULTIBYTE_GET_LENGTH(e), 1) << "Invalid write log entry length";
  186. ++write_iter;
  187. } else {
  188. // Multibyte write
  189. e.raw16[0] = write_iter->value;
  190. EXPECT_EQ(LOG_ENTRY_GET_TYPE(e), LOG_ENTRY_TYPE_MULTIBYTE) << "Invalid write log entry type";
  191. EXPECT_EQ(LOG_ENTRY_MULTIBYTE_GET_LENGTH(e), 2) << "Invalid write log entry length";
  192. ++write_iter;
  193. }
  194. // Keep track of the final address written, so we can verify the entire logical range was handled
  195. final_address = address + offset;
  196. }
  197. // Verify the number of writes that occurred to the backing store
  198. size_t backing_write_count = std::distance(inst.log_begin(), inst.log_end());
  199. EXPECT_EQ(backing_write_count, expected) << "Invalid write count at address " << address;
  200. // Verify the data is what we expected
  201. std::array<std::uint8_t, WEAR_LEVELING_LOGICAL_SIZE> readback;
  202. EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data";
  203. EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback for address " << address << " did not match";
  204. // Re-init and re-read, testing the reload capability
  205. EXPECT_NE(wear_leveling_init(), WEAR_LEVELING_FAILED) << "Re-initialisation failed";
  206. EXPECT_EQ(wear_leveling_read(0, readback.data(), WEAR_LEVELING_LOGICAL_SIZE), WEAR_LEVELING_SUCCESS) << "Failed to read back the saved data";
  207. EXPECT_TRUE(memcmp(readback.data(), verify_data.data(), WEAR_LEVELING_LOGICAL_SIZE) == 0) << "Readback for address " << address << " did not match";
  208. }
  209. // Verify the full range of the logical area got written
  210. EXPECT_EQ(final_address, WEAR_LEVELING_LOGICAL_SIZE - 3) << "Invalid final write address";
  211. }
  212. /**
  213. * This test verifies readback after playback of the write log, simulating power loss and reboot.
  214. */
  215. TEST_F(WearLeveling2ByteOptimizedWrites, PlaybackReadbackOptimized64_Success) {
  216. auto& inst = MockBackingStore::Instance();
  217. auto logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t));
  218. // Invalid FNV1a_64 hash
  219. (logstart + 0)->set(0);
  220. (logstart + 1)->set(0);
  221. (logstart + 2)->set(0);
  222. (logstart + 3)->set(0);
  223. // Set up a 1-byte logical write of 0x11 at logical offset 0x01
  224. auto entry0 = LOG_ENTRY_MAKE_OPTIMIZED_64(0x01, 0x11);
  225. (logstart + 4)->set(~entry0.raw16[0]); // start at offset 4 to skip FNV1a_64 result
  226. wear_leveling_init();
  227. uint8_t tmp;
  228. wear_leveling_read(0x01, &tmp, sizeof(tmp));
  229. EXPECT_EQ(tmp, 0x11) << "Failed to read back the seeded data";
  230. }
  231. /**
  232. * This test verifies readback after playback of the write log, simulating power loss and reboot.
  233. */
  234. TEST_F(WearLeveling2ByteOptimizedWrites, PlaybackReadbackWord01_Success) {
  235. auto& inst = MockBackingStore::Instance();
  236. auto logstart = inst.storage_begin() + (WEAR_LEVELING_LOGICAL_SIZE / sizeof(backing_store_int_t));
  237. // Invalid FNV1a_64 hash
  238. (logstart + 0)->set(0);
  239. (logstart + 1)->set(0);
  240. (logstart + 2)->set(0);
  241. (logstart + 3)->set(0);
  242. // Set up a 1-byte logical write of 1 at logical offset 0x02
  243. auto entry0 = LOG_ENTRY_MAKE_WORD_01(0x02, 1);
  244. (logstart + 4)->set(~entry0.raw16[0]); // start at offset 4 to skip FNV1a_64 result
  245. wear_leveling_init();
  246. uint8_t tmp;
  247. wear_leveling_read(0x02, &tmp, sizeof(tmp));
  248. EXPECT_EQ(tmp, 1) << "Failed to read back the seeded data";
  249. }