blob: 55d030d13fab6f66bcefad359f199a5db979313a [file] [log] [blame]
Wyatt Heplerb7609542020-01-24 10:29:54 -08001// Copyright 2020 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
Wyatt Heplerb7609542020-01-24 10:29:54 -080015#include "pw_kvs/key_value_store.h"
16
Wyatt Heplerbab0e202020-02-04 07:40:08 -080017#include <algorithm>
Wyatt Hepler5a33d8c2020-02-06 09:32:58 -080018#include <cinttypes>
Wyatt Heplerb7609542020-01-24 10:29:54 -080019#include <cstring>
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080020#include <type_traits>
Wyatt Heplerb7609542020-01-24 10:29:54 -080021
Keir Mierle8c352dc2020-02-02 13:58:19 -080022#define PW_LOG_USE_ULTRA_SHORT_NAMES 1
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080023#include "pw_kvs_private/macros.h"
Keir Mierle8c352dc2020-02-02 13:58:19 -080024#include "pw_log/log.h"
Wyatt Heplerb7609542020-01-24 10:29:54 -080025
Wyatt Hepler2ad60672020-01-21 08:00:16 -080026namespace pw::kvs {
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080027namespace {
Wyatt Heplerb7609542020-01-24 10:29:54 -080028
Wyatt Hepleracaacf92020-01-24 10:58:30 -080029using std::byte;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080030using std::string_view;
Wyatt Hepleracaacf92020-01-24 10:58:30 -080031
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080032constexpr bool InvalidKey(std::string_view key) {
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -080033 return key.empty() || (key.size() > internal::Entry::kMaxKeyLength);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080034}
35
36} // namespace
37
Wyatt Heplerad0a7932020-02-06 08:20:38 -080038KeyValueStore::KeyValueStore(FlashPartition* partition,
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080039 span<const EntryFormat> formats,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070040 const Options& options,
41 size_t redundancy,
42 Vector<SectorDescriptor>& sector_descriptor_list,
43 const SectorDescriptor** temp_sectors_to_skip,
44 Vector<KeyDescriptor>& key_descriptor_list,
45 Address* addresses)
Wyatt Heplerad0a7932020-02-06 08:20:38 -080046 : partition_(*partition),
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080047 formats_(formats),
Wyatt Heplerc84393f2020-03-20 11:23:24 -070048 sectors_(sector_descriptor_list, *partition, temp_sectors_to_skip),
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070049 entry_cache_(key_descriptor_list, addresses, redundancy),
David Rogers49766d92020-03-20 10:55:54 -070050 options_(options),
David Rogers9abe3c72020-03-24 19:03:13 -070051 initialized_(InitializationState::kNotInitialized),
David Rogers49766d92020-03-20 10:55:54 -070052 error_detected_(false),
David Rogers9abe3c72020-03-24 19:03:13 -070053 error_stats_({}),
David Rogers49766d92020-03-20 10:55:54 -070054 last_transaction_id_(0) {}
Wyatt Heplerad0a7932020-02-06 08:20:38 -080055
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080056Status KeyValueStore::Init() {
David Rogers9abe3c72020-03-24 19:03:13 -070057 initialized_ = InitializationState::kNotInitialized;
David Rogers49766d92020-03-20 10:55:54 -070058 error_detected_ = false;
Keir Mierlebf904812020-03-11 17:28:22 -070059 last_transaction_id_ = 0;
Wyatt Heplerd2298282020-02-20 17:12:45 -080060
David Rogers2e9e0c82020-02-13 15:06:06 -080061 INF("Initializing key value store");
Wyatt Hepler38ce30f2020-02-19 11:48:31 -080062 if (partition_.sector_count() > sectors_.max_size()) {
David Rogers2e9e0c82020-02-13 15:06:06 -080063 ERR("KVS init failed: kMaxUsableSectors (=%zu) must be at least as "
64 "large as the number of sectors in the flash partition (=%zu)",
Wyatt Hepler38ce30f2020-02-19 11:48:31 -080065 sectors_.max_size(),
David Rogers2e9e0c82020-02-13 15:06:06 -080066 partition_.sector_count());
Wyatt Heplerad0a7932020-02-06 08:20:38 -080067 return Status::FAILED_PRECONDITION;
68 }
69
Keir Mierle8c352dc2020-02-02 13:58:19 -080070 const size_t sector_size_bytes = partition_.sector_size_bytes();
Keir Mierle8c352dc2020-02-02 13:58:19 -080071
David Rogers49766d92020-03-20 10:55:54 -070072 // TODO: investigate doing this as a static assert/compile-time check.
73 if (sector_size_bytes > SectorDescriptor::max_sector_size()) {
74 ERR("KVS init failed: sector_size_bytes (=%zu) is greater than maximum "
75 "allowed sector size (=%zu)",
76 sector_size_bytes,
77 SectorDescriptor::max_sector_size());
78 return Status::FAILED_PRECONDITION;
79 }
80
David Rogersfcea3252020-04-07 14:56:35 -070081 InitializeMetadata();
82
83 if (!error_detected_) {
84 initialized_ = InitializationState::kReady;
85 } else {
86 if (options_.recovery != ErrorRecovery::kManual) {
87 Status recovery_status = FixErrors();
88
89 if (recovery_status.ok()) {
90 WRN("KVS init: Corruption detected and fully repaired");
91 initialized_ = InitializationState::kReady;
92 } else if (recovery_status == Status::RESOURCE_EXHAUSTED) {
93 WRN("KVS init: Unable to maintain required free sector");
94 initialized_ = InitializationState::kNeedsMaintenance;
95 } else {
96 WRN("KVS init: Corruption detected and unable repair");
97 initialized_ = InitializationState::kNeedsMaintenance;
98 }
99 } else {
100 WRN("KVS init: Corruption detected, no repair attempted due to options");
101 initialized_ = InitializationState::kNeedsMaintenance;
102 }
103 }
104
105 INF("KeyValueStore init complete: active keys %zu, deleted keys %zu, sectors "
106 "%zu, logical sector size %zu bytes",
107 size(),
108 (entry_cache_.total_entries() - size()),
109 sectors_.size(),
110 partition_.sector_size_bytes());
111
112 // Report any corruption was not repaired.
113 if (error_detected_) {
114 WRN("KVS init: Corruption found but not repaired, KVS unavailable until "
115 "successful maintenance.");
116 return Status::DATA_LOSS;
117 }
118
119 return Status::OK;
120}
121
122void KeyValueStore::InitializeMetadata() {
123 const size_t sector_size_bytes = partition_.sector_size_bytes();
124
125 sectors_.Reset();
126 entry_cache_.Reset();
127
Keir Mierle8c352dc2020-02-02 13:58:19 -0800128 DBG("First pass: Read all entries from all sectors");
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800129 Address sector_address = 0;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800130
Alexei Frolovd4adf912020-02-21 13:29:15 -0800131 size_t total_corrupt_bytes = 0;
132 int corrupt_entries = 0;
David Rogers91627482020-02-27 17:38:12 -0800133 bool empty_sector_found = false;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800134
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800135 for (SectorDescriptor& sector : sectors_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800136 Address entry_address = sector_address;
137
Alexei Frolovd4adf912020-02-21 13:29:15 -0800138 size_t sector_corrupt_bytes = 0;
139
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800140 for (int num_entries_in_sector = 0; true; num_entries_in_sector++) {
David Rogersfcea3252020-04-07 14:56:35 -0700141 DBG("Load entry: sector=%u, entry#=%d, address=%u",
142 unsigned(sector_address),
Keir Mierle8c352dc2020-02-02 13:58:19 -0800143 num_entries_in_sector,
David Rogersfcea3252020-04-07 14:56:35 -0700144 unsigned(entry_address));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800145
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700146 if (!sectors_.AddressInSector(sector, entry_address)) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800147 DBG("Fell off end of sector; moving to the next sector");
148 break;
149 }
150
151 Address next_entry_address;
152 Status status = LoadEntry(entry_address, &next_entry_address);
153 if (status == Status::NOT_FOUND) {
154 DBG("Hit un-written data in sector; moving to the next sector");
155 break;
David Rogersfcea3252020-04-07 14:56:35 -0700156 } else if (!status.ok()) {
157 // The entry could not be read, indicating likely data corruption within
158 // the sector. Try to scan the remainder of the sector for other
159 // entries.
Alexei Frolovd4adf912020-02-21 13:29:15 -0800160
David Rogers49766d92020-03-20 10:55:54 -0700161 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800162 corrupt_entries++;
163
164 status = ScanForEntry(sector,
165 entry_address + Entry::kMinAlignmentBytes,
166 &next_entry_address);
David Rogersfcea3252020-04-07 14:56:35 -0700167 if (!status.ok()) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800168 // No further entries in this sector. Mark the remaining bytes in the
169 // sector as corrupt (since we can't reliably know the size of the
170 // corrupt entry).
171 sector_corrupt_bytes +=
172 sector_size_bytes - (entry_address - sector_address);
173 break;
174 }
175
Alexei Frolovd4adf912020-02-21 13:29:15 -0800176 sector_corrupt_bytes += next_entry_address - entry_address;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800177 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800178
179 // Entry loaded successfully; so get ready to load the next one.
180 entry_address = next_entry_address;
181
182 // Update of the number of writable bytes in this sector.
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800183 sector.set_writable_bytes(sector_size_bytes -
184 (entry_address - sector_address));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800185 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800186
Alexei Frolovd4adf912020-02-21 13:29:15 -0800187 if (sector_corrupt_bytes > 0) {
188 // If the sector contains corrupt data, prevent any further entries from
189 // being written to it by indicating that it has no space. This should
190 // also make it a decent GC candidate. Valid keys in the sector are still
191 // readable as normal.
David Rogers49766d92020-03-20 10:55:54 -0700192 sector.mark_corrupt();
193 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800194
195 WRN("Sector %u contains %zuB of corrupt data",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700196 sectors_.Index(sector),
Alexei Frolovd4adf912020-02-21 13:29:15 -0800197 sector_corrupt_bytes);
198 }
199
David Rogers91627482020-02-27 17:38:12 -0800200 if (sector.Empty(sector_size_bytes)) {
201 empty_sector_found = true;
202 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800203 sector_address += sector_size_bytes;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800204 total_corrupt_bytes += sector_corrupt_bytes;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800205 }
206
207 DBG("Second pass: Count valid bytes in each sector");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700208 Address newest_key = 0;
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800209
David Rogers98fea472020-04-01 15:43:48 -0700210 // For every valid entry, for each address, count the valid bytes in that
211 // sector. If the address fails to read, remove the address and mark the
212 // sector as corrupt. Track which entry has the newest transaction ID for
213 // initializing last_new_sector_.
214 for (EntryMetadata& metadata : entry_cache_) {
David Rogers49766d92020-03-20 10:55:54 -0700215 if (metadata.addresses().size() < redundancy()) {
216 error_detected_ = true;
217 }
David Rogers98fea472020-04-01 15:43:48 -0700218 size_t index = 0;
219 while (index < metadata.addresses().size()) {
220 Address address = metadata.addresses()[index];
David Rogersf56131c2020-03-04 10:19:22 -0800221 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700222
223 Status read_result = Entry::Read(partition_, address, formats_, &entry);
224
225 SectorDescriptor& sector = sectors_.FromAddress(address);
226
227 if (read_result.ok()) {
228 sector.AddValidBytes(entry.size());
229 index++;
230 } else {
231 corrupt_entries++;
232 total_corrupt_bytes += sector.writable_bytes();
233 error_detected_ = true;
234 sector.mark_corrupt();
235
236 // Remove the bad address and stay at this index. The removal
237 // replaces out the removed address with the back address so
238 // this index needs to be rechecked with the new address.
239 metadata.RemoveAddress(address);
240 }
David Rogersf56131c2020-03-04 10:19:22 -0800241 }
David Rogers98fea472020-04-01 15:43:48 -0700242
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700243 if (metadata.IsNewerThan(last_transaction_id_)) {
244 last_transaction_id_ = metadata.transaction_id();
245 newest_key = metadata.addresses().back();
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800246 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800247 }
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800248
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700249 sectors_.set_last_new_sector(newest_key);
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800250
David Rogers91627482020-02-27 17:38:12 -0800251 if (!empty_sector_found) {
David Rogers9abe3c72020-03-24 19:03:13 -0700252 error_detected_ = true;
David Rogers91627482020-02-27 17:38:12 -0800253 }
254
David Rogersfcea3252020-04-07 14:56:35 -0700255 if (total_corrupt_bytes != 0 || corrupt_entries != 0) {
256 WRN("Corruption detected. Found %zu corrupt bytes and %d corrupt entries.",
257 total_corrupt_bytes,
258 corrupt_entries);
David Rogers9abe3c72020-03-24 19:03:13 -0700259 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800260}
261
Alexei Frolov9e235832020-02-24 12:44:45 -0800262KeyValueStore::StorageStats KeyValueStore::GetStorageStats() const {
David Rogers9abe3c72020-03-24 19:03:13 -0700263 StorageStats stats{};
Alexei Frolov9e235832020-02-24 12:44:45 -0800264 const size_t sector_size = partition_.sector_size_bytes();
265 bool found_empty_sector = false;
David Rogers9abe3c72020-03-24 19:03:13 -0700266 stats.corrupt_sectors_recovered = error_stats_.corrupt_sectors_recovered;
267 stats.missing_redundant_entries_recovered =
268 error_stats_.missing_redundant_entries_recovered;
Alexei Frolov9e235832020-02-24 12:44:45 -0800269
270 for (const SectorDescriptor& sector : sectors_) {
271 stats.in_use_bytes += sector.valid_bytes();
272 stats.reclaimable_bytes += sector.RecoverableBytes(sector_size);
273
274 if (!found_empty_sector && sector.Empty(sector_size)) {
275 // The KVS tries to always keep an empty sector for GC, so don't count
276 // the first empty sector seen as writable space. However, a free sector
277 // cannot always be assumed to exist; if a GC operation fails, all sectors
278 // may be partially written, in which case the space reported might be
279 // inaccurate.
280 found_empty_sector = true;
281 continue;
282 }
283
284 stats.writable_bytes += sector.writable_bytes();
285 }
286
287 return stats;
288}
289
David Rogers98fea472020-04-01 15:43:48 -0700290// Check KVS for any error conditions. Primarily intended for test and
291// internal use.
David Rogers9abe3c72020-03-24 19:03:13 -0700292bool KeyValueStore::CheckForErrors() {
293 // Check for corrupted sectors
294 for (SectorDescriptor& sector : sectors_) {
295 if (sector.corrupt()) {
296 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700297 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700298 }
299 }
300
301 // Check for missing redundancy.
302 if (redundancy() > 1) {
303 for (const EntryMetadata& metadata : entry_cache_) {
304 if (metadata.addresses().size() < redundancy()) {
305 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700306 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700307 }
308 }
309 }
310
311 return error_detected();
312}
313
Keir Mierle8c352dc2020-02-02 13:58:19 -0800314Status KeyValueStore::LoadEntry(Address entry_address,
315 Address* next_entry_address) {
Wyatt Heplere541e072020-02-14 09:10:53 -0800316 Entry entry;
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800317 TRY(Entry::Read(partition_, entry_address, formats_, &entry));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800318
319 // Read the key from flash & validate the entry (which reads the value).
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800320 Entry::KeyBuffer key_buffer;
Wyatt Heplere541e072020-02-14 09:10:53 -0800321 TRY_ASSIGN(size_t key_length, entry.ReadKey(key_buffer));
322 const string_view key(key_buffer.data(), key_length);
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800323
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800324 TRY(entry.VerifyChecksumInFlash());
David Rogersf56131c2020-03-04 10:19:22 -0800325
326 // A valid entry was found, so update the next entry address before doing any
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700327 // of the checks that happen in AddNewOrUpdateExisting.
David Rogersf56131c2020-03-04 10:19:22 -0800328 *next_entry_address = entry.next_address();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700329 return entry_cache_.AddNewOrUpdateExisting(
330 entry.descriptor(key), entry.address(), partition_.sector_size_bytes());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800331}
332
Alexei Frolovd4adf912020-02-21 13:29:15 -0800333// Scans flash memory within a sector to find a KVS entry magic.
Alexei Frolovd4adf912020-02-21 13:29:15 -0800334Status KeyValueStore::ScanForEntry(const SectorDescriptor& sector,
335 Address start_address,
336 Address* next_entry_address) {
David Rogersfcea3252020-04-07 14:56:35 -0700337 DBG("Scanning sector %u for entries starting from address %u",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700338 sectors_.Index(sector),
David Rogersfcea3252020-04-07 14:56:35 -0700339 unsigned(start_address));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800340
341 // Entries must start at addresses which are aligned on a multiple of
342 // Entry::kMinAlignmentBytes. However, that multiple can vary between entries.
343 // When scanning, we don't have an entry to tell us what the current alignment
344 // is, so the minimum alignment is used to be exhaustive.
345 for (Address address = AlignUp(start_address, Entry::kMinAlignmentBytes);
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700346 sectors_.AddressInSector(sector, address);
Alexei Frolovd4adf912020-02-21 13:29:15 -0800347 address += Entry::kMinAlignmentBytes) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800348 uint32_t magic;
David Rogersfcea3252020-04-07 14:56:35 -0700349 StatusWithSize read_result =
350 partition_.Read(address, as_writable_bytes(span(&magic, 1)));
351 if (!read_result.ok()) {
352 continue;
353 }
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800354 if (formats_.KnownMagic(magic)) {
David Rogersfcea3252020-04-07 14:56:35 -0700355 DBG("Found entry magic at address %u", unsigned(address));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800356 *next_entry_address = address;
357 return Status::OK;
358 }
359 }
360
361 return Status::NOT_FOUND;
362}
363
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800364StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800365 span<byte> value_buffer,
366 size_t offset_bytes) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700367 TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800368
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700369 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700370 TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800371
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700372 return Get(key, metadata, value_buffer, offset_bytes);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800373}
374
Wyatt Heplerfac81132020-02-27 17:26:33 -0800375Status KeyValueStore::PutBytes(string_view key, span<const byte> value) {
David Rogers9abe3c72020-03-24 19:03:13 -0700376 TRY(CheckWriteOperation(key));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800377 DBG("Writing key/value; key length=%zu, value length=%zu",
378 key.size(),
379 value.size());
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800380
Wyatt Hepler5406a672020-02-18 15:42:38 -0800381 if (Entry::size(partition_, key, value) > partition_.sector_size_bytes()) {
382 DBG("%zu B value with %zu B key cannot fit in one sector",
383 value.size(),
384 key.size());
385 return Status::INVALID_ARGUMENT;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800386 }
387
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700388 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700389 Status status = FindEntry(key, &metadata);
Wyatt Hepler2d401692020-02-13 16:01:23 -0800390
391 if (status.ok()) {
David Rogersf56131c2020-03-04 10:19:22 -0800392 // TODO: figure out logging how to support multiple addresses.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700393 DBG("Overwriting entry for key 0x%08" PRIx32 " in %zu sectors including %u",
394 metadata.hash(),
395 metadata.addresses().size(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700396 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700397 return WriteEntryForExistingKey(metadata, EntryState::kValid, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800398 }
David Rogers2761aeb2020-01-31 17:09:00 -0800399
Wyatt Hepler2d401692020-02-13 16:01:23 -0800400 if (status == Status::NOT_FOUND) {
401 return WriteEntryForNewKey(key, value);
402 }
403
404 return status;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800405}
406
407Status KeyValueStore::Delete(string_view key) {
David Rogers9abe3c72020-03-24 19:03:13 -0700408 TRY(CheckWriteOperation(key));
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800409
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700410 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700411 TRY(FindExisting(key, &metadata));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800412
David Rogersf56131c2020-03-04 10:19:22 -0800413 // TODO: figure out logging how to support multiple addresses.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700414 DBG("Writing tombstone for key 0x%08" PRIx32 " in %zu sectors including %u",
415 metadata.hash(),
416 metadata.addresses().size(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700417 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700418 return WriteEntryForExistingKey(metadata, EntryState::kDeleted, key, {});
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800419}
420
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800421void KeyValueStore::Item::ReadKey() {
422 key_buffer_.fill('\0');
423
424 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700425 if (kvs_.ReadEntry(*iterator_, entry).ok()) {
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800426 entry.ReadKey(key_buffer_);
427 }
428}
429
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800430KeyValueStore::iterator& KeyValueStore::iterator::operator++() {
431 // Skip to the next entry that is valid (not deleted).
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700432 while (++item_.iterator_ != item_.kvs_.entry_cache_.end() &&
Wyatt Hepler02946272020-03-18 10:36:22 -0700433 item_.iterator_->state() != EntryState::kValid) {
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800434 }
435 return *this;
436}
437
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800438KeyValueStore::iterator KeyValueStore::begin() const {
Wyatt Heplerbfc6a522020-04-01 16:30:24 -0700439 internal::EntryCache::const_iterator cache_iterator = entry_cache_.begin();
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800440 // Skip over any deleted entries at the start of the descriptor list.
Wyatt Hepler02946272020-03-18 10:36:22 -0700441 while (cache_iterator != entry_cache_.end() &&
442 cache_iterator->state() != EntryState::kValid) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700443 ++cache_iterator;
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800444 }
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700445 return iterator(*this, cache_iterator);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800446}
447
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700448StatusWithSize KeyValueStore::ValueSize(string_view key) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700449 TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Heplered163b02020-02-03 17:49:32 -0800450
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700451 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700452 TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800453
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700454 return ValueSize(metadata);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800455}
Wyatt Heplered163b02020-02-03 17:49:32 -0800456
David Rogers98fea472020-04-01 15:43:48 -0700457Status KeyValueStore::ReadEntry(const EntryMetadata& metadata,
458 Entry& entry) const {
459 // Try to read an entry
460 Status read_result = Status::DATA_LOSS;
461 for (Address address : metadata.addresses()) {
462 read_result = Entry::Read(partition_, address, formats_, &entry);
463 if (read_result.ok()) {
464 return read_result;
465 }
466
467 // Found a bad address. Set the sector as corrupt.
468 error_detected_ = true;
469 sectors_.FromAddress(address).mark_corrupt();
470 }
471
472 ERR("No valid entries for key. Data has been lost!");
473 return read_result;
474}
475
476Status KeyValueStore::FindEntry(string_view key,
477 EntryMetadata* found_entry) const {
478 StatusWithSize find_result =
479 entry_cache_.Find(partition_, sectors_, formats_, key, found_entry);
480
481 if (find_result.size() > 0u) {
482 error_detected_ = true;
483 }
484 return find_result.status();
485}
486
487Status KeyValueStore::FindExisting(string_view key,
488 EntryMetadata* metadata) const {
489 Status status = FindEntry(key, metadata);
490
491 // If the key's hash collides with an existing key or if the key is deleted,
492 // treat it as if it is not in the KVS.
493 if (status == Status::ALREADY_EXISTS ||
494 (status.ok() && metadata->state() == EntryState::kDeleted)) {
495 return Status::NOT_FOUND;
496 }
497 return status;
498}
499
Wyatt Heplerfac81132020-02-27 17:26:33 -0800500StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700501 const EntryMetadata& metadata,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800502 span<std::byte> value_buffer,
503 size_t offset_bytes) const {
504 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700505
506 TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800507
508 StatusWithSize result = entry.ReadValue(value_buffer, offset_bytes);
509 if (result.ok() && options_.verify_on_read && offset_bytes == 0u) {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800510 Status verify_result =
511 entry.VerifyChecksum(key, value_buffer.first(result.size()));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800512 if (!verify_result.ok()) {
513 std::memset(value_buffer.data(), 0, result.size());
514 return StatusWithSize(verify_result, 0);
515 }
516
517 return StatusWithSize(verify_result, result.size());
518 }
519 return result;
Wyatt Heplered163b02020-02-03 17:49:32 -0800520}
521
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800522Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800523 void* value,
524 size_t size_bytes) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700525 TRY(CheckWriteOperation(key));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800526
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700527 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700528 TRY(FindExisting(key, &metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800529
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700530 return FixedSizeGet(key, metadata, value, size_bytes);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800531}
532
533Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700534 const EntryMetadata& metadata,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800535 void* value,
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800536 size_t size_bytes) const {
537 // Ensure that the size of the stored value matches the size of the type.
538 // Otherwise, report error. This check avoids potential memory corruption.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700539 TRY_ASSIGN(const size_t actual_size, ValueSize(metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800540
541 if (actual_size != size_bytes) {
542 DBG("Requested %zu B read, but value is %zu B", size_bytes, actual_size);
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800543 return Status::INVALID_ARGUMENT;
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800544 }
Wyatt Heplerfac81132020-02-27 17:26:33 -0800545
546 StatusWithSize result =
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700547 Get(key, metadata, span(static_cast<byte*>(value), size_bytes), 0);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800548
549 return result.status();
550}
551
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700552StatusWithSize KeyValueStore::ValueSize(const EntryMetadata& metadata) const {
Wyatt Heplerfac81132020-02-27 17:26:33 -0800553 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700554 TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800555
556 return StatusWithSize(entry.value_size());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800557}
558
David Rogers9abe3c72020-03-24 19:03:13 -0700559Status KeyValueStore::CheckWriteOperation(string_view key) const {
Wyatt Hepleracaacf92020-01-24 10:58:30 -0800560 if (InvalidKey(key)) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800561 return Status::INVALID_ARGUMENT;
562 }
David Rogers9abe3c72020-03-24 19:03:13 -0700563
564 // For normal write operation the KVS must be fully ready.
Wyatt Heplerd2298282020-02-20 17:12:45 -0800565 if (!initialized()) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800566 return Status::FAILED_PRECONDITION;
567 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800568 return Status::OK;
569}
570
David Rogers9abe3c72020-03-24 19:03:13 -0700571Status KeyValueStore::CheckReadOperation(string_view key) const {
572 if (InvalidKey(key)) {
573 return Status::INVALID_ARGUMENT;
574 }
575
576 // Operations that are explicitly read-only can be done after init() has been
577 // called but not fully ready (when needing maintenance).
578 if (initialized_ == InitializationState::kNotInitialized) {
579 return Status::FAILED_PRECONDITION;
580 }
581 return Status::OK;
582}
583
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700584Status KeyValueStore::WriteEntryForExistingKey(EntryMetadata& metadata,
585 EntryState new_state,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800586 string_view key,
587 span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700588 // Read the original entry to get the size for sector accounting purposes.
589 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700590 TRY(ReadEntry(metadata, entry));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800591
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700592 return WriteEntry(key, value, new_state, &metadata, entry.size());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800593}
594
595Status KeyValueStore::WriteEntryForNewKey(string_view key,
596 span<const byte> value) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700597 if (entry_cache_.full()) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800598 WRN("KVS full: trying to store a new entry, but can't. Have %zu entries",
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700599 entry_cache_.total_entries());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800600 return Status::RESOURCE_EXHAUSTED;
601 }
602
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700603 return WriteEntry(key, value, EntryState::kValid);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700604}
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800605
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700606Status KeyValueStore::WriteEntry(string_view key,
607 span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700608 EntryState new_state,
609 EntryMetadata* prior_metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700610 size_t prior_size) {
611 const size_t entry_size = Entry::size(partition_, key, value);
612
613 // List of addresses for sectors with space for this entry.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700614 Address* reserved_addresses = entry_cache_.TempReservedAddressesForWrite();
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700615
616 // Find sectors to write the entry to. This may involve garbage collecting one
617 // or more sectors.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700618 for (size_t i = 0; i < redundancy(); i++) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700619 SectorDescriptor* sector;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700620 TRY(GetSectorForWrite(&sector, entry_size, span(reserved_addresses, i)));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700621
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700622 DBG("Found space for entry in sector %u", sectors_.Index(sector));
623 reserved_addresses[i] = sectors_.NextWritableAddress(*sector);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700624 }
625
626 // Write the entry at the first address that was found.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700627 Entry entry = CreateEntry(reserved_addresses[0], key, value, new_state);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700628 TRY(AppendEntry(entry, key, value));
629
630 // After writing the first entry successfully, update the key descriptors.
631 // Once a single new the entry is written, the old entries are invalidated.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700632 EntryMetadata new_metadata =
633 UpdateKeyDescriptor(entry, key, prior_metadata, prior_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700634
635 // Write the additional copies of the entry, if redundancy is greater than 1.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700636 for (size_t i = 1; i < redundancy(); ++i) {
637 entry.set_address(reserved_addresses[i]);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700638 TRY(AppendEntry(entry, key, value));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700639 new_metadata.AddNewAddress(reserved_addresses[i]);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700640 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800641 return Status::OK;
642}
643
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700644KeyValueStore::EntryMetadata KeyValueStore::UpdateKeyDescriptor(
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700645 const Entry& entry,
646 string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700647 EntryMetadata* prior_metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700648 size_t prior_size) {
649 // If there is no prior descriptor, create a new one.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700650 if (prior_metadata == nullptr) {
651 return entry_cache_.AddNew(entry.descriptor(key), entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800652 }
653
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700654 // Remove valid bytes for the old entry and its copies, which are now stale.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700655 for (Address address : prior_metadata->addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700656 sectors_.FromAddress(address).RemoveValidBytes(prior_size);
David Rogersa2562b52020-03-05 15:30:05 -0800657 }
658
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700659 prior_metadata->Reset(entry.descriptor(key), entry.address());
660 return *prior_metadata;
David Rogersa2562b52020-03-05 15:30:05 -0800661}
662
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700663// Finds a sector to use for writing a new entry to. Does automatic garbage
David Rogersa2562b52020-03-05 15:30:05 -0800664// collection if needed and allowed.
665//
666// OK: Sector found with needed space.
667// RESOURCE_EXHAUSTED: No sector available with the needed space.
668Status KeyValueStore::GetSectorForWrite(SectorDescriptor** sector,
669 size_t entry_size,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700670 span<const Address> reserved) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700671 Status result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800672
David Rogersf3884eb2020-03-08 19:21:40 -0700673 size_t gc_sector_count = 0;
David Rogersa2562b52020-03-05 15:30:05 -0800674 bool do_auto_gc = options_.gc_on_write != GargbageCollectOnWrite::kDisabled;
675
676 // Do garbage collection as needed, so long as policy allows.
677 while (result == Status::RESOURCE_EXHAUSTED && do_auto_gc) {
678 if (options_.gc_on_write == GargbageCollectOnWrite::kOneSector) {
679 // If GC config option is kOneSector clear the flag to not do any more
680 // GC after this try.
681 do_auto_gc = false;
682 }
683 // Garbage collect and then try again to find the best sector.
David Rogers9abe3c72020-03-24 19:03:13 -0700684 Status gc_status = GarbageCollect(reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800685 if (!gc_status.ok()) {
686 if (gc_status == Status::NOT_FOUND) {
687 // Not enough space, and no reclaimable bytes, this KVS is full!
688 return Status::RESOURCE_EXHAUSTED;
689 }
690 return gc_status;
691 }
692
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700693 result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersf3884eb2020-03-08 19:21:40 -0700694
695 gc_sector_count++;
696 // Allow total sectors + 2 number of GC cycles so that once reclaimable
697 // bytes in all the sectors have been reclaimed can try and free up space by
698 // moving entries for keys other than the one being worked on in to sectors
699 // that have copies of the key trying to be written.
700 if (gc_sector_count > (partition_.sector_count() + 2)) {
701 ERR("Did more GC sectors than total sectors!!!!");
702 return Status::RESOURCE_EXHAUSTED;
703 }
David Rogersa2562b52020-03-05 15:30:05 -0800704 }
705
706 if (!result.ok()) {
707 WRN("Unable to find sector to write %zu B", entry_size);
708 }
709 return result;
710}
711
David Rogers9abe3c72020-03-24 19:03:13 -0700712Status KeyValueStore::MarkSectorCorruptIfNotOk(Status status,
713 SectorDescriptor* sector) {
714 if (!status.ok()) {
715 DBG(" Sector %u corrupt", sectors_.Index(sector));
716 sector->mark_corrupt();
717 error_detected_ = true;
718 }
719 return status;
720}
721
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700722Status KeyValueStore::AppendEntry(const Entry& entry,
David Rogersa2562b52020-03-05 15:30:05 -0800723 string_view key,
724 span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700725 const StatusWithSize result = entry.Write(key, value);
David Rogersa2562b52020-03-05 15:30:05 -0800726
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700727 SectorDescriptor& sector = sectors_.FromAddress(entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800728
729 if (!result.ok()) {
730 ERR("Failed to write %zu bytes at %#zx. %zu actually written",
731 entry.size(),
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700732 size_t(entry.address()),
David Rogersa2562b52020-03-05 15:30:05 -0800733 result.size());
David Rogers9abe3c72020-03-24 19:03:13 -0700734 TRY(MarkSectorCorruptIfNotOk(result.status(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800735 }
736
737 if (options_.verify_on_write) {
David Rogers9abe3c72020-03-24 19:03:13 -0700738 TRY(MarkSectorCorruptIfNotOk(entry.VerifyChecksumInFlash(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800739 }
740
David Rogers98fea472020-04-01 15:43:48 -0700741 sector.RemoveWritableBytes(result.size());
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700742 sector.AddValidBytes(result.size());
David Rogersa2562b52020-03-05 15:30:05 -0800743 return Status::OK;
744}
745
David Rogers98fea472020-04-01 15:43:48 -0700746StatusWithSize KeyValueStore::CopyEntryToSector(Entry& entry,
747 SectorDescriptor* new_sector,
748 Address& new_address) {
749 new_address = sectors_.NextWritableAddress(*new_sector);
750 const StatusWithSize result = entry.Copy(new_address);
751
752 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(result.status(), new_sector));
753
754 if (options_.verify_on_write) {
755 TRY_WITH_SIZE(
756 MarkSectorCorruptIfNotOk(entry.VerifyChecksumInFlash(), new_sector));
757 }
758 // Entry was written successfully; update descriptor's address and the sector
759 // descriptors to reflect the new entry.
760 new_sector->RemoveWritableBytes(result.size());
761 new_sector->AddValidBytes(result.size());
762
763 return result;
764}
765
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700766Status KeyValueStore::RelocateEntry(const EntryMetadata& metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700767 KeyValueStore::Address& address,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700768 span<const Address> reserved_addresses) {
David Rogersa2562b52020-03-05 15:30:05 -0800769 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700770 TRY(ReadEntry(metadata, entry));
David Rogersa2562b52020-03-05 15:30:05 -0800771
772 // Find a new sector for the entry and write it to the new location. For
773 // relocation the find should not not be a sector already containing the key
774 // but can be the always empty sector, since this is part of the GC process
775 // that will result in a new empty sector. Also find a sector that does not
776 // have reclaimable space (mostly for the full GC, where that would result in
777 // an immediate extra relocation).
778 SectorDescriptor* new_sector;
779
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700780 TRY(sectors_.FindSpaceDuringGarbageCollection(
781 &new_sector, entry.size(), metadata.addresses(), reserved_addresses));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700782
David Rogers98fea472020-04-01 15:43:48 -0700783 Address new_address;
784 TRY_ASSIGN(const size_t result_size,
785 CopyEntryToSector(entry, new_sector, new_address));
786 sectors_.FromAddress(address).RemoveValidBytes(result_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700787 address = new_address;
David Rogersa2562b52020-03-05 15:30:05 -0800788
789 return Status::OK;
790}
791
David Rogers9abe3c72020-03-24 19:03:13 -0700792Status KeyValueStore::FullMaintenance() {
793 if (initialized_ == InitializationState::kNotInitialized) {
794 return Status::FAILED_PRECONDITION;
795 }
796
797 DBG("Do full maintenance");
David Rogers98fea472020-04-01 15:43:48 -0700798 CheckForErrors();
David Rogers9abe3c72020-03-24 19:03:13 -0700799
800 if (error_detected_) {
801 TRY(Repair());
802 }
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700803
804 SectorDescriptor* sector = sectors_.last_new();
David Rogerscd87c322020-02-27 14:04:08 -0800805
806 // TODO: look in to making an iterator method for cycling through sectors
807 // starting from last_new_sector_.
808 for (size_t j = 0; j < sectors_.size(); j++) {
809 sector += 1;
810 if (sector == sectors_.end()) {
811 sector = sectors_.begin();
812 }
813
814 if (sector->RecoverableBytes(partition_.sector_size_bytes()) > 0) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700815 TRY(GarbageCollectSector(*sector, {}));
David Rogerscd87c322020-02-27 14:04:08 -0800816 }
817 }
818
David Rogers9abe3c72020-03-24 19:03:13 -0700819 DBG("Full maintenance complete");
David Rogerscd87c322020-02-27 14:04:08 -0800820 return Status::OK;
821}
822
David Rogers9abe3c72020-03-24 19:03:13 -0700823Status KeyValueStore::GarbageCollect(span<const Address> reserved_addresses) {
824 if (initialized_ == InitializationState::kNotInitialized) {
825 return Status::FAILED_PRECONDITION;
826 }
827
David Rogersc9d545e2020-03-11 17:47:43 -0700828 DBG("Garbage Collect a single sector");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700829 for (Address address : reserved_addresses) {
David Rogersc9d545e2020-03-11 17:47:43 -0700830 DBG(" Avoid address %u", unsigned(address));
831 }
David Rogers67f4b6c2020-02-06 16:17:09 -0800832
David Rogersfcea3252020-04-07 14:56:35 -0700833 // Do automatic repair, if KVS options allow for it.
834 if (error_detected_ && options_.recovery != ErrorRecovery::kManual) {
835 TRY(Repair());
836 }
837
David Rogersa12786b2020-01-31 16:02:33 -0800838 // Step 1: Find the sector to garbage collect
David Rogersc9d545e2020-03-11 17:47:43 -0700839 SectorDescriptor* sector_to_gc =
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700840 sectors_.FindSectorToGarbageCollect(reserved_addresses);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800841
David Rogersa12786b2020-01-31 16:02:33 -0800842 if (sector_to_gc == nullptr) {
David Rogersa2562b52020-03-05 15:30:05 -0800843 // Nothing to GC.
844 return Status::NOT_FOUND;
David Rogersa12786b2020-01-31 16:02:33 -0800845 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800846
David Rogersc9d545e2020-03-11 17:47:43 -0700847 // Step 2: Garbage collect the selected sector.
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700848 return GarbageCollectSector(*sector_to_gc, reserved_addresses);
David Rogerscd87c322020-02-27 14:04:08 -0800849}
850
David Rogersf3884eb2020-03-08 19:21:40 -0700851Status KeyValueStore::RelocateKeyAddressesInSector(
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700852 SectorDescriptor& sector_to_gc,
David Rogersfcea3252020-04-07 14:56:35 -0700853 const EntryMetadata& metadata,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700854 span<const Address> reserved_addresses) {
855 for (FlashPartition::Address& address : metadata.addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700856 if (sectors_.AddressInSector(sector_to_gc, address)) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700857 DBG(" Relocate entry for Key 0x%08" PRIx32 ", sector %u",
858 metadata.hash(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700859 sectors_.Index(sectors_.FromAddress(address)));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700860 TRY(RelocateEntry(metadata, address, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700861 }
862 }
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700863
David Rogersf3884eb2020-03-08 19:21:40 -0700864 return Status::OK;
865};
866
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700867Status KeyValueStore::GarbageCollectSector(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700868 SectorDescriptor& sector_to_gc, span<const Address> reserved_addresses) {
David Rogers9abe3c72020-03-24 19:03:13 -0700869 DBG(" Garbage Collect sector %u", sectors_.Index(sector_to_gc));
David Rogersf3884eb2020-03-08 19:21:40 -0700870 // Step 1: Move any valid entries in the GC sector to other sectors
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700871 if (sector_to_gc.valid_bytes() != 0) {
David Rogers98fea472020-04-01 15:43:48 -0700872 for (EntryMetadata& metadata : entry_cache_) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700873 TRY(RelocateKeyAddressesInSector(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700874 sector_to_gc, metadata, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700875 }
876 }
877
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700878 if (sector_to_gc.valid_bytes() != 0) {
David Rogers67f4b6c2020-02-06 16:17:09 -0800879 ERR(" Failed to relocate valid entries from sector being garbage "
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800880 "collected, %zu valid bytes remain",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700881 sector_to_gc.valid_bytes());
Wyatt Heplerb7609542020-01-24 10:29:54 -0800882 return Status::INTERNAL;
883 }
884
David Rogerscd87c322020-02-27 14:04:08 -0800885 // Step 2: Reinitialize the sector
David Rogers9abe3c72020-03-24 19:03:13 -0700886 sector_to_gc.mark_corrupt();
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700887 TRY(partition_.Erase(sectors_.BaseAddress(sector_to_gc), 1));
888 sector_to_gc.set_writable_bytes(partition_.sector_size_bytes());
Wyatt Heplerb7609542020-01-24 10:29:54 -0800889
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700890 DBG(" Garbage Collect sector %u complete", sectors_.Index(sector_to_gc));
David Rogersa12786b2020-01-31 16:02:33 -0800891 return Status::OK;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800892}
893
David Rogers9abe3c72020-03-24 19:03:13 -0700894// Add any missing redundant entries/copies for a key.
895Status KeyValueStore::AddRedundantEntries(EntryMetadata& metadata) {
896 SectorDescriptor* new_sector;
897
898 Entry entry;
899
David Rogers98fea472020-04-01 15:43:48 -0700900 TRY(ReadEntry(metadata, entry));
David Rogers9abe3c72020-03-24 19:03:13 -0700901 TRY(entry.VerifyChecksumInFlash());
902
903 for (size_t i = metadata.addresses().size();
904 metadata.addresses().size() < redundancy();
905 i++) {
906 TRY(sectors_.FindSpace(&new_sector, entry.size(), metadata.addresses()));
907
David Rogers98fea472020-04-01 15:43:48 -0700908 Address new_address;
909 TRY(CopyEntryToSector(entry, new_sector, new_address));
David Rogers9abe3c72020-03-24 19:03:13 -0700910
911 metadata.AddNewAddress(new_address);
912 }
913 return Status::OK;
914}
915
916Status KeyValueStore::RepairCorruptSectors() {
917 // Try to GC each corrupt sector, even if previous sectors fail. If GC of a
918 // sector failed on the first pass, then do a second pass, since a later
919 // sector might have cleared up space or otherwise unblocked the earlier
920 // failed sector.
921 Status repair_status = Status::OK;
922
923 size_t loop_count = 0;
924 do {
925 loop_count++;
926 // Error of RESOURCE_EXHAUSTED indicates no space found for relocation.
927 // Reset back to OK for the next pass.
928 if (repair_status == Status::RESOURCE_EXHAUSTED) {
929 repair_status = Status::OK;
930 }
931
932 DBG(" Pass %u", unsigned(loop_count));
933 for (SectorDescriptor& sector : sectors_) {
934 if (sector.corrupt()) {
935 DBG(" Found sector %u with corruption", sectors_.Index(sector));
936 Status sector_status = GarbageCollectSector(sector, {});
937 if (sector_status.ok()) {
938 error_stats_.corrupt_sectors_recovered += 1;
939 } else if (repair_status.ok() ||
940 repair_status == Status::RESOURCE_EXHAUSTED) {
941 repair_status = sector_status;
942 }
943 }
944 }
945 DBG(" Pass %u complete", unsigned(loop_count));
946 } while (!repair_status.ok() && loop_count < 2);
947
948 return repair_status;
949}
950
951Status KeyValueStore::EnsureFreeSectorExists() {
952 Status repair_status = Status::OK;
953 bool empty_sector_found = false;
954
955 DBG(" Find empty sector");
956 for (SectorDescriptor& sector : sectors_) {
957 if (sector.Empty(partition_.sector_size_bytes())) {
958 empty_sector_found = true;
959 DBG(" Empty sector found");
960 break;
961 }
962 }
963 if (empty_sector_found == false) {
964 DBG(" No empty sector found, attempting to GC a free sector");
965 Status sector_status = GarbageCollect(span<const Address, 0>());
966 if (repair_status.ok() && !sector_status.ok()) {
967 DBG(" Unable to free an empty sector");
968 repair_status = sector_status;
969 }
970 }
971
972 return repair_status;
973}
974
975Status KeyValueStore::EnsureEntryRedundancy() {
976 Status repair_status = Status::OK;
977
978 if (redundancy() == 1) {
David Rogers98fea472020-04-01 15:43:48 -0700979 DBG(" Redundancy not in use, nothting to check");
David Rogers9abe3c72020-03-24 19:03:13 -0700980 return Status::OK;
981 }
982
David Rogers98fea472020-04-01 15:43:48 -0700983 DBG(" Write any needed additional duplicate copies of keys to fulfill %u"
984 " redundancy",
David Rogers9abe3c72020-03-24 19:03:13 -0700985 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -0700986 for (EntryMetadata& metadata : entry_cache_) {
David Rogers9abe3c72020-03-24 19:03:13 -0700987 if (metadata.addresses().size() >= redundancy()) {
988 continue;
989 }
990
991 DBG(" Key with %u of %u copies found, adding missing copies",
992 unsigned(metadata.addresses().size()),
993 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -0700994 Status fill_status = AddRedundantEntries(metadata);
David Rogers9abe3c72020-03-24 19:03:13 -0700995 if (fill_status.ok()) {
996 error_stats_.missing_redundant_entries_recovered += 1;
997 DBG(" Key missing copies added");
998 } else {
999 DBG(" Failed to add key missing copies");
1000 if (repair_status.ok()) {
1001 repair_status = fill_status;
1002 }
1003 }
1004 }
1005
1006 return repair_status;
1007}
1008
David Rogersfcea3252020-04-07 14:56:35 -07001009Status KeyValueStore::FixErrors() {
1010 DBG("Fixing KVS errors");
David Rogers9abe3c72020-03-24 19:03:13 -07001011
1012 // Step 1: Garbage collect any sectors marked as corrupt.
David Rogersfcea3252020-04-07 14:56:35 -07001013 Status overall_status = RepairCorruptSectors();
David Rogers9abe3c72020-03-24 19:03:13 -07001014
1015 // Step 2: Make sure there is at least 1 empty sector. This needs to be a
1016 // seperate check of sectors from step 1, because a found empty sector might
1017 // get written to by a later GC that fails and does not result in a free
1018 // sector.
David Rogersfcea3252020-04-07 14:56:35 -07001019 Status repair_status = EnsureFreeSectorExists();
David Rogers9abe3c72020-03-24 19:03:13 -07001020 if (overall_status.ok()) {
1021 overall_status = repair_status;
1022 }
1023
1024 // Step 3: Make sure each stored key has the full number of redundant
1025 // entries.
1026 repair_status = EnsureEntryRedundancy();
1027 if (overall_status.ok()) {
1028 overall_status = repair_status;
1029 }
1030
1031 if (overall_status.ok()) {
1032 error_detected_ = false;
1033 initialized_ = InitializationState::kReady;
1034 }
1035 return overall_status;
1036}
1037
David Rogersfcea3252020-04-07 14:56:35 -07001038Status KeyValueStore::Repair() {
1039 // If errors have been detected, just reinit the KVS metadata. This does a
1040 // full deep error check and any needed repairs. Then repair any errors.
1041 INF("Starting KVS repair");
1042
1043 DBG("Reinitialize KVS metadata");
1044 InitializeMetadata();
1045
1046 return FixErrors();
1047}
1048
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -08001049KeyValueStore::Entry KeyValueStore::CreateEntry(Address address,
Wyatt Heplerab3b2492020-03-11 16:15:16 -07001050 string_view key,
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -08001051 span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001052 EntryState state) {
Keir Mierle9e38b402020-02-21 13:06:21 -08001053 // Always bump the transaction ID when creating a new entry.
1054 //
1055 // Burning transaction IDs prevents inconsistencies between flash and memory
1056 // that which could happen if a write succeeds, but for some reason the read
1057 // and verify step fails. Here's how this would happen:
1058 //
1059 // 1. The entry is written but for some reason the flash reports failure OR
1060 // The write succeeds, but the read / verify operation fails.
1061 // 2. The transaction ID is NOT incremented, because of the failure
1062 // 3. (later) A new entry is written, re-using the transaction ID (oops)
1063 //
1064 // By always burning transaction IDs, the above problem can't happen.
1065 last_transaction_id_ += 1;
1066
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001067 if (state == EntryState::kDeleted) {
Wyatt Hepler7465be32020-02-21 15:30:53 -08001068 return Entry::Tombstone(
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -08001069 partition_, address, formats_.primary(), key, last_transaction_id_);
Wyatt Hepler1fc11042020-02-19 17:17:51 -08001070 }
1071 return Entry::Valid(partition_,
1072 address,
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -08001073 formats_.primary(),
Wyatt Hepler1fc11042020-02-19 17:17:51 -08001074 key,
1075 value,
Keir Mierle9e38b402020-02-21 13:06:21 -08001076 last_transaction_id_);
Wyatt Heplerd2298282020-02-20 17:12:45 -08001077}
1078
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001079void KeyValueStore::LogDebugInfo() const {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001080 const size_t sector_size_bytes = partition_.sector_size_bytes();
1081 DBG("====================== KEY VALUE STORE DUMP =========================");
1082 DBG(" ");
1083 DBG("Flash partition:");
Wyatt Heplerad0a7932020-02-06 08:20:38 -08001084 DBG(" Sector count = %zu", partition_.sector_count());
Wyatt Hepler38ce30f2020-02-19 11:48:31 -08001085 DBG(" Sector max count = %zu", sectors_.max_size());
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001086 DBG(" Sectors in use = %zu", sectors_.size());
Keir Mierle8c352dc2020-02-02 13:58:19 -08001087 DBG(" Sector size = %zu", sector_size_bytes);
1088 DBG(" Total size = %zu", partition_.size_bytes());
1089 DBG(" Alignment = %zu", partition_.alignment_bytes());
1090 DBG(" ");
1091 DBG("Key descriptors:");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001092 DBG(" Entry count = %zu", entry_cache_.total_entries());
1093 DBG(" Max entry count = %zu", entry_cache_.max_entries());
Keir Mierle8c352dc2020-02-02 13:58:19 -08001094 DBG(" ");
1095 DBG(" # hash version address address (hex)");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001096 size_t i = 0;
1097 for (const EntryMetadata& metadata : entry_cache_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001098 DBG(" |%3zu: | %8zx |%8zu | %8zu | %8zx",
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001099 i++,
1100 size_t(metadata.hash()),
1101 size_t(metadata.transaction_id()),
1102 size_t(metadata.first_address()),
1103 size_t(metadata.first_address()));
Keir Mierle8c352dc2020-02-02 13:58:19 -08001104 }
1105 DBG(" ");
1106
1107 DBG("Sector descriptors:");
1108 DBG(" # tail free valid has_space");
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001109 for (const SectorDescriptor& sd : sectors_) {
1110 DBG(" |%3u: | %8zu |%8zu | %s",
1111 sectors_.Index(sd),
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001112 size_t(sd.writable_bytes()),
1113 sd.valid_bytes(),
1114 sd.writable_bytes() ? "YES" : "");
Keir Mierle8c352dc2020-02-02 13:58:19 -08001115 }
1116 DBG(" ");
1117
1118 // TODO: This should stop logging after some threshold.
1119 // size_t dumped_bytes = 0;
1120 DBG("Sector raw data:");
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001121 for (size_t sector_id = 0; sector_id < sectors_.size(); ++sector_id) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001122 // Read sector data. Yes, this will blow the stack on embedded.
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001123 std::array<byte, 500> raw_sector_data; // TODO!!!
Keir Mierle8c352dc2020-02-02 13:58:19 -08001124 StatusWithSize sws =
1125 partition_.Read(sector_id * sector_size_bytes, raw_sector_data);
1126 DBG("Read: %zu bytes", sws.size());
1127
1128 DBG(" base addr offs 0 1 2 3 4 5 6 7");
1129 for (size_t i = 0; i < sector_size_bytes; i += 8) {
1130 DBG(" %3zu %8zx %5zu | %02x %02x %02x %02x %02x %02x %02x %02x",
1131 sector_id,
1132 (sector_id * sector_size_bytes) + i,
1133 i,
1134 static_cast<unsigned int>(raw_sector_data[i + 0]),
1135 static_cast<unsigned int>(raw_sector_data[i + 1]),
1136 static_cast<unsigned int>(raw_sector_data[i + 2]),
1137 static_cast<unsigned int>(raw_sector_data[i + 3]),
1138 static_cast<unsigned int>(raw_sector_data[i + 4]),
1139 static_cast<unsigned int>(raw_sector_data[i + 5]),
1140 static_cast<unsigned int>(raw_sector_data[i + 6]),
1141 static_cast<unsigned int>(raw_sector_data[i + 7]));
1142
1143 // TODO: Fix exit condition.
1144 if (i > 128) {
1145 break;
1146 }
1147 }
1148 DBG(" ");
1149 }
1150
1151 DBG("////////////////////// KEY VALUE STORE DUMP END /////////////////////");
1152}
1153
David Rogerscf680ab2020-02-12 23:28:32 -08001154void KeyValueStore::LogSectors() const {
1155 DBG("Sector descriptors: count %zu", sectors_.size());
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001156 for (auto& sector : sectors_) {
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001157 DBG(" - Sector %u: valid %zu, recoverable %zu, free %zu",
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001158 sectors_.Index(sector),
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001159 sector.valid_bytes(),
1160 sector.RecoverableBytes(partition_.sector_size_bytes()),
1161 sector.writable_bytes());
David Rogers50185ad2020-02-07 00:02:46 -08001162 }
1163}
1164
David Rogerscf680ab2020-02-12 23:28:32 -08001165void KeyValueStore::LogKeyDescriptor() const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001166 DBG("Key descriptors: count %zu", entry_cache_.total_entries());
David Rogers9abe3c72020-03-24 19:03:13 -07001167 for (const EntryMetadata& metadata : entry_cache_) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001168 DBG(" - Key: %s, hash %#zx, transaction ID %zu, first address %#zx",
Wyatt Hepler02946272020-03-18 10:36:22 -07001169 metadata.state() == EntryState::kDeleted ? "Deleted" : "Valid",
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001170 static_cast<size_t>(metadata.hash()),
1171 static_cast<size_t>(metadata.transaction_id()),
1172 static_cast<size_t>(metadata.first_address()));
David Rogerscf680ab2020-02-12 23:28:32 -08001173 }
1174}
1175
Wyatt Hepler2ad60672020-01-21 08:00:16 -08001176} // namespace pw::kvs