blob: 68a8412fd20ede9b1639880b46196707c0ff96a5 [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
Armando Montanez28ecccb2020-05-04 15:35:54 -070015#define PW_LOG_MODULE_NAME "KVS"
16
Wyatt Heplerb7609542020-01-24 10:29:54 -080017#include "pw_kvs/key_value_store.h"
18
Wyatt Heplerbab0e202020-02-04 07:40:08 -080019#include <algorithm>
Wyatt Hepler5a33d8c2020-02-06 09:32:58 -080020#include <cinttypes>
Wyatt Heplerb7609542020-01-24 10:29:54 -080021#include <cstring>
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080022#include <type_traits>
Wyatt Heplerb7609542020-01-24 10:29:54 -080023
Keir Mierle8c352dc2020-02-02 13:58:19 -080024#define PW_LOG_USE_ULTRA_SHORT_NAMES 1
David Rogersc0104462020-05-08 15:50:23 -070025#include "pw_assert/assert.h"
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080026#include "pw_kvs_private/macros.h"
Keir Mierle8c352dc2020-02-02 13:58:19 -080027#include "pw_log/log.h"
Wyatt Heplerb7609542020-01-24 10:29:54 -080028
Wyatt Hepler2ad60672020-01-21 08:00:16 -080029namespace pw::kvs {
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080030namespace {
Wyatt Heplerb7609542020-01-24 10:29:54 -080031
Wyatt Hepleracaacf92020-01-24 10:58:30 -080032using std::byte;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080033using std::string_view;
Wyatt Hepleracaacf92020-01-24 10:58:30 -080034
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080035constexpr bool InvalidKey(std::string_view key) {
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -080036 return key.empty() || (key.size() > internal::Entry::kMaxKeyLength);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080037}
38
39} // namespace
40
Wyatt Heplerad0a7932020-02-06 08:20:38 -080041KeyValueStore::KeyValueStore(FlashPartition* partition,
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080042 span<const EntryFormat> formats,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070043 const Options& options,
44 size_t redundancy,
45 Vector<SectorDescriptor>& sector_descriptor_list,
46 const SectorDescriptor** temp_sectors_to_skip,
47 Vector<KeyDescriptor>& key_descriptor_list,
48 Address* addresses)
Wyatt Heplerad0a7932020-02-06 08:20:38 -080049 : partition_(*partition),
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080050 formats_(formats),
Wyatt Heplerc84393f2020-03-20 11:23:24 -070051 sectors_(sector_descriptor_list, *partition, temp_sectors_to_skip),
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070052 entry_cache_(key_descriptor_list, addresses, redundancy),
David Rogers49766d92020-03-20 10:55:54 -070053 options_(options),
David Rogers9abe3c72020-03-24 19:03:13 -070054 initialized_(InitializationState::kNotInitialized),
David Rogers49766d92020-03-20 10:55:54 -070055 error_detected_(false),
David Rogers9abe3c72020-03-24 19:03:13 -070056 error_stats_({}),
David Rogers49766d92020-03-20 10:55:54 -070057 last_transaction_id_(0) {}
Wyatt Heplerad0a7932020-02-06 08:20:38 -080058
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080059Status KeyValueStore::Init() {
David Rogers9abe3c72020-03-24 19:03:13 -070060 initialized_ = InitializationState::kNotInitialized;
David Rogers49766d92020-03-20 10:55:54 -070061 error_detected_ = false;
Keir Mierlebf904812020-03-11 17:28:22 -070062 last_transaction_id_ = 0;
Wyatt Heplerd2298282020-02-20 17:12:45 -080063
David Rogers2e9e0c82020-02-13 15:06:06 -080064 INF("Initializing key value store");
Wyatt Hepler38ce30f2020-02-19 11:48:31 -080065 if (partition_.sector_count() > sectors_.max_size()) {
David Rogers9fc78b82020-06-12 13:56:12 -070066 ERR("KVS init failed: kMaxUsableSectors (=%u) must be at least as "
67 "large as the number of sectors in the flash partition (=%u)",
68 unsigned(sectors_.max_size()),
69 unsigned(partition_.sector_count()));
Wyatt Heplerad0a7932020-02-06 08:20:38 -080070 return Status::FAILED_PRECONDITION;
71 }
72
David Rogers178002a2020-06-16 13:52:54 -070073 if (partition_.sector_count() < 2) {
74 ERR("KVS init failed: FlashParition sector count (=%u) must be at 2. KVS "
75 "requires at least 1 working sector + 1 free/reserved sector",
76 unsigned(partition_.sector_count()));
77 return Status::FAILED_PRECONDITION;
78 }
79
Keir Mierle8c352dc2020-02-02 13:58:19 -080080 const size_t sector_size_bytes = partition_.sector_size_bytes();
Keir Mierle8c352dc2020-02-02 13:58:19 -080081
David Rogers49766d92020-03-20 10:55:54 -070082 // TODO: investigate doing this as a static assert/compile-time check.
83 if (sector_size_bytes > SectorDescriptor::max_sector_size()) {
David Rogers9fc78b82020-06-12 13:56:12 -070084 ERR("KVS init failed: sector_size_bytes (=%u) is greater than maximum "
85 "allowed sector size (=%u)",
86 unsigned(sector_size_bytes),
87 unsigned(SectorDescriptor::max_sector_size()));
David Rogers49766d92020-03-20 10:55:54 -070088 return Status::FAILED_PRECONDITION;
89 }
90
David Rogerscd134352020-04-17 19:37:15 -070091 Status metadata_result = InitializeMetadata();
David Rogersfcea3252020-04-07 14:56:35 -070092
93 if (!error_detected_) {
94 initialized_ = InitializationState::kReady;
95 } else {
David Rogers0f8a1bb2020-04-21 18:50:19 -070096 initialized_ = InitializationState::kNeedsMaintenance;
97
David Rogersfcea3252020-04-07 14:56:35 -070098 if (options_.recovery != ErrorRecovery::kManual) {
Armando Montanezf8419ae2020-04-21 10:03:05 -070099 size_t pre_fix_redundancy_errors =
100 error_stats_.missing_redundant_entries_recovered;
David Rogersfcea3252020-04-07 14:56:35 -0700101 Status recovery_status = FixErrors();
102
103 if (recovery_status.ok()) {
David Rogerscd134352020-04-17 19:37:15 -0700104 if (metadata_result == Status::OUT_OF_RANGE) {
Armando Montanezf8419ae2020-04-21 10:03:05 -0700105 error_stats_.missing_redundant_entries_recovered =
106 pre_fix_redundancy_errors;
David Rogerscd134352020-04-17 19:37:15 -0700107 INF("KVS init: Redundancy level successfully updated");
108 } else {
109 WRN("KVS init: Corruption detected and fully repaired");
110 }
David Rogersfcea3252020-04-07 14:56:35 -0700111 initialized_ = InitializationState::kReady;
112 } else if (recovery_status == Status::RESOURCE_EXHAUSTED) {
113 WRN("KVS init: Unable to maintain required free sector");
David Rogersfcea3252020-04-07 14:56:35 -0700114 } else {
115 WRN("KVS init: Corruption detected and unable repair");
David Rogersfcea3252020-04-07 14:56:35 -0700116 }
117 } else {
118 WRN("KVS init: Corruption detected, no repair attempted due to options");
David Rogersfcea3252020-04-07 14:56:35 -0700119 }
120 }
121
David Rogers9fc78b82020-06-12 13:56:12 -0700122 INF("KeyValueStore init complete: active keys %u, deleted keys %u, sectors "
123 "%u, logical sector size %u bytes",
124 unsigned(size()),
125 unsigned(entry_cache_.total_entries() - size()),
126 unsigned(sectors_.size()),
127 unsigned(partition_.sector_size_bytes()));
David Rogersfcea3252020-04-07 14:56:35 -0700128
129 // Report any corruption was not repaired.
130 if (error_detected_) {
131 WRN("KVS init: Corruption found but not repaired, KVS unavailable until "
132 "successful maintenance.");
133 return Status::DATA_LOSS;
134 }
135
136 return Status::OK;
137}
138
David Rogerscd134352020-04-17 19:37:15 -0700139Status KeyValueStore::InitializeMetadata() {
David Rogersfcea3252020-04-07 14:56:35 -0700140 const size_t sector_size_bytes = partition_.sector_size_bytes();
141
142 sectors_.Reset();
143 entry_cache_.Reset();
144
Keir Mierle8c352dc2020-02-02 13:58:19 -0800145 DBG("First pass: Read all entries from all sectors");
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800146 Address sector_address = 0;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800147
Alexei Frolovd4adf912020-02-21 13:29:15 -0800148 size_t total_corrupt_bytes = 0;
David Rogerscd134352020-04-17 19:37:15 -0700149 size_t corrupt_entries = 0;
David Rogers91627482020-02-27 17:38:12 -0800150 bool empty_sector_found = false;
David Rogerscd134352020-04-17 19:37:15 -0700151 size_t entry_copies_missing = 0;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800152
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800153 for (SectorDescriptor& sector : sectors_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800154 Address entry_address = sector_address;
155
Alexei Frolovd4adf912020-02-21 13:29:15 -0800156 size_t sector_corrupt_bytes = 0;
157
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800158 for (int num_entries_in_sector = 0; true; num_entries_in_sector++) {
David Rogersfcea3252020-04-07 14:56:35 -0700159 DBG("Load entry: sector=%u, entry#=%d, address=%u",
160 unsigned(sector_address),
Keir Mierle8c352dc2020-02-02 13:58:19 -0800161 num_entries_in_sector,
David Rogersfcea3252020-04-07 14:56:35 -0700162 unsigned(entry_address));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800163
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700164 if (!sectors_.AddressInSector(sector, entry_address)) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800165 DBG("Fell off end of sector; moving to the next sector");
166 break;
167 }
168
169 Address next_entry_address;
170 Status status = LoadEntry(entry_address, &next_entry_address);
171 if (status == Status::NOT_FOUND) {
172 DBG("Hit un-written data in sector; moving to the next sector");
173 break;
David Rogersfcea3252020-04-07 14:56:35 -0700174 } else if (!status.ok()) {
175 // The entry could not be read, indicating likely data corruption within
176 // the sector. Try to scan the remainder of the sector for other
177 // entries.
Alexei Frolovd4adf912020-02-21 13:29:15 -0800178
David Rogers49766d92020-03-20 10:55:54 -0700179 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800180 corrupt_entries++;
181
182 status = ScanForEntry(sector,
183 entry_address + Entry::kMinAlignmentBytes,
184 &next_entry_address);
David Rogersfcea3252020-04-07 14:56:35 -0700185 if (!status.ok()) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800186 // No further entries in this sector. Mark the remaining bytes in the
187 // sector as corrupt (since we can't reliably know the size of the
188 // corrupt entry).
189 sector_corrupt_bytes +=
190 sector_size_bytes - (entry_address - sector_address);
191 break;
192 }
193
Alexei Frolovd4adf912020-02-21 13:29:15 -0800194 sector_corrupt_bytes += next_entry_address - entry_address;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800195 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800196
197 // Entry loaded successfully; so get ready to load the next one.
198 entry_address = next_entry_address;
199
200 // Update of the number of writable bytes in this sector.
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800201 sector.set_writable_bytes(sector_size_bytes -
202 (entry_address - sector_address));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800203 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800204
Alexei Frolovd4adf912020-02-21 13:29:15 -0800205 if (sector_corrupt_bytes > 0) {
206 // If the sector contains corrupt data, prevent any further entries from
207 // being written to it by indicating that it has no space. This should
208 // also make it a decent GC candidate. Valid keys in the sector are still
209 // readable as normal.
David Rogers49766d92020-03-20 10:55:54 -0700210 sector.mark_corrupt();
211 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800212
David Rogers9fc78b82020-06-12 13:56:12 -0700213 WRN("Sector %u contains %uB of corrupt data",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700214 sectors_.Index(sector),
David Rogers9fc78b82020-06-12 13:56:12 -0700215 unsigned(sector_corrupt_bytes));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800216 }
217
David Rogers91627482020-02-27 17:38:12 -0800218 if (sector.Empty(sector_size_bytes)) {
219 empty_sector_found = true;
220 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800221 sector_address += sector_size_bytes;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800222 total_corrupt_bytes += sector_corrupt_bytes;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800223 }
224
225 DBG("Second pass: Count valid bytes in each sector");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700226 Address newest_key = 0;
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800227
David Rogers98fea472020-04-01 15:43:48 -0700228 // For every valid entry, for each address, count the valid bytes in that
229 // sector. If the address fails to read, remove the address and mark the
230 // sector as corrupt. Track which entry has the newest transaction ID for
231 // initializing last_new_sector_.
232 for (EntryMetadata& metadata : entry_cache_) {
David Rogers49766d92020-03-20 10:55:54 -0700233 if (metadata.addresses().size() < redundancy()) {
David Rogers31b358b2020-04-15 05:00:50 -0700234 DBG("Key 0x%08x missing copies, has %u, needs %u",
235 unsigned(metadata.hash()),
236 unsigned(metadata.addresses().size()),
237 unsigned(redundancy()));
David Rogerscd134352020-04-17 19:37:15 -0700238 entry_copies_missing++;
David Rogers49766d92020-03-20 10:55:54 -0700239 }
David Rogers98fea472020-04-01 15:43:48 -0700240 size_t index = 0;
241 while (index < metadata.addresses().size()) {
242 Address address = metadata.addresses()[index];
David Rogersf56131c2020-03-04 10:19:22 -0800243 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700244
245 Status read_result = Entry::Read(partition_, address, formats_, &entry);
246
247 SectorDescriptor& sector = sectors_.FromAddress(address);
248
249 if (read_result.ok()) {
250 sector.AddValidBytes(entry.size());
251 index++;
252 } else {
253 corrupt_entries++;
254 total_corrupt_bytes += sector.writable_bytes();
255 error_detected_ = true;
256 sector.mark_corrupt();
257
258 // Remove the bad address and stay at this index. The removal
259 // replaces out the removed address with the back address so
260 // this index needs to be rechecked with the new address.
261 metadata.RemoveAddress(address);
262 }
David Rogersf56131c2020-03-04 10:19:22 -0800263 }
David Rogers98fea472020-04-01 15:43:48 -0700264
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700265 if (metadata.IsNewerThan(last_transaction_id_)) {
266 last_transaction_id_ = metadata.transaction_id();
267 newest_key = metadata.addresses().back();
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800268 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800269 }
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800270
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700271 sectors_.set_last_new_sector(newest_key);
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800272
David Rogers91627482020-02-27 17:38:12 -0800273 if (!empty_sector_found) {
David Rogers31b358b2020-04-15 05:00:50 -0700274 DBG("No empty sector found");
David Rogers9abe3c72020-03-24 19:03:13 -0700275 error_detected_ = true;
David Rogers91627482020-02-27 17:38:12 -0800276 }
277
David Rogerscd134352020-04-17 19:37:15 -0700278 if (entry_copies_missing > 0) {
279 bool other_errors = error_detected_;
280 error_detected_ = true;
281
Armando Montanez344814b2020-04-24 15:05:42 -0700282 if (!other_errors && entry_copies_missing == entry_cache_.total_entries()) {
David Rogers9fc78b82020-06-12 13:56:12 -0700283 INF("KVS configuration changed to redundancy of %u total copies per key",
284 unsigned(redundancy()));
David Rogerscd134352020-04-17 19:37:15 -0700285 return Status::OUT_OF_RANGE;
286 }
David Rogers9abe3c72020-03-24 19:03:13 -0700287 }
David Rogerscd134352020-04-17 19:37:15 -0700288
289 if (error_detected_) {
David Rogers9fc78b82020-06-12 13:56:12 -0700290 WRN("Corruption detected. Found %u corrupt bytes, %u corrupt entries, "
291 "and %u keys missing redundant copies.",
292 unsigned(total_corrupt_bytes),
293 unsigned(corrupt_entries),
294 unsigned(entry_copies_missing));
David Rogerscd134352020-04-17 19:37:15 -0700295 return Status::FAILED_PRECONDITION;
296 }
297 return Status::OK;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800298}
299
Alexei Frolov9e235832020-02-24 12:44:45 -0800300KeyValueStore::StorageStats KeyValueStore::GetStorageStats() const {
David Rogers9abe3c72020-03-24 19:03:13 -0700301 StorageStats stats{};
Alexei Frolov9e235832020-02-24 12:44:45 -0800302 const size_t sector_size = partition_.sector_size_bytes();
303 bool found_empty_sector = false;
David Rogers9abe3c72020-03-24 19:03:13 -0700304 stats.corrupt_sectors_recovered = error_stats_.corrupt_sectors_recovered;
305 stats.missing_redundant_entries_recovered =
306 error_stats_.missing_redundant_entries_recovered;
Alexei Frolov9e235832020-02-24 12:44:45 -0800307
308 for (const SectorDescriptor& sector : sectors_) {
309 stats.in_use_bytes += sector.valid_bytes();
310 stats.reclaimable_bytes += sector.RecoverableBytes(sector_size);
311
312 if (!found_empty_sector && sector.Empty(sector_size)) {
313 // The KVS tries to always keep an empty sector for GC, so don't count
314 // the first empty sector seen as writable space. However, a free sector
315 // cannot always be assumed to exist; if a GC operation fails, all sectors
316 // may be partially written, in which case the space reported might be
317 // inaccurate.
318 found_empty_sector = true;
319 continue;
320 }
321
322 stats.writable_bytes += sector.writable_bytes();
323 }
324
325 return stats;
326}
327
David Rogers98fea472020-04-01 15:43:48 -0700328// Check KVS for any error conditions. Primarily intended for test and
329// internal use.
David Rogers9abe3c72020-03-24 19:03:13 -0700330bool KeyValueStore::CheckForErrors() {
331 // Check for corrupted sectors
332 for (SectorDescriptor& sector : sectors_) {
333 if (sector.corrupt()) {
334 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700335 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700336 }
337 }
338
339 // Check for missing redundancy.
340 if (redundancy() > 1) {
341 for (const EntryMetadata& metadata : entry_cache_) {
342 if (metadata.addresses().size() < redundancy()) {
343 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700344 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700345 }
346 }
347 }
348
349 return error_detected();
350}
351
Keir Mierle8c352dc2020-02-02 13:58:19 -0800352Status KeyValueStore::LoadEntry(Address entry_address,
353 Address* next_entry_address) {
Wyatt Heplere541e072020-02-14 09:10:53 -0800354 Entry entry;
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800355 TRY(Entry::Read(partition_, entry_address, formats_, &entry));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800356
357 // Read the key from flash & validate the entry (which reads the value).
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800358 Entry::KeyBuffer key_buffer;
Wyatt Heplere541e072020-02-14 09:10:53 -0800359 TRY_ASSIGN(size_t key_length, entry.ReadKey(key_buffer));
360 const string_view key(key_buffer.data(), key_length);
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800361
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800362 TRY(entry.VerifyChecksumInFlash());
David Rogersf56131c2020-03-04 10:19:22 -0800363
364 // A valid entry was found, so update the next entry address before doing any
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700365 // of the checks that happen in AddNewOrUpdateExisting.
David Rogersf56131c2020-03-04 10:19:22 -0800366 *next_entry_address = entry.next_address();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700367 return entry_cache_.AddNewOrUpdateExisting(
368 entry.descriptor(key), entry.address(), partition_.sector_size_bytes());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800369}
370
Alexei Frolovd4adf912020-02-21 13:29:15 -0800371// Scans flash memory within a sector to find a KVS entry magic.
Alexei Frolovd4adf912020-02-21 13:29:15 -0800372Status KeyValueStore::ScanForEntry(const SectorDescriptor& sector,
373 Address start_address,
374 Address* next_entry_address) {
David Rogersfcea3252020-04-07 14:56:35 -0700375 DBG("Scanning sector %u for entries starting from address %u",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700376 sectors_.Index(sector),
David Rogersfcea3252020-04-07 14:56:35 -0700377 unsigned(start_address));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800378
379 // Entries must start at addresses which are aligned on a multiple of
380 // Entry::kMinAlignmentBytes. However, that multiple can vary between entries.
381 // When scanning, we don't have an entry to tell us what the current alignment
382 // is, so the minimum alignment is used to be exhaustive.
383 for (Address address = AlignUp(start_address, Entry::kMinAlignmentBytes);
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700384 sectors_.AddressInSector(sector, address);
Alexei Frolovd4adf912020-02-21 13:29:15 -0800385 address += Entry::kMinAlignmentBytes) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800386 uint32_t magic;
David Rogersfcea3252020-04-07 14:56:35 -0700387 StatusWithSize read_result =
388 partition_.Read(address, as_writable_bytes(span(&magic, 1)));
389 if (!read_result.ok()) {
390 continue;
391 }
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800392 if (formats_.KnownMagic(magic)) {
David Rogersfcea3252020-04-07 14:56:35 -0700393 DBG("Found entry magic at address %u", unsigned(address));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800394 *next_entry_address = address;
395 return Status::OK;
396 }
397 }
398
399 return Status::NOT_FOUND;
400}
401
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800402StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800403 span<byte> value_buffer,
404 size_t offset_bytes) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700405 TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800406
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700407 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700408 TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800409
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700410 return Get(key, metadata, value_buffer, offset_bytes);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800411}
412
Wyatt Heplerfac81132020-02-27 17:26:33 -0800413Status KeyValueStore::PutBytes(string_view key, span<const byte> value) {
David Rogers9abe3c72020-03-24 19:03:13 -0700414 TRY(CheckWriteOperation(key));
David Rogers9fc78b82020-06-12 13:56:12 -0700415 DBG("Writing key/value; key length=%u, value length=%u",
416 unsigned(key.size()),
417 unsigned(value.size()));
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800418
Wyatt Hepler5406a672020-02-18 15:42:38 -0800419 if (Entry::size(partition_, key, value) > partition_.sector_size_bytes()) {
David Rogers9fc78b82020-06-12 13:56:12 -0700420 DBG("%u B value with %u B key cannot fit in one sector",
421 unsigned(value.size()),
422 unsigned(key.size()));
Wyatt Hepler5406a672020-02-18 15:42:38 -0800423 return Status::INVALID_ARGUMENT;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800424 }
425
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700426 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700427 Status status = FindEntry(key, &metadata);
Wyatt Hepler2d401692020-02-13 16:01:23 -0800428
429 if (status.ok()) {
David Rogersf56131c2020-03-04 10:19:22 -0800430 // TODO: figure out logging how to support multiple addresses.
David Rogers9fc78b82020-06-12 13:56:12 -0700431 DBG("Overwriting entry for key 0x%08x in %u sectors including %u",
432 unsigned(metadata.hash()),
433 unsigned(metadata.addresses().size()),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700434 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700435 return WriteEntryForExistingKey(metadata, EntryState::kValid, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800436 }
David Rogers2761aeb2020-01-31 17:09:00 -0800437
Wyatt Hepler2d401692020-02-13 16:01:23 -0800438 if (status == Status::NOT_FOUND) {
439 return WriteEntryForNewKey(key, value);
440 }
441
442 return status;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800443}
444
445Status KeyValueStore::Delete(string_view key) {
David Rogers9abe3c72020-03-24 19:03:13 -0700446 TRY(CheckWriteOperation(key));
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800447
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700448 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700449 TRY(FindExisting(key, &metadata));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800450
David Rogersf56131c2020-03-04 10:19:22 -0800451 // TODO: figure out logging how to support multiple addresses.
David Rogers9fc78b82020-06-12 13:56:12 -0700452 DBG("Writing tombstone for key 0x%08x in %u sectors including %u",
453 unsigned(metadata.hash()),
454 unsigned(metadata.addresses().size()),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700455 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700456 return WriteEntryForExistingKey(metadata, EntryState::kDeleted, key, {});
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800457}
458
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800459void KeyValueStore::Item::ReadKey() {
460 key_buffer_.fill('\0');
461
462 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700463 if (kvs_.ReadEntry(*iterator_, entry).ok()) {
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800464 entry.ReadKey(key_buffer_);
465 }
466}
467
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800468KeyValueStore::iterator& KeyValueStore::iterator::operator++() {
469 // Skip to the next entry that is valid (not deleted).
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700470 while (++item_.iterator_ != item_.kvs_.entry_cache_.end() &&
Wyatt Hepler02946272020-03-18 10:36:22 -0700471 item_.iterator_->state() != EntryState::kValid) {
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800472 }
473 return *this;
474}
475
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800476KeyValueStore::iterator KeyValueStore::begin() const {
Wyatt Heplerbfc6a522020-04-01 16:30:24 -0700477 internal::EntryCache::const_iterator cache_iterator = entry_cache_.begin();
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800478 // Skip over any deleted entries at the start of the descriptor list.
Wyatt Hepler02946272020-03-18 10:36:22 -0700479 while (cache_iterator != entry_cache_.end() &&
480 cache_iterator->state() != EntryState::kValid) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700481 ++cache_iterator;
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800482 }
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700483 return iterator(*this, cache_iterator);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800484}
485
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700486StatusWithSize KeyValueStore::ValueSize(string_view key) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700487 TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Heplered163b02020-02-03 17:49:32 -0800488
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700489 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700490 TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800491
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700492 return ValueSize(metadata);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800493}
Wyatt Heplered163b02020-02-03 17:49:32 -0800494
David Rogers98fea472020-04-01 15:43:48 -0700495Status KeyValueStore::ReadEntry(const EntryMetadata& metadata,
496 Entry& entry) const {
497 // Try to read an entry
498 Status read_result = Status::DATA_LOSS;
499 for (Address address : metadata.addresses()) {
500 read_result = Entry::Read(partition_, address, formats_, &entry);
501 if (read_result.ok()) {
502 return read_result;
503 }
504
505 // Found a bad address. Set the sector as corrupt.
506 error_detected_ = true;
507 sectors_.FromAddress(address).mark_corrupt();
508 }
509
510 ERR("No valid entries for key. Data has been lost!");
511 return read_result;
512}
513
514Status KeyValueStore::FindEntry(string_view key,
515 EntryMetadata* found_entry) const {
516 StatusWithSize find_result =
517 entry_cache_.Find(partition_, sectors_, formats_, key, found_entry);
518
519 if (find_result.size() > 0u) {
520 error_detected_ = true;
521 }
522 return find_result.status();
523}
524
525Status KeyValueStore::FindExisting(string_view key,
526 EntryMetadata* metadata) const {
527 Status status = FindEntry(key, metadata);
528
529 // If the key's hash collides with an existing key or if the key is deleted,
530 // treat it as if it is not in the KVS.
531 if (status == Status::ALREADY_EXISTS ||
532 (status.ok() && metadata->state() == EntryState::kDeleted)) {
533 return Status::NOT_FOUND;
534 }
535 return status;
536}
537
Wyatt Heplerfac81132020-02-27 17:26:33 -0800538StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700539 const EntryMetadata& metadata,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800540 span<std::byte> value_buffer,
541 size_t offset_bytes) const {
542 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700543
544 TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800545
546 StatusWithSize result = entry.ReadValue(value_buffer, offset_bytes);
547 if (result.ok() && options_.verify_on_read && offset_bytes == 0u) {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800548 Status verify_result =
549 entry.VerifyChecksum(key, value_buffer.first(result.size()));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800550 if (!verify_result.ok()) {
551 std::memset(value_buffer.data(), 0, result.size());
552 return StatusWithSize(verify_result, 0);
553 }
554
555 return StatusWithSize(verify_result, result.size());
556 }
557 return result;
Wyatt Heplered163b02020-02-03 17:49:32 -0800558}
559
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800560Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800561 void* value,
562 size_t size_bytes) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700563 TRY(CheckWriteOperation(key));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800564
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700565 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700566 TRY(FindExisting(key, &metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800567
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700568 return FixedSizeGet(key, metadata, value, size_bytes);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800569}
570
571Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700572 const EntryMetadata& metadata,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800573 void* value,
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800574 size_t size_bytes) const {
575 // Ensure that the size of the stored value matches the size of the type.
576 // Otherwise, report error. This check avoids potential memory corruption.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700577 TRY_ASSIGN(const size_t actual_size, ValueSize(metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800578
579 if (actual_size != size_bytes) {
David Rogers9fc78b82020-06-12 13:56:12 -0700580 DBG("Requested %u B read, but value is %u B",
581 unsigned(size_bytes),
582 unsigned(actual_size));
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800583 return Status::INVALID_ARGUMENT;
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800584 }
Wyatt Heplerfac81132020-02-27 17:26:33 -0800585
586 StatusWithSize result =
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700587 Get(key, metadata, span(static_cast<byte*>(value), size_bytes), 0);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800588
589 return result.status();
590}
591
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700592StatusWithSize KeyValueStore::ValueSize(const EntryMetadata& metadata) const {
Wyatt Heplerfac81132020-02-27 17:26:33 -0800593 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700594 TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800595
596 return StatusWithSize(entry.value_size());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800597}
598
David Rogers9abe3c72020-03-24 19:03:13 -0700599Status KeyValueStore::CheckWriteOperation(string_view key) const {
Wyatt Hepleracaacf92020-01-24 10:58:30 -0800600 if (InvalidKey(key)) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800601 return Status::INVALID_ARGUMENT;
602 }
David Rogers9abe3c72020-03-24 19:03:13 -0700603
604 // For normal write operation the KVS must be fully ready.
Wyatt Heplerd2298282020-02-20 17:12:45 -0800605 if (!initialized()) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800606 return Status::FAILED_PRECONDITION;
607 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800608 return Status::OK;
609}
610
David Rogers9abe3c72020-03-24 19:03:13 -0700611Status KeyValueStore::CheckReadOperation(string_view key) const {
612 if (InvalidKey(key)) {
613 return Status::INVALID_ARGUMENT;
614 }
615
616 // Operations that are explicitly read-only can be done after init() has been
617 // called but not fully ready (when needing maintenance).
618 if (initialized_ == InitializationState::kNotInitialized) {
619 return Status::FAILED_PRECONDITION;
620 }
621 return Status::OK;
622}
623
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700624Status KeyValueStore::WriteEntryForExistingKey(EntryMetadata& metadata,
625 EntryState new_state,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800626 string_view key,
627 span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700628 // Read the original entry to get the size for sector accounting purposes.
629 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700630 TRY(ReadEntry(metadata, entry));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800631
David Rogersc0104462020-05-08 15:50:23 -0700632 return WriteEntry(key, value, new_state, &metadata, &entry);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800633}
634
635Status KeyValueStore::WriteEntryForNewKey(string_view key,
636 span<const byte> value) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700637 if (entry_cache_.full()) {
David Rogers9fc78b82020-06-12 13:56:12 -0700638 WRN("KVS full: trying to store a new entry, but can't. Have %u entries",
639 unsigned(entry_cache_.total_entries()));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800640 return Status::RESOURCE_EXHAUSTED;
641 }
642
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700643 return WriteEntry(key, value, EntryState::kValid);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700644}
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800645
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700646Status KeyValueStore::WriteEntry(string_view key,
647 span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700648 EntryState new_state,
649 EntryMetadata* prior_metadata,
David Rogersc0104462020-05-08 15:50:23 -0700650 const Entry* prior_entry) {
David Rogersc0104462020-05-08 15:50:23 -0700651 // If new entry and prior entry have matching value size, state, and checksum,
652 // check if the values match. Directly compare the prior and new values
653 // because the checksum can not be depended on to establish equality, it can
654 // only be depended on to establish inequality.
David Rogersd50eb1c2020-05-12 17:46:36 -0700655 if (prior_entry != nullptr && prior_entry->value_size() == value.size() &&
David Rogersc0104462020-05-08 15:50:23 -0700656 prior_metadata->state() == new_state &&
David Rogersc0104462020-05-08 15:50:23 -0700657 prior_entry->ValueMatches(value).ok()) {
David Rogersd50eb1c2020-05-12 17:46:36 -0700658 // The new value matches the prior value, don't need to write anything. Just
659 // keep the existing entry.
David Rogersc0104462020-05-08 15:50:23 -0700660 DBG("Write for key 0x%08x with matching value skipped",
661 unsigned(prior_metadata->hash()));
662 return Status::OK;
663 }
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700664
665 // List of addresses for sectors with space for this entry.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700666 Address* reserved_addresses = entry_cache_.TempReservedAddressesForWrite();
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700667
David Rogers31b358b2020-04-15 05:00:50 -0700668 // Find addresses to write the entry to. This may involve garbage collecting
669 // one or more sectors.
David Rogersc0104462020-05-08 15:50:23 -0700670 const size_t entry_size = Entry::size(partition_, key, value);
David Rogers31b358b2020-04-15 05:00:50 -0700671 TRY(GetAddressesForWrite(reserved_addresses, entry_size));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700672
673 // Write the entry at the first address that was found.
David Rogersd50eb1c2020-05-12 17:46:36 -0700674 Entry entry = CreateEntry(reserved_addresses[0], key, value, new_state);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700675 TRY(AppendEntry(entry, key, value));
676
677 // After writing the first entry successfully, update the key descriptors.
678 // Once a single new the entry is written, the old entries are invalidated.
David Rogersc0104462020-05-08 15:50:23 -0700679 size_t prior_size = prior_entry != nullptr ? prior_entry->size() : 0;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700680 EntryMetadata new_metadata =
David Rogers31b358b2020-04-15 05:00:50 -0700681 CreateOrUpdateKeyDescriptor(entry, key, prior_metadata, prior_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700682
683 // Write the additional copies of the entry, if redundancy is greater than 1.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700684 for (size_t i = 1; i < redundancy(); ++i) {
685 entry.set_address(reserved_addresses[i]);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700686 TRY(AppendEntry(entry, key, value));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700687 new_metadata.AddNewAddress(reserved_addresses[i]);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700688 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800689 return Status::OK;
690}
691
David Rogers31b358b2020-04-15 05:00:50 -0700692KeyValueStore::EntryMetadata KeyValueStore::CreateOrUpdateKeyDescriptor(
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700693 const Entry& entry,
694 string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700695 EntryMetadata* prior_metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700696 size_t prior_size) {
697 // If there is no prior descriptor, create a new one.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700698 if (prior_metadata == nullptr) {
699 return entry_cache_.AddNew(entry.descriptor(key), entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800700 }
701
David Rogers31b358b2020-04-15 05:00:50 -0700702 return UpdateKeyDescriptor(
703 entry, entry.address(), prior_metadata, prior_size);
704}
705
706KeyValueStore::EntryMetadata KeyValueStore::UpdateKeyDescriptor(
707 const Entry& entry,
708 Address new_address,
709 EntryMetadata* prior_metadata,
710 size_t prior_size) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700711 // Remove valid bytes for the old entry and its copies, which are now stale.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700712 for (Address address : prior_metadata->addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700713 sectors_.FromAddress(address).RemoveValidBytes(prior_size);
David Rogersa2562b52020-03-05 15:30:05 -0800714 }
715
David Rogers31b358b2020-04-15 05:00:50 -0700716 prior_metadata->Reset(entry.descriptor(prior_metadata->hash()), new_address);
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700717 return *prior_metadata;
David Rogersa2562b52020-03-05 15:30:05 -0800718}
719
David Rogers31b358b2020-04-15 05:00:50 -0700720Status KeyValueStore::GetAddressesForWrite(Address* write_addresses,
721 size_t write_size) {
722 for (size_t i = 0; i < redundancy(); i++) {
723 SectorDescriptor* sector;
724 TRY(GetSectorForWrite(&sector, write_size, span(write_addresses, i)));
725 write_addresses[i] = sectors_.NextWritableAddress(*sector);
726
727 DBG("Found space for entry in sector %u at address %u",
728 sectors_.Index(sector),
729 unsigned(write_addresses[i]));
730 }
731
732 return Status::OK;
733}
734
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700735// Finds a sector to use for writing a new entry to. Does automatic garbage
David Rogersa2562b52020-03-05 15:30:05 -0800736// collection if needed and allowed.
737//
738// OK: Sector found with needed space.
739// RESOURCE_EXHAUSTED: No sector available with the needed space.
740Status KeyValueStore::GetSectorForWrite(SectorDescriptor** sector,
741 size_t entry_size,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700742 span<const Address> reserved) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700743 Status result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800744
David Rogersf3884eb2020-03-08 19:21:40 -0700745 size_t gc_sector_count = 0;
David Rogersa2562b52020-03-05 15:30:05 -0800746 bool do_auto_gc = options_.gc_on_write != GargbageCollectOnWrite::kDisabled;
747
748 // Do garbage collection as needed, so long as policy allows.
749 while (result == Status::RESOURCE_EXHAUSTED && do_auto_gc) {
750 if (options_.gc_on_write == GargbageCollectOnWrite::kOneSector) {
751 // If GC config option is kOneSector clear the flag to not do any more
752 // GC after this try.
753 do_auto_gc = false;
754 }
755 // Garbage collect and then try again to find the best sector.
David Rogers9abe3c72020-03-24 19:03:13 -0700756 Status gc_status = GarbageCollect(reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800757 if (!gc_status.ok()) {
758 if (gc_status == Status::NOT_FOUND) {
759 // Not enough space, and no reclaimable bytes, this KVS is full!
760 return Status::RESOURCE_EXHAUSTED;
761 }
762 return gc_status;
763 }
764
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700765 result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersf3884eb2020-03-08 19:21:40 -0700766
767 gc_sector_count++;
768 // Allow total sectors + 2 number of GC cycles so that once reclaimable
769 // bytes in all the sectors have been reclaimed can try and free up space by
770 // moving entries for keys other than the one being worked on in to sectors
771 // that have copies of the key trying to be written.
772 if (gc_sector_count > (partition_.sector_count() + 2)) {
773 ERR("Did more GC sectors than total sectors!!!!");
774 return Status::RESOURCE_EXHAUSTED;
775 }
David Rogersa2562b52020-03-05 15:30:05 -0800776 }
777
778 if (!result.ok()) {
David Rogers9fc78b82020-06-12 13:56:12 -0700779 WRN("Unable to find sector to write %u B", unsigned(entry_size));
David Rogersa2562b52020-03-05 15:30:05 -0800780 }
781 return result;
782}
783
David Rogers9abe3c72020-03-24 19:03:13 -0700784Status KeyValueStore::MarkSectorCorruptIfNotOk(Status status,
785 SectorDescriptor* sector) {
786 if (!status.ok()) {
787 DBG(" Sector %u corrupt", sectors_.Index(sector));
788 sector->mark_corrupt();
789 error_detected_ = true;
790 }
791 return status;
792}
793
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700794Status KeyValueStore::AppendEntry(const Entry& entry,
David Rogersa2562b52020-03-05 15:30:05 -0800795 string_view key,
796 span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700797 const StatusWithSize result = entry.Write(key, value);
David Rogersa2562b52020-03-05 15:30:05 -0800798
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700799 SectorDescriptor& sector = sectors_.FromAddress(entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800800
801 if (!result.ok()) {
David Rogers9fc78b82020-06-12 13:56:12 -0700802 ERR("Failed to write %u bytes at %#x. %u actually written",
803 unsigned(entry.size()),
804 unsigned(entry.address()),
805 unsigned(result.size()));
David Rogers9abe3c72020-03-24 19:03:13 -0700806 TRY(MarkSectorCorruptIfNotOk(result.status(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800807 }
808
809 if (options_.verify_on_write) {
David Rogers9abe3c72020-03-24 19:03:13 -0700810 TRY(MarkSectorCorruptIfNotOk(entry.VerifyChecksumInFlash(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800811 }
812
David Rogers98fea472020-04-01 15:43:48 -0700813 sector.RemoveWritableBytes(result.size());
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700814 sector.AddValidBytes(result.size());
David Rogersa2562b52020-03-05 15:30:05 -0800815 return Status::OK;
816}
817
David Rogers98fea472020-04-01 15:43:48 -0700818StatusWithSize KeyValueStore::CopyEntryToSector(Entry& entry,
819 SectorDescriptor* new_sector,
David Rogers31b358b2020-04-15 05:00:50 -0700820 Address new_address) {
David Rogers98fea472020-04-01 15:43:48 -0700821 const StatusWithSize result = entry.Copy(new_address);
822
823 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(result.status(), new_sector));
824
825 if (options_.verify_on_write) {
David Rogers31b358b2020-04-15 05:00:50 -0700826 Entry new_entry;
827 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(
828 Entry::Read(partition_, new_address, formats_, &new_entry),
829 new_sector));
830 // TODO: add test that catches doing the verify on the old entry.
831 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(new_entry.VerifyChecksumInFlash(),
832 new_sector));
David Rogers98fea472020-04-01 15:43:48 -0700833 }
834 // Entry was written successfully; update descriptor's address and the sector
835 // descriptors to reflect the new entry.
836 new_sector->RemoveWritableBytes(result.size());
837 new_sector->AddValidBytes(result.size());
838
839 return result;
840}
841
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700842Status KeyValueStore::RelocateEntry(const EntryMetadata& metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700843 KeyValueStore::Address& address,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700844 span<const Address> reserved_addresses) {
David Rogersa2562b52020-03-05 15:30:05 -0800845 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700846 TRY(ReadEntry(metadata, entry));
David Rogersa2562b52020-03-05 15:30:05 -0800847
848 // Find a new sector for the entry and write it to the new location. For
849 // relocation the find should not not be a sector already containing the key
850 // but can be the always empty sector, since this is part of the GC process
851 // that will result in a new empty sector. Also find a sector that does not
852 // have reclaimable space (mostly for the full GC, where that would result in
853 // an immediate extra relocation).
854 SectorDescriptor* new_sector;
855
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700856 TRY(sectors_.FindSpaceDuringGarbageCollection(
857 &new_sector, entry.size(), metadata.addresses(), reserved_addresses));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700858
David Rogers31b358b2020-04-15 05:00:50 -0700859 Address new_address = sectors_.NextWritableAddress(*new_sector);
David Rogers98fea472020-04-01 15:43:48 -0700860 TRY_ASSIGN(const size_t result_size,
861 CopyEntryToSector(entry, new_sector, new_address));
862 sectors_.FromAddress(address).RemoveValidBytes(result_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700863 address = new_address;
David Rogersa2562b52020-03-05 15:30:05 -0800864
865 return Status::OK;
866}
867
David Rogers9abe3c72020-03-24 19:03:13 -0700868Status KeyValueStore::FullMaintenance() {
869 if (initialized_ == InitializationState::kNotInitialized) {
870 return Status::FAILED_PRECONDITION;
871 }
872
Armando Montanez17083bb2020-04-24 10:18:26 -0700873 // Full maintenance can be a potentially heavy operation, and should be
874 // relatively infrequent, so log start/end at INFO level.
875 INF("Beginning full maintenance");
David Rogers98fea472020-04-01 15:43:48 -0700876 CheckForErrors();
David Rogers9abe3c72020-03-24 19:03:13 -0700877
878 if (error_detected_) {
879 TRY(Repair());
880 }
David Rogers35c3f842020-04-22 15:34:05 -0700881 StatusWithSize update_status = UpdateEntriesToPrimaryFormat();
882 Status overall_status = update_status.status();
883
David Rogers31b358b2020-04-15 05:00:50 -0700884 // Make sure all the entries are on the primary format.
Armando Montanez17083bb2020-04-24 10:18:26 -0700885 if (!overall_status.ok()) {
886 ERR("Failed to update all entries to the primary format");
887 }
David Rogers31b358b2020-04-15 05:00:50 -0700888
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700889 SectorDescriptor* sector = sectors_.last_new();
David Rogerscd87c322020-02-27 14:04:08 -0800890
David Rogers35c3f842020-04-22 15:34:05 -0700891 // Calculate number of bytes for the threshold.
892 size_t threshold_bytes =
893 (partition_.size_bytes() * kGcUsageThresholdPercentage) / 100;
894
895 // Is bytes in use over the threshold.
896 StorageStats stats = GetStorageStats();
897 bool over_usage_threshold = stats.in_use_bytes > threshold_bytes;
898 bool force_gc = over_usage_threshold || (update_status.size() > 0);
899
David Rogerscd87c322020-02-27 14:04:08 -0800900 // TODO: look in to making an iterator method for cycling through sectors
901 // starting from last_new_sector_.
Armando Montanez17083bb2020-04-24 10:18:26 -0700902 Status gc_status;
David Rogerscd87c322020-02-27 14:04:08 -0800903 for (size_t j = 0; j < sectors_.size(); j++) {
904 sector += 1;
905 if (sector == sectors_.end()) {
906 sector = sectors_.begin();
907 }
908
David Rogers35c3f842020-04-22 15:34:05 -0700909 if (sector->RecoverableBytes(partition_.sector_size_bytes()) > 0 &&
910 (force_gc || sector->valid_bytes() == 0)) {
Armando Montanez17083bb2020-04-24 10:18:26 -0700911 gc_status = GarbageCollectSector(*sector, {});
912 if (!gc_status.ok()) {
913 ERR("Failed to garbage collect all sectors");
914 break;
915 }
David Rogerscd87c322020-02-27 14:04:08 -0800916 }
917 }
Armando Montanez17083bb2020-04-24 10:18:26 -0700918 if (overall_status.ok()) {
919 overall_status = gc_status;
920 }
David Rogerscd87c322020-02-27 14:04:08 -0800921
Armando Montanez17083bb2020-04-24 10:18:26 -0700922 if (overall_status.ok()) {
923 INF("Full maintenance complete");
924 } else {
925 ERR("Full maintenance finished with some errors");
926 }
927 return overall_status;
David Rogerscd87c322020-02-27 14:04:08 -0800928}
929
David Rogers0f8a1bb2020-04-21 18:50:19 -0700930Status KeyValueStore::PartialMaintenance() {
David Rogers9abe3c72020-03-24 19:03:13 -0700931 if (initialized_ == InitializationState::kNotInitialized) {
932 return Status::FAILED_PRECONDITION;
933 }
934
David Rogers0f8a1bb2020-04-21 18:50:19 -0700935 CheckForErrors();
David Rogersfcea3252020-04-07 14:56:35 -0700936 // Do automatic repair, if KVS options allow for it.
937 if (error_detected_ && options_.recovery != ErrorRecovery::kManual) {
938 TRY(Repair());
939 }
David Rogers0f8a1bb2020-04-21 18:50:19 -0700940 return GarbageCollect(span<const Address>());
941}
942
943Status KeyValueStore::GarbageCollect(span<const Address> reserved_addresses) {
944 DBG("Garbage Collect a single sector");
945 for (Address address : reserved_addresses) {
946 DBG(" Avoid address %u", unsigned(address));
947 }
David Rogersfcea3252020-04-07 14:56:35 -0700948
David Rogersa12786b2020-01-31 16:02:33 -0800949 // Step 1: Find the sector to garbage collect
David Rogersc9d545e2020-03-11 17:47:43 -0700950 SectorDescriptor* sector_to_gc =
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700951 sectors_.FindSectorToGarbageCollect(reserved_addresses);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800952
David Rogersa12786b2020-01-31 16:02:33 -0800953 if (sector_to_gc == nullptr) {
David Rogersa2562b52020-03-05 15:30:05 -0800954 // Nothing to GC.
955 return Status::NOT_FOUND;
David Rogersa12786b2020-01-31 16:02:33 -0800956 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800957
David Rogersc9d545e2020-03-11 17:47:43 -0700958 // Step 2: Garbage collect the selected sector.
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700959 return GarbageCollectSector(*sector_to_gc, reserved_addresses);
David Rogerscd87c322020-02-27 14:04:08 -0800960}
961
David Rogersf3884eb2020-03-08 19:21:40 -0700962Status KeyValueStore::RelocateKeyAddressesInSector(
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700963 SectorDescriptor& sector_to_gc,
David Rogersfcea3252020-04-07 14:56:35 -0700964 const EntryMetadata& metadata,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700965 span<const Address> reserved_addresses) {
966 for (FlashPartition::Address& address : metadata.addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700967 if (sectors_.AddressInSector(sector_to_gc, address)) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700968 DBG(" Relocate entry for Key 0x%08" PRIx32 ", sector %u",
969 metadata.hash(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700970 sectors_.Index(sectors_.FromAddress(address)));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700971 TRY(RelocateEntry(metadata, address, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700972 }
973 }
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700974
David Rogersf3884eb2020-03-08 19:21:40 -0700975 return Status::OK;
976};
977
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700978Status KeyValueStore::GarbageCollectSector(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700979 SectorDescriptor& sector_to_gc, span<const Address> reserved_addresses) {
David Rogers9abe3c72020-03-24 19:03:13 -0700980 DBG(" Garbage Collect sector %u", sectors_.Index(sector_to_gc));
David Rogersf3884eb2020-03-08 19:21:40 -0700981 // Step 1: Move any valid entries in the GC sector to other sectors
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700982 if (sector_to_gc.valid_bytes() != 0) {
David Rogers98fea472020-04-01 15:43:48 -0700983 for (EntryMetadata& metadata : entry_cache_) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700984 TRY(RelocateKeyAddressesInSector(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700985 sector_to_gc, metadata, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700986 }
987 }
988
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700989 if (sector_to_gc.valid_bytes() != 0) {
David Rogers67f4b6c2020-02-06 16:17:09 -0800990 ERR(" Failed to relocate valid entries from sector being garbage "
David Rogers9fc78b82020-06-12 13:56:12 -0700991 "collected, %u valid bytes remain",
992 unsigned(sector_to_gc.valid_bytes()));
Wyatt Heplerb7609542020-01-24 10:29:54 -0800993 return Status::INTERNAL;
994 }
995
David Rogerscd87c322020-02-27 14:04:08 -0800996 // Step 2: Reinitialize the sector
David Rogers9abe3c72020-03-24 19:03:13 -0700997 sector_to_gc.mark_corrupt();
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700998 TRY(partition_.Erase(sectors_.BaseAddress(sector_to_gc), 1));
999 sector_to_gc.set_writable_bytes(partition_.sector_size_bytes());
Wyatt Heplerb7609542020-01-24 10:29:54 -08001000
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001001 DBG(" Garbage Collect sector %u complete", sectors_.Index(sector_to_gc));
David Rogersa12786b2020-01-31 16:02:33 -08001002 return Status::OK;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -08001003}
1004
David Rogers35c3f842020-04-22 15:34:05 -07001005StatusWithSize KeyValueStore::UpdateEntriesToPrimaryFormat() {
1006 size_t entries_updated = 0;
David Rogers31b358b2020-04-15 05:00:50 -07001007 for (EntryMetadata& prior_metadata : entry_cache_) {
1008 Entry entry;
David Rogers35c3f842020-04-22 15:34:05 -07001009 TRY_WITH_SIZE(ReadEntry(prior_metadata, entry));
David Rogers31b358b2020-04-15 05:00:50 -07001010 if (formats_.primary().magic == entry.magic()) {
1011 // Ignore entries that are already on the primary format.
1012 continue;
1013 }
1014
1015 DBG("Updating entry 0x%08x from old format [0x%08x] to new format "
1016 "[0x%08x]",
1017 unsigned(prior_metadata.hash()),
1018 unsigned(entry.magic()),
1019 unsigned(formats_.primary().magic));
1020
David Rogers35c3f842020-04-22 15:34:05 -07001021 entries_updated++;
1022
David Rogers31b358b2020-04-15 05:00:50 -07001023 last_transaction_id_ += 1;
David Rogers35c3f842020-04-22 15:34:05 -07001024 TRY_WITH_SIZE(entry.Update(formats_.primary(), last_transaction_id_));
David Rogers31b358b2020-04-15 05:00:50 -07001025
1026 // List of addresses for sectors with space for this entry.
1027 Address* reserved_addresses = entry_cache_.TempReservedAddressesForWrite();
1028
1029 // Find addresses to write the entry to. This may involve garbage collecting
1030 // one or more sectors.
David Rogers35c3f842020-04-22 15:34:05 -07001031 TRY_WITH_SIZE(GetAddressesForWrite(reserved_addresses, entry.size()));
David Rogers31b358b2020-04-15 05:00:50 -07001032
David Rogers35c3f842020-04-22 15:34:05 -07001033 TRY_WITH_SIZE(
1034 CopyEntryToSector(entry,
David Rogers31b358b2020-04-15 05:00:50 -07001035 &sectors_.FromAddress(reserved_addresses[0]),
1036 reserved_addresses[0]));
1037
1038 // After writing the first entry successfully, update the key descriptors.
1039 // Once a single new the entry is written, the old entries are invalidated.
1040 EntryMetadata new_metadata = UpdateKeyDescriptor(
1041 entry, reserved_addresses[0], &prior_metadata, entry.size());
1042
1043 // Write the additional copies of the entry, if redundancy is greater
1044 // than 1.
1045 for (size_t i = 1; i < redundancy(); ++i) {
David Rogers35c3f842020-04-22 15:34:05 -07001046 TRY_WITH_SIZE(
1047 CopyEntryToSector(entry,
David Rogers31b358b2020-04-15 05:00:50 -07001048 &sectors_.FromAddress(reserved_addresses[i]),
1049 reserved_addresses[i]));
1050 new_metadata.AddNewAddress(reserved_addresses[i]);
1051 }
1052 }
David Rogers35c3f842020-04-22 15:34:05 -07001053
1054 return StatusWithSize(entries_updated);
David Rogers31b358b2020-04-15 05:00:50 -07001055}
1056
David Rogers9abe3c72020-03-24 19:03:13 -07001057// Add any missing redundant entries/copies for a key.
1058Status KeyValueStore::AddRedundantEntries(EntryMetadata& metadata) {
David Rogers9abe3c72020-03-24 19:03:13 -07001059 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -07001060 TRY(ReadEntry(metadata, entry));
David Rogers9abe3c72020-03-24 19:03:13 -07001061 TRY(entry.VerifyChecksumInFlash());
1062
David Rogers0f8a1bb2020-04-21 18:50:19 -07001063 while (metadata.addresses().size() < redundancy()) {
1064 SectorDescriptor* new_sector;
1065 TRY(GetSectorForWrite(&new_sector, entry.size(), metadata.addresses()));
David Rogers9abe3c72020-03-24 19:03:13 -07001066
David Rogers31b358b2020-04-15 05:00:50 -07001067 Address new_address = sectors_.NextWritableAddress(*new_sector);
David Rogers98fea472020-04-01 15:43:48 -07001068 TRY(CopyEntryToSector(entry, new_sector, new_address));
David Rogers9abe3c72020-03-24 19:03:13 -07001069
1070 metadata.AddNewAddress(new_address);
1071 }
1072 return Status::OK;
1073}
1074
1075Status KeyValueStore::RepairCorruptSectors() {
1076 // Try to GC each corrupt sector, even if previous sectors fail. If GC of a
1077 // sector failed on the first pass, then do a second pass, since a later
1078 // sector might have cleared up space or otherwise unblocked the earlier
1079 // failed sector.
1080 Status repair_status = Status::OK;
1081
1082 size_t loop_count = 0;
1083 do {
1084 loop_count++;
1085 // Error of RESOURCE_EXHAUSTED indicates no space found for relocation.
1086 // Reset back to OK for the next pass.
1087 if (repair_status == Status::RESOURCE_EXHAUSTED) {
1088 repair_status = Status::OK;
1089 }
1090
1091 DBG(" Pass %u", unsigned(loop_count));
1092 for (SectorDescriptor& sector : sectors_) {
1093 if (sector.corrupt()) {
1094 DBG(" Found sector %u with corruption", sectors_.Index(sector));
1095 Status sector_status = GarbageCollectSector(sector, {});
1096 if (sector_status.ok()) {
1097 error_stats_.corrupt_sectors_recovered += 1;
1098 } else if (repair_status.ok() ||
1099 repair_status == Status::RESOURCE_EXHAUSTED) {
1100 repair_status = sector_status;
1101 }
1102 }
1103 }
1104 DBG(" Pass %u complete", unsigned(loop_count));
1105 } while (!repair_status.ok() && loop_count < 2);
1106
1107 return repair_status;
1108}
1109
1110Status KeyValueStore::EnsureFreeSectorExists() {
1111 Status repair_status = Status::OK;
1112 bool empty_sector_found = false;
1113
1114 DBG(" Find empty sector");
1115 for (SectorDescriptor& sector : sectors_) {
1116 if (sector.Empty(partition_.sector_size_bytes())) {
1117 empty_sector_found = true;
1118 DBG(" Empty sector found");
1119 break;
1120 }
1121 }
1122 if (empty_sector_found == false) {
1123 DBG(" No empty sector found, attempting to GC a free sector");
1124 Status sector_status = GarbageCollect(span<const Address, 0>());
1125 if (repair_status.ok() && !sector_status.ok()) {
1126 DBG(" Unable to free an empty sector");
1127 repair_status = sector_status;
1128 }
1129 }
1130
1131 return repair_status;
1132}
1133
1134Status KeyValueStore::EnsureEntryRedundancy() {
1135 Status repair_status = Status::OK;
1136
1137 if (redundancy() == 1) {
David Rogers98fea472020-04-01 15:43:48 -07001138 DBG(" Redundancy not in use, nothting to check");
David Rogers9abe3c72020-03-24 19:03:13 -07001139 return Status::OK;
1140 }
1141
David Rogers98fea472020-04-01 15:43:48 -07001142 DBG(" Write any needed additional duplicate copies of keys to fulfill %u"
1143 " redundancy",
David Rogers9abe3c72020-03-24 19:03:13 -07001144 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -07001145 for (EntryMetadata& metadata : entry_cache_) {
David Rogers9abe3c72020-03-24 19:03:13 -07001146 if (metadata.addresses().size() >= redundancy()) {
1147 continue;
1148 }
1149
1150 DBG(" Key with %u of %u copies found, adding missing copies",
1151 unsigned(metadata.addresses().size()),
1152 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -07001153 Status fill_status = AddRedundantEntries(metadata);
David Rogers9abe3c72020-03-24 19:03:13 -07001154 if (fill_status.ok()) {
1155 error_stats_.missing_redundant_entries_recovered += 1;
1156 DBG(" Key missing copies added");
1157 } else {
1158 DBG(" Failed to add key missing copies");
1159 if (repair_status.ok()) {
1160 repair_status = fill_status;
1161 }
1162 }
1163 }
1164
1165 return repair_status;
1166}
1167
David Rogersfcea3252020-04-07 14:56:35 -07001168Status KeyValueStore::FixErrors() {
1169 DBG("Fixing KVS errors");
David Rogers9abe3c72020-03-24 19:03:13 -07001170
1171 // Step 1: Garbage collect any sectors marked as corrupt.
David Rogersfcea3252020-04-07 14:56:35 -07001172 Status overall_status = RepairCorruptSectors();
David Rogers9abe3c72020-03-24 19:03:13 -07001173
1174 // Step 2: Make sure there is at least 1 empty sector. This needs to be a
1175 // seperate check of sectors from step 1, because a found empty sector might
1176 // get written to by a later GC that fails and does not result in a free
1177 // sector.
David Rogersfcea3252020-04-07 14:56:35 -07001178 Status repair_status = EnsureFreeSectorExists();
David Rogers9abe3c72020-03-24 19:03:13 -07001179 if (overall_status.ok()) {
1180 overall_status = repair_status;
1181 }
1182
1183 // Step 3: Make sure each stored key has the full number of redundant
1184 // entries.
1185 repair_status = EnsureEntryRedundancy();
1186 if (overall_status.ok()) {
1187 overall_status = repair_status;
1188 }
1189
1190 if (overall_status.ok()) {
1191 error_detected_ = false;
1192 initialized_ = InitializationState::kReady;
1193 }
1194 return overall_status;
1195}
1196
David Rogersfcea3252020-04-07 14:56:35 -07001197Status KeyValueStore::Repair() {
1198 // If errors have been detected, just reinit the KVS metadata. This does a
1199 // full deep error check and any needed repairs. Then repair any errors.
1200 INF("Starting KVS repair");
1201
1202 DBG("Reinitialize KVS metadata");
1203 InitializeMetadata();
1204
1205 return FixErrors();
1206}
1207
David Rogersd50eb1c2020-05-12 17:46:36 -07001208KeyValueStore::Entry KeyValueStore::CreateEntry(Address address,
1209 string_view key,
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -08001210 span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001211 EntryState state) {
Keir Mierle9e38b402020-02-21 13:06:21 -08001212 // Always bump the transaction ID when creating a new entry.
1213 //
1214 // Burning transaction IDs prevents inconsistencies between flash and memory
1215 // that which could happen if a write succeeds, but for some reason the read
1216 // and verify step fails. Here's how this would happen:
1217 //
1218 // 1. The entry is written but for some reason the flash reports failure OR
1219 // The write succeeds, but the read / verify operation fails.
1220 // 2. The transaction ID is NOT incremented, because of the failure
1221 // 3. (later) A new entry is written, re-using the transaction ID (oops)
1222 //
David Rogersd50eb1c2020-05-12 17:46:36 -07001223 // By always burning transaction IDs, the above problem can't happen.
1224 last_transaction_id_ += 1;
Keir Mierle9e38b402020-02-21 13:06:21 -08001225
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001226 if (state == EntryState::kDeleted) {
Wyatt Hepler7465be32020-02-21 15:30:53 -08001227 return Entry::Tombstone(
David Rogersd50eb1c2020-05-12 17:46:36 -07001228 partition_, address, formats_.primary(), key, last_transaction_id_);
Wyatt Hepler1fc11042020-02-19 17:17:51 -08001229 }
David Rogersd50eb1c2020-05-12 17:46:36 -07001230 return Entry::Valid(partition_,
1231 address,
1232 formats_.primary(),
1233 key,
1234 value,
1235 last_transaction_id_);
Wyatt Heplerd2298282020-02-20 17:12:45 -08001236}
1237
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001238void KeyValueStore::LogDebugInfo() const {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001239 const size_t sector_size_bytes = partition_.sector_size_bytes();
1240 DBG("====================== KEY VALUE STORE DUMP =========================");
1241 DBG(" ");
1242 DBG("Flash partition:");
David Rogers9fc78b82020-06-12 13:56:12 -07001243 DBG(" Sector count = %u", unsigned(partition_.sector_count()));
1244 DBG(" Sector max count = %u", unsigned(sectors_.max_size()));
1245 DBG(" Sectors in use = %u", unsigned(sectors_.size()));
1246 DBG(" Sector size = %u", unsigned(sector_size_bytes));
1247 DBG(" Total size = %u", unsigned(partition_.size_bytes()));
1248 DBG(" Alignment = %u", unsigned(partition_.alignment_bytes()));
Keir Mierle8c352dc2020-02-02 13:58:19 -08001249 DBG(" ");
1250 DBG("Key descriptors:");
David Rogers9fc78b82020-06-12 13:56:12 -07001251 DBG(" Entry count = %u", unsigned(entry_cache_.total_entries()));
1252 DBG(" Max entry count = %u", unsigned(entry_cache_.max_entries()));
Keir Mierle8c352dc2020-02-02 13:58:19 -08001253 DBG(" ");
1254 DBG(" # hash version address address (hex)");
Armando Montanez888370d2020-05-01 18:29:22 -07001255 size_t count = 0;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001256 for (const EntryMetadata& metadata : entry_cache_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001257 DBG(" |%3zu: | %8zx |%8zu | %8zu | %8zx",
Armando Montanez888370d2020-05-01 18:29:22 -07001258 count++,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001259 size_t(metadata.hash()),
1260 size_t(metadata.transaction_id()),
1261 size_t(metadata.first_address()),
1262 size_t(metadata.first_address()));
Keir Mierle8c352dc2020-02-02 13:58:19 -08001263 }
1264 DBG(" ");
1265
1266 DBG("Sector descriptors:");
1267 DBG(" # tail free valid has_space");
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001268 for (const SectorDescriptor& sd : sectors_) {
1269 DBG(" |%3u: | %8zu |%8zu | %s",
1270 sectors_.Index(sd),
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001271 size_t(sd.writable_bytes()),
1272 sd.valid_bytes(),
1273 sd.writable_bytes() ? "YES" : "");
Keir Mierle8c352dc2020-02-02 13:58:19 -08001274 }
1275 DBG(" ");
1276
1277 // TODO: This should stop logging after some threshold.
1278 // size_t dumped_bytes = 0;
1279 DBG("Sector raw data:");
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001280 for (size_t sector_id = 0; sector_id < sectors_.size(); ++sector_id) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001281 // Read sector data. Yes, this will blow the stack on embedded.
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001282 std::array<byte, 500> raw_sector_data; // TODO!!!
Keir Mierle8c352dc2020-02-02 13:58:19 -08001283 StatusWithSize sws =
1284 partition_.Read(sector_id * sector_size_bytes, raw_sector_data);
David Rogers9fc78b82020-06-12 13:56:12 -07001285 DBG("Read: %u bytes", unsigned(sws.size()));
Keir Mierle8c352dc2020-02-02 13:58:19 -08001286
1287 DBG(" base addr offs 0 1 2 3 4 5 6 7");
1288 for (size_t i = 0; i < sector_size_bytes; i += 8) {
1289 DBG(" %3zu %8zx %5zu | %02x %02x %02x %02x %02x %02x %02x %02x",
1290 sector_id,
1291 (sector_id * sector_size_bytes) + i,
1292 i,
1293 static_cast<unsigned int>(raw_sector_data[i + 0]),
1294 static_cast<unsigned int>(raw_sector_data[i + 1]),
1295 static_cast<unsigned int>(raw_sector_data[i + 2]),
1296 static_cast<unsigned int>(raw_sector_data[i + 3]),
1297 static_cast<unsigned int>(raw_sector_data[i + 4]),
1298 static_cast<unsigned int>(raw_sector_data[i + 5]),
1299 static_cast<unsigned int>(raw_sector_data[i + 6]),
1300 static_cast<unsigned int>(raw_sector_data[i + 7]));
1301
1302 // TODO: Fix exit condition.
1303 if (i > 128) {
1304 break;
1305 }
1306 }
1307 DBG(" ");
1308 }
1309
1310 DBG("////////////////////// KEY VALUE STORE DUMP END /////////////////////");
1311}
1312
David Rogerscf680ab2020-02-12 23:28:32 -08001313void KeyValueStore::LogSectors() const {
David Rogers9fc78b82020-06-12 13:56:12 -07001314 DBG("Sector descriptors: count %u", unsigned(sectors_.size()));
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001315 for (auto& sector : sectors_) {
David Rogers9fc78b82020-06-12 13:56:12 -07001316 DBG(" - Sector %u: valid %u, recoverable %u, free %u",
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001317 sectors_.Index(sector),
David Rogers9fc78b82020-06-12 13:56:12 -07001318 unsigned(sector.valid_bytes()),
1319 unsigned(sector.RecoverableBytes(partition_.sector_size_bytes())),
1320 unsigned(sector.writable_bytes()));
David Rogers50185ad2020-02-07 00:02:46 -08001321 }
1322}
1323
David Rogerscf680ab2020-02-12 23:28:32 -08001324void KeyValueStore::LogKeyDescriptor() const {
David Rogers9fc78b82020-06-12 13:56:12 -07001325 DBG("Key descriptors: count %u", unsigned(entry_cache_.total_entries()));
David Rogers9abe3c72020-03-24 19:03:13 -07001326 for (const EntryMetadata& metadata : entry_cache_) {
David Rogers9fc78b82020-06-12 13:56:12 -07001327 DBG(" - Key: %s, hash %#x, transaction ID %u, first address %#x",
Wyatt Hepler02946272020-03-18 10:36:22 -07001328 metadata.state() == EntryState::kDeleted ? "Deleted" : "Valid",
David Rogers9fc78b82020-06-12 13:56:12 -07001329 unsigned(metadata.hash()),
1330 unsigned(metadata.transaction_id()),
1331 unsigned(metadata.first_address()));
David Rogerscf680ab2020-02-12 23:28:32 -08001332 }
1333}
1334
Wyatt Hepler2ad60672020-01-21 08:00:16 -08001335} // namespace pw::kvs