blob: 170ddf005468a894f765f93c1c3a1df3bdef01b3 [file] [log] [blame]
Wyatt Heplerb7609542020-01-24 10:29:54 -08001// Copyright 2020 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
Wyatt Heplerb7609542020-01-24 10:29:54 -080015#include "pw_kvs/key_value_store.h"
16
Wyatt Heplerbab0e202020-02-04 07:40:08 -080017#include <algorithm>
Wyatt Hepler5a33d8c2020-02-06 09:32:58 -080018#include <cinttypes>
Wyatt Heplerb7609542020-01-24 10:29:54 -080019#include <cstring>
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080020#include <type_traits>
Wyatt Heplerb7609542020-01-24 10:29:54 -080021
Keir Mierle8c352dc2020-02-02 13:58:19 -080022#define PW_LOG_USE_ULTRA_SHORT_NAMES 1
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080023#include "pw_kvs_private/macros.h"
Keir Mierle8c352dc2020-02-02 13:58:19 -080024#include "pw_log/log.h"
Wyatt Heplerb7609542020-01-24 10:29:54 -080025
Wyatt Hepler2ad60672020-01-21 08:00:16 -080026namespace pw::kvs {
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080027namespace {
Wyatt Heplerb7609542020-01-24 10:29:54 -080028
Wyatt Hepleracaacf92020-01-24 10:58:30 -080029using std::byte;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080030using std::string_view;
Wyatt Hepleracaacf92020-01-24 10:58:30 -080031
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080032constexpr bool InvalidKey(std::string_view key) {
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -080033 return key.empty() || (key.size() > internal::Entry::kMaxKeyLength);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080034}
35
36} // namespace
37
Wyatt Heplerad0a7932020-02-06 08:20:38 -080038KeyValueStore::KeyValueStore(FlashPartition* partition,
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080039 span<const EntryFormat> formats,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070040 const Options& options,
41 size_t redundancy,
42 Vector<SectorDescriptor>& sector_descriptor_list,
43 const SectorDescriptor** temp_sectors_to_skip,
44 Vector<KeyDescriptor>& key_descriptor_list,
45 Address* addresses)
Wyatt Heplerad0a7932020-02-06 08:20:38 -080046 : partition_(*partition),
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080047 formats_(formats),
Wyatt Heplerc84393f2020-03-20 11:23:24 -070048 sectors_(sector_descriptor_list, *partition, temp_sectors_to_skip),
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070049 entry_cache_(key_descriptor_list, addresses, redundancy),
David Rogers49766d92020-03-20 10:55:54 -070050 options_(options),
David Rogers9abe3c72020-03-24 19:03:13 -070051 initialized_(InitializationState::kNotInitialized),
David Rogers49766d92020-03-20 10:55:54 -070052 error_detected_(false),
David Rogers9abe3c72020-03-24 19:03:13 -070053 error_stats_({}),
David Rogers49766d92020-03-20 10:55:54 -070054 last_transaction_id_(0) {}
Wyatt Heplerad0a7932020-02-06 08:20:38 -080055
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080056Status KeyValueStore::Init() {
David Rogers9abe3c72020-03-24 19:03:13 -070057 initialized_ = InitializationState::kNotInitialized;
David Rogers49766d92020-03-20 10:55:54 -070058 error_detected_ = false;
Keir Mierlebf904812020-03-11 17:28:22 -070059 last_transaction_id_ = 0;
Wyatt Heplerd2298282020-02-20 17:12:45 -080060
David Rogers2e9e0c82020-02-13 15:06:06 -080061 INF("Initializing key value store");
Wyatt Hepler38ce30f2020-02-19 11:48:31 -080062 if (partition_.sector_count() > sectors_.max_size()) {
David Rogers2e9e0c82020-02-13 15:06:06 -080063 ERR("KVS init failed: kMaxUsableSectors (=%zu) must be at least as "
64 "large as the number of sectors in the flash partition (=%zu)",
Wyatt Hepler38ce30f2020-02-19 11:48:31 -080065 sectors_.max_size(),
David Rogers2e9e0c82020-02-13 15:06:06 -080066 partition_.sector_count());
Wyatt Heplerad0a7932020-02-06 08:20:38 -080067 return Status::FAILED_PRECONDITION;
68 }
69
Keir Mierle8c352dc2020-02-02 13:58:19 -080070 const size_t sector_size_bytes = partition_.sector_size_bytes();
Keir Mierle8c352dc2020-02-02 13:58:19 -080071
David Rogers49766d92020-03-20 10:55:54 -070072 // TODO: investigate doing this as a static assert/compile-time check.
73 if (sector_size_bytes > SectorDescriptor::max_sector_size()) {
74 ERR("KVS init failed: sector_size_bytes (=%zu) is greater than maximum "
75 "allowed sector size (=%zu)",
76 sector_size_bytes,
77 SectorDescriptor::max_sector_size());
78 return Status::FAILED_PRECONDITION;
79 }
80
David Rogerscd134352020-04-17 19:37:15 -070081 Status metadata_result = InitializeMetadata();
David Rogersfcea3252020-04-07 14:56:35 -070082
83 if (!error_detected_) {
84 initialized_ = InitializationState::kReady;
85 } else {
David Rogers0f8a1bb2020-04-21 18:50:19 -070086 initialized_ = InitializationState::kNeedsMaintenance;
87
David Rogersfcea3252020-04-07 14:56:35 -070088 if (options_.recovery != ErrorRecovery::kManual) {
Armando Montanezf8419ae2020-04-21 10:03:05 -070089 size_t pre_fix_redundancy_errors =
90 error_stats_.missing_redundant_entries_recovered;
David Rogersfcea3252020-04-07 14:56:35 -070091 Status recovery_status = FixErrors();
92
93 if (recovery_status.ok()) {
David Rogerscd134352020-04-17 19:37:15 -070094 if (metadata_result == Status::OUT_OF_RANGE) {
Armando Montanezf8419ae2020-04-21 10:03:05 -070095 error_stats_.missing_redundant_entries_recovered =
96 pre_fix_redundancy_errors;
David Rogerscd134352020-04-17 19:37:15 -070097 INF("KVS init: Redundancy level successfully updated");
98 } else {
99 WRN("KVS init: Corruption detected and fully repaired");
100 }
David Rogersfcea3252020-04-07 14:56:35 -0700101 initialized_ = InitializationState::kReady;
102 } else if (recovery_status == Status::RESOURCE_EXHAUSTED) {
103 WRN("KVS init: Unable to maintain required free sector");
David Rogersfcea3252020-04-07 14:56:35 -0700104 } else {
105 WRN("KVS init: Corruption detected and unable repair");
David Rogersfcea3252020-04-07 14:56:35 -0700106 }
107 } else {
108 WRN("KVS init: Corruption detected, no repair attempted due to options");
David Rogersfcea3252020-04-07 14:56:35 -0700109 }
110 }
111
112 INF("KeyValueStore init complete: active keys %zu, deleted keys %zu, sectors "
113 "%zu, logical sector size %zu bytes",
114 size(),
115 (entry_cache_.total_entries() - size()),
116 sectors_.size(),
117 partition_.sector_size_bytes());
118
119 // Report any corruption was not repaired.
120 if (error_detected_) {
121 WRN("KVS init: Corruption found but not repaired, KVS unavailable until "
122 "successful maintenance.");
123 return Status::DATA_LOSS;
124 }
125
126 return Status::OK;
127}
128
David Rogerscd134352020-04-17 19:37:15 -0700129Status KeyValueStore::InitializeMetadata() {
David Rogersfcea3252020-04-07 14:56:35 -0700130 const size_t sector_size_bytes = partition_.sector_size_bytes();
131
132 sectors_.Reset();
133 entry_cache_.Reset();
134
Keir Mierle8c352dc2020-02-02 13:58:19 -0800135 DBG("First pass: Read all entries from all sectors");
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800136 Address sector_address = 0;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800137
Alexei Frolovd4adf912020-02-21 13:29:15 -0800138 size_t total_corrupt_bytes = 0;
David Rogerscd134352020-04-17 19:37:15 -0700139 size_t corrupt_entries = 0;
David Rogers91627482020-02-27 17:38:12 -0800140 bool empty_sector_found = false;
David Rogerscd134352020-04-17 19:37:15 -0700141 size_t entry_copies_missing = 0;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800142
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800143 for (SectorDescriptor& sector : sectors_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800144 Address entry_address = sector_address;
145
Alexei Frolovd4adf912020-02-21 13:29:15 -0800146 size_t sector_corrupt_bytes = 0;
147
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800148 for (int num_entries_in_sector = 0; true; num_entries_in_sector++) {
David Rogersfcea3252020-04-07 14:56:35 -0700149 DBG("Load entry: sector=%u, entry#=%d, address=%u",
150 unsigned(sector_address),
Keir Mierle8c352dc2020-02-02 13:58:19 -0800151 num_entries_in_sector,
David Rogersfcea3252020-04-07 14:56:35 -0700152 unsigned(entry_address));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800153
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700154 if (!sectors_.AddressInSector(sector, entry_address)) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800155 DBG("Fell off end of sector; moving to the next sector");
156 break;
157 }
158
159 Address next_entry_address;
160 Status status = LoadEntry(entry_address, &next_entry_address);
161 if (status == Status::NOT_FOUND) {
162 DBG("Hit un-written data in sector; moving to the next sector");
163 break;
David Rogersfcea3252020-04-07 14:56:35 -0700164 } else if (!status.ok()) {
165 // The entry could not be read, indicating likely data corruption within
166 // the sector. Try to scan the remainder of the sector for other
167 // entries.
Alexei Frolovd4adf912020-02-21 13:29:15 -0800168
David Rogers49766d92020-03-20 10:55:54 -0700169 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800170 corrupt_entries++;
171
172 status = ScanForEntry(sector,
173 entry_address + Entry::kMinAlignmentBytes,
174 &next_entry_address);
David Rogersfcea3252020-04-07 14:56:35 -0700175 if (!status.ok()) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800176 // No further entries in this sector. Mark the remaining bytes in the
177 // sector as corrupt (since we can't reliably know the size of the
178 // corrupt entry).
179 sector_corrupt_bytes +=
180 sector_size_bytes - (entry_address - sector_address);
181 break;
182 }
183
Alexei Frolovd4adf912020-02-21 13:29:15 -0800184 sector_corrupt_bytes += next_entry_address - entry_address;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800185 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800186
187 // Entry loaded successfully; so get ready to load the next one.
188 entry_address = next_entry_address;
189
190 // Update of the number of writable bytes in this sector.
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800191 sector.set_writable_bytes(sector_size_bytes -
192 (entry_address - sector_address));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800193 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800194
Alexei Frolovd4adf912020-02-21 13:29:15 -0800195 if (sector_corrupt_bytes > 0) {
196 // If the sector contains corrupt data, prevent any further entries from
197 // being written to it by indicating that it has no space. This should
198 // also make it a decent GC candidate. Valid keys in the sector are still
199 // readable as normal.
David Rogers49766d92020-03-20 10:55:54 -0700200 sector.mark_corrupt();
201 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800202
203 WRN("Sector %u contains %zuB of corrupt data",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700204 sectors_.Index(sector),
Alexei Frolovd4adf912020-02-21 13:29:15 -0800205 sector_corrupt_bytes);
206 }
207
David Rogers91627482020-02-27 17:38:12 -0800208 if (sector.Empty(sector_size_bytes)) {
209 empty_sector_found = true;
210 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800211 sector_address += sector_size_bytes;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800212 total_corrupt_bytes += sector_corrupt_bytes;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800213 }
214
215 DBG("Second pass: Count valid bytes in each sector");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700216 Address newest_key = 0;
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800217
David Rogers98fea472020-04-01 15:43:48 -0700218 // For every valid entry, for each address, count the valid bytes in that
219 // sector. If the address fails to read, remove the address and mark the
220 // sector as corrupt. Track which entry has the newest transaction ID for
221 // initializing last_new_sector_.
222 for (EntryMetadata& metadata : entry_cache_) {
David Rogers49766d92020-03-20 10:55:54 -0700223 if (metadata.addresses().size() < redundancy()) {
David Rogers31b358b2020-04-15 05:00:50 -0700224 DBG("Key 0x%08x missing copies, has %u, needs %u",
225 unsigned(metadata.hash()),
226 unsigned(metadata.addresses().size()),
227 unsigned(redundancy()));
David Rogerscd134352020-04-17 19:37:15 -0700228 entry_copies_missing++;
David Rogers49766d92020-03-20 10:55:54 -0700229 }
David Rogers98fea472020-04-01 15:43:48 -0700230 size_t index = 0;
231 while (index < metadata.addresses().size()) {
232 Address address = metadata.addresses()[index];
David Rogersf56131c2020-03-04 10:19:22 -0800233 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700234
235 Status read_result = Entry::Read(partition_, address, formats_, &entry);
236
237 SectorDescriptor& sector = sectors_.FromAddress(address);
238
239 if (read_result.ok()) {
240 sector.AddValidBytes(entry.size());
241 index++;
242 } else {
243 corrupt_entries++;
244 total_corrupt_bytes += sector.writable_bytes();
245 error_detected_ = true;
246 sector.mark_corrupt();
247
248 // Remove the bad address and stay at this index. The removal
249 // replaces out the removed address with the back address so
250 // this index needs to be rechecked with the new address.
251 metadata.RemoveAddress(address);
252 }
David Rogersf56131c2020-03-04 10:19:22 -0800253 }
David Rogers98fea472020-04-01 15:43:48 -0700254
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700255 if (metadata.IsNewerThan(last_transaction_id_)) {
256 last_transaction_id_ = metadata.transaction_id();
257 newest_key = metadata.addresses().back();
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800258 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800259 }
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800260
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700261 sectors_.set_last_new_sector(newest_key);
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800262
David Rogers91627482020-02-27 17:38:12 -0800263 if (!empty_sector_found) {
David Rogers31b358b2020-04-15 05:00:50 -0700264 DBG("No empty sector found");
David Rogers9abe3c72020-03-24 19:03:13 -0700265 error_detected_ = true;
David Rogers91627482020-02-27 17:38:12 -0800266 }
267
David Rogerscd134352020-04-17 19:37:15 -0700268 if (entry_copies_missing > 0) {
269 bool other_errors = error_detected_;
270 error_detected_ = true;
271
Armando Montanez344814b2020-04-24 15:05:42 -0700272 if (!other_errors && entry_copies_missing == entry_cache_.total_entries()) {
David Rogerscd134352020-04-17 19:37:15 -0700273 INF("KVS configuration changed to redundancy of %zu total copies per key",
274 redundancy());
275 return Status::OUT_OF_RANGE;
276 }
David Rogers9abe3c72020-03-24 19:03:13 -0700277 }
David Rogerscd134352020-04-17 19:37:15 -0700278
279 if (error_detected_) {
280 WRN("Corruption detected. Found %zu corrupt bytes, %zu corrupt entries, "
281 "and %zu keys missing redundant copies.",
282 total_corrupt_bytes,
283 corrupt_entries,
284 entry_copies_missing);
285 return Status::FAILED_PRECONDITION;
286 }
287 return Status::OK;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800288}
289
Alexei Frolov9e235832020-02-24 12:44:45 -0800290KeyValueStore::StorageStats KeyValueStore::GetStorageStats() const {
David Rogers9abe3c72020-03-24 19:03:13 -0700291 StorageStats stats{};
Alexei Frolov9e235832020-02-24 12:44:45 -0800292 const size_t sector_size = partition_.sector_size_bytes();
293 bool found_empty_sector = false;
David Rogers9abe3c72020-03-24 19:03:13 -0700294 stats.corrupt_sectors_recovered = error_stats_.corrupt_sectors_recovered;
295 stats.missing_redundant_entries_recovered =
296 error_stats_.missing_redundant_entries_recovered;
Alexei Frolov9e235832020-02-24 12:44:45 -0800297
298 for (const SectorDescriptor& sector : sectors_) {
299 stats.in_use_bytes += sector.valid_bytes();
300 stats.reclaimable_bytes += sector.RecoverableBytes(sector_size);
301
302 if (!found_empty_sector && sector.Empty(sector_size)) {
303 // The KVS tries to always keep an empty sector for GC, so don't count
304 // the first empty sector seen as writable space. However, a free sector
305 // cannot always be assumed to exist; if a GC operation fails, all sectors
306 // may be partially written, in which case the space reported might be
307 // inaccurate.
308 found_empty_sector = true;
309 continue;
310 }
311
312 stats.writable_bytes += sector.writable_bytes();
313 }
314
315 return stats;
316}
317
David Rogers98fea472020-04-01 15:43:48 -0700318// Check KVS for any error conditions. Primarily intended for test and
319// internal use.
David Rogers9abe3c72020-03-24 19:03:13 -0700320bool KeyValueStore::CheckForErrors() {
321 // Check for corrupted sectors
322 for (SectorDescriptor& sector : sectors_) {
323 if (sector.corrupt()) {
324 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700325 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700326 }
327 }
328
329 // Check for missing redundancy.
330 if (redundancy() > 1) {
331 for (const EntryMetadata& metadata : entry_cache_) {
332 if (metadata.addresses().size() < redundancy()) {
333 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700334 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700335 }
336 }
337 }
338
339 return error_detected();
340}
341
Keir Mierle8c352dc2020-02-02 13:58:19 -0800342Status KeyValueStore::LoadEntry(Address entry_address,
343 Address* next_entry_address) {
Wyatt Heplere541e072020-02-14 09:10:53 -0800344 Entry entry;
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800345 TRY(Entry::Read(partition_, entry_address, formats_, &entry));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800346
347 // Read the key from flash & validate the entry (which reads the value).
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800348 Entry::KeyBuffer key_buffer;
Wyatt Heplere541e072020-02-14 09:10:53 -0800349 TRY_ASSIGN(size_t key_length, entry.ReadKey(key_buffer));
350 const string_view key(key_buffer.data(), key_length);
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800351
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800352 TRY(entry.VerifyChecksumInFlash());
David Rogersf56131c2020-03-04 10:19:22 -0800353
354 // A valid entry was found, so update the next entry address before doing any
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700355 // of the checks that happen in AddNewOrUpdateExisting.
David Rogersf56131c2020-03-04 10:19:22 -0800356 *next_entry_address = entry.next_address();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700357 return entry_cache_.AddNewOrUpdateExisting(
358 entry.descriptor(key), entry.address(), partition_.sector_size_bytes());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800359}
360
Alexei Frolovd4adf912020-02-21 13:29:15 -0800361// Scans flash memory within a sector to find a KVS entry magic.
Alexei Frolovd4adf912020-02-21 13:29:15 -0800362Status KeyValueStore::ScanForEntry(const SectorDescriptor& sector,
363 Address start_address,
364 Address* next_entry_address) {
David Rogersfcea3252020-04-07 14:56:35 -0700365 DBG("Scanning sector %u for entries starting from address %u",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700366 sectors_.Index(sector),
David Rogersfcea3252020-04-07 14:56:35 -0700367 unsigned(start_address));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800368
369 // Entries must start at addresses which are aligned on a multiple of
370 // Entry::kMinAlignmentBytes. However, that multiple can vary between entries.
371 // When scanning, we don't have an entry to tell us what the current alignment
372 // is, so the minimum alignment is used to be exhaustive.
373 for (Address address = AlignUp(start_address, Entry::kMinAlignmentBytes);
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700374 sectors_.AddressInSector(sector, address);
Alexei Frolovd4adf912020-02-21 13:29:15 -0800375 address += Entry::kMinAlignmentBytes) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800376 uint32_t magic;
David Rogersfcea3252020-04-07 14:56:35 -0700377 StatusWithSize read_result =
378 partition_.Read(address, as_writable_bytes(span(&magic, 1)));
379 if (!read_result.ok()) {
380 continue;
381 }
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800382 if (formats_.KnownMagic(magic)) {
David Rogersfcea3252020-04-07 14:56:35 -0700383 DBG("Found entry magic at address %u", unsigned(address));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800384 *next_entry_address = address;
385 return Status::OK;
386 }
387 }
388
389 return Status::NOT_FOUND;
390}
391
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800392StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800393 span<byte> value_buffer,
394 size_t offset_bytes) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700395 TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800396
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700397 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700398 TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800399
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700400 return Get(key, metadata, value_buffer, offset_bytes);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800401}
402
Wyatt Heplerfac81132020-02-27 17:26:33 -0800403Status KeyValueStore::PutBytes(string_view key, span<const byte> value) {
David Rogers9abe3c72020-03-24 19:03:13 -0700404 TRY(CheckWriteOperation(key));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800405 DBG("Writing key/value; key length=%zu, value length=%zu",
406 key.size(),
407 value.size());
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800408
Wyatt Hepler5406a672020-02-18 15:42:38 -0800409 if (Entry::size(partition_, key, value) > partition_.sector_size_bytes()) {
410 DBG("%zu B value with %zu B key cannot fit in one sector",
411 value.size(),
412 key.size());
413 return Status::INVALID_ARGUMENT;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800414 }
415
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700416 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700417 Status status = FindEntry(key, &metadata);
Wyatt Hepler2d401692020-02-13 16:01:23 -0800418
419 if (status.ok()) {
David Rogersf56131c2020-03-04 10:19:22 -0800420 // TODO: figure out logging how to support multiple addresses.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700421 DBG("Overwriting entry for key 0x%08" PRIx32 " in %zu sectors including %u",
422 metadata.hash(),
423 metadata.addresses().size(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700424 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700425 return WriteEntryForExistingKey(metadata, EntryState::kValid, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800426 }
David Rogers2761aeb2020-01-31 17:09:00 -0800427
Wyatt Hepler2d401692020-02-13 16:01:23 -0800428 if (status == Status::NOT_FOUND) {
429 return WriteEntryForNewKey(key, value);
430 }
431
432 return status;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800433}
434
435Status KeyValueStore::Delete(string_view key) {
David Rogers9abe3c72020-03-24 19:03:13 -0700436 TRY(CheckWriteOperation(key));
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800437
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700438 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700439 TRY(FindExisting(key, &metadata));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800440
David Rogersf56131c2020-03-04 10:19:22 -0800441 // TODO: figure out logging how to support multiple addresses.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700442 DBG("Writing tombstone for key 0x%08" PRIx32 " in %zu sectors including %u",
443 metadata.hash(),
444 metadata.addresses().size(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700445 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700446 return WriteEntryForExistingKey(metadata, EntryState::kDeleted, key, {});
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800447}
448
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800449void KeyValueStore::Item::ReadKey() {
450 key_buffer_.fill('\0');
451
452 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700453 if (kvs_.ReadEntry(*iterator_, entry).ok()) {
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800454 entry.ReadKey(key_buffer_);
455 }
456}
457
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800458KeyValueStore::iterator& KeyValueStore::iterator::operator++() {
459 // Skip to the next entry that is valid (not deleted).
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700460 while (++item_.iterator_ != item_.kvs_.entry_cache_.end() &&
Wyatt Hepler02946272020-03-18 10:36:22 -0700461 item_.iterator_->state() != EntryState::kValid) {
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800462 }
463 return *this;
464}
465
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800466KeyValueStore::iterator KeyValueStore::begin() const {
Wyatt Heplerbfc6a522020-04-01 16:30:24 -0700467 internal::EntryCache::const_iterator cache_iterator = entry_cache_.begin();
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800468 // Skip over any deleted entries at the start of the descriptor list.
Wyatt Hepler02946272020-03-18 10:36:22 -0700469 while (cache_iterator != entry_cache_.end() &&
470 cache_iterator->state() != EntryState::kValid) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700471 ++cache_iterator;
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800472 }
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700473 return iterator(*this, cache_iterator);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800474}
475
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700476StatusWithSize KeyValueStore::ValueSize(string_view key) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700477 TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Heplered163b02020-02-03 17:49:32 -0800478
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700479 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700480 TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800481
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700482 return ValueSize(metadata);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800483}
Wyatt Heplered163b02020-02-03 17:49:32 -0800484
David Rogers98fea472020-04-01 15:43:48 -0700485Status KeyValueStore::ReadEntry(const EntryMetadata& metadata,
486 Entry& entry) const {
487 // Try to read an entry
488 Status read_result = Status::DATA_LOSS;
489 for (Address address : metadata.addresses()) {
490 read_result = Entry::Read(partition_, address, formats_, &entry);
491 if (read_result.ok()) {
492 return read_result;
493 }
494
495 // Found a bad address. Set the sector as corrupt.
496 error_detected_ = true;
497 sectors_.FromAddress(address).mark_corrupt();
498 }
499
500 ERR("No valid entries for key. Data has been lost!");
501 return read_result;
502}
503
504Status KeyValueStore::FindEntry(string_view key,
505 EntryMetadata* found_entry) const {
506 StatusWithSize find_result =
507 entry_cache_.Find(partition_, sectors_, formats_, key, found_entry);
508
509 if (find_result.size() > 0u) {
510 error_detected_ = true;
511 }
512 return find_result.status();
513}
514
515Status KeyValueStore::FindExisting(string_view key,
516 EntryMetadata* metadata) const {
517 Status status = FindEntry(key, metadata);
518
519 // If the key's hash collides with an existing key or if the key is deleted,
520 // treat it as if it is not in the KVS.
521 if (status == Status::ALREADY_EXISTS ||
522 (status.ok() && metadata->state() == EntryState::kDeleted)) {
523 return Status::NOT_FOUND;
524 }
525 return status;
526}
527
Wyatt Heplerfac81132020-02-27 17:26:33 -0800528StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700529 const EntryMetadata& metadata,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800530 span<std::byte> value_buffer,
531 size_t offset_bytes) const {
532 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700533
534 TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800535
536 StatusWithSize result = entry.ReadValue(value_buffer, offset_bytes);
537 if (result.ok() && options_.verify_on_read && offset_bytes == 0u) {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800538 Status verify_result =
539 entry.VerifyChecksum(key, value_buffer.first(result.size()));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800540 if (!verify_result.ok()) {
541 std::memset(value_buffer.data(), 0, result.size());
542 return StatusWithSize(verify_result, 0);
543 }
544
545 return StatusWithSize(verify_result, result.size());
546 }
547 return result;
Wyatt Heplered163b02020-02-03 17:49:32 -0800548}
549
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800550Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800551 void* value,
552 size_t size_bytes) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700553 TRY(CheckWriteOperation(key));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800554
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700555 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700556 TRY(FindExisting(key, &metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800557
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700558 return FixedSizeGet(key, metadata, value, size_bytes);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800559}
560
561Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700562 const EntryMetadata& metadata,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800563 void* value,
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800564 size_t size_bytes) const {
565 // Ensure that the size of the stored value matches the size of the type.
566 // Otherwise, report error. This check avoids potential memory corruption.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700567 TRY_ASSIGN(const size_t actual_size, ValueSize(metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800568
569 if (actual_size != size_bytes) {
570 DBG("Requested %zu B read, but value is %zu B", size_bytes, actual_size);
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800571 return Status::INVALID_ARGUMENT;
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800572 }
Wyatt Heplerfac81132020-02-27 17:26:33 -0800573
574 StatusWithSize result =
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700575 Get(key, metadata, span(static_cast<byte*>(value), size_bytes), 0);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800576
577 return result.status();
578}
579
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700580StatusWithSize KeyValueStore::ValueSize(const EntryMetadata& metadata) const {
Wyatt Heplerfac81132020-02-27 17:26:33 -0800581 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700582 TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800583
584 return StatusWithSize(entry.value_size());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800585}
586
David Rogers9abe3c72020-03-24 19:03:13 -0700587Status KeyValueStore::CheckWriteOperation(string_view key) const {
Wyatt Hepleracaacf92020-01-24 10:58:30 -0800588 if (InvalidKey(key)) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800589 return Status::INVALID_ARGUMENT;
590 }
David Rogers9abe3c72020-03-24 19:03:13 -0700591
592 // For normal write operation the KVS must be fully ready.
Wyatt Heplerd2298282020-02-20 17:12:45 -0800593 if (!initialized()) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800594 return Status::FAILED_PRECONDITION;
595 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800596 return Status::OK;
597}
598
David Rogers9abe3c72020-03-24 19:03:13 -0700599Status KeyValueStore::CheckReadOperation(string_view key) const {
600 if (InvalidKey(key)) {
601 return Status::INVALID_ARGUMENT;
602 }
603
604 // Operations that are explicitly read-only can be done after init() has been
605 // called but not fully ready (when needing maintenance).
606 if (initialized_ == InitializationState::kNotInitialized) {
607 return Status::FAILED_PRECONDITION;
608 }
609 return Status::OK;
610}
611
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700612Status KeyValueStore::WriteEntryForExistingKey(EntryMetadata& metadata,
613 EntryState new_state,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800614 string_view key,
615 span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700616 // Read the original entry to get the size for sector accounting purposes.
617 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700618 TRY(ReadEntry(metadata, entry));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800619
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700620 return WriteEntry(key, value, new_state, &metadata, entry.size());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800621}
622
623Status KeyValueStore::WriteEntryForNewKey(string_view key,
624 span<const byte> value) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700625 if (entry_cache_.full()) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800626 WRN("KVS full: trying to store a new entry, but can't. Have %zu entries",
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700627 entry_cache_.total_entries());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800628 return Status::RESOURCE_EXHAUSTED;
629 }
630
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700631 return WriteEntry(key, value, EntryState::kValid);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700632}
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800633
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700634Status KeyValueStore::WriteEntry(string_view key,
635 span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700636 EntryState new_state,
637 EntryMetadata* prior_metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700638 size_t prior_size) {
639 const size_t entry_size = Entry::size(partition_, key, value);
640
641 // List of addresses for sectors with space for this entry.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700642 Address* reserved_addresses = entry_cache_.TempReservedAddressesForWrite();
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700643
David Rogers31b358b2020-04-15 05:00:50 -0700644 // Find addresses to write the entry to. This may involve garbage collecting
645 // one or more sectors.
646 TRY(GetAddressesForWrite(reserved_addresses, entry_size));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700647
648 // Write the entry at the first address that was found.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700649 Entry entry = CreateEntry(reserved_addresses[0], key, value, new_state);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700650 TRY(AppendEntry(entry, key, value));
651
652 // After writing the first entry successfully, update the key descriptors.
653 // Once a single new the entry is written, the old entries are invalidated.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700654 EntryMetadata new_metadata =
David Rogers31b358b2020-04-15 05:00:50 -0700655 CreateOrUpdateKeyDescriptor(entry, key, prior_metadata, prior_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700656
657 // Write the additional copies of the entry, if redundancy is greater than 1.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700658 for (size_t i = 1; i < redundancy(); ++i) {
659 entry.set_address(reserved_addresses[i]);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700660 TRY(AppendEntry(entry, key, value));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700661 new_metadata.AddNewAddress(reserved_addresses[i]);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700662 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800663 return Status::OK;
664}
665
David Rogers31b358b2020-04-15 05:00:50 -0700666KeyValueStore::EntryMetadata KeyValueStore::CreateOrUpdateKeyDescriptor(
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700667 const Entry& entry,
668 string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700669 EntryMetadata* prior_metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700670 size_t prior_size) {
671 // If there is no prior descriptor, create a new one.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700672 if (prior_metadata == nullptr) {
673 return entry_cache_.AddNew(entry.descriptor(key), entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800674 }
675
David Rogers31b358b2020-04-15 05:00:50 -0700676 return UpdateKeyDescriptor(
677 entry, entry.address(), prior_metadata, prior_size);
678}
679
680KeyValueStore::EntryMetadata KeyValueStore::UpdateKeyDescriptor(
681 const Entry& entry,
682 Address new_address,
683 EntryMetadata* prior_metadata,
684 size_t prior_size) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700685 // Remove valid bytes for the old entry and its copies, which are now stale.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700686 for (Address address : prior_metadata->addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700687 sectors_.FromAddress(address).RemoveValidBytes(prior_size);
David Rogersa2562b52020-03-05 15:30:05 -0800688 }
689
David Rogers31b358b2020-04-15 05:00:50 -0700690 prior_metadata->Reset(entry.descriptor(prior_metadata->hash()), new_address);
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700691 return *prior_metadata;
David Rogersa2562b52020-03-05 15:30:05 -0800692}
693
David Rogers31b358b2020-04-15 05:00:50 -0700694Status KeyValueStore::GetAddressesForWrite(Address* write_addresses,
695 size_t write_size) {
696 for (size_t i = 0; i < redundancy(); i++) {
697 SectorDescriptor* sector;
698 TRY(GetSectorForWrite(&sector, write_size, span(write_addresses, i)));
699 write_addresses[i] = sectors_.NextWritableAddress(*sector);
700
701 DBG("Found space for entry in sector %u at address %u",
702 sectors_.Index(sector),
703 unsigned(write_addresses[i]));
704 }
705
706 return Status::OK;
707}
708
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700709// Finds a sector to use for writing a new entry to. Does automatic garbage
David Rogersa2562b52020-03-05 15:30:05 -0800710// collection if needed and allowed.
711//
712// OK: Sector found with needed space.
713// RESOURCE_EXHAUSTED: No sector available with the needed space.
714Status KeyValueStore::GetSectorForWrite(SectorDescriptor** sector,
715 size_t entry_size,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700716 span<const Address> reserved) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700717 Status result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800718
David Rogersf3884eb2020-03-08 19:21:40 -0700719 size_t gc_sector_count = 0;
David Rogersa2562b52020-03-05 15:30:05 -0800720 bool do_auto_gc = options_.gc_on_write != GargbageCollectOnWrite::kDisabled;
721
722 // Do garbage collection as needed, so long as policy allows.
723 while (result == Status::RESOURCE_EXHAUSTED && do_auto_gc) {
724 if (options_.gc_on_write == GargbageCollectOnWrite::kOneSector) {
725 // If GC config option is kOneSector clear the flag to not do any more
726 // GC after this try.
727 do_auto_gc = false;
728 }
729 // Garbage collect and then try again to find the best sector.
David Rogers9abe3c72020-03-24 19:03:13 -0700730 Status gc_status = GarbageCollect(reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800731 if (!gc_status.ok()) {
732 if (gc_status == Status::NOT_FOUND) {
733 // Not enough space, and no reclaimable bytes, this KVS is full!
734 return Status::RESOURCE_EXHAUSTED;
735 }
736 return gc_status;
737 }
738
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700739 result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersf3884eb2020-03-08 19:21:40 -0700740
741 gc_sector_count++;
742 // Allow total sectors + 2 number of GC cycles so that once reclaimable
743 // bytes in all the sectors have been reclaimed can try and free up space by
744 // moving entries for keys other than the one being worked on in to sectors
745 // that have copies of the key trying to be written.
746 if (gc_sector_count > (partition_.sector_count() + 2)) {
747 ERR("Did more GC sectors than total sectors!!!!");
748 return Status::RESOURCE_EXHAUSTED;
749 }
David Rogersa2562b52020-03-05 15:30:05 -0800750 }
751
752 if (!result.ok()) {
753 WRN("Unable to find sector to write %zu B", entry_size);
754 }
755 return result;
756}
757
David Rogers9abe3c72020-03-24 19:03:13 -0700758Status KeyValueStore::MarkSectorCorruptIfNotOk(Status status,
759 SectorDescriptor* sector) {
760 if (!status.ok()) {
761 DBG(" Sector %u corrupt", sectors_.Index(sector));
762 sector->mark_corrupt();
763 error_detected_ = true;
764 }
765 return status;
766}
767
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700768Status KeyValueStore::AppendEntry(const Entry& entry,
David Rogersa2562b52020-03-05 15:30:05 -0800769 string_view key,
770 span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700771 const StatusWithSize result = entry.Write(key, value);
David Rogersa2562b52020-03-05 15:30:05 -0800772
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700773 SectorDescriptor& sector = sectors_.FromAddress(entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800774
775 if (!result.ok()) {
776 ERR("Failed to write %zu bytes at %#zx. %zu actually written",
777 entry.size(),
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700778 size_t(entry.address()),
David Rogersa2562b52020-03-05 15:30:05 -0800779 result.size());
David Rogers9abe3c72020-03-24 19:03:13 -0700780 TRY(MarkSectorCorruptIfNotOk(result.status(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800781 }
782
783 if (options_.verify_on_write) {
David Rogers9abe3c72020-03-24 19:03:13 -0700784 TRY(MarkSectorCorruptIfNotOk(entry.VerifyChecksumInFlash(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800785 }
786
David Rogers98fea472020-04-01 15:43:48 -0700787 sector.RemoveWritableBytes(result.size());
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700788 sector.AddValidBytes(result.size());
David Rogersa2562b52020-03-05 15:30:05 -0800789 return Status::OK;
790}
791
David Rogers98fea472020-04-01 15:43:48 -0700792StatusWithSize KeyValueStore::CopyEntryToSector(Entry& entry,
793 SectorDescriptor* new_sector,
David Rogers31b358b2020-04-15 05:00:50 -0700794 Address new_address) {
David Rogers98fea472020-04-01 15:43:48 -0700795 const StatusWithSize result = entry.Copy(new_address);
796
797 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(result.status(), new_sector));
798
799 if (options_.verify_on_write) {
David Rogers31b358b2020-04-15 05:00:50 -0700800 Entry new_entry;
801 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(
802 Entry::Read(partition_, new_address, formats_, &new_entry),
803 new_sector));
804 // TODO: add test that catches doing the verify on the old entry.
805 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(new_entry.VerifyChecksumInFlash(),
806 new_sector));
David Rogers98fea472020-04-01 15:43:48 -0700807 }
808 // Entry was written successfully; update descriptor's address and the sector
809 // descriptors to reflect the new entry.
810 new_sector->RemoveWritableBytes(result.size());
811 new_sector->AddValidBytes(result.size());
812
813 return result;
814}
815
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700816Status KeyValueStore::RelocateEntry(const EntryMetadata& metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700817 KeyValueStore::Address& address,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700818 span<const Address> reserved_addresses) {
David Rogersa2562b52020-03-05 15:30:05 -0800819 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700820 TRY(ReadEntry(metadata, entry));
David Rogersa2562b52020-03-05 15:30:05 -0800821
822 // Find a new sector for the entry and write it to the new location. For
823 // relocation the find should not not be a sector already containing the key
824 // but can be the always empty sector, since this is part of the GC process
825 // that will result in a new empty sector. Also find a sector that does not
826 // have reclaimable space (mostly for the full GC, where that would result in
827 // an immediate extra relocation).
828 SectorDescriptor* new_sector;
829
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700830 TRY(sectors_.FindSpaceDuringGarbageCollection(
831 &new_sector, entry.size(), metadata.addresses(), reserved_addresses));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700832
David Rogers31b358b2020-04-15 05:00:50 -0700833 Address new_address = sectors_.NextWritableAddress(*new_sector);
David Rogers98fea472020-04-01 15:43:48 -0700834 TRY_ASSIGN(const size_t result_size,
835 CopyEntryToSector(entry, new_sector, new_address));
836 sectors_.FromAddress(address).RemoveValidBytes(result_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700837 address = new_address;
David Rogersa2562b52020-03-05 15:30:05 -0800838
839 return Status::OK;
840}
841
David Rogers9abe3c72020-03-24 19:03:13 -0700842Status KeyValueStore::FullMaintenance() {
843 if (initialized_ == InitializationState::kNotInitialized) {
844 return Status::FAILED_PRECONDITION;
845 }
846
Armando Montanez17083bb2020-04-24 10:18:26 -0700847 // Full maintenance can be a potentially heavy operation, and should be
848 // relatively infrequent, so log start/end at INFO level.
849 INF("Beginning full maintenance");
David Rogers98fea472020-04-01 15:43:48 -0700850 CheckForErrors();
David Rogers9abe3c72020-03-24 19:03:13 -0700851
852 if (error_detected_) {
853 TRY(Repair());
854 }
Armando Montanez17083bb2020-04-24 10:18:26 -0700855 Status overall_status = UpdateEntriesToPrimaryFormat();
David Rogers31b358b2020-04-15 05:00:50 -0700856 // Make sure all the entries are on the primary format.
Armando Montanez17083bb2020-04-24 10:18:26 -0700857 if (!overall_status.ok()) {
858 ERR("Failed to update all entries to the primary format");
859 }
David Rogers31b358b2020-04-15 05:00:50 -0700860
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700861 SectorDescriptor* sector = sectors_.last_new();
David Rogerscd87c322020-02-27 14:04:08 -0800862
863 // TODO: look in to making an iterator method for cycling through sectors
864 // starting from last_new_sector_.
Armando Montanez17083bb2020-04-24 10:18:26 -0700865 Status gc_status;
David Rogerscd87c322020-02-27 14:04:08 -0800866 for (size_t j = 0; j < sectors_.size(); j++) {
867 sector += 1;
868 if (sector == sectors_.end()) {
869 sector = sectors_.begin();
870 }
871
872 if (sector->RecoverableBytes(partition_.sector_size_bytes()) > 0) {
Armando Montanez17083bb2020-04-24 10:18:26 -0700873 gc_status = GarbageCollectSector(*sector, {});
874 if (!gc_status.ok()) {
875 ERR("Failed to garbage collect all sectors");
876 break;
877 }
David Rogerscd87c322020-02-27 14:04:08 -0800878 }
879 }
Armando Montanez17083bb2020-04-24 10:18:26 -0700880 if (overall_status.ok()) {
881 overall_status = gc_status;
882 }
David Rogerscd87c322020-02-27 14:04:08 -0800883
Armando Montanez17083bb2020-04-24 10:18:26 -0700884 if (overall_status.ok()) {
885 INF("Full maintenance complete");
886 } else {
887 ERR("Full maintenance finished with some errors");
888 }
889 return overall_status;
David Rogerscd87c322020-02-27 14:04:08 -0800890}
891
David Rogers0f8a1bb2020-04-21 18:50:19 -0700892Status KeyValueStore::PartialMaintenance() {
David Rogers9abe3c72020-03-24 19:03:13 -0700893 if (initialized_ == InitializationState::kNotInitialized) {
894 return Status::FAILED_PRECONDITION;
895 }
896
David Rogers0f8a1bb2020-04-21 18:50:19 -0700897 CheckForErrors();
David Rogersfcea3252020-04-07 14:56:35 -0700898 // Do automatic repair, if KVS options allow for it.
899 if (error_detected_ && options_.recovery != ErrorRecovery::kManual) {
900 TRY(Repair());
901 }
David Rogers0f8a1bb2020-04-21 18:50:19 -0700902 return GarbageCollect(span<const Address>());
903}
904
905Status KeyValueStore::GarbageCollect(span<const Address> reserved_addresses) {
906 DBG("Garbage Collect a single sector");
907 for (Address address : reserved_addresses) {
908 DBG(" Avoid address %u", unsigned(address));
909 }
David Rogersfcea3252020-04-07 14:56:35 -0700910
David Rogersa12786b2020-01-31 16:02:33 -0800911 // Step 1: Find the sector to garbage collect
David Rogersc9d545e2020-03-11 17:47:43 -0700912 SectorDescriptor* sector_to_gc =
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700913 sectors_.FindSectorToGarbageCollect(reserved_addresses);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800914
David Rogersa12786b2020-01-31 16:02:33 -0800915 if (sector_to_gc == nullptr) {
David Rogersa2562b52020-03-05 15:30:05 -0800916 // Nothing to GC.
917 return Status::NOT_FOUND;
David Rogersa12786b2020-01-31 16:02:33 -0800918 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800919
David Rogersc9d545e2020-03-11 17:47:43 -0700920 // Step 2: Garbage collect the selected sector.
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700921 return GarbageCollectSector(*sector_to_gc, reserved_addresses);
David Rogerscd87c322020-02-27 14:04:08 -0800922}
923
David Rogersf3884eb2020-03-08 19:21:40 -0700924Status KeyValueStore::RelocateKeyAddressesInSector(
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700925 SectorDescriptor& sector_to_gc,
David Rogersfcea3252020-04-07 14:56:35 -0700926 const EntryMetadata& metadata,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700927 span<const Address> reserved_addresses) {
928 for (FlashPartition::Address& address : metadata.addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700929 if (sectors_.AddressInSector(sector_to_gc, address)) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700930 DBG(" Relocate entry for Key 0x%08" PRIx32 ", sector %u",
931 metadata.hash(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700932 sectors_.Index(sectors_.FromAddress(address)));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700933 TRY(RelocateEntry(metadata, address, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700934 }
935 }
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700936
David Rogersf3884eb2020-03-08 19:21:40 -0700937 return Status::OK;
938};
939
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700940Status KeyValueStore::GarbageCollectSector(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700941 SectorDescriptor& sector_to_gc, span<const Address> reserved_addresses) {
David Rogers9abe3c72020-03-24 19:03:13 -0700942 DBG(" Garbage Collect sector %u", sectors_.Index(sector_to_gc));
David Rogersf3884eb2020-03-08 19:21:40 -0700943 // Step 1: Move any valid entries in the GC sector to other sectors
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700944 if (sector_to_gc.valid_bytes() != 0) {
David Rogers98fea472020-04-01 15:43:48 -0700945 for (EntryMetadata& metadata : entry_cache_) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700946 TRY(RelocateKeyAddressesInSector(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700947 sector_to_gc, metadata, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700948 }
949 }
950
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700951 if (sector_to_gc.valid_bytes() != 0) {
David Rogers67f4b6c2020-02-06 16:17:09 -0800952 ERR(" Failed to relocate valid entries from sector being garbage "
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800953 "collected, %zu valid bytes remain",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700954 sector_to_gc.valid_bytes());
Wyatt Heplerb7609542020-01-24 10:29:54 -0800955 return Status::INTERNAL;
956 }
957
David Rogerscd87c322020-02-27 14:04:08 -0800958 // Step 2: Reinitialize the sector
David Rogers9abe3c72020-03-24 19:03:13 -0700959 sector_to_gc.mark_corrupt();
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700960 TRY(partition_.Erase(sectors_.BaseAddress(sector_to_gc), 1));
961 sector_to_gc.set_writable_bytes(partition_.sector_size_bytes());
Wyatt Heplerb7609542020-01-24 10:29:54 -0800962
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700963 DBG(" Garbage Collect sector %u complete", sectors_.Index(sector_to_gc));
David Rogersa12786b2020-01-31 16:02:33 -0800964 return Status::OK;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800965}
966
David Rogers31b358b2020-04-15 05:00:50 -0700967Status KeyValueStore::UpdateEntriesToPrimaryFormat() {
968 for (EntryMetadata& prior_metadata : entry_cache_) {
969 Entry entry;
970 TRY(ReadEntry(prior_metadata, entry));
971 if (formats_.primary().magic == entry.magic()) {
972 // Ignore entries that are already on the primary format.
973 continue;
974 }
975
976 DBG("Updating entry 0x%08x from old format [0x%08x] to new format "
977 "[0x%08x]",
978 unsigned(prior_metadata.hash()),
979 unsigned(entry.magic()),
980 unsigned(formats_.primary().magic));
981
982 last_transaction_id_ += 1;
983 TRY(entry.Update(formats_.primary(), last_transaction_id_));
984
985 // List of addresses for sectors with space for this entry.
986 Address* reserved_addresses = entry_cache_.TempReservedAddressesForWrite();
987
988 // Find addresses to write the entry to. This may involve garbage collecting
989 // one or more sectors.
990 TRY(GetAddressesForWrite(reserved_addresses, entry.size()));
991
992 TRY(CopyEntryToSector(entry,
993 &sectors_.FromAddress(reserved_addresses[0]),
994 reserved_addresses[0]));
995
996 // After writing the first entry successfully, update the key descriptors.
997 // Once a single new the entry is written, the old entries are invalidated.
998 EntryMetadata new_metadata = UpdateKeyDescriptor(
999 entry, reserved_addresses[0], &prior_metadata, entry.size());
1000
1001 // Write the additional copies of the entry, if redundancy is greater
1002 // than 1.
1003 for (size_t i = 1; i < redundancy(); ++i) {
1004 TRY(CopyEntryToSector(entry,
1005 &sectors_.FromAddress(reserved_addresses[i]),
1006 reserved_addresses[i]));
1007 new_metadata.AddNewAddress(reserved_addresses[i]);
1008 }
1009 }
1010 return Status::OK;
1011}
1012
David Rogers9abe3c72020-03-24 19:03:13 -07001013// Add any missing redundant entries/copies for a key.
1014Status KeyValueStore::AddRedundantEntries(EntryMetadata& metadata) {
David Rogers9abe3c72020-03-24 19:03:13 -07001015 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -07001016 TRY(ReadEntry(metadata, entry));
David Rogers9abe3c72020-03-24 19:03:13 -07001017 TRY(entry.VerifyChecksumInFlash());
1018
David Rogers0f8a1bb2020-04-21 18:50:19 -07001019 while (metadata.addresses().size() < redundancy()) {
1020 SectorDescriptor* new_sector;
1021 TRY(GetSectorForWrite(&new_sector, entry.size(), metadata.addresses()));
David Rogers9abe3c72020-03-24 19:03:13 -07001022
David Rogers31b358b2020-04-15 05:00:50 -07001023 Address new_address = sectors_.NextWritableAddress(*new_sector);
David Rogers98fea472020-04-01 15:43:48 -07001024 TRY(CopyEntryToSector(entry, new_sector, new_address));
David Rogers9abe3c72020-03-24 19:03:13 -07001025
1026 metadata.AddNewAddress(new_address);
1027 }
1028 return Status::OK;
1029}
1030
1031Status KeyValueStore::RepairCorruptSectors() {
1032 // Try to GC each corrupt sector, even if previous sectors fail. If GC of a
1033 // sector failed on the first pass, then do a second pass, since a later
1034 // sector might have cleared up space or otherwise unblocked the earlier
1035 // failed sector.
1036 Status repair_status = Status::OK;
1037
1038 size_t loop_count = 0;
1039 do {
1040 loop_count++;
1041 // Error of RESOURCE_EXHAUSTED indicates no space found for relocation.
1042 // Reset back to OK for the next pass.
1043 if (repair_status == Status::RESOURCE_EXHAUSTED) {
1044 repair_status = Status::OK;
1045 }
1046
1047 DBG(" Pass %u", unsigned(loop_count));
1048 for (SectorDescriptor& sector : sectors_) {
1049 if (sector.corrupt()) {
1050 DBG(" Found sector %u with corruption", sectors_.Index(sector));
1051 Status sector_status = GarbageCollectSector(sector, {});
1052 if (sector_status.ok()) {
1053 error_stats_.corrupt_sectors_recovered += 1;
1054 } else if (repair_status.ok() ||
1055 repair_status == Status::RESOURCE_EXHAUSTED) {
1056 repair_status = sector_status;
1057 }
1058 }
1059 }
1060 DBG(" Pass %u complete", unsigned(loop_count));
1061 } while (!repair_status.ok() && loop_count < 2);
1062
1063 return repair_status;
1064}
1065
1066Status KeyValueStore::EnsureFreeSectorExists() {
1067 Status repair_status = Status::OK;
1068 bool empty_sector_found = false;
1069
1070 DBG(" Find empty sector");
1071 for (SectorDescriptor& sector : sectors_) {
1072 if (sector.Empty(partition_.sector_size_bytes())) {
1073 empty_sector_found = true;
1074 DBG(" Empty sector found");
1075 break;
1076 }
1077 }
1078 if (empty_sector_found == false) {
1079 DBG(" No empty sector found, attempting to GC a free sector");
1080 Status sector_status = GarbageCollect(span<const Address, 0>());
1081 if (repair_status.ok() && !sector_status.ok()) {
1082 DBG(" Unable to free an empty sector");
1083 repair_status = sector_status;
1084 }
1085 }
1086
1087 return repair_status;
1088}
1089
1090Status KeyValueStore::EnsureEntryRedundancy() {
1091 Status repair_status = Status::OK;
1092
1093 if (redundancy() == 1) {
David Rogers98fea472020-04-01 15:43:48 -07001094 DBG(" Redundancy not in use, nothting to check");
David Rogers9abe3c72020-03-24 19:03:13 -07001095 return Status::OK;
1096 }
1097
David Rogers98fea472020-04-01 15:43:48 -07001098 DBG(" Write any needed additional duplicate copies of keys to fulfill %u"
1099 " redundancy",
David Rogers9abe3c72020-03-24 19:03:13 -07001100 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -07001101 for (EntryMetadata& metadata : entry_cache_) {
David Rogers9abe3c72020-03-24 19:03:13 -07001102 if (metadata.addresses().size() >= redundancy()) {
1103 continue;
1104 }
1105
1106 DBG(" Key with %u of %u copies found, adding missing copies",
1107 unsigned(metadata.addresses().size()),
1108 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -07001109 Status fill_status = AddRedundantEntries(metadata);
David Rogers9abe3c72020-03-24 19:03:13 -07001110 if (fill_status.ok()) {
1111 error_stats_.missing_redundant_entries_recovered += 1;
1112 DBG(" Key missing copies added");
1113 } else {
1114 DBG(" Failed to add key missing copies");
1115 if (repair_status.ok()) {
1116 repair_status = fill_status;
1117 }
1118 }
1119 }
1120
1121 return repair_status;
1122}
1123
David Rogersfcea3252020-04-07 14:56:35 -07001124Status KeyValueStore::FixErrors() {
1125 DBG("Fixing KVS errors");
David Rogers9abe3c72020-03-24 19:03:13 -07001126
1127 // Step 1: Garbage collect any sectors marked as corrupt.
David Rogersfcea3252020-04-07 14:56:35 -07001128 Status overall_status = RepairCorruptSectors();
David Rogers9abe3c72020-03-24 19:03:13 -07001129
1130 // Step 2: Make sure there is at least 1 empty sector. This needs to be a
1131 // seperate check of sectors from step 1, because a found empty sector might
1132 // get written to by a later GC that fails and does not result in a free
1133 // sector.
David Rogersfcea3252020-04-07 14:56:35 -07001134 Status repair_status = EnsureFreeSectorExists();
David Rogers9abe3c72020-03-24 19:03:13 -07001135 if (overall_status.ok()) {
1136 overall_status = repair_status;
1137 }
1138
1139 // Step 3: Make sure each stored key has the full number of redundant
1140 // entries.
1141 repair_status = EnsureEntryRedundancy();
1142 if (overall_status.ok()) {
1143 overall_status = repair_status;
1144 }
1145
1146 if (overall_status.ok()) {
1147 error_detected_ = false;
1148 initialized_ = InitializationState::kReady;
1149 }
1150 return overall_status;
1151}
1152
David Rogersfcea3252020-04-07 14:56:35 -07001153Status KeyValueStore::Repair() {
1154 // If errors have been detected, just reinit the KVS metadata. This does a
1155 // full deep error check and any needed repairs. Then repair any errors.
1156 INF("Starting KVS repair");
1157
1158 DBG("Reinitialize KVS metadata");
1159 InitializeMetadata();
1160
1161 return FixErrors();
1162}
1163
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -08001164KeyValueStore::Entry KeyValueStore::CreateEntry(Address address,
Wyatt Heplerab3b2492020-03-11 16:15:16 -07001165 string_view key,
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -08001166 span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001167 EntryState state) {
Keir Mierle9e38b402020-02-21 13:06:21 -08001168 // Always bump the transaction ID when creating a new entry.
1169 //
1170 // Burning transaction IDs prevents inconsistencies between flash and memory
1171 // that which could happen if a write succeeds, but for some reason the read
1172 // and verify step fails. Here's how this would happen:
1173 //
1174 // 1. The entry is written but for some reason the flash reports failure OR
1175 // The write succeeds, but the read / verify operation fails.
1176 // 2. The transaction ID is NOT incremented, because of the failure
1177 // 3. (later) A new entry is written, re-using the transaction ID (oops)
1178 //
1179 // By always burning transaction IDs, the above problem can't happen.
1180 last_transaction_id_ += 1;
1181
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001182 if (state == EntryState::kDeleted) {
Wyatt Hepler7465be32020-02-21 15:30:53 -08001183 return Entry::Tombstone(
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -08001184 partition_, address, formats_.primary(), key, last_transaction_id_);
Wyatt Hepler1fc11042020-02-19 17:17:51 -08001185 }
1186 return Entry::Valid(partition_,
1187 address,
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -08001188 formats_.primary(),
Wyatt Hepler1fc11042020-02-19 17:17:51 -08001189 key,
1190 value,
Keir Mierle9e38b402020-02-21 13:06:21 -08001191 last_transaction_id_);
Wyatt Heplerd2298282020-02-20 17:12:45 -08001192}
1193
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001194void KeyValueStore::LogDebugInfo() const {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001195 const size_t sector_size_bytes = partition_.sector_size_bytes();
1196 DBG("====================== KEY VALUE STORE DUMP =========================");
1197 DBG(" ");
1198 DBG("Flash partition:");
Wyatt Heplerad0a7932020-02-06 08:20:38 -08001199 DBG(" Sector count = %zu", partition_.sector_count());
Wyatt Hepler38ce30f2020-02-19 11:48:31 -08001200 DBG(" Sector max count = %zu", sectors_.max_size());
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001201 DBG(" Sectors in use = %zu", sectors_.size());
Keir Mierle8c352dc2020-02-02 13:58:19 -08001202 DBG(" Sector size = %zu", sector_size_bytes);
1203 DBG(" Total size = %zu", partition_.size_bytes());
1204 DBG(" Alignment = %zu", partition_.alignment_bytes());
1205 DBG(" ");
1206 DBG("Key descriptors:");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001207 DBG(" Entry count = %zu", entry_cache_.total_entries());
1208 DBG(" Max entry count = %zu", entry_cache_.max_entries());
Keir Mierle8c352dc2020-02-02 13:58:19 -08001209 DBG(" ");
1210 DBG(" # hash version address address (hex)");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001211 size_t i = 0;
1212 for (const EntryMetadata& metadata : entry_cache_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001213 DBG(" |%3zu: | %8zx |%8zu | %8zu | %8zx",
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001214 i++,
1215 size_t(metadata.hash()),
1216 size_t(metadata.transaction_id()),
1217 size_t(metadata.first_address()),
1218 size_t(metadata.first_address()));
Keir Mierle8c352dc2020-02-02 13:58:19 -08001219 }
1220 DBG(" ");
1221
1222 DBG("Sector descriptors:");
1223 DBG(" # tail free valid has_space");
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001224 for (const SectorDescriptor& sd : sectors_) {
1225 DBG(" |%3u: | %8zu |%8zu | %s",
1226 sectors_.Index(sd),
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001227 size_t(sd.writable_bytes()),
1228 sd.valid_bytes(),
1229 sd.writable_bytes() ? "YES" : "");
Keir Mierle8c352dc2020-02-02 13:58:19 -08001230 }
1231 DBG(" ");
1232
1233 // TODO: This should stop logging after some threshold.
1234 // size_t dumped_bytes = 0;
1235 DBG("Sector raw data:");
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001236 for (size_t sector_id = 0; sector_id < sectors_.size(); ++sector_id) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001237 // Read sector data. Yes, this will blow the stack on embedded.
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001238 std::array<byte, 500> raw_sector_data; // TODO!!!
Keir Mierle8c352dc2020-02-02 13:58:19 -08001239 StatusWithSize sws =
1240 partition_.Read(sector_id * sector_size_bytes, raw_sector_data);
1241 DBG("Read: %zu bytes", sws.size());
1242
1243 DBG(" base addr offs 0 1 2 3 4 5 6 7");
1244 for (size_t i = 0; i < sector_size_bytes; i += 8) {
1245 DBG(" %3zu %8zx %5zu | %02x %02x %02x %02x %02x %02x %02x %02x",
1246 sector_id,
1247 (sector_id * sector_size_bytes) + i,
1248 i,
1249 static_cast<unsigned int>(raw_sector_data[i + 0]),
1250 static_cast<unsigned int>(raw_sector_data[i + 1]),
1251 static_cast<unsigned int>(raw_sector_data[i + 2]),
1252 static_cast<unsigned int>(raw_sector_data[i + 3]),
1253 static_cast<unsigned int>(raw_sector_data[i + 4]),
1254 static_cast<unsigned int>(raw_sector_data[i + 5]),
1255 static_cast<unsigned int>(raw_sector_data[i + 6]),
1256 static_cast<unsigned int>(raw_sector_data[i + 7]));
1257
1258 // TODO: Fix exit condition.
1259 if (i > 128) {
1260 break;
1261 }
1262 }
1263 DBG(" ");
1264 }
1265
1266 DBG("////////////////////// KEY VALUE STORE DUMP END /////////////////////");
1267}
1268
David Rogerscf680ab2020-02-12 23:28:32 -08001269void KeyValueStore::LogSectors() const {
1270 DBG("Sector descriptors: count %zu", sectors_.size());
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001271 for (auto& sector : sectors_) {
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001272 DBG(" - Sector %u: valid %zu, recoverable %zu, free %zu",
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001273 sectors_.Index(sector),
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001274 sector.valid_bytes(),
1275 sector.RecoverableBytes(partition_.sector_size_bytes()),
1276 sector.writable_bytes());
David Rogers50185ad2020-02-07 00:02:46 -08001277 }
1278}
1279
David Rogerscf680ab2020-02-12 23:28:32 -08001280void KeyValueStore::LogKeyDescriptor() const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001281 DBG("Key descriptors: count %zu", entry_cache_.total_entries());
David Rogers9abe3c72020-03-24 19:03:13 -07001282 for (const EntryMetadata& metadata : entry_cache_) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001283 DBG(" - Key: %s, hash %#zx, transaction ID %zu, first address %#zx",
Wyatt Hepler02946272020-03-18 10:36:22 -07001284 metadata.state() == EntryState::kDeleted ? "Deleted" : "Valid",
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001285 static_cast<size_t>(metadata.hash()),
1286 static_cast<size_t>(metadata.transaction_id()),
1287 static_cast<size_t>(metadata.first_address()));
David Rogerscf680ab2020-02-12 23:28:32 -08001288 }
1289}
1290
Wyatt Hepler2ad60672020-01-21 08:00:16 -08001291} // namespace pw::kvs