blob: 534313eb58ee4b8298011526c991764bc4c51da2 [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"
Wyatt Heplerae222dc2020-10-14 10:46:27 -070016#define PW_LOG_LEVEL PW_KVS_LOG_LEVEL
Keir Mierled28068e2020-08-14 16:41:44 -070017#define PW_LOG_USE_ULTRA_SHORT_NAMES 1
Armando Montanez28ecccb2020-05-04 15:35:54 -070018
Wyatt Heplerb7609542020-01-24 10:29:54 -080019#include "pw_kvs/key_value_store.h"
20
Wyatt Heplerbab0e202020-02-04 07:40:08 -080021#include <algorithm>
Wyatt Hepler5a33d8c2020-02-06 09:32:58 -080022#include <cinttypes>
Wyatt Heplerb7609542020-01-24 10:29:54 -080023#include <cstring>
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080024#include <type_traits>
Wyatt Heplerb7609542020-01-24 10:29:54 -080025
David Rogersc0104462020-05-08 15:50:23 -070026#include "pw_assert/assert.h"
Wyatt Heplerae222dc2020-10-14 10:46:27 -070027#include "pw_kvs_private/config.h"
Keir Mierle8c352dc2020-02-02 13:58:19 -080028#include "pw_log/log.h"
David Rogersb6b14b82020-09-11 16:27:27 -070029#include "pw_status/try.h"
Wyatt Heplerb7609542020-01-24 10:29:54 -080030
Wyatt Hepler2ad60672020-01-21 08:00:16 -080031namespace pw::kvs {
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080032namespace {
Wyatt Heplerb7609542020-01-24 10:29:54 -080033
Wyatt Hepleracaacf92020-01-24 10:58:30 -080034using std::byte;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080035using std::string_view;
Wyatt Hepleracaacf92020-01-24 10:58:30 -080036
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080037constexpr bool InvalidKey(std::string_view key) {
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -080038 return key.empty() || (key.size() > internal::Entry::kMaxKeyLength);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080039}
40
41} // namespace
42
Wyatt Heplerad0a7932020-02-06 08:20:38 -080043KeyValueStore::KeyValueStore(FlashPartition* partition,
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070044 std::span<const EntryFormat> formats,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070045 const Options& options,
46 size_t redundancy,
47 Vector<SectorDescriptor>& sector_descriptor_list,
48 const SectorDescriptor** temp_sectors_to_skip,
49 Vector<KeyDescriptor>& key_descriptor_list,
50 Address* addresses)
Wyatt Heplerad0a7932020-02-06 08:20:38 -080051 : partition_(*partition),
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080052 formats_(formats),
Wyatt Heplerc84393f2020-03-20 11:23:24 -070053 sectors_(sector_descriptor_list, *partition, temp_sectors_to_skip),
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070054 entry_cache_(key_descriptor_list, addresses, redundancy),
David Rogers49766d92020-03-20 10:55:54 -070055 options_(options),
David Rogers9abe3c72020-03-24 19:03:13 -070056 initialized_(InitializationState::kNotInitialized),
David Rogers49766d92020-03-20 10:55:54 -070057 error_detected_(false),
David Rogers3c298372020-10-12 14:15:39 -070058 internal_stats_({}),
David Rogers49766d92020-03-20 10:55:54 -070059 last_transaction_id_(0) {}
Wyatt Heplerad0a7932020-02-06 08:20:38 -080060
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080061Status KeyValueStore::Init() {
David Rogers9abe3c72020-03-24 19:03:13 -070062 initialized_ = InitializationState::kNotInitialized;
David Rogers49766d92020-03-20 10:55:54 -070063 error_detected_ = false;
Keir Mierlebf904812020-03-11 17:28:22 -070064 last_transaction_id_ = 0;
Wyatt Heplerd2298282020-02-20 17:12:45 -080065
David Rogers2e9e0c82020-02-13 15:06:06 -080066 INF("Initializing key value store");
Wyatt Hepler38ce30f2020-02-19 11:48:31 -080067 if (partition_.sector_count() > sectors_.max_size()) {
David Rogers9fc78b82020-06-12 13:56:12 -070068 ERR("KVS init failed: kMaxUsableSectors (=%u) must be at least as "
69 "large as the number of sectors in the flash partition (=%u)",
70 unsigned(sectors_.max_size()),
71 unsigned(partition_.sector_count()));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070072 return Status::FailedPrecondition();
Wyatt Heplerad0a7932020-02-06 08:20:38 -080073 }
74
David Rogers178002a2020-06-16 13:52:54 -070075 if (partition_.sector_count() < 2) {
76 ERR("KVS init failed: FlashParition sector count (=%u) must be at 2. KVS "
77 "requires at least 1 working sector + 1 free/reserved sector",
78 unsigned(partition_.sector_count()));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070079 return Status::FailedPrecondition();
David Rogers178002a2020-06-16 13:52:54 -070080 }
81
Keir Mierle8c352dc2020-02-02 13:58:19 -080082 const size_t sector_size_bytes = partition_.sector_size_bytes();
Keir Mierle8c352dc2020-02-02 13:58:19 -080083
David Rogers49766d92020-03-20 10:55:54 -070084 // TODO: investigate doing this as a static assert/compile-time check.
85 if (sector_size_bytes > SectorDescriptor::max_sector_size()) {
David Rogers9fc78b82020-06-12 13:56:12 -070086 ERR("KVS init failed: sector_size_bytes (=%u) is greater than maximum "
87 "allowed sector size (=%u)",
88 unsigned(sector_size_bytes),
89 unsigned(SectorDescriptor::max_sector_size()));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -070090 return Status::FailedPrecondition();
David Rogers49766d92020-03-20 10:55:54 -070091 }
92
David Rogerscd134352020-04-17 19:37:15 -070093 Status metadata_result = InitializeMetadata();
David Rogersfcea3252020-04-07 14:56:35 -070094
95 if (!error_detected_) {
96 initialized_ = InitializationState::kReady;
97 } else {
David Rogers0f8a1bb2020-04-21 18:50:19 -070098 initialized_ = InitializationState::kNeedsMaintenance;
99
David Rogersfcea3252020-04-07 14:56:35 -0700100 if (options_.recovery != ErrorRecovery::kManual) {
Armando Montanezf8419ae2020-04-21 10:03:05 -0700101 size_t pre_fix_redundancy_errors =
David Rogers3c298372020-10-12 14:15:39 -0700102 internal_stats_.missing_redundant_entries_recovered;
David Rogersfcea3252020-04-07 14:56:35 -0700103 Status recovery_status = FixErrors();
104
105 if (recovery_status.ok()) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700106 if (metadata_result == Status::OutOfRange()) {
David Rogers3c298372020-10-12 14:15:39 -0700107 internal_stats_.missing_redundant_entries_recovered =
Armando Montanezf8419ae2020-04-21 10:03:05 -0700108 pre_fix_redundancy_errors;
David Rogerscd134352020-04-17 19:37:15 -0700109 INF("KVS init: Redundancy level successfully updated");
110 } else {
111 WRN("KVS init: Corruption detected and fully repaired");
112 }
David Rogersfcea3252020-04-07 14:56:35 -0700113 initialized_ = InitializationState::kReady;
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700114 } else if (recovery_status == Status::ResourceExhausted()) {
David Rogersfcea3252020-04-07 14:56:35 -0700115 WRN("KVS init: Unable to maintain required free sector");
David Rogersfcea3252020-04-07 14:56:35 -0700116 } else {
117 WRN("KVS init: Corruption detected and unable repair");
David Rogersfcea3252020-04-07 14:56:35 -0700118 }
119 } else {
120 WRN("KVS init: Corruption detected, no repair attempted due to options");
David Rogersfcea3252020-04-07 14:56:35 -0700121 }
122 }
123
David Rogers9fc78b82020-06-12 13:56:12 -0700124 INF("KeyValueStore init complete: active keys %u, deleted keys %u, sectors "
125 "%u, logical sector size %u bytes",
126 unsigned(size()),
127 unsigned(entry_cache_.total_entries() - size()),
128 unsigned(sectors_.size()),
129 unsigned(partition_.sector_size_bytes()));
David Rogersfcea3252020-04-07 14:56:35 -0700130
131 // Report any corruption was not repaired.
132 if (error_detected_) {
133 WRN("KVS init: Corruption found but not repaired, KVS unavailable until "
134 "successful maintenance.");
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700135 return Status::DataLoss();
David Rogersfcea3252020-04-07 14:56:35 -0700136 }
137
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700138 return Status::Ok();
David Rogersfcea3252020-04-07 14:56:35 -0700139}
140
David Rogerscd134352020-04-17 19:37:15 -0700141Status KeyValueStore::InitializeMetadata() {
David Rogersfcea3252020-04-07 14:56:35 -0700142 const size_t sector_size_bytes = partition_.sector_size_bytes();
143
144 sectors_.Reset();
145 entry_cache_.Reset();
146
Keir Mierle8c352dc2020-02-02 13:58:19 -0800147 DBG("First pass: Read all entries from all sectors");
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800148 Address sector_address = 0;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800149
Alexei Frolovd4adf912020-02-21 13:29:15 -0800150 size_t total_corrupt_bytes = 0;
David Rogerscd134352020-04-17 19:37:15 -0700151 size_t corrupt_entries = 0;
David Rogers91627482020-02-27 17:38:12 -0800152 bool empty_sector_found = false;
David Rogerscd134352020-04-17 19:37:15 -0700153 size_t entry_copies_missing = 0;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800154
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800155 for (SectorDescriptor& sector : sectors_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800156 Address entry_address = sector_address;
157
Alexei Frolovd4adf912020-02-21 13:29:15 -0800158 size_t sector_corrupt_bytes = 0;
159
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800160 for (int num_entries_in_sector = 0; true; num_entries_in_sector++) {
David Rogersfcea3252020-04-07 14:56:35 -0700161 DBG("Load entry: sector=%u, entry#=%d, address=%u",
162 unsigned(sector_address),
Keir Mierle8c352dc2020-02-02 13:58:19 -0800163 num_entries_in_sector,
David Rogersfcea3252020-04-07 14:56:35 -0700164 unsigned(entry_address));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800165
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700166 if (!sectors_.AddressInSector(sector, entry_address)) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800167 DBG("Fell off end of sector; moving to the next sector");
168 break;
169 }
170
171 Address next_entry_address;
172 Status status = LoadEntry(entry_address, &next_entry_address);
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700173 if (status == Status::NotFound()) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800174 DBG("Hit un-written data in sector; moving to the next sector");
175 break;
David Rogersfcea3252020-04-07 14:56:35 -0700176 } else if (!status.ok()) {
177 // The entry could not be read, indicating likely data corruption within
178 // the sector. Try to scan the remainder of the sector for other
179 // entries.
Alexei Frolovd4adf912020-02-21 13:29:15 -0800180
David Rogers49766d92020-03-20 10:55:54 -0700181 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800182 corrupt_entries++;
183
184 status = ScanForEntry(sector,
185 entry_address + Entry::kMinAlignmentBytes,
186 &next_entry_address);
David Rogersfcea3252020-04-07 14:56:35 -0700187 if (!status.ok()) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800188 // No further entries in this sector. Mark the remaining bytes in the
189 // sector as corrupt (since we can't reliably know the size of the
190 // corrupt entry).
191 sector_corrupt_bytes +=
192 sector_size_bytes - (entry_address - sector_address);
193 break;
194 }
195
Alexei Frolovd4adf912020-02-21 13:29:15 -0800196 sector_corrupt_bytes += next_entry_address - entry_address;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800197 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800198
199 // Entry loaded successfully; so get ready to load the next one.
200 entry_address = next_entry_address;
201
202 // Update of the number of writable bytes in this sector.
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800203 sector.set_writable_bytes(sector_size_bytes -
204 (entry_address - sector_address));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800205 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800206
Alexei Frolovd4adf912020-02-21 13:29:15 -0800207 if (sector_corrupt_bytes > 0) {
208 // If the sector contains corrupt data, prevent any further entries from
209 // being written to it by indicating that it has no space. This should
210 // also make it a decent GC candidate. Valid keys in the sector are still
211 // readable as normal.
David Rogers49766d92020-03-20 10:55:54 -0700212 sector.mark_corrupt();
213 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800214
David Rogers9fc78b82020-06-12 13:56:12 -0700215 WRN("Sector %u contains %uB of corrupt data",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700216 sectors_.Index(sector),
David Rogers9fc78b82020-06-12 13:56:12 -0700217 unsigned(sector_corrupt_bytes));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800218 }
219
David Rogers91627482020-02-27 17:38:12 -0800220 if (sector.Empty(sector_size_bytes)) {
221 empty_sector_found = true;
222 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800223 sector_address += sector_size_bytes;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800224 total_corrupt_bytes += sector_corrupt_bytes;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800225 }
226
227 DBG("Second pass: Count valid bytes in each sector");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700228 Address newest_key = 0;
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800229
David Rogers98fea472020-04-01 15:43:48 -0700230 // For every valid entry, for each address, count the valid bytes in that
231 // sector. If the address fails to read, remove the address and mark the
232 // sector as corrupt. Track which entry has the newest transaction ID for
233 // initializing last_new_sector_.
234 for (EntryMetadata& metadata : entry_cache_) {
David Rogers49766d92020-03-20 10:55:54 -0700235 if (metadata.addresses().size() < redundancy()) {
David Rogers31b358b2020-04-15 05:00:50 -0700236 DBG("Key 0x%08x missing copies, has %u, needs %u",
237 unsigned(metadata.hash()),
238 unsigned(metadata.addresses().size()),
239 unsigned(redundancy()));
David Rogerscd134352020-04-17 19:37:15 -0700240 entry_copies_missing++;
David Rogers49766d92020-03-20 10:55:54 -0700241 }
David Rogers98fea472020-04-01 15:43:48 -0700242 size_t index = 0;
243 while (index < metadata.addresses().size()) {
244 Address address = metadata.addresses()[index];
David Rogersf56131c2020-03-04 10:19:22 -0800245 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700246
247 Status read_result = Entry::Read(partition_, address, formats_, &entry);
248
249 SectorDescriptor& sector = sectors_.FromAddress(address);
250
251 if (read_result.ok()) {
252 sector.AddValidBytes(entry.size());
253 index++;
254 } else {
255 corrupt_entries++;
256 total_corrupt_bytes += sector.writable_bytes();
257 error_detected_ = true;
258 sector.mark_corrupt();
259
260 // Remove the bad address and stay at this index. The removal
261 // replaces out the removed address with the back address so
262 // this index needs to be rechecked with the new address.
263 metadata.RemoveAddress(address);
264 }
David Rogersf56131c2020-03-04 10:19:22 -0800265 }
David Rogers98fea472020-04-01 15:43:48 -0700266
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700267 if (metadata.IsNewerThan(last_transaction_id_)) {
268 last_transaction_id_ = metadata.transaction_id();
269 newest_key = metadata.addresses().back();
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800270 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800271 }
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800272
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700273 sectors_.set_last_new_sector(newest_key);
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800274
David Rogers91627482020-02-27 17:38:12 -0800275 if (!empty_sector_found) {
David Rogers31b358b2020-04-15 05:00:50 -0700276 DBG("No empty sector found");
David Rogers9abe3c72020-03-24 19:03:13 -0700277 error_detected_ = true;
David Rogers91627482020-02-27 17:38:12 -0800278 }
279
David Rogerscd134352020-04-17 19:37:15 -0700280 if (entry_copies_missing > 0) {
281 bool other_errors = error_detected_;
282 error_detected_ = true;
283
Armando Montanez344814b2020-04-24 15:05:42 -0700284 if (!other_errors && entry_copies_missing == entry_cache_.total_entries()) {
David Rogers9fc78b82020-06-12 13:56:12 -0700285 INF("KVS configuration changed to redundancy of %u total copies per key",
286 unsigned(redundancy()));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700287 return Status::OutOfRange();
David Rogerscd134352020-04-17 19:37:15 -0700288 }
David Rogers9abe3c72020-03-24 19:03:13 -0700289 }
David Rogerscd134352020-04-17 19:37:15 -0700290
291 if (error_detected_) {
David Rogers9fc78b82020-06-12 13:56:12 -0700292 WRN("Corruption detected. Found %u corrupt bytes, %u corrupt entries, "
293 "and %u keys missing redundant copies.",
294 unsigned(total_corrupt_bytes),
295 unsigned(corrupt_entries),
296 unsigned(entry_copies_missing));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700297 return Status::FailedPrecondition();
David Rogerscd134352020-04-17 19:37:15 -0700298 }
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700299 return Status::Ok();
Keir Mierle8c352dc2020-02-02 13:58:19 -0800300}
301
Alexei Frolov9e235832020-02-24 12:44:45 -0800302KeyValueStore::StorageStats KeyValueStore::GetStorageStats() const {
David Rogers9abe3c72020-03-24 19:03:13 -0700303 StorageStats stats{};
Alexei Frolov9e235832020-02-24 12:44:45 -0800304 const size_t sector_size = partition_.sector_size_bytes();
305 bool found_empty_sector = false;
David Rogers3c298372020-10-12 14:15:39 -0700306 stats.sector_erase_count = internal_stats_.sector_erase_count;
307 stats.corrupt_sectors_recovered = internal_stats_.corrupt_sectors_recovered;
David Rogers9abe3c72020-03-24 19:03:13 -0700308 stats.missing_redundant_entries_recovered =
David Rogers3c298372020-10-12 14:15:39 -0700309 internal_stats_.missing_redundant_entries_recovered;
Alexei Frolov9e235832020-02-24 12:44:45 -0800310
311 for (const SectorDescriptor& sector : sectors_) {
312 stats.in_use_bytes += sector.valid_bytes();
313 stats.reclaimable_bytes += sector.RecoverableBytes(sector_size);
314
315 if (!found_empty_sector && sector.Empty(sector_size)) {
316 // The KVS tries to always keep an empty sector for GC, so don't count
317 // the first empty sector seen as writable space. However, a free sector
318 // cannot always be assumed to exist; if a GC operation fails, all sectors
319 // may be partially written, in which case the space reported might be
320 // inaccurate.
321 found_empty_sector = true;
322 continue;
323 }
324
325 stats.writable_bytes += sector.writable_bytes();
326 }
327
328 return stats;
329}
330
David Rogers98fea472020-04-01 15:43:48 -0700331// Check KVS for any error conditions. Primarily intended for test and
332// internal use.
David Rogers9abe3c72020-03-24 19:03:13 -0700333bool KeyValueStore::CheckForErrors() {
334 // Check for corrupted sectors
335 for (SectorDescriptor& sector : sectors_) {
336 if (sector.corrupt()) {
337 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700338 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700339 }
340 }
341
342 // Check for missing redundancy.
343 if (redundancy() > 1) {
344 for (const EntryMetadata& metadata : entry_cache_) {
345 if (metadata.addresses().size() < redundancy()) {
346 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700347 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700348 }
349 }
350 }
351
352 return error_detected();
353}
354
Keir Mierle8c352dc2020-02-02 13:58:19 -0800355Status KeyValueStore::LoadEntry(Address entry_address,
356 Address* next_entry_address) {
Wyatt Heplere541e072020-02-14 09:10:53 -0800357 Entry entry;
David Rogersc4dc8642020-09-14 10:52:36 -0700358 PW_TRY(Entry::Read(partition_, entry_address, formats_, &entry));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800359
360 // Read the key from flash & validate the entry (which reads the value).
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800361 Entry::KeyBuffer key_buffer;
David Rogersc4dc8642020-09-14 10:52:36 -0700362 PW_TRY_ASSIGN(size_t key_length, entry.ReadKey(key_buffer));
Wyatt Heplere541e072020-02-14 09:10:53 -0800363 const string_view key(key_buffer.data(), key_length);
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800364
David Rogersc4dc8642020-09-14 10:52:36 -0700365 PW_TRY(entry.VerifyChecksumInFlash());
David Rogersf56131c2020-03-04 10:19:22 -0800366
367 // A valid entry was found, so update the next entry address before doing any
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700368 // of the checks that happen in AddNewOrUpdateExisting.
David Rogersf56131c2020-03-04 10:19:22 -0800369 *next_entry_address = entry.next_address();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700370 return entry_cache_.AddNewOrUpdateExisting(
371 entry.descriptor(key), entry.address(), partition_.sector_size_bytes());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800372}
373
Alexei Frolovd4adf912020-02-21 13:29:15 -0800374// Scans flash memory within a sector to find a KVS entry magic.
Alexei Frolovd4adf912020-02-21 13:29:15 -0800375Status KeyValueStore::ScanForEntry(const SectorDescriptor& sector,
376 Address start_address,
377 Address* next_entry_address) {
David Rogersfcea3252020-04-07 14:56:35 -0700378 DBG("Scanning sector %u for entries starting from address %u",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700379 sectors_.Index(sector),
David Rogersfcea3252020-04-07 14:56:35 -0700380 unsigned(start_address));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800381
382 // Entries must start at addresses which are aligned on a multiple of
383 // Entry::kMinAlignmentBytes. However, that multiple can vary between entries.
384 // When scanning, we don't have an entry to tell us what the current alignment
385 // is, so the minimum alignment is used to be exhaustive.
386 for (Address address = AlignUp(start_address, Entry::kMinAlignmentBytes);
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700387 sectors_.AddressInSector(sector, address);
Alexei Frolovd4adf912020-02-21 13:29:15 -0800388 address += Entry::kMinAlignmentBytes) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800389 uint32_t magic;
David Rogersfcea3252020-04-07 14:56:35 -0700390 StatusWithSize read_result =
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700391 partition_.Read(address, std::as_writable_bytes(std::span(&magic, 1)));
David Rogersfcea3252020-04-07 14:56:35 -0700392 if (!read_result.ok()) {
393 continue;
394 }
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800395 if (formats_.KnownMagic(magic)) {
David Rogersfcea3252020-04-07 14:56:35 -0700396 DBG("Found entry magic at address %u", unsigned(address));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800397 *next_entry_address = address;
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700398 return Status::Ok();
Alexei Frolovd4adf912020-02-21 13:29:15 -0800399 }
400 }
401
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700402 return Status::NotFound();
Alexei Frolovd4adf912020-02-21 13:29:15 -0800403}
404
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800405StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700406 std::span<byte> value_buffer,
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800407 size_t offset_bytes) const {
David Rogersc4dc8642020-09-14 10:52:36 -0700408 PW_TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800409
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700410 EntryMetadata metadata;
David Rogersc4dc8642020-09-14 10:52:36 -0700411 PW_TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800412
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700413 return Get(key, metadata, value_buffer, offset_bytes);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800414}
415
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700416Status KeyValueStore::PutBytes(string_view key, std::span<const byte> value) {
David Rogersc4dc8642020-09-14 10:52:36 -0700417 PW_TRY(CheckWriteOperation(key));
David Rogers9fc78b82020-06-12 13:56:12 -0700418 DBG("Writing key/value; key length=%u, value length=%u",
419 unsigned(key.size()),
420 unsigned(value.size()));
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800421
Wyatt Hepler5406a672020-02-18 15:42:38 -0800422 if (Entry::size(partition_, key, value) > partition_.sector_size_bytes()) {
David Rogers9fc78b82020-06-12 13:56:12 -0700423 DBG("%u B value with %u B key cannot fit in one sector",
424 unsigned(value.size()),
425 unsigned(key.size()));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700426 return Status::InvalidArgument();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800427 }
428
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700429 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700430 Status status = FindEntry(key, &metadata);
Wyatt Hepler2d401692020-02-13 16:01:23 -0800431
432 if (status.ok()) {
David Rogersf56131c2020-03-04 10:19:22 -0800433 // TODO: figure out logging how to support multiple addresses.
David Rogers9fc78b82020-06-12 13:56:12 -0700434 DBG("Overwriting entry for key 0x%08x in %u sectors including %u",
435 unsigned(metadata.hash()),
436 unsigned(metadata.addresses().size()),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700437 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700438 return WriteEntryForExistingKey(metadata, EntryState::kValid, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800439 }
David Rogers2761aeb2020-01-31 17:09:00 -0800440
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700441 if (status == Status::NotFound()) {
Wyatt Hepler2d401692020-02-13 16:01:23 -0800442 return WriteEntryForNewKey(key, value);
443 }
444
445 return status;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800446}
447
448Status KeyValueStore::Delete(string_view key) {
David Rogersc4dc8642020-09-14 10:52:36 -0700449 PW_TRY(CheckWriteOperation(key));
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800450
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700451 EntryMetadata metadata;
David Rogersc4dc8642020-09-14 10:52:36 -0700452 PW_TRY(FindExisting(key, &metadata));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800453
David Rogersf56131c2020-03-04 10:19:22 -0800454 // TODO: figure out logging how to support multiple addresses.
David Rogers9fc78b82020-06-12 13:56:12 -0700455 DBG("Writing tombstone for key 0x%08x in %u sectors including %u",
456 unsigned(metadata.hash()),
457 unsigned(metadata.addresses().size()),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700458 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700459 return WriteEntryForExistingKey(metadata, EntryState::kDeleted, key, {});
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800460}
461
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800462void KeyValueStore::Item::ReadKey() {
463 key_buffer_.fill('\0');
464
465 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700466 if (kvs_.ReadEntry(*iterator_, entry).ok()) {
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800467 entry.ReadKey(key_buffer_);
468 }
469}
470
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800471KeyValueStore::iterator& KeyValueStore::iterator::operator++() {
472 // Skip to the next entry that is valid (not deleted).
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700473 while (++item_.iterator_ != item_.kvs_.entry_cache_.end() &&
Wyatt Hepler02946272020-03-18 10:36:22 -0700474 item_.iterator_->state() != EntryState::kValid) {
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800475 }
476 return *this;
477}
478
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800479KeyValueStore::iterator KeyValueStore::begin() const {
Wyatt Heplerbfc6a522020-04-01 16:30:24 -0700480 internal::EntryCache::const_iterator cache_iterator = entry_cache_.begin();
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800481 // Skip over any deleted entries at the start of the descriptor list.
Wyatt Hepler02946272020-03-18 10:36:22 -0700482 while (cache_iterator != entry_cache_.end() &&
483 cache_iterator->state() != EntryState::kValid) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700484 ++cache_iterator;
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800485 }
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700486 return iterator(*this, cache_iterator);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800487}
488
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700489StatusWithSize KeyValueStore::ValueSize(string_view key) const {
David Rogersc4dc8642020-09-14 10:52:36 -0700490 PW_TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Heplered163b02020-02-03 17:49:32 -0800491
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700492 EntryMetadata metadata;
David Rogersc4dc8642020-09-14 10:52:36 -0700493 PW_TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800494
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700495 return ValueSize(metadata);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800496}
Wyatt Heplered163b02020-02-03 17:49:32 -0800497
David Rogers98fea472020-04-01 15:43:48 -0700498Status KeyValueStore::ReadEntry(const EntryMetadata& metadata,
499 Entry& entry) const {
500 // Try to read an entry
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700501 Status read_result = Status::DataLoss();
David Rogers98fea472020-04-01 15:43:48 -0700502 for (Address address : metadata.addresses()) {
503 read_result = Entry::Read(partition_, address, formats_, &entry);
504 if (read_result.ok()) {
505 return read_result;
506 }
507
508 // Found a bad address. Set the sector as corrupt.
509 error_detected_ = true;
510 sectors_.FromAddress(address).mark_corrupt();
511 }
512
513 ERR("No valid entries for key. Data has been lost!");
514 return read_result;
515}
516
517Status KeyValueStore::FindEntry(string_view key,
518 EntryMetadata* found_entry) const {
519 StatusWithSize find_result =
520 entry_cache_.Find(partition_, sectors_, formats_, key, found_entry);
521
522 if (find_result.size() > 0u) {
523 error_detected_ = true;
524 }
525 return find_result.status();
526}
527
528Status KeyValueStore::FindExisting(string_view key,
529 EntryMetadata* metadata) const {
530 Status status = FindEntry(key, metadata);
531
532 // If the key's hash collides with an existing key or if the key is deleted,
533 // treat it as if it is not in the KVS.
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700534 if (status == Status::AlreadyExists() ||
David Rogers98fea472020-04-01 15:43:48 -0700535 (status.ok() && metadata->state() == EntryState::kDeleted)) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700536 return Status::NotFound();
David Rogers98fea472020-04-01 15:43:48 -0700537 }
538 return status;
539}
540
Wyatt Heplerfac81132020-02-27 17:26:33 -0800541StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700542 const EntryMetadata& metadata,
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700543 std::span<std::byte> value_buffer,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800544 size_t offset_bytes) const {
545 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700546
David Rogersc4dc8642020-09-14 10:52:36 -0700547 PW_TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800548
549 StatusWithSize result = entry.ReadValue(value_buffer, offset_bytes);
550 if (result.ok() && options_.verify_on_read && offset_bytes == 0u) {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800551 Status verify_result =
552 entry.VerifyChecksum(key, value_buffer.first(result.size()));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800553 if (!verify_result.ok()) {
554 std::memset(value_buffer.data(), 0, result.size());
555 return StatusWithSize(verify_result, 0);
556 }
557
558 return StatusWithSize(verify_result, result.size());
559 }
560 return result;
Wyatt Heplered163b02020-02-03 17:49:32 -0800561}
562
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800563Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800564 void* value,
565 size_t size_bytes) const {
David Rogersc4dc8642020-09-14 10:52:36 -0700566 PW_TRY(CheckWriteOperation(key));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800567
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700568 EntryMetadata metadata;
David Rogersc4dc8642020-09-14 10:52:36 -0700569 PW_TRY(FindExisting(key, &metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800570
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700571 return FixedSizeGet(key, metadata, value, size_bytes);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800572}
573
574Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700575 const EntryMetadata& metadata,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800576 void* value,
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800577 size_t size_bytes) const {
578 // Ensure that the size of the stored value matches the size of the type.
579 // Otherwise, report error. This check avoids potential memory corruption.
David Rogersc4dc8642020-09-14 10:52:36 -0700580 PW_TRY_ASSIGN(const size_t actual_size, ValueSize(metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800581
582 if (actual_size != size_bytes) {
David Rogers9fc78b82020-06-12 13:56:12 -0700583 DBG("Requested %u B read, but value is %u B",
584 unsigned(size_bytes),
585 unsigned(actual_size));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700586 return Status::InvalidArgument();
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800587 }
Wyatt Heplerfac81132020-02-27 17:26:33 -0800588
589 StatusWithSize result =
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700590 Get(key, metadata, std::span(static_cast<byte*>(value), size_bytes), 0);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800591
592 return result.status();
593}
594
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700595StatusWithSize KeyValueStore::ValueSize(const EntryMetadata& metadata) const {
Wyatt Heplerfac81132020-02-27 17:26:33 -0800596 Entry entry;
David Rogersc4dc8642020-09-14 10:52:36 -0700597 PW_TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800598
599 return StatusWithSize(entry.value_size());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800600}
601
David Rogers9abe3c72020-03-24 19:03:13 -0700602Status KeyValueStore::CheckWriteOperation(string_view key) const {
Wyatt Hepleracaacf92020-01-24 10:58:30 -0800603 if (InvalidKey(key)) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700604 return Status::InvalidArgument();
Wyatt Heplerb7609542020-01-24 10:29:54 -0800605 }
David Rogers9abe3c72020-03-24 19:03:13 -0700606
607 // For normal write operation the KVS must be fully ready.
Wyatt Heplerd2298282020-02-20 17:12:45 -0800608 if (!initialized()) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700609 return Status::FailedPrecondition();
Wyatt Heplerb7609542020-01-24 10:29:54 -0800610 }
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700611 return Status::Ok();
Wyatt Heplerb7609542020-01-24 10:29:54 -0800612}
613
David Rogers9abe3c72020-03-24 19:03:13 -0700614Status KeyValueStore::CheckReadOperation(string_view key) const {
615 if (InvalidKey(key)) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700616 return Status::InvalidArgument();
David Rogers9abe3c72020-03-24 19:03:13 -0700617 }
618
619 // Operations that are explicitly read-only can be done after init() has been
620 // called but not fully ready (when needing maintenance).
621 if (initialized_ == InitializationState::kNotInitialized) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700622 return Status::FailedPrecondition();
David Rogers9abe3c72020-03-24 19:03:13 -0700623 }
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700624 return Status::Ok();
David Rogers9abe3c72020-03-24 19:03:13 -0700625}
626
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700627Status KeyValueStore::WriteEntryForExistingKey(EntryMetadata& metadata,
628 EntryState new_state,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800629 string_view key,
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700630 std::span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700631 // Read the original entry to get the size for sector accounting purposes.
632 Entry entry;
David Rogersc4dc8642020-09-14 10:52:36 -0700633 PW_TRY(ReadEntry(metadata, entry));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800634
David Rogersc0104462020-05-08 15:50:23 -0700635 return WriteEntry(key, value, new_state, &metadata, &entry);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800636}
637
638Status KeyValueStore::WriteEntryForNewKey(string_view key,
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700639 std::span<const byte> value) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700640 if (entry_cache_.full()) {
David Rogers9fc78b82020-06-12 13:56:12 -0700641 WRN("KVS full: trying to store a new entry, but can't. Have %u entries",
642 unsigned(entry_cache_.total_entries()));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700643 return Status::ResourceExhausted();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800644 }
645
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700646 return WriteEntry(key, value, EntryState::kValid);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700647}
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800648
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700649Status KeyValueStore::WriteEntry(string_view key,
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700650 std::span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700651 EntryState new_state,
652 EntryMetadata* prior_metadata,
David Rogersc0104462020-05-08 15:50:23 -0700653 const Entry* prior_entry) {
David Rogersc0104462020-05-08 15:50:23 -0700654 // If new entry and prior entry have matching value size, state, and checksum,
655 // check if the values match. Directly compare the prior and new values
656 // because the checksum can not be depended on to establish equality, it can
657 // only be depended on to establish inequality.
David Rogersd50eb1c2020-05-12 17:46:36 -0700658 if (prior_entry != nullptr && prior_entry->value_size() == value.size() &&
David Rogersc0104462020-05-08 15:50:23 -0700659 prior_metadata->state() == new_state &&
David Rogersc0104462020-05-08 15:50:23 -0700660 prior_entry->ValueMatches(value).ok()) {
David Rogersd50eb1c2020-05-12 17:46:36 -0700661 // The new value matches the prior value, don't need to write anything. Just
662 // keep the existing entry.
David Rogersc0104462020-05-08 15:50:23 -0700663 DBG("Write for key 0x%08x with matching value skipped",
664 unsigned(prior_metadata->hash()));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700665 return Status::Ok();
David Rogersc0104462020-05-08 15:50:23 -0700666 }
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700667
668 // List of addresses for sectors with space for this entry.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700669 Address* reserved_addresses = entry_cache_.TempReservedAddressesForWrite();
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700670
David Rogers31b358b2020-04-15 05:00:50 -0700671 // Find addresses to write the entry to. This may involve garbage collecting
672 // one or more sectors.
David Rogersc0104462020-05-08 15:50:23 -0700673 const size_t entry_size = Entry::size(partition_, key, value);
David Rogersc4dc8642020-09-14 10:52:36 -0700674 PW_TRY(GetAddressesForWrite(reserved_addresses, entry_size));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700675
676 // Write the entry at the first address that was found.
David Rogersd50eb1c2020-05-12 17:46:36 -0700677 Entry entry = CreateEntry(reserved_addresses[0], key, value, new_state);
David Rogersc4dc8642020-09-14 10:52:36 -0700678 PW_TRY(AppendEntry(entry, key, value));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700679
680 // After writing the first entry successfully, update the key descriptors.
681 // Once a single new the entry is written, the old entries are invalidated.
David Rogersc0104462020-05-08 15:50:23 -0700682 size_t prior_size = prior_entry != nullptr ? prior_entry->size() : 0;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700683 EntryMetadata new_metadata =
David Rogers31b358b2020-04-15 05:00:50 -0700684 CreateOrUpdateKeyDescriptor(entry, key, prior_metadata, prior_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700685
686 // Write the additional copies of the entry, if redundancy is greater than 1.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700687 for (size_t i = 1; i < redundancy(); ++i) {
688 entry.set_address(reserved_addresses[i]);
David Rogersc4dc8642020-09-14 10:52:36 -0700689 PW_TRY(AppendEntry(entry, key, value));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700690 new_metadata.AddNewAddress(reserved_addresses[i]);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700691 }
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700692 return Status::Ok();
Wyatt Heplerb7609542020-01-24 10:29:54 -0800693}
694
David Rogers31b358b2020-04-15 05:00:50 -0700695KeyValueStore::EntryMetadata KeyValueStore::CreateOrUpdateKeyDescriptor(
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700696 const Entry& entry,
697 string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700698 EntryMetadata* prior_metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700699 size_t prior_size) {
700 // If there is no prior descriptor, create a new one.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700701 if (prior_metadata == nullptr) {
702 return entry_cache_.AddNew(entry.descriptor(key), entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800703 }
704
David Rogers31b358b2020-04-15 05:00:50 -0700705 return UpdateKeyDescriptor(
706 entry, entry.address(), prior_metadata, prior_size);
707}
708
709KeyValueStore::EntryMetadata KeyValueStore::UpdateKeyDescriptor(
710 const Entry& entry,
711 Address new_address,
712 EntryMetadata* prior_metadata,
713 size_t prior_size) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700714 // Remove valid bytes for the old entry and its copies, which are now stale.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700715 for (Address address : prior_metadata->addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700716 sectors_.FromAddress(address).RemoveValidBytes(prior_size);
David Rogersa2562b52020-03-05 15:30:05 -0800717 }
718
David Rogers31b358b2020-04-15 05:00:50 -0700719 prior_metadata->Reset(entry.descriptor(prior_metadata->hash()), new_address);
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700720 return *prior_metadata;
David Rogersa2562b52020-03-05 15:30:05 -0800721}
722
David Rogers31b358b2020-04-15 05:00:50 -0700723Status KeyValueStore::GetAddressesForWrite(Address* write_addresses,
724 size_t write_size) {
725 for (size_t i = 0; i < redundancy(); i++) {
726 SectorDescriptor* sector;
David Rogersc4dc8642020-09-14 10:52:36 -0700727 PW_TRY(
728 GetSectorForWrite(&sector, write_size, std::span(write_addresses, i)));
David Rogers31b358b2020-04-15 05:00:50 -0700729 write_addresses[i] = sectors_.NextWritableAddress(*sector);
730
731 DBG("Found space for entry in sector %u at address %u",
732 sectors_.Index(sector),
733 unsigned(write_addresses[i]));
734 }
735
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700736 return Status::Ok();
David Rogers31b358b2020-04-15 05:00:50 -0700737}
738
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700739// Finds a sector to use for writing a new entry to. Does automatic garbage
David Rogersa2562b52020-03-05 15:30:05 -0800740// collection if needed and allowed.
741//
742// OK: Sector found with needed space.
743// RESOURCE_EXHAUSTED: No sector available with the needed space.
744Status KeyValueStore::GetSectorForWrite(SectorDescriptor** sector,
745 size_t entry_size,
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700746 std::span<const Address> reserved) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700747 Status result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800748
David Rogersf3884eb2020-03-08 19:21:40 -0700749 size_t gc_sector_count = 0;
David Rogersa2562b52020-03-05 15:30:05 -0800750 bool do_auto_gc = options_.gc_on_write != GargbageCollectOnWrite::kDisabled;
751
752 // Do garbage collection as needed, so long as policy allows.
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700753 while (result == Status::ResourceExhausted() && do_auto_gc) {
David Rogersa2562b52020-03-05 15:30:05 -0800754 if (options_.gc_on_write == GargbageCollectOnWrite::kOneSector) {
755 // If GC config option is kOneSector clear the flag to not do any more
756 // GC after this try.
757 do_auto_gc = false;
758 }
759 // Garbage collect and then try again to find the best sector.
David Rogers9abe3c72020-03-24 19:03:13 -0700760 Status gc_status = GarbageCollect(reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800761 if (!gc_status.ok()) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700762 if (gc_status == Status::NotFound()) {
David Rogersa2562b52020-03-05 15:30:05 -0800763 // Not enough space, and no reclaimable bytes, this KVS is full!
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700764 return Status::ResourceExhausted();
David Rogersa2562b52020-03-05 15:30:05 -0800765 }
766 return gc_status;
767 }
768
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700769 result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersf3884eb2020-03-08 19:21:40 -0700770
771 gc_sector_count++;
772 // Allow total sectors + 2 number of GC cycles so that once reclaimable
773 // bytes in all the sectors have been reclaimed can try and free up space by
774 // moving entries for keys other than the one being worked on in to sectors
775 // that have copies of the key trying to be written.
776 if (gc_sector_count > (partition_.sector_count() + 2)) {
777 ERR("Did more GC sectors than total sectors!!!!");
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700778 return Status::ResourceExhausted();
David Rogersf3884eb2020-03-08 19:21:40 -0700779 }
David Rogersa2562b52020-03-05 15:30:05 -0800780 }
781
782 if (!result.ok()) {
David Rogers9fc78b82020-06-12 13:56:12 -0700783 WRN("Unable to find sector to write %u B", unsigned(entry_size));
David Rogersa2562b52020-03-05 15:30:05 -0800784 }
785 return result;
786}
787
David Rogers9abe3c72020-03-24 19:03:13 -0700788Status KeyValueStore::MarkSectorCorruptIfNotOk(Status status,
789 SectorDescriptor* sector) {
790 if (!status.ok()) {
791 DBG(" Sector %u corrupt", sectors_.Index(sector));
792 sector->mark_corrupt();
793 error_detected_ = true;
794 }
795 return status;
796}
797
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700798Status KeyValueStore::AppendEntry(const Entry& entry,
David Rogersa2562b52020-03-05 15:30:05 -0800799 string_view key,
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700800 std::span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700801 const StatusWithSize result = entry.Write(key, value);
David Rogersa2562b52020-03-05 15:30:05 -0800802
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700803 SectorDescriptor& sector = sectors_.FromAddress(entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800804
805 if (!result.ok()) {
David Rogers9fc78b82020-06-12 13:56:12 -0700806 ERR("Failed to write %u bytes at %#x. %u actually written",
807 unsigned(entry.size()),
808 unsigned(entry.address()),
809 unsigned(result.size()));
David Rogersc4dc8642020-09-14 10:52:36 -0700810 PW_TRY(MarkSectorCorruptIfNotOk(result.status(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800811 }
812
813 if (options_.verify_on_write) {
David Rogersc4dc8642020-09-14 10:52:36 -0700814 PW_TRY(MarkSectorCorruptIfNotOk(entry.VerifyChecksumInFlash(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800815 }
816
David Rogers98fea472020-04-01 15:43:48 -0700817 sector.RemoveWritableBytes(result.size());
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700818 sector.AddValidBytes(result.size());
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700819 return Status::Ok();
David Rogersa2562b52020-03-05 15:30:05 -0800820}
821
David Rogers98fea472020-04-01 15:43:48 -0700822StatusWithSize KeyValueStore::CopyEntryToSector(Entry& entry,
823 SectorDescriptor* new_sector,
David Rogers31b358b2020-04-15 05:00:50 -0700824 Address new_address) {
David Rogers98fea472020-04-01 15:43:48 -0700825 const StatusWithSize result = entry.Copy(new_address);
826
David Rogersc4dc8642020-09-14 10:52:36 -0700827 PW_TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(result.status(), new_sector));
David Rogers98fea472020-04-01 15:43:48 -0700828
829 if (options_.verify_on_write) {
David Rogers31b358b2020-04-15 05:00:50 -0700830 Entry new_entry;
David Rogersc4dc8642020-09-14 10:52:36 -0700831 PW_TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(
David Rogers31b358b2020-04-15 05:00:50 -0700832 Entry::Read(partition_, new_address, formats_, &new_entry),
833 new_sector));
834 // TODO: add test that catches doing the verify on the old entry.
David Rogersc4dc8642020-09-14 10:52:36 -0700835 PW_TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(new_entry.VerifyChecksumInFlash(),
836 new_sector));
David Rogers98fea472020-04-01 15:43:48 -0700837 }
838 // Entry was written successfully; update descriptor's address and the sector
839 // descriptors to reflect the new entry.
840 new_sector->RemoveWritableBytes(result.size());
841 new_sector->AddValidBytes(result.size());
842
843 return result;
844}
845
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700846Status KeyValueStore::RelocateEntry(
847 const EntryMetadata& metadata,
848 KeyValueStore::Address& address,
849 std::span<const Address> reserved_addresses) {
David Rogersa2562b52020-03-05 15:30:05 -0800850 Entry entry;
David Rogersc4dc8642020-09-14 10:52:36 -0700851 PW_TRY(ReadEntry(metadata, entry));
David Rogersa2562b52020-03-05 15:30:05 -0800852
853 // Find a new sector for the entry and write it to the new location. For
854 // relocation the find should not not be a sector already containing the key
855 // but can be the always empty sector, since this is part of the GC process
856 // that will result in a new empty sector. Also find a sector that does not
857 // have reclaimable space (mostly for the full GC, where that would result in
858 // an immediate extra relocation).
859 SectorDescriptor* new_sector;
860
David Rogersc4dc8642020-09-14 10:52:36 -0700861 PW_TRY(sectors_.FindSpaceDuringGarbageCollection(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700862 &new_sector, entry.size(), metadata.addresses(), reserved_addresses));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700863
David Rogers31b358b2020-04-15 05:00:50 -0700864 Address new_address = sectors_.NextWritableAddress(*new_sector);
David Rogersc4dc8642020-09-14 10:52:36 -0700865 PW_TRY_ASSIGN(const size_t result_size,
866 CopyEntryToSector(entry, new_sector, new_address));
David Rogers98fea472020-04-01 15:43:48 -0700867 sectors_.FromAddress(address).RemoveValidBytes(result_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700868 address = new_address;
David Rogersa2562b52020-03-05 15:30:05 -0800869
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700870 return Status::Ok();
David Rogersa2562b52020-03-05 15:30:05 -0800871}
872
David Rogers3c298372020-10-12 14:15:39 -0700873Status KeyValueStore::FullMaintenanceHelper(MaintenanceType maintenance_type) {
David Rogers9abe3c72020-03-24 19:03:13 -0700874 if (initialized_ == InitializationState::kNotInitialized) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700875 return Status::FailedPrecondition();
David Rogers9abe3c72020-03-24 19:03:13 -0700876 }
877
Armando Montanez17083bb2020-04-24 10:18:26 -0700878 // Full maintenance can be a potentially heavy operation, and should be
879 // relatively infrequent, so log start/end at INFO level.
880 INF("Beginning full maintenance");
David Rogers98fea472020-04-01 15:43:48 -0700881 CheckForErrors();
David Rogers9abe3c72020-03-24 19:03:13 -0700882
883 if (error_detected_) {
David Rogersc4dc8642020-09-14 10:52:36 -0700884 PW_TRY(Repair());
David Rogers9abe3c72020-03-24 19:03:13 -0700885 }
David Rogers35c3f842020-04-22 15:34:05 -0700886 StatusWithSize update_status = UpdateEntriesToPrimaryFormat();
887 Status overall_status = update_status.status();
888
David Rogers31b358b2020-04-15 05:00:50 -0700889 // Make sure all the entries are on the primary format.
Armando Montanez17083bb2020-04-24 10:18:26 -0700890 if (!overall_status.ok()) {
891 ERR("Failed to update all entries to the primary format");
892 }
David Rogers31b358b2020-04-15 05:00:50 -0700893
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700894 SectorDescriptor* sector = sectors_.last_new();
David Rogerscd87c322020-02-27 14:04:08 -0800895
David Rogers35c3f842020-04-22 15:34:05 -0700896 // Calculate number of bytes for the threshold.
897 size_t threshold_bytes =
898 (partition_.size_bytes() * kGcUsageThresholdPercentage) / 100;
899
900 // Is bytes in use over the threshold.
901 StorageStats stats = GetStorageStats();
902 bool over_usage_threshold = stats.in_use_bytes > threshold_bytes;
David Rogers3c298372020-10-12 14:15:39 -0700903 bool heavy = (maintenance_type == MaintenanceType::kHeavy);
904 bool force_gc = heavy || over_usage_threshold || (update_status.size() > 0);
David Rogers35c3f842020-04-22 15:34:05 -0700905
David Rogerscd87c322020-02-27 14:04:08 -0800906 // TODO: look in to making an iterator method for cycling through sectors
907 // starting from last_new_sector_.
Armando Montanez17083bb2020-04-24 10:18:26 -0700908 Status gc_status;
David Rogerscd87c322020-02-27 14:04:08 -0800909 for (size_t j = 0; j < sectors_.size(); j++) {
910 sector += 1;
911 if (sector == sectors_.end()) {
912 sector = sectors_.begin();
913 }
914
David Rogers35c3f842020-04-22 15:34:05 -0700915 if (sector->RecoverableBytes(partition_.sector_size_bytes()) > 0 &&
916 (force_gc || sector->valid_bytes() == 0)) {
Armando Montanez17083bb2020-04-24 10:18:26 -0700917 gc_status = GarbageCollectSector(*sector, {});
918 if (!gc_status.ok()) {
919 ERR("Failed to garbage collect all sectors");
920 break;
921 }
David Rogerscd87c322020-02-27 14:04:08 -0800922 }
923 }
Armando Montanez17083bb2020-04-24 10:18:26 -0700924 if (overall_status.ok()) {
925 overall_status = gc_status;
926 }
David Rogerscd87c322020-02-27 14:04:08 -0800927
Armando Montanez17083bb2020-04-24 10:18:26 -0700928 if (overall_status.ok()) {
929 INF("Full maintenance complete");
930 } else {
931 ERR("Full maintenance finished with some errors");
932 }
933 return overall_status;
David Rogerscd87c322020-02-27 14:04:08 -0800934}
935
David Rogers0f8a1bb2020-04-21 18:50:19 -0700936Status KeyValueStore::PartialMaintenance() {
David Rogers9abe3c72020-03-24 19:03:13 -0700937 if (initialized_ == InitializationState::kNotInitialized) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700938 return Status::FailedPrecondition();
David Rogers9abe3c72020-03-24 19:03:13 -0700939 }
940
David Rogers0f8a1bb2020-04-21 18:50:19 -0700941 CheckForErrors();
David Rogersfcea3252020-04-07 14:56:35 -0700942 // Do automatic repair, if KVS options allow for it.
943 if (error_detected_ && options_.recovery != ErrorRecovery::kManual) {
David Rogersc4dc8642020-09-14 10:52:36 -0700944 PW_TRY(Repair());
David Rogersfcea3252020-04-07 14:56:35 -0700945 }
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700946 return GarbageCollect(std::span<const Address>());
David Rogers0f8a1bb2020-04-21 18:50:19 -0700947}
948
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700949Status KeyValueStore::GarbageCollect(
950 std::span<const Address> reserved_addresses) {
David Rogers0f8a1bb2020-04-21 18:50:19 -0700951 DBG("Garbage Collect a single sector");
952 for (Address address : reserved_addresses) {
953 DBG(" Avoid address %u", unsigned(address));
954 }
David Rogersfcea3252020-04-07 14:56:35 -0700955
David Rogersa12786b2020-01-31 16:02:33 -0800956 // Step 1: Find the sector to garbage collect
David Rogersc9d545e2020-03-11 17:47:43 -0700957 SectorDescriptor* sector_to_gc =
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700958 sectors_.FindSectorToGarbageCollect(reserved_addresses);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800959
David Rogersa12786b2020-01-31 16:02:33 -0800960 if (sector_to_gc == nullptr) {
David Rogersa2562b52020-03-05 15:30:05 -0800961 // Nothing to GC.
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700962 return Status::NotFound();
David Rogersa12786b2020-01-31 16:02:33 -0800963 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800964
David Rogersc9d545e2020-03-11 17:47:43 -0700965 // Step 2: Garbage collect the selected sector.
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700966 return GarbageCollectSector(*sector_to_gc, reserved_addresses);
David Rogerscd87c322020-02-27 14:04:08 -0800967}
968
David Rogersf3884eb2020-03-08 19:21:40 -0700969Status KeyValueStore::RelocateKeyAddressesInSector(
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700970 SectorDescriptor& sector_to_gc,
David Rogersfcea3252020-04-07 14:56:35 -0700971 const EntryMetadata& metadata,
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700972 std::span<const Address> reserved_addresses) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700973 for (FlashPartition::Address& address : metadata.addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700974 if (sectors_.AddressInSector(sector_to_gc, address)) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700975 DBG(" Relocate entry for Key 0x%08" PRIx32 ", sector %u",
976 metadata.hash(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700977 sectors_.Index(sectors_.FromAddress(address)));
David Rogersc4dc8642020-09-14 10:52:36 -0700978 PW_TRY(RelocateEntry(metadata, address, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700979 }
980 }
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700981
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700982 return Status::Ok();
David Rogersf3884eb2020-03-08 19:21:40 -0700983};
984
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700985Status KeyValueStore::GarbageCollectSector(
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700986 SectorDescriptor& sector_to_gc,
987 std::span<const Address> reserved_addresses) {
David Rogers9abe3c72020-03-24 19:03:13 -0700988 DBG(" Garbage Collect sector %u", sectors_.Index(sector_to_gc));
David Rogers3c298372020-10-12 14:15:39 -0700989
David Rogersf3884eb2020-03-08 19:21:40 -0700990 // Step 1: Move any valid entries in the GC sector to other sectors
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700991 if (sector_to_gc.valid_bytes() != 0) {
David Rogers98fea472020-04-01 15:43:48 -0700992 for (EntryMetadata& metadata : entry_cache_) {
David Rogersc4dc8642020-09-14 10:52:36 -0700993 PW_TRY(RelocateKeyAddressesInSector(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700994 sector_to_gc, metadata, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700995 }
996 }
997
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700998 if (sector_to_gc.valid_bytes() != 0) {
David Rogers67f4b6c2020-02-06 16:17:09 -0800999 ERR(" Failed to relocate valid entries from sector being garbage "
David Rogers9fc78b82020-06-12 13:56:12 -07001000 "collected, %u valid bytes remain",
1001 unsigned(sector_to_gc.valid_bytes()));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -07001002 return Status::Internal();
Wyatt Heplerb7609542020-01-24 10:29:54 -08001003 }
1004
David Rogerscd87c322020-02-27 14:04:08 -08001005 // Step 2: Reinitialize the sector
David Rogers3c298372020-10-12 14:15:39 -07001006 if (!sector_to_gc.Empty(partition_.sector_size_bytes())) {
1007 sector_to_gc.mark_corrupt();
1008 internal_stats_.sector_erase_count++;
1009 PW_TRY(partition_.Erase(sectors_.BaseAddress(sector_to_gc), 1));
1010 sector_to_gc.set_writable_bytes(partition_.sector_size_bytes());
1011 }
Wyatt Heplerb7609542020-01-24 10:29:54 -08001012
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001013 DBG(" Garbage Collect sector %u complete", sectors_.Index(sector_to_gc));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -07001014 return Status::Ok();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -08001015}
1016
David Rogers35c3f842020-04-22 15:34:05 -07001017StatusWithSize KeyValueStore::UpdateEntriesToPrimaryFormat() {
1018 size_t entries_updated = 0;
David Rogers31b358b2020-04-15 05:00:50 -07001019 for (EntryMetadata& prior_metadata : entry_cache_) {
1020 Entry entry;
David Rogersc4dc8642020-09-14 10:52:36 -07001021 PW_TRY_WITH_SIZE(ReadEntry(prior_metadata, entry));
David Rogers31b358b2020-04-15 05:00:50 -07001022 if (formats_.primary().magic == entry.magic()) {
1023 // Ignore entries that are already on the primary format.
1024 continue;
1025 }
1026
1027 DBG("Updating entry 0x%08x from old format [0x%08x] to new format "
1028 "[0x%08x]",
1029 unsigned(prior_metadata.hash()),
1030 unsigned(entry.magic()),
1031 unsigned(formats_.primary().magic));
1032
David Rogers35c3f842020-04-22 15:34:05 -07001033 entries_updated++;
1034
David Rogers31b358b2020-04-15 05:00:50 -07001035 last_transaction_id_ += 1;
David Rogersc4dc8642020-09-14 10:52:36 -07001036 PW_TRY_WITH_SIZE(entry.Update(formats_.primary(), last_transaction_id_));
David Rogers31b358b2020-04-15 05:00:50 -07001037
1038 // List of addresses for sectors with space for this entry.
1039 Address* reserved_addresses = entry_cache_.TempReservedAddressesForWrite();
1040
1041 // Find addresses to write the entry to. This may involve garbage collecting
1042 // one or more sectors.
David Rogersc4dc8642020-09-14 10:52:36 -07001043 PW_TRY_WITH_SIZE(GetAddressesForWrite(reserved_addresses, entry.size()));
David Rogers31b358b2020-04-15 05:00:50 -07001044
David Rogersc4dc8642020-09-14 10:52:36 -07001045 PW_TRY_WITH_SIZE(
David Rogers35c3f842020-04-22 15:34:05 -07001046 CopyEntryToSector(entry,
David Rogers31b358b2020-04-15 05:00:50 -07001047 &sectors_.FromAddress(reserved_addresses[0]),
1048 reserved_addresses[0]));
1049
1050 // After writing the first entry successfully, update the key descriptors.
1051 // Once a single new the entry is written, the old entries are invalidated.
1052 EntryMetadata new_metadata = UpdateKeyDescriptor(
1053 entry, reserved_addresses[0], &prior_metadata, entry.size());
1054
1055 // Write the additional copies of the entry, if redundancy is greater
1056 // than 1.
1057 for (size_t i = 1; i < redundancy(); ++i) {
David Rogersc4dc8642020-09-14 10:52:36 -07001058 PW_TRY_WITH_SIZE(
David Rogers35c3f842020-04-22 15:34:05 -07001059 CopyEntryToSector(entry,
David Rogers31b358b2020-04-15 05:00:50 -07001060 &sectors_.FromAddress(reserved_addresses[i]),
1061 reserved_addresses[i]));
1062 new_metadata.AddNewAddress(reserved_addresses[i]);
1063 }
1064 }
David Rogers35c3f842020-04-22 15:34:05 -07001065
1066 return StatusWithSize(entries_updated);
David Rogers31b358b2020-04-15 05:00:50 -07001067}
1068
David Rogers9abe3c72020-03-24 19:03:13 -07001069// Add any missing redundant entries/copies for a key.
1070Status KeyValueStore::AddRedundantEntries(EntryMetadata& metadata) {
David Rogers9abe3c72020-03-24 19:03:13 -07001071 Entry entry;
David Rogersc4dc8642020-09-14 10:52:36 -07001072 PW_TRY(ReadEntry(metadata, entry));
1073 PW_TRY(entry.VerifyChecksumInFlash());
David Rogers9abe3c72020-03-24 19:03:13 -07001074
David Rogers0f8a1bb2020-04-21 18:50:19 -07001075 while (metadata.addresses().size() < redundancy()) {
1076 SectorDescriptor* new_sector;
David Rogersc4dc8642020-09-14 10:52:36 -07001077 PW_TRY(GetSectorForWrite(&new_sector, entry.size(), metadata.addresses()));
David Rogers9abe3c72020-03-24 19:03:13 -07001078
David Rogers31b358b2020-04-15 05:00:50 -07001079 Address new_address = sectors_.NextWritableAddress(*new_sector);
David Rogersc4dc8642020-09-14 10:52:36 -07001080 PW_TRY(CopyEntryToSector(entry, new_sector, new_address));
David Rogers9abe3c72020-03-24 19:03:13 -07001081
1082 metadata.AddNewAddress(new_address);
1083 }
Wyatt Heplerd78f7c62020-09-28 14:27:32 -07001084 return Status::Ok();
David Rogers9abe3c72020-03-24 19:03:13 -07001085}
1086
1087Status KeyValueStore::RepairCorruptSectors() {
1088 // Try to GC each corrupt sector, even if previous sectors fail. If GC of a
1089 // sector failed on the first pass, then do a second pass, since a later
1090 // sector might have cleared up space or otherwise unblocked the earlier
1091 // failed sector.
Wyatt Heplerd78f7c62020-09-28 14:27:32 -07001092 Status repair_status = Status::Ok();
David Rogers9abe3c72020-03-24 19:03:13 -07001093
1094 size_t loop_count = 0;
1095 do {
1096 loop_count++;
1097 // Error of RESOURCE_EXHAUSTED indicates no space found for relocation.
1098 // Reset back to OK for the next pass.
Wyatt Heplerd78f7c62020-09-28 14:27:32 -07001099 if (repair_status == Status::ResourceExhausted()) {
1100 repair_status = Status::Ok();
David Rogers9abe3c72020-03-24 19:03:13 -07001101 }
1102
1103 DBG(" Pass %u", unsigned(loop_count));
1104 for (SectorDescriptor& sector : sectors_) {
1105 if (sector.corrupt()) {
1106 DBG(" Found sector %u with corruption", sectors_.Index(sector));
1107 Status sector_status = GarbageCollectSector(sector, {});
1108 if (sector_status.ok()) {
David Rogers3c298372020-10-12 14:15:39 -07001109 internal_stats_.corrupt_sectors_recovered += 1;
David Rogers9abe3c72020-03-24 19:03:13 -07001110 } else if (repair_status.ok() ||
Wyatt Heplerd78f7c62020-09-28 14:27:32 -07001111 repair_status == Status::ResourceExhausted()) {
David Rogers9abe3c72020-03-24 19:03:13 -07001112 repair_status = sector_status;
1113 }
1114 }
1115 }
1116 DBG(" Pass %u complete", unsigned(loop_count));
1117 } while (!repair_status.ok() && loop_count < 2);
1118
1119 return repair_status;
1120}
1121
1122Status KeyValueStore::EnsureFreeSectorExists() {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -07001123 Status repair_status = Status::Ok();
David Rogers9abe3c72020-03-24 19:03:13 -07001124 bool empty_sector_found = false;
1125
1126 DBG(" Find empty sector");
1127 for (SectorDescriptor& sector : sectors_) {
1128 if (sector.Empty(partition_.sector_size_bytes())) {
1129 empty_sector_found = true;
1130 DBG(" Empty sector found");
1131 break;
1132 }
1133 }
1134 if (empty_sector_found == false) {
1135 DBG(" No empty sector found, attempting to GC a free sector");
Wyatt Heplere2cbadf2020-06-22 11:21:45 -07001136 Status sector_status = GarbageCollect(std::span<const Address, 0>());
David Rogers9abe3c72020-03-24 19:03:13 -07001137 if (repair_status.ok() && !sector_status.ok()) {
1138 DBG(" Unable to free an empty sector");
1139 repair_status = sector_status;
1140 }
1141 }
1142
1143 return repair_status;
1144}
1145
1146Status KeyValueStore::EnsureEntryRedundancy() {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -07001147 Status repair_status = Status::Ok();
David Rogers9abe3c72020-03-24 19:03:13 -07001148
1149 if (redundancy() == 1) {
David Rogers98fea472020-04-01 15:43:48 -07001150 DBG(" Redundancy not in use, nothting to check");
Wyatt Heplerd78f7c62020-09-28 14:27:32 -07001151 return Status::Ok();
David Rogers9abe3c72020-03-24 19:03:13 -07001152 }
1153
David Rogers98fea472020-04-01 15:43:48 -07001154 DBG(" Write any needed additional duplicate copies of keys to fulfill %u"
1155 " redundancy",
David Rogers9abe3c72020-03-24 19:03:13 -07001156 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -07001157 for (EntryMetadata& metadata : entry_cache_) {
David Rogers9abe3c72020-03-24 19:03:13 -07001158 if (metadata.addresses().size() >= redundancy()) {
1159 continue;
1160 }
1161
1162 DBG(" Key with %u of %u copies found, adding missing copies",
1163 unsigned(metadata.addresses().size()),
1164 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -07001165 Status fill_status = AddRedundantEntries(metadata);
David Rogers9abe3c72020-03-24 19:03:13 -07001166 if (fill_status.ok()) {
David Rogers3c298372020-10-12 14:15:39 -07001167 internal_stats_.missing_redundant_entries_recovered += 1;
David Rogers9abe3c72020-03-24 19:03:13 -07001168 DBG(" Key missing copies added");
1169 } else {
1170 DBG(" Failed to add key missing copies");
1171 if (repair_status.ok()) {
1172 repair_status = fill_status;
1173 }
1174 }
1175 }
1176
1177 return repair_status;
1178}
1179
David Rogersfcea3252020-04-07 14:56:35 -07001180Status KeyValueStore::FixErrors() {
1181 DBG("Fixing KVS errors");
David Rogers9abe3c72020-03-24 19:03:13 -07001182
1183 // Step 1: Garbage collect any sectors marked as corrupt.
David Rogersfcea3252020-04-07 14:56:35 -07001184 Status overall_status = RepairCorruptSectors();
David Rogers9abe3c72020-03-24 19:03:13 -07001185
1186 // Step 2: Make sure there is at least 1 empty sector. This needs to be a
1187 // seperate check of sectors from step 1, because a found empty sector might
1188 // get written to by a later GC that fails and does not result in a free
1189 // sector.
David Rogersfcea3252020-04-07 14:56:35 -07001190 Status repair_status = EnsureFreeSectorExists();
David Rogers9abe3c72020-03-24 19:03:13 -07001191 if (overall_status.ok()) {
1192 overall_status = repair_status;
1193 }
1194
1195 // Step 3: Make sure each stored key has the full number of redundant
1196 // entries.
1197 repair_status = EnsureEntryRedundancy();
1198 if (overall_status.ok()) {
1199 overall_status = repair_status;
1200 }
1201
1202 if (overall_status.ok()) {
1203 error_detected_ = false;
1204 initialized_ = InitializationState::kReady;
1205 }
1206 return overall_status;
1207}
1208
David Rogersfcea3252020-04-07 14:56:35 -07001209Status KeyValueStore::Repair() {
1210 // If errors have been detected, just reinit the KVS metadata. This does a
1211 // full deep error check and any needed repairs. Then repair any errors.
1212 INF("Starting KVS repair");
1213
1214 DBG("Reinitialize KVS metadata");
1215 InitializeMetadata();
1216
1217 return FixErrors();
1218}
1219
David Rogersd50eb1c2020-05-12 17:46:36 -07001220KeyValueStore::Entry KeyValueStore::CreateEntry(Address address,
1221 string_view key,
Wyatt Heplere2cbadf2020-06-22 11:21:45 -07001222 std::span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001223 EntryState state) {
Keir Mierle9e38b402020-02-21 13:06:21 -08001224 // Always bump the transaction ID when creating a new entry.
1225 //
1226 // Burning transaction IDs prevents inconsistencies between flash and memory
1227 // that which could happen if a write succeeds, but for some reason the read
1228 // and verify step fails. Here's how this would happen:
1229 //
1230 // 1. The entry is written but for some reason the flash reports failure OR
1231 // The write succeeds, but the read / verify operation fails.
1232 // 2. The transaction ID is NOT incremented, because of the failure
1233 // 3. (later) A new entry is written, re-using the transaction ID (oops)
1234 //
David Rogersd50eb1c2020-05-12 17:46:36 -07001235 // By always burning transaction IDs, the above problem can't happen.
1236 last_transaction_id_ += 1;
Keir Mierle9e38b402020-02-21 13:06:21 -08001237
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001238 if (state == EntryState::kDeleted) {
Wyatt Hepler7465be32020-02-21 15:30:53 -08001239 return Entry::Tombstone(
David Rogersd50eb1c2020-05-12 17:46:36 -07001240 partition_, address, formats_.primary(), key, last_transaction_id_);
Wyatt Hepler1fc11042020-02-19 17:17:51 -08001241 }
David Rogersd50eb1c2020-05-12 17:46:36 -07001242 return Entry::Valid(partition_,
1243 address,
1244 formats_.primary(),
1245 key,
1246 value,
1247 last_transaction_id_);
Wyatt Heplerd2298282020-02-20 17:12:45 -08001248}
1249
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001250void KeyValueStore::LogDebugInfo() const {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001251 const size_t sector_size_bytes = partition_.sector_size_bytes();
1252 DBG("====================== KEY VALUE STORE DUMP =========================");
1253 DBG(" ");
1254 DBG("Flash partition:");
David Rogers9fc78b82020-06-12 13:56:12 -07001255 DBG(" Sector count = %u", unsigned(partition_.sector_count()));
1256 DBG(" Sector max count = %u", unsigned(sectors_.max_size()));
1257 DBG(" Sectors in use = %u", unsigned(sectors_.size()));
1258 DBG(" Sector size = %u", unsigned(sector_size_bytes));
1259 DBG(" Total size = %u", unsigned(partition_.size_bytes()));
1260 DBG(" Alignment = %u", unsigned(partition_.alignment_bytes()));
Keir Mierle8c352dc2020-02-02 13:58:19 -08001261 DBG(" ");
1262 DBG("Key descriptors:");
David Rogers9fc78b82020-06-12 13:56:12 -07001263 DBG(" Entry count = %u", unsigned(entry_cache_.total_entries()));
1264 DBG(" Max entry count = %u", unsigned(entry_cache_.max_entries()));
Keir Mierle8c352dc2020-02-02 13:58:19 -08001265 DBG(" ");
1266 DBG(" # hash version address address (hex)");
Armando Montanez888370d2020-05-01 18:29:22 -07001267 size_t count = 0;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001268 for (const EntryMetadata& metadata : entry_cache_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001269 DBG(" |%3zu: | %8zx |%8zu | %8zu | %8zx",
Armando Montanez888370d2020-05-01 18:29:22 -07001270 count++,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001271 size_t(metadata.hash()),
1272 size_t(metadata.transaction_id()),
1273 size_t(metadata.first_address()),
1274 size_t(metadata.first_address()));
Keir Mierle8c352dc2020-02-02 13:58:19 -08001275 }
1276 DBG(" ");
1277
1278 DBG("Sector descriptors:");
1279 DBG(" # tail free valid has_space");
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001280 for (const SectorDescriptor& sd : sectors_) {
1281 DBG(" |%3u: | %8zu |%8zu | %s",
1282 sectors_.Index(sd),
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001283 size_t(sd.writable_bytes()),
1284 sd.valid_bytes(),
1285 sd.writable_bytes() ? "YES" : "");
Keir Mierle8c352dc2020-02-02 13:58:19 -08001286 }
1287 DBG(" ");
1288
1289 // TODO: This should stop logging after some threshold.
1290 // size_t dumped_bytes = 0;
1291 DBG("Sector raw data:");
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001292 for (size_t sector_id = 0; sector_id < sectors_.size(); ++sector_id) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001293 // Read sector data. Yes, this will blow the stack on embedded.
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001294 std::array<byte, 500> raw_sector_data; // TODO!!!
Keir Mierle8c352dc2020-02-02 13:58:19 -08001295 StatusWithSize sws =
1296 partition_.Read(sector_id * sector_size_bytes, raw_sector_data);
David Rogers9fc78b82020-06-12 13:56:12 -07001297 DBG("Read: %u bytes", unsigned(sws.size()));
Keir Mierle8c352dc2020-02-02 13:58:19 -08001298
1299 DBG(" base addr offs 0 1 2 3 4 5 6 7");
1300 for (size_t i = 0; i < sector_size_bytes; i += 8) {
1301 DBG(" %3zu %8zx %5zu | %02x %02x %02x %02x %02x %02x %02x %02x",
1302 sector_id,
1303 (sector_id * sector_size_bytes) + i,
1304 i,
1305 static_cast<unsigned int>(raw_sector_data[i + 0]),
1306 static_cast<unsigned int>(raw_sector_data[i + 1]),
1307 static_cast<unsigned int>(raw_sector_data[i + 2]),
1308 static_cast<unsigned int>(raw_sector_data[i + 3]),
1309 static_cast<unsigned int>(raw_sector_data[i + 4]),
1310 static_cast<unsigned int>(raw_sector_data[i + 5]),
1311 static_cast<unsigned int>(raw_sector_data[i + 6]),
1312 static_cast<unsigned int>(raw_sector_data[i + 7]));
1313
1314 // TODO: Fix exit condition.
1315 if (i > 128) {
1316 break;
1317 }
1318 }
1319 DBG(" ");
1320 }
1321
1322 DBG("////////////////////// KEY VALUE STORE DUMP END /////////////////////");
1323}
1324
David Rogerscf680ab2020-02-12 23:28:32 -08001325void KeyValueStore::LogSectors() const {
David Rogers9fc78b82020-06-12 13:56:12 -07001326 DBG("Sector descriptors: count %u", unsigned(sectors_.size()));
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001327 for (auto& sector : sectors_) {
David Rogers9fc78b82020-06-12 13:56:12 -07001328 DBG(" - Sector %u: valid %u, recoverable %u, free %u",
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001329 sectors_.Index(sector),
David Rogers9fc78b82020-06-12 13:56:12 -07001330 unsigned(sector.valid_bytes()),
1331 unsigned(sector.RecoverableBytes(partition_.sector_size_bytes())),
1332 unsigned(sector.writable_bytes()));
David Rogers50185ad2020-02-07 00:02:46 -08001333 }
1334}
1335
David Rogerscf680ab2020-02-12 23:28:32 -08001336void KeyValueStore::LogKeyDescriptor() const {
David Rogers9fc78b82020-06-12 13:56:12 -07001337 DBG("Key descriptors: count %u", unsigned(entry_cache_.total_entries()));
David Rogers9abe3c72020-03-24 19:03:13 -07001338 for (const EntryMetadata& metadata : entry_cache_) {
David Rogers9fc78b82020-06-12 13:56:12 -07001339 DBG(" - Key: %s, hash %#x, transaction ID %u, first address %#x",
Wyatt Hepler02946272020-03-18 10:36:22 -07001340 metadata.state() == EntryState::kDeleted ? "Deleted" : "Valid",
David Rogers9fc78b82020-06-12 13:56:12 -07001341 unsigned(metadata.hash()),
1342 unsigned(metadata.transaction_id()),
1343 unsigned(metadata.first_address()));
David Rogerscf680ab2020-02-12 23:28:32 -08001344 }
1345}
1346
Wyatt Hepler2ad60672020-01-21 08:00:16 -08001347} // namespace pw::kvs