blob: 50b025ba058dc56685779017c0834b8f0eb87eb0 [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 Rogers2e9e0c82020-02-13 15:06:06 -080066 ERR("KVS init failed: kMaxUsableSectors (=%zu) must be at least as "
67 "large as the number of sectors in the flash partition (=%zu)",
Wyatt Hepler38ce30f2020-02-19 11:48:31 -080068 sectors_.max_size(),
David Rogers2e9e0c82020-02-13 15:06:06 -080069 partition_.sector_count());
Wyatt Heplerad0a7932020-02-06 08:20:38 -080070 return Status::FAILED_PRECONDITION;
71 }
72
Keir Mierle8c352dc2020-02-02 13:58:19 -080073 const size_t sector_size_bytes = partition_.sector_size_bytes();
Keir Mierle8c352dc2020-02-02 13:58:19 -080074
David Rogers49766d92020-03-20 10:55:54 -070075 // TODO: investigate doing this as a static assert/compile-time check.
76 if (sector_size_bytes > SectorDescriptor::max_sector_size()) {
77 ERR("KVS init failed: sector_size_bytes (=%zu) is greater than maximum "
78 "allowed sector size (=%zu)",
79 sector_size_bytes,
80 SectorDescriptor::max_sector_size());
81 return Status::FAILED_PRECONDITION;
82 }
83
David Rogerscd134352020-04-17 19:37:15 -070084 Status metadata_result = InitializeMetadata();
David Rogersfcea3252020-04-07 14:56:35 -070085
86 if (!error_detected_) {
87 initialized_ = InitializationState::kReady;
88 } else {
David Rogers0f8a1bb2020-04-21 18:50:19 -070089 initialized_ = InitializationState::kNeedsMaintenance;
90
David Rogersfcea3252020-04-07 14:56:35 -070091 if (options_.recovery != ErrorRecovery::kManual) {
Armando Montanezf8419ae2020-04-21 10:03:05 -070092 size_t pre_fix_redundancy_errors =
93 error_stats_.missing_redundant_entries_recovered;
David Rogersfcea3252020-04-07 14:56:35 -070094 Status recovery_status = FixErrors();
95
96 if (recovery_status.ok()) {
David Rogerscd134352020-04-17 19:37:15 -070097 if (metadata_result == Status::OUT_OF_RANGE) {
Armando Montanezf8419ae2020-04-21 10:03:05 -070098 error_stats_.missing_redundant_entries_recovered =
99 pre_fix_redundancy_errors;
David Rogerscd134352020-04-17 19:37:15 -0700100 INF("KVS init: Redundancy level successfully updated");
101 } else {
102 WRN("KVS init: Corruption detected and fully repaired");
103 }
David Rogersfcea3252020-04-07 14:56:35 -0700104 initialized_ = InitializationState::kReady;
105 } else if (recovery_status == Status::RESOURCE_EXHAUSTED) {
106 WRN("KVS init: Unable to maintain required free sector");
David Rogersfcea3252020-04-07 14:56:35 -0700107 } else {
108 WRN("KVS init: Corruption detected and unable repair");
David Rogersfcea3252020-04-07 14:56:35 -0700109 }
110 } else {
111 WRN("KVS init: Corruption detected, no repair attempted due to options");
David Rogersfcea3252020-04-07 14:56:35 -0700112 }
113 }
114
115 INF("KeyValueStore init complete: active keys %zu, deleted keys %zu, sectors "
116 "%zu, logical sector size %zu bytes",
117 size(),
118 (entry_cache_.total_entries() - size()),
119 sectors_.size(),
120 partition_.sector_size_bytes());
121
122 // Report any corruption was not repaired.
123 if (error_detected_) {
124 WRN("KVS init: Corruption found but not repaired, KVS unavailable until "
125 "successful maintenance.");
126 return Status::DATA_LOSS;
127 }
128
129 return Status::OK;
130}
131
David Rogerscd134352020-04-17 19:37:15 -0700132Status KeyValueStore::InitializeMetadata() {
David Rogersfcea3252020-04-07 14:56:35 -0700133 const size_t sector_size_bytes = partition_.sector_size_bytes();
134
135 sectors_.Reset();
136 entry_cache_.Reset();
137
Keir Mierle8c352dc2020-02-02 13:58:19 -0800138 DBG("First pass: Read all entries from all sectors");
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800139 Address sector_address = 0;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800140
Alexei Frolovd4adf912020-02-21 13:29:15 -0800141 size_t total_corrupt_bytes = 0;
David Rogerscd134352020-04-17 19:37:15 -0700142 size_t corrupt_entries = 0;
David Rogers91627482020-02-27 17:38:12 -0800143 bool empty_sector_found = false;
David Rogerscd134352020-04-17 19:37:15 -0700144 size_t entry_copies_missing = 0;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800145
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800146 for (SectorDescriptor& sector : sectors_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800147 Address entry_address = sector_address;
148
Alexei Frolovd4adf912020-02-21 13:29:15 -0800149 size_t sector_corrupt_bytes = 0;
150
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800151 for (int num_entries_in_sector = 0; true; num_entries_in_sector++) {
David Rogersfcea3252020-04-07 14:56:35 -0700152 DBG("Load entry: sector=%u, entry#=%d, address=%u",
153 unsigned(sector_address),
Keir Mierle8c352dc2020-02-02 13:58:19 -0800154 num_entries_in_sector,
David Rogersfcea3252020-04-07 14:56:35 -0700155 unsigned(entry_address));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800156
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700157 if (!sectors_.AddressInSector(sector, entry_address)) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800158 DBG("Fell off end of sector; moving to the next sector");
159 break;
160 }
161
162 Address next_entry_address;
163 Status status = LoadEntry(entry_address, &next_entry_address);
164 if (status == Status::NOT_FOUND) {
165 DBG("Hit un-written data in sector; moving to the next sector");
166 break;
David Rogersfcea3252020-04-07 14:56:35 -0700167 } else if (!status.ok()) {
168 // The entry could not be read, indicating likely data corruption within
169 // the sector. Try to scan the remainder of the sector for other
170 // entries.
Alexei Frolovd4adf912020-02-21 13:29:15 -0800171
David Rogers49766d92020-03-20 10:55:54 -0700172 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800173 corrupt_entries++;
174
175 status = ScanForEntry(sector,
176 entry_address + Entry::kMinAlignmentBytes,
177 &next_entry_address);
David Rogersfcea3252020-04-07 14:56:35 -0700178 if (!status.ok()) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800179 // No further entries in this sector. Mark the remaining bytes in the
180 // sector as corrupt (since we can't reliably know the size of the
181 // corrupt entry).
182 sector_corrupt_bytes +=
183 sector_size_bytes - (entry_address - sector_address);
184 break;
185 }
186
Alexei Frolovd4adf912020-02-21 13:29:15 -0800187 sector_corrupt_bytes += next_entry_address - entry_address;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800188 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800189
190 // Entry loaded successfully; so get ready to load the next one.
191 entry_address = next_entry_address;
192
193 // Update of the number of writable bytes in this sector.
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800194 sector.set_writable_bytes(sector_size_bytes -
195 (entry_address - sector_address));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800196 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800197
Alexei Frolovd4adf912020-02-21 13:29:15 -0800198 if (sector_corrupt_bytes > 0) {
199 // If the sector contains corrupt data, prevent any further entries from
200 // being written to it by indicating that it has no space. This should
201 // also make it a decent GC candidate. Valid keys in the sector are still
202 // readable as normal.
David Rogers49766d92020-03-20 10:55:54 -0700203 sector.mark_corrupt();
204 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800205
206 WRN("Sector %u contains %zuB of corrupt data",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700207 sectors_.Index(sector),
Alexei Frolovd4adf912020-02-21 13:29:15 -0800208 sector_corrupt_bytes);
209 }
210
David Rogers91627482020-02-27 17:38:12 -0800211 if (sector.Empty(sector_size_bytes)) {
212 empty_sector_found = true;
213 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800214 sector_address += sector_size_bytes;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800215 total_corrupt_bytes += sector_corrupt_bytes;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800216 }
217
218 DBG("Second pass: Count valid bytes in each sector");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700219 Address newest_key = 0;
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800220
David Rogers98fea472020-04-01 15:43:48 -0700221 // For every valid entry, for each address, count the valid bytes in that
222 // sector. If the address fails to read, remove the address and mark the
223 // sector as corrupt. Track which entry has the newest transaction ID for
224 // initializing last_new_sector_.
225 for (EntryMetadata& metadata : entry_cache_) {
David Rogers49766d92020-03-20 10:55:54 -0700226 if (metadata.addresses().size() < redundancy()) {
David Rogers31b358b2020-04-15 05:00:50 -0700227 DBG("Key 0x%08x missing copies, has %u, needs %u",
228 unsigned(metadata.hash()),
229 unsigned(metadata.addresses().size()),
230 unsigned(redundancy()));
David Rogerscd134352020-04-17 19:37:15 -0700231 entry_copies_missing++;
David Rogers49766d92020-03-20 10:55:54 -0700232 }
David Rogers98fea472020-04-01 15:43:48 -0700233 size_t index = 0;
234 while (index < metadata.addresses().size()) {
235 Address address = metadata.addresses()[index];
David Rogersf56131c2020-03-04 10:19:22 -0800236 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700237
238 Status read_result = Entry::Read(partition_, address, formats_, &entry);
239
240 SectorDescriptor& sector = sectors_.FromAddress(address);
241
242 if (read_result.ok()) {
243 sector.AddValidBytes(entry.size());
244 index++;
245 } else {
246 corrupt_entries++;
247 total_corrupt_bytes += sector.writable_bytes();
248 error_detected_ = true;
249 sector.mark_corrupt();
250
251 // Remove the bad address and stay at this index. The removal
252 // replaces out the removed address with the back address so
253 // this index needs to be rechecked with the new address.
254 metadata.RemoveAddress(address);
255 }
David Rogersf56131c2020-03-04 10:19:22 -0800256 }
David Rogers98fea472020-04-01 15:43:48 -0700257
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700258 if (metadata.IsNewerThan(last_transaction_id_)) {
259 last_transaction_id_ = metadata.transaction_id();
260 newest_key = metadata.addresses().back();
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800261 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800262 }
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800263
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700264 sectors_.set_last_new_sector(newest_key);
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800265
David Rogers91627482020-02-27 17:38:12 -0800266 if (!empty_sector_found) {
David Rogers31b358b2020-04-15 05:00:50 -0700267 DBG("No empty sector found");
David Rogers9abe3c72020-03-24 19:03:13 -0700268 error_detected_ = true;
David Rogers91627482020-02-27 17:38:12 -0800269 }
270
David Rogerscd134352020-04-17 19:37:15 -0700271 if (entry_copies_missing > 0) {
272 bool other_errors = error_detected_;
273 error_detected_ = true;
274
Armando Montanez344814b2020-04-24 15:05:42 -0700275 if (!other_errors && entry_copies_missing == entry_cache_.total_entries()) {
David Rogerscd134352020-04-17 19:37:15 -0700276 INF("KVS configuration changed to redundancy of %zu total copies per key",
277 redundancy());
278 return Status::OUT_OF_RANGE;
279 }
David Rogers9abe3c72020-03-24 19:03:13 -0700280 }
David Rogerscd134352020-04-17 19:37:15 -0700281
282 if (error_detected_) {
283 WRN("Corruption detected. Found %zu corrupt bytes, %zu corrupt entries, "
284 "and %zu keys missing redundant copies.",
285 total_corrupt_bytes,
286 corrupt_entries,
287 entry_copies_missing);
288 return Status::FAILED_PRECONDITION;
289 }
290 return Status::OK;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800291}
292
Alexei Frolov9e235832020-02-24 12:44:45 -0800293KeyValueStore::StorageStats KeyValueStore::GetStorageStats() const {
David Rogers9abe3c72020-03-24 19:03:13 -0700294 StorageStats stats{};
Alexei Frolov9e235832020-02-24 12:44:45 -0800295 const size_t sector_size = partition_.sector_size_bytes();
296 bool found_empty_sector = false;
David Rogers9abe3c72020-03-24 19:03:13 -0700297 stats.corrupt_sectors_recovered = error_stats_.corrupt_sectors_recovered;
298 stats.missing_redundant_entries_recovered =
299 error_stats_.missing_redundant_entries_recovered;
Alexei Frolov9e235832020-02-24 12:44:45 -0800300
301 for (const SectorDescriptor& sector : sectors_) {
302 stats.in_use_bytes += sector.valid_bytes();
303 stats.reclaimable_bytes += sector.RecoverableBytes(sector_size);
304
305 if (!found_empty_sector && sector.Empty(sector_size)) {
306 // The KVS tries to always keep an empty sector for GC, so don't count
307 // the first empty sector seen as writable space. However, a free sector
308 // cannot always be assumed to exist; if a GC operation fails, all sectors
309 // may be partially written, in which case the space reported might be
310 // inaccurate.
311 found_empty_sector = true;
312 continue;
313 }
314
315 stats.writable_bytes += sector.writable_bytes();
316 }
317
318 return stats;
319}
320
David Rogers98fea472020-04-01 15:43:48 -0700321// Check KVS for any error conditions. Primarily intended for test and
322// internal use.
David Rogers9abe3c72020-03-24 19:03:13 -0700323bool KeyValueStore::CheckForErrors() {
324 // Check for corrupted sectors
325 for (SectorDescriptor& sector : sectors_) {
326 if (sector.corrupt()) {
327 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700328 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700329 }
330 }
331
332 // Check for missing redundancy.
333 if (redundancy() > 1) {
334 for (const EntryMetadata& metadata : entry_cache_) {
335 if (metadata.addresses().size() < redundancy()) {
336 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700337 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700338 }
339 }
340 }
341
342 return error_detected();
343}
344
Keir Mierle8c352dc2020-02-02 13:58:19 -0800345Status KeyValueStore::LoadEntry(Address entry_address,
346 Address* next_entry_address) {
Wyatt Heplere541e072020-02-14 09:10:53 -0800347 Entry entry;
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800348 TRY(Entry::Read(partition_, entry_address, formats_, &entry));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800349
350 // Read the key from flash & validate the entry (which reads the value).
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800351 Entry::KeyBuffer key_buffer;
Wyatt Heplere541e072020-02-14 09:10:53 -0800352 TRY_ASSIGN(size_t key_length, entry.ReadKey(key_buffer));
353 const string_view key(key_buffer.data(), key_length);
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800354
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800355 TRY(entry.VerifyChecksumInFlash());
David Rogersf56131c2020-03-04 10:19:22 -0800356
357 // A valid entry was found, so update the next entry address before doing any
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700358 // of the checks that happen in AddNewOrUpdateExisting.
David Rogersf56131c2020-03-04 10:19:22 -0800359 *next_entry_address = entry.next_address();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700360 return entry_cache_.AddNewOrUpdateExisting(
361 entry.descriptor(key), entry.address(), partition_.sector_size_bytes());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800362}
363
Alexei Frolovd4adf912020-02-21 13:29:15 -0800364// Scans flash memory within a sector to find a KVS entry magic.
Alexei Frolovd4adf912020-02-21 13:29:15 -0800365Status KeyValueStore::ScanForEntry(const SectorDescriptor& sector,
366 Address start_address,
367 Address* next_entry_address) {
David Rogersfcea3252020-04-07 14:56:35 -0700368 DBG("Scanning sector %u for entries starting from address %u",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700369 sectors_.Index(sector),
David Rogersfcea3252020-04-07 14:56:35 -0700370 unsigned(start_address));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800371
372 // Entries must start at addresses which are aligned on a multiple of
373 // Entry::kMinAlignmentBytes. However, that multiple can vary between entries.
374 // When scanning, we don't have an entry to tell us what the current alignment
375 // is, so the minimum alignment is used to be exhaustive.
376 for (Address address = AlignUp(start_address, Entry::kMinAlignmentBytes);
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700377 sectors_.AddressInSector(sector, address);
Alexei Frolovd4adf912020-02-21 13:29:15 -0800378 address += Entry::kMinAlignmentBytes) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800379 uint32_t magic;
David Rogersfcea3252020-04-07 14:56:35 -0700380 StatusWithSize read_result =
381 partition_.Read(address, as_writable_bytes(span(&magic, 1)));
382 if (!read_result.ok()) {
383 continue;
384 }
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800385 if (formats_.KnownMagic(magic)) {
David Rogersfcea3252020-04-07 14:56:35 -0700386 DBG("Found entry magic at address %u", unsigned(address));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800387 *next_entry_address = address;
388 return Status::OK;
389 }
390 }
391
392 return Status::NOT_FOUND;
393}
394
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800395StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800396 span<byte> value_buffer,
397 size_t offset_bytes) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700398 TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800399
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700400 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700401 TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800402
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700403 return Get(key, metadata, value_buffer, offset_bytes);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800404}
405
Wyatt Heplerfac81132020-02-27 17:26:33 -0800406Status KeyValueStore::PutBytes(string_view key, span<const byte> value) {
David Rogers9abe3c72020-03-24 19:03:13 -0700407 TRY(CheckWriteOperation(key));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800408 DBG("Writing key/value; key length=%zu, value length=%zu",
409 key.size(),
410 value.size());
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800411
Wyatt Hepler5406a672020-02-18 15:42:38 -0800412 if (Entry::size(partition_, key, value) > partition_.sector_size_bytes()) {
413 DBG("%zu B value with %zu B key cannot fit in one sector",
414 value.size(),
415 key.size());
416 return Status::INVALID_ARGUMENT;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800417 }
418
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700419 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700420 Status status = FindEntry(key, &metadata);
Wyatt Hepler2d401692020-02-13 16:01:23 -0800421
422 if (status.ok()) {
David Rogersf56131c2020-03-04 10:19:22 -0800423 // TODO: figure out logging how to support multiple addresses.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700424 DBG("Overwriting entry for key 0x%08" PRIx32 " in %zu sectors including %u",
425 metadata.hash(),
426 metadata.addresses().size(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700427 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700428 return WriteEntryForExistingKey(metadata, EntryState::kValid, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800429 }
David Rogers2761aeb2020-01-31 17:09:00 -0800430
Wyatt Hepler2d401692020-02-13 16:01:23 -0800431 if (status == Status::NOT_FOUND) {
432 return WriteEntryForNewKey(key, value);
433 }
434
435 return status;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800436}
437
438Status KeyValueStore::Delete(string_view key) {
David Rogers9abe3c72020-03-24 19:03:13 -0700439 TRY(CheckWriteOperation(key));
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800440
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700441 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700442 TRY(FindExisting(key, &metadata));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800443
David Rogersf56131c2020-03-04 10:19:22 -0800444 // TODO: figure out logging how to support multiple addresses.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700445 DBG("Writing tombstone for key 0x%08" PRIx32 " in %zu sectors including %u",
446 metadata.hash(),
447 metadata.addresses().size(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700448 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700449 return WriteEntryForExistingKey(metadata, EntryState::kDeleted, key, {});
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800450}
451
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800452void KeyValueStore::Item::ReadKey() {
453 key_buffer_.fill('\0');
454
455 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700456 if (kvs_.ReadEntry(*iterator_, entry).ok()) {
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800457 entry.ReadKey(key_buffer_);
458 }
459}
460
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800461KeyValueStore::iterator& KeyValueStore::iterator::operator++() {
462 // Skip to the next entry that is valid (not deleted).
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700463 while (++item_.iterator_ != item_.kvs_.entry_cache_.end() &&
Wyatt Hepler02946272020-03-18 10:36:22 -0700464 item_.iterator_->state() != EntryState::kValid) {
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800465 }
466 return *this;
467}
468
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800469KeyValueStore::iterator KeyValueStore::begin() const {
Wyatt Heplerbfc6a522020-04-01 16:30:24 -0700470 internal::EntryCache::const_iterator cache_iterator = entry_cache_.begin();
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800471 // Skip over any deleted entries at the start of the descriptor list.
Wyatt Hepler02946272020-03-18 10:36:22 -0700472 while (cache_iterator != entry_cache_.end() &&
473 cache_iterator->state() != EntryState::kValid) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700474 ++cache_iterator;
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800475 }
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700476 return iterator(*this, cache_iterator);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800477}
478
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700479StatusWithSize KeyValueStore::ValueSize(string_view key) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700480 TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Heplered163b02020-02-03 17:49:32 -0800481
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700482 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700483 TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800484
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700485 return ValueSize(metadata);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800486}
Wyatt Heplered163b02020-02-03 17:49:32 -0800487
David Rogers98fea472020-04-01 15:43:48 -0700488Status KeyValueStore::ReadEntry(const EntryMetadata& metadata,
489 Entry& entry) const {
490 // Try to read an entry
491 Status read_result = Status::DATA_LOSS;
492 for (Address address : metadata.addresses()) {
493 read_result = Entry::Read(partition_, address, formats_, &entry);
494 if (read_result.ok()) {
495 return read_result;
496 }
497
498 // Found a bad address. Set the sector as corrupt.
499 error_detected_ = true;
500 sectors_.FromAddress(address).mark_corrupt();
501 }
502
503 ERR("No valid entries for key. Data has been lost!");
504 return read_result;
505}
506
507Status KeyValueStore::FindEntry(string_view key,
508 EntryMetadata* found_entry) const {
509 StatusWithSize find_result =
510 entry_cache_.Find(partition_, sectors_, formats_, key, found_entry);
511
512 if (find_result.size() > 0u) {
513 error_detected_ = true;
514 }
515 return find_result.status();
516}
517
518Status KeyValueStore::FindExisting(string_view key,
519 EntryMetadata* metadata) const {
520 Status status = FindEntry(key, metadata);
521
522 // If the key's hash collides with an existing key or if the key is deleted,
523 // treat it as if it is not in the KVS.
524 if (status == Status::ALREADY_EXISTS ||
525 (status.ok() && metadata->state() == EntryState::kDeleted)) {
526 return Status::NOT_FOUND;
527 }
528 return status;
529}
530
Wyatt Heplerfac81132020-02-27 17:26:33 -0800531StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700532 const EntryMetadata& metadata,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800533 span<std::byte> value_buffer,
534 size_t offset_bytes) const {
535 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700536
537 TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800538
539 StatusWithSize result = entry.ReadValue(value_buffer, offset_bytes);
540 if (result.ok() && options_.verify_on_read && offset_bytes == 0u) {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800541 Status verify_result =
542 entry.VerifyChecksum(key, value_buffer.first(result.size()));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800543 if (!verify_result.ok()) {
544 std::memset(value_buffer.data(), 0, result.size());
545 return StatusWithSize(verify_result, 0);
546 }
547
548 return StatusWithSize(verify_result, result.size());
549 }
550 return result;
Wyatt Heplered163b02020-02-03 17:49:32 -0800551}
552
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800553Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800554 void* value,
555 size_t size_bytes) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700556 TRY(CheckWriteOperation(key));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800557
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700558 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700559 TRY(FindExisting(key, &metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800560
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700561 return FixedSizeGet(key, metadata, value, size_bytes);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800562}
563
564Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700565 const EntryMetadata& metadata,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800566 void* value,
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800567 size_t size_bytes) const {
568 // Ensure that the size of the stored value matches the size of the type.
569 // Otherwise, report error. This check avoids potential memory corruption.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700570 TRY_ASSIGN(const size_t actual_size, ValueSize(metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800571
572 if (actual_size != size_bytes) {
573 DBG("Requested %zu B read, but value is %zu B", size_bytes, actual_size);
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800574 return Status::INVALID_ARGUMENT;
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800575 }
Wyatt Heplerfac81132020-02-27 17:26:33 -0800576
577 StatusWithSize result =
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700578 Get(key, metadata, span(static_cast<byte*>(value), size_bytes), 0);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800579
580 return result.status();
581}
582
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700583StatusWithSize KeyValueStore::ValueSize(const EntryMetadata& metadata) const {
Wyatt Heplerfac81132020-02-27 17:26:33 -0800584 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700585 TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800586
587 return StatusWithSize(entry.value_size());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800588}
589
David Rogers9abe3c72020-03-24 19:03:13 -0700590Status KeyValueStore::CheckWriteOperation(string_view key) const {
Wyatt Hepleracaacf92020-01-24 10:58:30 -0800591 if (InvalidKey(key)) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800592 return Status::INVALID_ARGUMENT;
593 }
David Rogers9abe3c72020-03-24 19:03:13 -0700594
595 // For normal write operation the KVS must be fully ready.
Wyatt Heplerd2298282020-02-20 17:12:45 -0800596 if (!initialized()) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800597 return Status::FAILED_PRECONDITION;
598 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800599 return Status::OK;
600}
601
David Rogers9abe3c72020-03-24 19:03:13 -0700602Status KeyValueStore::CheckReadOperation(string_view key) const {
603 if (InvalidKey(key)) {
604 return Status::INVALID_ARGUMENT;
605 }
606
607 // Operations that are explicitly read-only can be done after init() has been
608 // called but not fully ready (when needing maintenance).
609 if (initialized_ == InitializationState::kNotInitialized) {
610 return Status::FAILED_PRECONDITION;
611 }
612 return Status::OK;
613}
614
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700615Status KeyValueStore::WriteEntryForExistingKey(EntryMetadata& metadata,
616 EntryState new_state,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800617 string_view key,
618 span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700619 // Read the original entry to get the size for sector accounting purposes.
620 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700621 TRY(ReadEntry(metadata, entry));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800622
David Rogersc0104462020-05-08 15:50:23 -0700623 return WriteEntry(key, value, new_state, &metadata, &entry);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800624}
625
626Status KeyValueStore::WriteEntryForNewKey(string_view key,
627 span<const byte> value) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700628 if (entry_cache_.full()) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800629 WRN("KVS full: trying to store a new entry, but can't. Have %zu entries",
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700630 entry_cache_.total_entries());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800631 return Status::RESOURCE_EXHAUSTED;
632 }
633
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700634 return WriteEntry(key, value, EntryState::kValid);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700635}
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800636
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700637Status KeyValueStore::WriteEntry(string_view key,
638 span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700639 EntryState new_state,
640 EntryMetadata* prior_metadata,
David Rogersc0104462020-05-08 15:50:23 -0700641 const Entry* prior_entry) {
David Rogersc0104462020-05-08 15:50:23 -0700642 // If new entry and prior entry have matching value size, state, and checksum,
643 // check if the values match. Directly compare the prior and new values
644 // because the checksum can not be depended on to establish equality, it can
645 // only be depended on to establish inequality.
David Rogersd50eb1c2020-05-12 17:46:36 -0700646 if (prior_entry != nullptr && prior_entry->value_size() == value.size() &&
David Rogersc0104462020-05-08 15:50:23 -0700647 prior_metadata->state() == new_state &&
David Rogersc0104462020-05-08 15:50:23 -0700648 prior_entry->ValueMatches(value).ok()) {
David Rogersd50eb1c2020-05-12 17:46:36 -0700649 // The new value matches the prior value, don't need to write anything. Just
650 // keep the existing entry.
David Rogersc0104462020-05-08 15:50:23 -0700651 DBG("Write for key 0x%08x with matching value skipped",
652 unsigned(prior_metadata->hash()));
653 return Status::OK;
654 }
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700655
656 // List of addresses for sectors with space for this entry.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700657 Address* reserved_addresses = entry_cache_.TempReservedAddressesForWrite();
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700658
David Rogers31b358b2020-04-15 05:00:50 -0700659 // Find addresses to write the entry to. This may involve garbage collecting
660 // one or more sectors.
David Rogersc0104462020-05-08 15:50:23 -0700661 const size_t entry_size = Entry::size(partition_, key, value);
David Rogers31b358b2020-04-15 05:00:50 -0700662 TRY(GetAddressesForWrite(reserved_addresses, entry_size));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700663
664 // Write the entry at the first address that was found.
David Rogersd50eb1c2020-05-12 17:46:36 -0700665 Entry entry = CreateEntry(reserved_addresses[0], key, value, new_state);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700666 TRY(AppendEntry(entry, key, value));
667
668 // After writing the first entry successfully, update the key descriptors.
669 // Once a single new the entry is written, the old entries are invalidated.
David Rogersc0104462020-05-08 15:50:23 -0700670 size_t prior_size = prior_entry != nullptr ? prior_entry->size() : 0;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700671 EntryMetadata new_metadata =
David Rogers31b358b2020-04-15 05:00:50 -0700672 CreateOrUpdateKeyDescriptor(entry, key, prior_metadata, prior_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700673
674 // Write the additional copies of the entry, if redundancy is greater than 1.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700675 for (size_t i = 1; i < redundancy(); ++i) {
676 entry.set_address(reserved_addresses[i]);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700677 TRY(AppendEntry(entry, key, value));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700678 new_metadata.AddNewAddress(reserved_addresses[i]);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700679 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800680 return Status::OK;
681}
682
David Rogers31b358b2020-04-15 05:00:50 -0700683KeyValueStore::EntryMetadata KeyValueStore::CreateOrUpdateKeyDescriptor(
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700684 const Entry& entry,
685 string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700686 EntryMetadata* prior_metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700687 size_t prior_size) {
688 // If there is no prior descriptor, create a new one.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700689 if (prior_metadata == nullptr) {
690 return entry_cache_.AddNew(entry.descriptor(key), entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800691 }
692
David Rogers31b358b2020-04-15 05:00:50 -0700693 return UpdateKeyDescriptor(
694 entry, entry.address(), prior_metadata, prior_size);
695}
696
697KeyValueStore::EntryMetadata KeyValueStore::UpdateKeyDescriptor(
698 const Entry& entry,
699 Address new_address,
700 EntryMetadata* prior_metadata,
701 size_t prior_size) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700702 // Remove valid bytes for the old entry and its copies, which are now stale.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700703 for (Address address : prior_metadata->addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700704 sectors_.FromAddress(address).RemoveValidBytes(prior_size);
David Rogersa2562b52020-03-05 15:30:05 -0800705 }
706
David Rogers31b358b2020-04-15 05:00:50 -0700707 prior_metadata->Reset(entry.descriptor(prior_metadata->hash()), new_address);
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700708 return *prior_metadata;
David Rogersa2562b52020-03-05 15:30:05 -0800709}
710
David Rogers31b358b2020-04-15 05:00:50 -0700711Status KeyValueStore::GetAddressesForWrite(Address* write_addresses,
712 size_t write_size) {
713 for (size_t i = 0; i < redundancy(); i++) {
714 SectorDescriptor* sector;
715 TRY(GetSectorForWrite(&sector, write_size, span(write_addresses, i)));
716 write_addresses[i] = sectors_.NextWritableAddress(*sector);
717
718 DBG("Found space for entry in sector %u at address %u",
719 sectors_.Index(sector),
720 unsigned(write_addresses[i]));
721 }
722
723 return Status::OK;
724}
725
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700726// Finds a sector to use for writing a new entry to. Does automatic garbage
David Rogersa2562b52020-03-05 15:30:05 -0800727// collection if needed and allowed.
728//
729// OK: Sector found with needed space.
730// RESOURCE_EXHAUSTED: No sector available with the needed space.
731Status KeyValueStore::GetSectorForWrite(SectorDescriptor** sector,
732 size_t entry_size,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700733 span<const Address> reserved) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700734 Status result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800735
David Rogersf3884eb2020-03-08 19:21:40 -0700736 size_t gc_sector_count = 0;
David Rogersa2562b52020-03-05 15:30:05 -0800737 bool do_auto_gc = options_.gc_on_write != GargbageCollectOnWrite::kDisabled;
738
739 // Do garbage collection as needed, so long as policy allows.
740 while (result == Status::RESOURCE_EXHAUSTED && do_auto_gc) {
741 if (options_.gc_on_write == GargbageCollectOnWrite::kOneSector) {
742 // If GC config option is kOneSector clear the flag to not do any more
743 // GC after this try.
744 do_auto_gc = false;
745 }
746 // Garbage collect and then try again to find the best sector.
David Rogers9abe3c72020-03-24 19:03:13 -0700747 Status gc_status = GarbageCollect(reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800748 if (!gc_status.ok()) {
749 if (gc_status == Status::NOT_FOUND) {
750 // Not enough space, and no reclaimable bytes, this KVS is full!
751 return Status::RESOURCE_EXHAUSTED;
752 }
753 return gc_status;
754 }
755
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700756 result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersf3884eb2020-03-08 19:21:40 -0700757
758 gc_sector_count++;
759 // Allow total sectors + 2 number of GC cycles so that once reclaimable
760 // bytes in all the sectors have been reclaimed can try and free up space by
761 // moving entries for keys other than the one being worked on in to sectors
762 // that have copies of the key trying to be written.
763 if (gc_sector_count > (partition_.sector_count() + 2)) {
764 ERR("Did more GC sectors than total sectors!!!!");
765 return Status::RESOURCE_EXHAUSTED;
766 }
David Rogersa2562b52020-03-05 15:30:05 -0800767 }
768
769 if (!result.ok()) {
770 WRN("Unable to find sector to write %zu B", entry_size);
771 }
772 return result;
773}
774
David Rogers9abe3c72020-03-24 19:03:13 -0700775Status KeyValueStore::MarkSectorCorruptIfNotOk(Status status,
776 SectorDescriptor* sector) {
777 if (!status.ok()) {
778 DBG(" Sector %u corrupt", sectors_.Index(sector));
779 sector->mark_corrupt();
780 error_detected_ = true;
781 }
782 return status;
783}
784
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700785Status KeyValueStore::AppendEntry(const Entry& entry,
David Rogersa2562b52020-03-05 15:30:05 -0800786 string_view key,
787 span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700788 const StatusWithSize result = entry.Write(key, value);
David Rogersa2562b52020-03-05 15:30:05 -0800789
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700790 SectorDescriptor& sector = sectors_.FromAddress(entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800791
792 if (!result.ok()) {
793 ERR("Failed to write %zu bytes at %#zx. %zu actually written",
794 entry.size(),
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700795 size_t(entry.address()),
David Rogersa2562b52020-03-05 15:30:05 -0800796 result.size());
David Rogers9abe3c72020-03-24 19:03:13 -0700797 TRY(MarkSectorCorruptIfNotOk(result.status(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800798 }
799
800 if (options_.verify_on_write) {
David Rogers9abe3c72020-03-24 19:03:13 -0700801 TRY(MarkSectorCorruptIfNotOk(entry.VerifyChecksumInFlash(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800802 }
803
David Rogers98fea472020-04-01 15:43:48 -0700804 sector.RemoveWritableBytes(result.size());
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700805 sector.AddValidBytes(result.size());
David Rogersa2562b52020-03-05 15:30:05 -0800806 return Status::OK;
807}
808
David Rogers98fea472020-04-01 15:43:48 -0700809StatusWithSize KeyValueStore::CopyEntryToSector(Entry& entry,
810 SectorDescriptor* new_sector,
David Rogers31b358b2020-04-15 05:00:50 -0700811 Address new_address) {
David Rogers98fea472020-04-01 15:43:48 -0700812 const StatusWithSize result = entry.Copy(new_address);
813
814 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(result.status(), new_sector));
815
816 if (options_.verify_on_write) {
David Rogers31b358b2020-04-15 05:00:50 -0700817 Entry new_entry;
818 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(
819 Entry::Read(partition_, new_address, formats_, &new_entry),
820 new_sector));
821 // TODO: add test that catches doing the verify on the old entry.
822 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(new_entry.VerifyChecksumInFlash(),
823 new_sector));
David Rogers98fea472020-04-01 15:43:48 -0700824 }
825 // Entry was written successfully; update descriptor's address and the sector
826 // descriptors to reflect the new entry.
827 new_sector->RemoveWritableBytes(result.size());
828 new_sector->AddValidBytes(result.size());
829
830 return result;
831}
832
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700833Status KeyValueStore::RelocateEntry(const EntryMetadata& metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700834 KeyValueStore::Address& address,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700835 span<const Address> reserved_addresses) {
David Rogersa2562b52020-03-05 15:30:05 -0800836 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700837 TRY(ReadEntry(metadata, entry));
David Rogersa2562b52020-03-05 15:30:05 -0800838
839 // Find a new sector for the entry and write it to the new location. For
840 // relocation the find should not not be a sector already containing the key
841 // but can be the always empty sector, since this is part of the GC process
842 // that will result in a new empty sector. Also find a sector that does not
843 // have reclaimable space (mostly for the full GC, where that would result in
844 // an immediate extra relocation).
845 SectorDescriptor* new_sector;
846
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700847 TRY(sectors_.FindSpaceDuringGarbageCollection(
848 &new_sector, entry.size(), metadata.addresses(), reserved_addresses));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700849
David Rogers31b358b2020-04-15 05:00:50 -0700850 Address new_address = sectors_.NextWritableAddress(*new_sector);
David Rogers98fea472020-04-01 15:43:48 -0700851 TRY_ASSIGN(const size_t result_size,
852 CopyEntryToSector(entry, new_sector, new_address));
853 sectors_.FromAddress(address).RemoveValidBytes(result_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700854 address = new_address;
David Rogersa2562b52020-03-05 15:30:05 -0800855
856 return Status::OK;
857}
858
David Rogers9abe3c72020-03-24 19:03:13 -0700859Status KeyValueStore::FullMaintenance() {
860 if (initialized_ == InitializationState::kNotInitialized) {
861 return Status::FAILED_PRECONDITION;
862 }
863
Armando Montanez17083bb2020-04-24 10:18:26 -0700864 // Full maintenance can be a potentially heavy operation, and should be
865 // relatively infrequent, so log start/end at INFO level.
866 INF("Beginning full maintenance");
David Rogers98fea472020-04-01 15:43:48 -0700867 CheckForErrors();
David Rogers9abe3c72020-03-24 19:03:13 -0700868
869 if (error_detected_) {
870 TRY(Repair());
871 }
David Rogers35c3f842020-04-22 15:34:05 -0700872 StatusWithSize update_status = UpdateEntriesToPrimaryFormat();
873 Status overall_status = update_status.status();
874
David Rogers31b358b2020-04-15 05:00:50 -0700875 // Make sure all the entries are on the primary format.
Armando Montanez17083bb2020-04-24 10:18:26 -0700876 if (!overall_status.ok()) {
877 ERR("Failed to update all entries to the primary format");
878 }
David Rogers31b358b2020-04-15 05:00:50 -0700879
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700880 SectorDescriptor* sector = sectors_.last_new();
David Rogerscd87c322020-02-27 14:04:08 -0800881
David Rogers35c3f842020-04-22 15:34:05 -0700882 // Calculate number of bytes for the threshold.
883 size_t threshold_bytes =
884 (partition_.size_bytes() * kGcUsageThresholdPercentage) / 100;
885
886 // Is bytes in use over the threshold.
887 StorageStats stats = GetStorageStats();
888 bool over_usage_threshold = stats.in_use_bytes > threshold_bytes;
889 bool force_gc = over_usage_threshold || (update_status.size() > 0);
890
David Rogerscd87c322020-02-27 14:04:08 -0800891 // TODO: look in to making an iterator method for cycling through sectors
892 // starting from last_new_sector_.
Armando Montanez17083bb2020-04-24 10:18:26 -0700893 Status gc_status;
David Rogerscd87c322020-02-27 14:04:08 -0800894 for (size_t j = 0; j < sectors_.size(); j++) {
895 sector += 1;
896 if (sector == sectors_.end()) {
897 sector = sectors_.begin();
898 }
899
David Rogers35c3f842020-04-22 15:34:05 -0700900 if (sector->RecoverableBytes(partition_.sector_size_bytes()) > 0 &&
901 (force_gc || sector->valid_bytes() == 0)) {
Armando Montanez17083bb2020-04-24 10:18:26 -0700902 gc_status = GarbageCollectSector(*sector, {});
903 if (!gc_status.ok()) {
904 ERR("Failed to garbage collect all sectors");
905 break;
906 }
David Rogerscd87c322020-02-27 14:04:08 -0800907 }
908 }
Armando Montanez17083bb2020-04-24 10:18:26 -0700909 if (overall_status.ok()) {
910 overall_status = gc_status;
911 }
David Rogerscd87c322020-02-27 14:04:08 -0800912
Armando Montanez17083bb2020-04-24 10:18:26 -0700913 if (overall_status.ok()) {
914 INF("Full maintenance complete");
915 } else {
916 ERR("Full maintenance finished with some errors");
917 }
918 return overall_status;
David Rogerscd87c322020-02-27 14:04:08 -0800919}
920
David Rogers0f8a1bb2020-04-21 18:50:19 -0700921Status KeyValueStore::PartialMaintenance() {
David Rogers9abe3c72020-03-24 19:03:13 -0700922 if (initialized_ == InitializationState::kNotInitialized) {
923 return Status::FAILED_PRECONDITION;
924 }
925
David Rogers0f8a1bb2020-04-21 18:50:19 -0700926 CheckForErrors();
David Rogersfcea3252020-04-07 14:56:35 -0700927 // Do automatic repair, if KVS options allow for it.
928 if (error_detected_ && options_.recovery != ErrorRecovery::kManual) {
929 TRY(Repair());
930 }
David Rogers0f8a1bb2020-04-21 18:50:19 -0700931 return GarbageCollect(span<const Address>());
932}
933
934Status KeyValueStore::GarbageCollect(span<const Address> reserved_addresses) {
935 DBG("Garbage Collect a single sector");
936 for (Address address : reserved_addresses) {
937 DBG(" Avoid address %u", unsigned(address));
938 }
David Rogersfcea3252020-04-07 14:56:35 -0700939
David Rogersa12786b2020-01-31 16:02:33 -0800940 // Step 1: Find the sector to garbage collect
David Rogersc9d545e2020-03-11 17:47:43 -0700941 SectorDescriptor* sector_to_gc =
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700942 sectors_.FindSectorToGarbageCollect(reserved_addresses);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800943
David Rogersa12786b2020-01-31 16:02:33 -0800944 if (sector_to_gc == nullptr) {
David Rogersa2562b52020-03-05 15:30:05 -0800945 // Nothing to GC.
946 return Status::NOT_FOUND;
David Rogersa12786b2020-01-31 16:02:33 -0800947 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800948
David Rogersc9d545e2020-03-11 17:47:43 -0700949 // Step 2: Garbage collect the selected sector.
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700950 return GarbageCollectSector(*sector_to_gc, reserved_addresses);
David Rogerscd87c322020-02-27 14:04:08 -0800951}
952
David Rogersf3884eb2020-03-08 19:21:40 -0700953Status KeyValueStore::RelocateKeyAddressesInSector(
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700954 SectorDescriptor& sector_to_gc,
David Rogersfcea3252020-04-07 14:56:35 -0700955 const EntryMetadata& metadata,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700956 span<const Address> reserved_addresses) {
957 for (FlashPartition::Address& address : metadata.addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700958 if (sectors_.AddressInSector(sector_to_gc, address)) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700959 DBG(" Relocate entry for Key 0x%08" PRIx32 ", sector %u",
960 metadata.hash(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700961 sectors_.Index(sectors_.FromAddress(address)));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700962 TRY(RelocateEntry(metadata, address, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700963 }
964 }
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700965
David Rogersf3884eb2020-03-08 19:21:40 -0700966 return Status::OK;
967};
968
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700969Status KeyValueStore::GarbageCollectSector(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700970 SectorDescriptor& sector_to_gc, span<const Address> reserved_addresses) {
David Rogers9abe3c72020-03-24 19:03:13 -0700971 DBG(" Garbage Collect sector %u", sectors_.Index(sector_to_gc));
David Rogersf3884eb2020-03-08 19:21:40 -0700972 // Step 1: Move any valid entries in the GC sector to other sectors
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700973 if (sector_to_gc.valid_bytes() != 0) {
David Rogers98fea472020-04-01 15:43:48 -0700974 for (EntryMetadata& metadata : entry_cache_) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700975 TRY(RelocateKeyAddressesInSector(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700976 sector_to_gc, metadata, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700977 }
978 }
979
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700980 if (sector_to_gc.valid_bytes() != 0) {
David Rogers67f4b6c2020-02-06 16:17:09 -0800981 ERR(" Failed to relocate valid entries from sector being garbage "
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800982 "collected, %zu valid bytes remain",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700983 sector_to_gc.valid_bytes());
Wyatt Heplerb7609542020-01-24 10:29:54 -0800984 return Status::INTERNAL;
985 }
986
David Rogerscd87c322020-02-27 14:04:08 -0800987 // Step 2: Reinitialize the sector
David Rogers9abe3c72020-03-24 19:03:13 -0700988 sector_to_gc.mark_corrupt();
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700989 TRY(partition_.Erase(sectors_.BaseAddress(sector_to_gc), 1));
990 sector_to_gc.set_writable_bytes(partition_.sector_size_bytes());
Wyatt Heplerb7609542020-01-24 10:29:54 -0800991
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700992 DBG(" Garbage Collect sector %u complete", sectors_.Index(sector_to_gc));
David Rogersa12786b2020-01-31 16:02:33 -0800993 return Status::OK;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800994}
995
David Rogers35c3f842020-04-22 15:34:05 -0700996StatusWithSize KeyValueStore::UpdateEntriesToPrimaryFormat() {
997 size_t entries_updated = 0;
David Rogers31b358b2020-04-15 05:00:50 -0700998 for (EntryMetadata& prior_metadata : entry_cache_) {
999 Entry entry;
David Rogers35c3f842020-04-22 15:34:05 -07001000 TRY_WITH_SIZE(ReadEntry(prior_metadata, entry));
David Rogers31b358b2020-04-15 05:00:50 -07001001 if (formats_.primary().magic == entry.magic()) {
1002 // Ignore entries that are already on the primary format.
1003 continue;
1004 }
1005
1006 DBG("Updating entry 0x%08x from old format [0x%08x] to new format "
1007 "[0x%08x]",
1008 unsigned(prior_metadata.hash()),
1009 unsigned(entry.magic()),
1010 unsigned(formats_.primary().magic));
1011
David Rogers35c3f842020-04-22 15:34:05 -07001012 entries_updated++;
1013
David Rogers31b358b2020-04-15 05:00:50 -07001014 last_transaction_id_ += 1;
David Rogers35c3f842020-04-22 15:34:05 -07001015 TRY_WITH_SIZE(entry.Update(formats_.primary(), last_transaction_id_));
David Rogers31b358b2020-04-15 05:00:50 -07001016
1017 // List of addresses for sectors with space for this entry.
1018 Address* reserved_addresses = entry_cache_.TempReservedAddressesForWrite();
1019
1020 // Find addresses to write the entry to. This may involve garbage collecting
1021 // one or more sectors.
David Rogers35c3f842020-04-22 15:34:05 -07001022 TRY_WITH_SIZE(GetAddressesForWrite(reserved_addresses, entry.size()));
David Rogers31b358b2020-04-15 05:00:50 -07001023
David Rogers35c3f842020-04-22 15:34:05 -07001024 TRY_WITH_SIZE(
1025 CopyEntryToSector(entry,
David Rogers31b358b2020-04-15 05:00:50 -07001026 &sectors_.FromAddress(reserved_addresses[0]),
1027 reserved_addresses[0]));
1028
1029 // After writing the first entry successfully, update the key descriptors.
1030 // Once a single new the entry is written, the old entries are invalidated.
1031 EntryMetadata new_metadata = UpdateKeyDescriptor(
1032 entry, reserved_addresses[0], &prior_metadata, entry.size());
1033
1034 // Write the additional copies of the entry, if redundancy is greater
1035 // than 1.
1036 for (size_t i = 1; i < redundancy(); ++i) {
David Rogers35c3f842020-04-22 15:34:05 -07001037 TRY_WITH_SIZE(
1038 CopyEntryToSector(entry,
David Rogers31b358b2020-04-15 05:00:50 -07001039 &sectors_.FromAddress(reserved_addresses[i]),
1040 reserved_addresses[i]));
1041 new_metadata.AddNewAddress(reserved_addresses[i]);
1042 }
1043 }
David Rogers35c3f842020-04-22 15:34:05 -07001044
1045 return StatusWithSize(entries_updated);
David Rogers31b358b2020-04-15 05:00:50 -07001046}
1047
David Rogers9abe3c72020-03-24 19:03:13 -07001048// Add any missing redundant entries/copies for a key.
1049Status KeyValueStore::AddRedundantEntries(EntryMetadata& metadata) {
David Rogers9abe3c72020-03-24 19:03:13 -07001050 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -07001051 TRY(ReadEntry(metadata, entry));
David Rogers9abe3c72020-03-24 19:03:13 -07001052 TRY(entry.VerifyChecksumInFlash());
1053
David Rogers0f8a1bb2020-04-21 18:50:19 -07001054 while (metadata.addresses().size() < redundancy()) {
1055 SectorDescriptor* new_sector;
1056 TRY(GetSectorForWrite(&new_sector, entry.size(), metadata.addresses()));
David Rogers9abe3c72020-03-24 19:03:13 -07001057
David Rogers31b358b2020-04-15 05:00:50 -07001058 Address new_address = sectors_.NextWritableAddress(*new_sector);
David Rogers98fea472020-04-01 15:43:48 -07001059 TRY(CopyEntryToSector(entry, new_sector, new_address));
David Rogers9abe3c72020-03-24 19:03:13 -07001060
1061 metadata.AddNewAddress(new_address);
1062 }
1063 return Status::OK;
1064}
1065
1066Status KeyValueStore::RepairCorruptSectors() {
1067 // Try to GC each corrupt sector, even if previous sectors fail. If GC of a
1068 // sector failed on the first pass, then do a second pass, since a later
1069 // sector might have cleared up space or otherwise unblocked the earlier
1070 // failed sector.
1071 Status repair_status = Status::OK;
1072
1073 size_t loop_count = 0;
1074 do {
1075 loop_count++;
1076 // Error of RESOURCE_EXHAUSTED indicates no space found for relocation.
1077 // Reset back to OK for the next pass.
1078 if (repair_status == Status::RESOURCE_EXHAUSTED) {
1079 repair_status = Status::OK;
1080 }
1081
1082 DBG(" Pass %u", unsigned(loop_count));
1083 for (SectorDescriptor& sector : sectors_) {
1084 if (sector.corrupt()) {
1085 DBG(" Found sector %u with corruption", sectors_.Index(sector));
1086 Status sector_status = GarbageCollectSector(sector, {});
1087 if (sector_status.ok()) {
1088 error_stats_.corrupt_sectors_recovered += 1;
1089 } else if (repair_status.ok() ||
1090 repair_status == Status::RESOURCE_EXHAUSTED) {
1091 repair_status = sector_status;
1092 }
1093 }
1094 }
1095 DBG(" Pass %u complete", unsigned(loop_count));
1096 } while (!repair_status.ok() && loop_count < 2);
1097
1098 return repair_status;
1099}
1100
1101Status KeyValueStore::EnsureFreeSectorExists() {
1102 Status repair_status = Status::OK;
1103 bool empty_sector_found = false;
1104
1105 DBG(" Find empty sector");
1106 for (SectorDescriptor& sector : sectors_) {
1107 if (sector.Empty(partition_.sector_size_bytes())) {
1108 empty_sector_found = true;
1109 DBG(" Empty sector found");
1110 break;
1111 }
1112 }
1113 if (empty_sector_found == false) {
1114 DBG(" No empty sector found, attempting to GC a free sector");
1115 Status sector_status = GarbageCollect(span<const Address, 0>());
1116 if (repair_status.ok() && !sector_status.ok()) {
1117 DBG(" Unable to free an empty sector");
1118 repair_status = sector_status;
1119 }
1120 }
1121
1122 return repair_status;
1123}
1124
1125Status KeyValueStore::EnsureEntryRedundancy() {
1126 Status repair_status = Status::OK;
1127
1128 if (redundancy() == 1) {
David Rogers98fea472020-04-01 15:43:48 -07001129 DBG(" Redundancy not in use, nothting to check");
David Rogers9abe3c72020-03-24 19:03:13 -07001130 return Status::OK;
1131 }
1132
David Rogers98fea472020-04-01 15:43:48 -07001133 DBG(" Write any needed additional duplicate copies of keys to fulfill %u"
1134 " redundancy",
David Rogers9abe3c72020-03-24 19:03:13 -07001135 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -07001136 for (EntryMetadata& metadata : entry_cache_) {
David Rogers9abe3c72020-03-24 19:03:13 -07001137 if (metadata.addresses().size() >= redundancy()) {
1138 continue;
1139 }
1140
1141 DBG(" Key with %u of %u copies found, adding missing copies",
1142 unsigned(metadata.addresses().size()),
1143 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -07001144 Status fill_status = AddRedundantEntries(metadata);
David Rogers9abe3c72020-03-24 19:03:13 -07001145 if (fill_status.ok()) {
1146 error_stats_.missing_redundant_entries_recovered += 1;
1147 DBG(" Key missing copies added");
1148 } else {
1149 DBG(" Failed to add key missing copies");
1150 if (repair_status.ok()) {
1151 repair_status = fill_status;
1152 }
1153 }
1154 }
1155
1156 return repair_status;
1157}
1158
David Rogersfcea3252020-04-07 14:56:35 -07001159Status KeyValueStore::FixErrors() {
1160 DBG("Fixing KVS errors");
David Rogers9abe3c72020-03-24 19:03:13 -07001161
1162 // Step 1: Garbage collect any sectors marked as corrupt.
David Rogersfcea3252020-04-07 14:56:35 -07001163 Status overall_status = RepairCorruptSectors();
David Rogers9abe3c72020-03-24 19:03:13 -07001164
1165 // Step 2: Make sure there is at least 1 empty sector. This needs to be a
1166 // seperate check of sectors from step 1, because a found empty sector might
1167 // get written to by a later GC that fails and does not result in a free
1168 // sector.
David Rogersfcea3252020-04-07 14:56:35 -07001169 Status repair_status = EnsureFreeSectorExists();
David Rogers9abe3c72020-03-24 19:03:13 -07001170 if (overall_status.ok()) {
1171 overall_status = repair_status;
1172 }
1173
1174 // Step 3: Make sure each stored key has the full number of redundant
1175 // entries.
1176 repair_status = EnsureEntryRedundancy();
1177 if (overall_status.ok()) {
1178 overall_status = repair_status;
1179 }
1180
1181 if (overall_status.ok()) {
1182 error_detected_ = false;
1183 initialized_ = InitializationState::kReady;
1184 }
1185 return overall_status;
1186}
1187
David Rogersfcea3252020-04-07 14:56:35 -07001188Status KeyValueStore::Repair() {
1189 // If errors have been detected, just reinit the KVS metadata. This does a
1190 // full deep error check and any needed repairs. Then repair any errors.
1191 INF("Starting KVS repair");
1192
1193 DBG("Reinitialize KVS metadata");
1194 InitializeMetadata();
1195
1196 return FixErrors();
1197}
1198
David Rogersd50eb1c2020-05-12 17:46:36 -07001199KeyValueStore::Entry KeyValueStore::CreateEntry(Address address,
1200 string_view key,
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -08001201 span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001202 EntryState state) {
Keir Mierle9e38b402020-02-21 13:06:21 -08001203 // Always bump the transaction ID when creating a new entry.
1204 //
1205 // Burning transaction IDs prevents inconsistencies between flash and memory
1206 // that which could happen if a write succeeds, but for some reason the read
1207 // and verify step fails. Here's how this would happen:
1208 //
1209 // 1. The entry is written but for some reason the flash reports failure OR
1210 // The write succeeds, but the read / verify operation fails.
1211 // 2. The transaction ID is NOT incremented, because of the failure
1212 // 3. (later) A new entry is written, re-using the transaction ID (oops)
1213 //
David Rogersd50eb1c2020-05-12 17:46:36 -07001214 // By always burning transaction IDs, the above problem can't happen.
1215 last_transaction_id_ += 1;
Keir Mierle9e38b402020-02-21 13:06:21 -08001216
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001217 if (state == EntryState::kDeleted) {
Wyatt Hepler7465be32020-02-21 15:30:53 -08001218 return Entry::Tombstone(
David Rogersd50eb1c2020-05-12 17:46:36 -07001219 partition_, address, formats_.primary(), key, last_transaction_id_);
Wyatt Hepler1fc11042020-02-19 17:17:51 -08001220 }
David Rogersd50eb1c2020-05-12 17:46:36 -07001221 return Entry::Valid(partition_,
1222 address,
1223 formats_.primary(),
1224 key,
1225 value,
1226 last_transaction_id_);
Wyatt Heplerd2298282020-02-20 17:12:45 -08001227}
1228
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001229void KeyValueStore::LogDebugInfo() const {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001230 const size_t sector_size_bytes = partition_.sector_size_bytes();
1231 DBG("====================== KEY VALUE STORE DUMP =========================");
1232 DBG(" ");
1233 DBG("Flash partition:");
Wyatt Heplerad0a7932020-02-06 08:20:38 -08001234 DBG(" Sector count = %zu", partition_.sector_count());
Wyatt Hepler38ce30f2020-02-19 11:48:31 -08001235 DBG(" Sector max count = %zu", sectors_.max_size());
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001236 DBG(" Sectors in use = %zu", sectors_.size());
Keir Mierle8c352dc2020-02-02 13:58:19 -08001237 DBG(" Sector size = %zu", sector_size_bytes);
1238 DBG(" Total size = %zu", partition_.size_bytes());
1239 DBG(" Alignment = %zu", partition_.alignment_bytes());
1240 DBG(" ");
1241 DBG("Key descriptors:");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001242 DBG(" Entry count = %zu", entry_cache_.total_entries());
1243 DBG(" Max entry count = %zu", entry_cache_.max_entries());
Keir Mierle8c352dc2020-02-02 13:58:19 -08001244 DBG(" ");
1245 DBG(" # hash version address address (hex)");
Armando Montanez888370d2020-05-01 18:29:22 -07001246 size_t count = 0;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001247 for (const EntryMetadata& metadata : entry_cache_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001248 DBG(" |%3zu: | %8zx |%8zu | %8zu | %8zx",
Armando Montanez888370d2020-05-01 18:29:22 -07001249 count++,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001250 size_t(metadata.hash()),
1251 size_t(metadata.transaction_id()),
1252 size_t(metadata.first_address()),
1253 size_t(metadata.first_address()));
Keir Mierle8c352dc2020-02-02 13:58:19 -08001254 }
1255 DBG(" ");
1256
1257 DBG("Sector descriptors:");
1258 DBG(" # tail free valid has_space");
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001259 for (const SectorDescriptor& sd : sectors_) {
1260 DBG(" |%3u: | %8zu |%8zu | %s",
1261 sectors_.Index(sd),
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001262 size_t(sd.writable_bytes()),
1263 sd.valid_bytes(),
1264 sd.writable_bytes() ? "YES" : "");
Keir Mierle8c352dc2020-02-02 13:58:19 -08001265 }
1266 DBG(" ");
1267
1268 // TODO: This should stop logging after some threshold.
1269 // size_t dumped_bytes = 0;
1270 DBG("Sector raw data:");
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001271 for (size_t sector_id = 0; sector_id < sectors_.size(); ++sector_id) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001272 // Read sector data. Yes, this will blow the stack on embedded.
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001273 std::array<byte, 500> raw_sector_data; // TODO!!!
Keir Mierle8c352dc2020-02-02 13:58:19 -08001274 StatusWithSize sws =
1275 partition_.Read(sector_id * sector_size_bytes, raw_sector_data);
1276 DBG("Read: %zu bytes", sws.size());
1277
1278 DBG(" base addr offs 0 1 2 3 4 5 6 7");
1279 for (size_t i = 0; i < sector_size_bytes; i += 8) {
1280 DBG(" %3zu %8zx %5zu | %02x %02x %02x %02x %02x %02x %02x %02x",
1281 sector_id,
1282 (sector_id * sector_size_bytes) + i,
1283 i,
1284 static_cast<unsigned int>(raw_sector_data[i + 0]),
1285 static_cast<unsigned int>(raw_sector_data[i + 1]),
1286 static_cast<unsigned int>(raw_sector_data[i + 2]),
1287 static_cast<unsigned int>(raw_sector_data[i + 3]),
1288 static_cast<unsigned int>(raw_sector_data[i + 4]),
1289 static_cast<unsigned int>(raw_sector_data[i + 5]),
1290 static_cast<unsigned int>(raw_sector_data[i + 6]),
1291 static_cast<unsigned int>(raw_sector_data[i + 7]));
1292
1293 // TODO: Fix exit condition.
1294 if (i > 128) {
1295 break;
1296 }
1297 }
1298 DBG(" ");
1299 }
1300
1301 DBG("////////////////////// KEY VALUE STORE DUMP END /////////////////////");
1302}
1303
David Rogerscf680ab2020-02-12 23:28:32 -08001304void KeyValueStore::LogSectors() const {
1305 DBG("Sector descriptors: count %zu", sectors_.size());
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001306 for (auto& sector : sectors_) {
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001307 DBG(" - Sector %u: valid %zu, recoverable %zu, free %zu",
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001308 sectors_.Index(sector),
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001309 sector.valid_bytes(),
1310 sector.RecoverableBytes(partition_.sector_size_bytes()),
1311 sector.writable_bytes());
David Rogers50185ad2020-02-07 00:02:46 -08001312 }
1313}
1314
David Rogerscf680ab2020-02-12 23:28:32 -08001315void KeyValueStore::LogKeyDescriptor() const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001316 DBG("Key descriptors: count %zu", entry_cache_.total_entries());
David Rogers9abe3c72020-03-24 19:03:13 -07001317 for (const EntryMetadata& metadata : entry_cache_) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001318 DBG(" - Key: %s, hash %#zx, transaction ID %zu, first address %#zx",
Wyatt Hepler02946272020-03-18 10:36:22 -07001319 metadata.state() == EntryState::kDeleted ? "Deleted" : "Valid",
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001320 static_cast<size_t>(metadata.hash()),
1321 static_cast<size_t>(metadata.transaction_id()),
1322 static_cast<size_t>(metadata.first_address()));
David Rogerscf680ab2020-02-12 23:28:32 -08001323 }
1324}
1325
Wyatt Hepler2ad60672020-01-21 08:00:16 -08001326} // namespace pw::kvs