blob: 2bc6ddf95b173115881b4ee46a49a47895f5e7ed [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 Heplerc84393f2020-03-20 11:23:24 -070060 sectors_.Reset();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070061 entry_cache_.Reset();
Wyatt Heplerd2298282020-02-20 17:12:45 -080062
David Rogers2e9e0c82020-02-13 15:06:06 -080063 INF("Initializing key value store");
Wyatt Hepler38ce30f2020-02-19 11:48:31 -080064 if (partition_.sector_count() > sectors_.max_size()) {
David Rogers2e9e0c82020-02-13 15:06:06 -080065 ERR("KVS init failed: kMaxUsableSectors (=%zu) must be at least as "
66 "large as the number of sectors in the flash partition (=%zu)",
Wyatt Hepler38ce30f2020-02-19 11:48:31 -080067 sectors_.max_size(),
David Rogers2e9e0c82020-02-13 15:06:06 -080068 partition_.sector_count());
Wyatt Heplerad0a7932020-02-06 08:20:38 -080069 return Status::FAILED_PRECONDITION;
70 }
71
Keir Mierle8c352dc2020-02-02 13:58:19 -080072 const size_t sector_size_bytes = partition_.sector_size_bytes();
Keir Mierle8c352dc2020-02-02 13:58:19 -080073
David Rogers49766d92020-03-20 10:55:54 -070074 // TODO: investigate doing this as a static assert/compile-time check.
75 if (sector_size_bytes > SectorDescriptor::max_sector_size()) {
76 ERR("KVS init failed: sector_size_bytes (=%zu) is greater than maximum "
77 "allowed sector size (=%zu)",
78 sector_size_bytes,
79 SectorDescriptor::max_sector_size());
80 return Status::FAILED_PRECONDITION;
81 }
82
Keir Mierle8c352dc2020-02-02 13:58:19 -080083 DBG("First pass: Read all entries from all sectors");
Wyatt Hepler2c7eca02020-02-18 16:01:42 -080084 Address sector_address = 0;
Keir Mierle8c352dc2020-02-02 13:58:19 -080085
Alexei Frolovd4adf912020-02-21 13:29:15 -080086 size_t total_corrupt_bytes = 0;
87 int corrupt_entries = 0;
David Rogers91627482020-02-27 17:38:12 -080088 bool empty_sector_found = false;
Alexei Frolovd4adf912020-02-21 13:29:15 -080089
Wyatt Hepler2c7eca02020-02-18 16:01:42 -080090 for (SectorDescriptor& sector : sectors_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -080091 Address entry_address = sector_address;
92
Alexei Frolovd4adf912020-02-21 13:29:15 -080093 size_t sector_corrupt_bytes = 0;
94
Wyatt Hepler2c7eca02020-02-18 16:01:42 -080095 for (int num_entries_in_sector = 0; true; num_entries_in_sector++) {
96 DBG("Load entry: sector=%" PRIx32 ", entry#=%d, address=%" PRIx32,
97 sector_address,
Keir Mierle8c352dc2020-02-02 13:58:19 -080098 num_entries_in_sector,
Wyatt Hepler2c7eca02020-02-18 16:01:42 -080099 entry_address);
Keir Mierle8c352dc2020-02-02 13:58:19 -0800100
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700101 if (!sectors_.AddressInSector(sector, entry_address)) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800102 DBG("Fell off end of sector; moving to the next sector");
103 break;
104 }
105
106 Address next_entry_address;
107 Status status = LoadEntry(entry_address, &next_entry_address);
108 if (status == Status::NOT_FOUND) {
109 DBG("Hit un-written data in sector; moving to the next sector");
110 break;
111 }
112 if (status == Status::DATA_LOSS) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800113 // The entry could not be read, indicating data corruption within the
114 // sector. Try to scan the remainder of the sector for other entries.
David Rogersa2562b52020-03-05 15:30:05 -0800115 WRN("KVS init: data loss detected in sector %u at address %zu",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700116 sectors_.Index(sector),
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800117 size_t(entry_address));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800118
David Rogers49766d92020-03-20 10:55:54 -0700119 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800120 corrupt_entries++;
121
122 status = ScanForEntry(sector,
123 entry_address + Entry::kMinAlignmentBytes,
124 &next_entry_address);
125 if (status == Status::NOT_FOUND) {
126 // No further entries in this sector. Mark the remaining bytes in the
127 // sector as corrupt (since we can't reliably know the size of the
128 // corrupt entry).
129 sector_corrupt_bytes +=
130 sector_size_bytes - (entry_address - sector_address);
131 break;
132 }
133
134 if (!status.ok()) {
135 ERR("Unexpected error in KVS initialization: %s", status.str());
136 return Status::UNKNOWN;
137 }
138
139 sector_corrupt_bytes += next_entry_address - entry_address;
140 } else if (!status.ok()) {
141 ERR("Unexpected error in KVS initialization: %s", status.str());
142 return Status::UNKNOWN;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800143 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800144
145 // Entry loaded successfully; so get ready to load the next one.
146 entry_address = next_entry_address;
147
148 // Update of the number of writable bytes in this sector.
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800149 sector.set_writable_bytes(sector_size_bytes -
150 (entry_address - sector_address));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800151 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800152
Alexei Frolovd4adf912020-02-21 13:29:15 -0800153 if (sector_corrupt_bytes > 0) {
154 // If the sector contains corrupt data, prevent any further entries from
155 // being written to it by indicating that it has no space. This should
156 // also make it a decent GC candidate. Valid keys in the sector are still
157 // readable as normal.
David Rogers49766d92020-03-20 10:55:54 -0700158 sector.mark_corrupt();
159 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800160
161 WRN("Sector %u contains %zuB of corrupt data",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700162 sectors_.Index(sector),
Alexei Frolovd4adf912020-02-21 13:29:15 -0800163 sector_corrupt_bytes);
164 }
165
David Rogers91627482020-02-27 17:38:12 -0800166 if (sector.Empty(sector_size_bytes)) {
167 empty_sector_found = true;
168 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800169 sector_address += sector_size_bytes;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800170 total_corrupt_bytes += sector_corrupt_bytes;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800171 }
172
173 DBG("Second pass: Count valid bytes in each sector");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700174 Address newest_key = 0;
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800175
David Rogers98fea472020-04-01 15:43:48 -0700176 // For every valid entry, for each address, count the valid bytes in that
177 // sector. If the address fails to read, remove the address and mark the
178 // sector as corrupt. Track which entry has the newest transaction ID for
179 // initializing last_new_sector_.
180 for (EntryMetadata& metadata : entry_cache_) {
David Rogers49766d92020-03-20 10:55:54 -0700181 if (metadata.addresses().size() < redundancy()) {
182 error_detected_ = true;
183 }
David Rogers98fea472020-04-01 15:43:48 -0700184 size_t index = 0;
185 while (index < metadata.addresses().size()) {
186 Address address = metadata.addresses()[index];
David Rogersf56131c2020-03-04 10:19:22 -0800187 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700188
189 Status read_result = Entry::Read(partition_, address, formats_, &entry);
190
191 SectorDescriptor& sector = sectors_.FromAddress(address);
192
193 if (read_result.ok()) {
194 sector.AddValidBytes(entry.size());
195 index++;
196 } else {
197 corrupt_entries++;
198 total_corrupt_bytes += sector.writable_bytes();
199 error_detected_ = true;
200 sector.mark_corrupt();
201
202 // Remove the bad address and stay at this index. The removal
203 // replaces out the removed address with the back address so
204 // this index needs to be rechecked with the new address.
205 metadata.RemoveAddress(address);
206 }
David Rogersf56131c2020-03-04 10:19:22 -0800207 }
David Rogers98fea472020-04-01 15:43:48 -0700208
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700209 if (metadata.IsNewerThan(last_transaction_id_)) {
210 last_transaction_id_ = metadata.transaction_id();
211 newest_key = metadata.addresses().back();
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800212 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800213 }
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800214
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700215 sectors_.set_last_new_sector(newest_key);
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800216
David Rogers91627482020-02-27 17:38:12 -0800217 if (!empty_sector_found) {
David Rogers9abe3c72020-03-24 19:03:13 -0700218 error_detected_ = true;
David Rogers91627482020-02-27 17:38:12 -0800219 }
220
David Rogers9abe3c72020-03-24 19:03:13 -0700221 if (!error_detected_) {
222 initialized_ = InitializationState::kReady;
223 } else {
224 if (options_.recovery != ErrorRecovery::kManual) {
David Rogers98fea472020-04-01 15:43:48 -0700225 WRN("KVS init: Corruption detected, beginning repair. Found %zu corrupt "
226 "bytes and %d corrupt entries.",
227 total_corrupt_bytes,
228 corrupt_entries);
David Rogers9abe3c72020-03-24 19:03:13 -0700229 Status recovery_status = Repair();
230
231 if (recovery_status.ok()) {
232 WRN("KVS init: Corruption detected and fully repaired");
233 initialized_ = InitializationState::kReady;
David Rogers9abe3c72020-03-24 19:03:13 -0700234 } else if (recovery_status == Status::RESOURCE_EXHAUSTED) {
235 WRN("KVS init: Unable to maintain required free sector");
236 initialized_ = InitializationState::kNeedsMaintenance;
237 } else {
238 WRN("KVS init: Corruption detected and unable repair");
239 initialized_ = InitializationState::kNeedsMaintenance;
240 }
241 } else {
242 WRN("KVS init: Corruption detected, no repair attempted due to options");
243 initialized_ = InitializationState::kNeedsMaintenance;
244 }
245 }
David Rogers2e9e0c82020-02-13 15:06:06 -0800246
Armando Montanez5464d5f2020-02-20 10:12:20 -0800247 INF("KeyValueStore init complete: active keys %zu, deleted keys %zu, sectors "
David Rogers2e9e0c82020-02-13 15:06:06 -0800248 "%zu, logical sector size %zu bytes",
249 size(),
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700250 (entry_cache_.total_entries() - size()),
David Rogers2e9e0c82020-02-13 15:06:06 -0800251 sectors_.size(),
252 partition_.sector_size_bytes());
253
David Rogers9abe3c72020-03-24 19:03:13 -0700254 // Report any corruption was not repaired.
David Rogers98fea472020-04-01 15:43:48 -0700255 if (error_detected_) {
256 WRN("KVS init: Corruption found but not repaired, KVS unavailable until "
257 "successful maintenance.");
Alexei Frolovd4adf912020-02-21 13:29:15 -0800258 return Status::DATA_LOSS;
259 }
260
Keir Mierle8c352dc2020-02-02 13:58:19 -0800261 return Status::OK;
262}
263
Alexei Frolov9e235832020-02-24 12:44:45 -0800264KeyValueStore::StorageStats KeyValueStore::GetStorageStats() const {
David Rogers9abe3c72020-03-24 19:03:13 -0700265 StorageStats stats{};
Alexei Frolov9e235832020-02-24 12:44:45 -0800266 const size_t sector_size = partition_.sector_size_bytes();
267 bool found_empty_sector = false;
David Rogers9abe3c72020-03-24 19:03:13 -0700268 stats.corrupt_sectors_recovered = error_stats_.corrupt_sectors_recovered;
269 stats.missing_redundant_entries_recovered =
270 error_stats_.missing_redundant_entries_recovered;
Alexei Frolov9e235832020-02-24 12:44:45 -0800271
272 for (const SectorDescriptor& sector : sectors_) {
273 stats.in_use_bytes += sector.valid_bytes();
274 stats.reclaimable_bytes += sector.RecoverableBytes(sector_size);
275
276 if (!found_empty_sector && sector.Empty(sector_size)) {
277 // The KVS tries to always keep an empty sector for GC, so don't count
278 // the first empty sector seen as writable space. However, a free sector
279 // cannot always be assumed to exist; if a GC operation fails, all sectors
280 // may be partially written, in which case the space reported might be
281 // inaccurate.
282 found_empty_sector = true;
283 continue;
284 }
285
286 stats.writable_bytes += sector.writable_bytes();
287 }
288
289 return stats;
290}
291
David Rogers98fea472020-04-01 15:43:48 -0700292// Check KVS for any error conditions. Primarily intended for test and
293// internal use.
David Rogers9abe3c72020-03-24 19:03:13 -0700294bool KeyValueStore::CheckForErrors() {
295 // Check for corrupted sectors
296 for (SectorDescriptor& sector : sectors_) {
297 if (sector.corrupt()) {
298 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700299 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700300 }
301 }
302
303 // Check for missing redundancy.
304 if (redundancy() > 1) {
305 for (const EntryMetadata& metadata : entry_cache_) {
306 if (metadata.addresses().size() < redundancy()) {
307 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700308 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700309 }
310 }
311 }
312
313 return error_detected();
314}
315
Keir Mierle8c352dc2020-02-02 13:58:19 -0800316Status KeyValueStore::LoadEntry(Address entry_address,
317 Address* next_entry_address) {
Wyatt Heplere541e072020-02-14 09:10:53 -0800318 Entry entry;
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800319 TRY(Entry::Read(partition_, entry_address, formats_, &entry));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800320
321 // Read the key from flash & validate the entry (which reads the value).
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800322 Entry::KeyBuffer key_buffer;
Wyatt Heplere541e072020-02-14 09:10:53 -0800323 TRY_ASSIGN(size_t key_length, entry.ReadKey(key_buffer));
324 const string_view key(key_buffer.data(), key_length);
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800325
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800326 TRY(entry.VerifyChecksumInFlash());
David Rogersf56131c2020-03-04 10:19:22 -0800327
328 // A valid entry was found, so update the next entry address before doing any
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700329 // of the checks that happen in AddNewOrUpdateExisting.
David Rogersf56131c2020-03-04 10:19:22 -0800330 *next_entry_address = entry.next_address();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700331 return entry_cache_.AddNewOrUpdateExisting(
332 entry.descriptor(key), entry.address(), partition_.sector_size_bytes());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800333}
334
Alexei Frolovd4adf912020-02-21 13:29:15 -0800335// Scans flash memory within a sector to find a KVS entry magic.
Alexei Frolovd4adf912020-02-21 13:29:15 -0800336Status KeyValueStore::ScanForEntry(const SectorDescriptor& sector,
337 Address start_address,
338 Address* next_entry_address) {
339 DBG("Scanning sector %u for entries starting from address %zx",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700340 sectors_.Index(sector),
Alexei Frolovd4adf912020-02-21 13:29:15 -0800341 size_t(start_address));
342
343 // Entries must start at addresses which are aligned on a multiple of
344 // Entry::kMinAlignmentBytes. However, that multiple can vary between entries.
345 // When scanning, we don't have an entry to tell us what the current alignment
346 // is, so the minimum alignment is used to be exhaustive.
347 for (Address address = AlignUp(start_address, Entry::kMinAlignmentBytes);
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700348 sectors_.AddressInSector(sector, address);
Alexei Frolovd4adf912020-02-21 13:29:15 -0800349 address += Entry::kMinAlignmentBytes) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800350 uint32_t magic;
351 TRY(partition_.Read(address, as_writable_bytes(span(&magic, 1))));
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800352 if (formats_.KnownMagic(magic)) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800353 DBG("Found entry magic at address %zx", size_t(address));
354 *next_entry_address = address;
355 return Status::OK;
356 }
357 }
358
359 return Status::NOT_FOUND;
360}
361
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800362StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800363 span<byte> value_buffer,
364 size_t offset_bytes) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700365 TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800366
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700367 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700368 TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800369
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700370 return Get(key, metadata, value_buffer, offset_bytes);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800371}
372
Wyatt Heplerfac81132020-02-27 17:26:33 -0800373Status KeyValueStore::PutBytes(string_view key, span<const byte> value) {
David Rogers9abe3c72020-03-24 19:03:13 -0700374 TRY(CheckWriteOperation(key));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800375 DBG("Writing key/value; key length=%zu, value length=%zu",
376 key.size(),
377 value.size());
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800378
Wyatt Hepler5406a672020-02-18 15:42:38 -0800379 if (Entry::size(partition_, key, value) > partition_.sector_size_bytes()) {
380 DBG("%zu B value with %zu B key cannot fit in one sector",
381 value.size(),
382 key.size());
383 return Status::INVALID_ARGUMENT;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800384 }
385
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700386 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700387 Status status = FindEntry(key, &metadata);
Wyatt Hepler2d401692020-02-13 16:01:23 -0800388
389 if (status.ok()) {
David Rogersf56131c2020-03-04 10:19:22 -0800390 // TODO: figure out logging how to support multiple addresses.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700391 DBG("Overwriting entry for key 0x%08" PRIx32 " in %zu sectors including %u",
392 metadata.hash(),
393 metadata.addresses().size(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700394 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700395 return WriteEntryForExistingKey(metadata, EntryState::kValid, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800396 }
David Rogers2761aeb2020-01-31 17:09:00 -0800397
Wyatt Hepler2d401692020-02-13 16:01:23 -0800398 if (status == Status::NOT_FOUND) {
399 return WriteEntryForNewKey(key, value);
400 }
401
402 return status;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800403}
404
405Status KeyValueStore::Delete(string_view key) {
David Rogers9abe3c72020-03-24 19:03:13 -0700406 TRY(CheckWriteOperation(key));
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800407
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700408 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700409 TRY(FindExisting(key, &metadata));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800410
David Rogersf56131c2020-03-04 10:19:22 -0800411 // TODO: figure out logging how to support multiple addresses.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700412 DBG("Writing tombstone for key 0x%08" PRIx32 " in %zu sectors including %u",
413 metadata.hash(),
414 metadata.addresses().size(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700415 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700416 return WriteEntryForExistingKey(metadata, EntryState::kDeleted, key, {});
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800417}
418
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800419void KeyValueStore::Item::ReadKey() {
420 key_buffer_.fill('\0');
421
422 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700423 if (kvs_.ReadEntry(*iterator_, entry).ok()) {
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800424 entry.ReadKey(key_buffer_);
425 }
426}
427
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800428KeyValueStore::iterator& KeyValueStore::iterator::operator++() {
429 // Skip to the next entry that is valid (not deleted).
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700430 while (++item_.iterator_ != item_.kvs_.entry_cache_.end() &&
Wyatt Hepler02946272020-03-18 10:36:22 -0700431 item_.iterator_->state() != EntryState::kValid) {
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800432 }
433 return *this;
434}
435
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800436KeyValueStore::iterator KeyValueStore::begin() const {
Wyatt Heplerbfc6a522020-04-01 16:30:24 -0700437 internal::EntryCache::const_iterator cache_iterator = entry_cache_.begin();
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800438 // Skip over any deleted entries at the start of the descriptor list.
Wyatt Hepler02946272020-03-18 10:36:22 -0700439 while (cache_iterator != entry_cache_.end() &&
440 cache_iterator->state() != EntryState::kValid) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700441 ++cache_iterator;
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800442 }
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700443 return iterator(*this, cache_iterator);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800444}
445
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700446StatusWithSize KeyValueStore::ValueSize(string_view key) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700447 TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Heplered163b02020-02-03 17:49:32 -0800448
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700449 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700450 TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800451
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700452 return ValueSize(metadata);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800453}
Wyatt Heplered163b02020-02-03 17:49:32 -0800454
David Rogers98fea472020-04-01 15:43:48 -0700455Status KeyValueStore::ReadEntry(const EntryMetadata& metadata,
456 Entry& entry) const {
457 // Try to read an entry
458 Status read_result = Status::DATA_LOSS;
459 for (Address address : metadata.addresses()) {
460 read_result = Entry::Read(partition_, address, formats_, &entry);
461 if (read_result.ok()) {
462 return read_result;
463 }
464
465 // Found a bad address. Set the sector as corrupt.
466 error_detected_ = true;
467 sectors_.FromAddress(address).mark_corrupt();
468 }
469
470 ERR("No valid entries for key. Data has been lost!");
471 return read_result;
472}
473
474Status KeyValueStore::FindEntry(string_view key,
475 EntryMetadata* found_entry) const {
476 StatusWithSize find_result =
477 entry_cache_.Find(partition_, sectors_, formats_, key, found_entry);
478
479 if (find_result.size() > 0u) {
480 error_detected_ = true;
481 }
482 return find_result.status();
483}
484
485Status KeyValueStore::FindExisting(string_view key,
486 EntryMetadata* metadata) const {
487 Status status = FindEntry(key, metadata);
488
489 // If the key's hash collides with an existing key or if the key is deleted,
490 // treat it as if it is not in the KVS.
491 if (status == Status::ALREADY_EXISTS ||
492 (status.ok() && metadata->state() == EntryState::kDeleted)) {
493 return Status::NOT_FOUND;
494 }
495 return status;
496}
497
Wyatt Heplerfac81132020-02-27 17:26:33 -0800498StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700499 const EntryMetadata& metadata,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800500 span<std::byte> value_buffer,
501 size_t offset_bytes) const {
502 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700503
504 TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800505
506 StatusWithSize result = entry.ReadValue(value_buffer, offset_bytes);
507 if (result.ok() && options_.verify_on_read && offset_bytes == 0u) {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800508 Status verify_result =
509 entry.VerifyChecksum(key, value_buffer.first(result.size()));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800510 if (!verify_result.ok()) {
511 std::memset(value_buffer.data(), 0, result.size());
512 return StatusWithSize(verify_result, 0);
513 }
514
515 return StatusWithSize(verify_result, result.size());
516 }
517 return result;
Wyatt Heplered163b02020-02-03 17:49:32 -0800518}
519
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800520Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800521 void* value,
522 size_t size_bytes) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700523 TRY(CheckWriteOperation(key));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800524
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700525 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700526 TRY(FindExisting(key, &metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800527
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700528 return FixedSizeGet(key, metadata, value, size_bytes);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800529}
530
531Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700532 const EntryMetadata& metadata,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800533 void* value,
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800534 size_t size_bytes) const {
535 // Ensure that the size of the stored value matches the size of the type.
536 // Otherwise, report error. This check avoids potential memory corruption.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700537 TRY_ASSIGN(const size_t actual_size, ValueSize(metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800538
539 if (actual_size != size_bytes) {
540 DBG("Requested %zu B read, but value is %zu B", size_bytes, actual_size);
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800541 return Status::INVALID_ARGUMENT;
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800542 }
Wyatt Heplerfac81132020-02-27 17:26:33 -0800543
544 StatusWithSize result =
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700545 Get(key, metadata, span(static_cast<byte*>(value), size_bytes), 0);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800546
547 return result.status();
548}
549
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700550StatusWithSize KeyValueStore::ValueSize(const EntryMetadata& metadata) const {
Wyatt Heplerfac81132020-02-27 17:26:33 -0800551 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700552 TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800553
554 return StatusWithSize(entry.value_size());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800555}
556
David Rogers9abe3c72020-03-24 19:03:13 -0700557Status KeyValueStore::CheckWriteOperation(string_view key) const {
Wyatt Hepleracaacf92020-01-24 10:58:30 -0800558 if (InvalidKey(key)) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800559 return Status::INVALID_ARGUMENT;
560 }
David Rogers9abe3c72020-03-24 19:03:13 -0700561
562 // For normal write operation the KVS must be fully ready.
Wyatt Heplerd2298282020-02-20 17:12:45 -0800563 if (!initialized()) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800564 return Status::FAILED_PRECONDITION;
565 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800566 return Status::OK;
567}
568
David Rogers9abe3c72020-03-24 19:03:13 -0700569Status KeyValueStore::CheckReadOperation(string_view key) const {
570 if (InvalidKey(key)) {
571 return Status::INVALID_ARGUMENT;
572 }
573
574 // Operations that are explicitly read-only can be done after init() has been
575 // called but not fully ready (when needing maintenance).
576 if (initialized_ == InitializationState::kNotInitialized) {
577 return Status::FAILED_PRECONDITION;
578 }
579 return Status::OK;
580}
581
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700582Status KeyValueStore::WriteEntryForExistingKey(EntryMetadata& metadata,
583 EntryState new_state,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800584 string_view key,
585 span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700586 // Read the original entry to get the size for sector accounting purposes.
587 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700588 TRY(ReadEntry(metadata, entry));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800589
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700590 return WriteEntry(key, value, new_state, &metadata, entry.size());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800591}
592
593Status KeyValueStore::WriteEntryForNewKey(string_view key,
594 span<const byte> value) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700595 if (entry_cache_.full()) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800596 WRN("KVS full: trying to store a new entry, but can't. Have %zu entries",
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700597 entry_cache_.total_entries());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800598 return Status::RESOURCE_EXHAUSTED;
599 }
600
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700601 return WriteEntry(key, value, EntryState::kValid);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700602}
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800603
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700604Status KeyValueStore::WriteEntry(string_view key,
605 span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700606 EntryState new_state,
607 EntryMetadata* prior_metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700608 size_t prior_size) {
609 const size_t entry_size = Entry::size(partition_, key, value);
610
611 // List of addresses for sectors with space for this entry.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700612 Address* reserved_addresses = entry_cache_.TempReservedAddressesForWrite();
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700613
614 // Find sectors to write the entry to. This may involve garbage collecting one
615 // or more sectors.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700616 for (size_t i = 0; i < redundancy(); i++) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700617 SectorDescriptor* sector;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700618 TRY(GetSectorForWrite(&sector, entry_size, span(reserved_addresses, i)));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700619
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700620 DBG("Found space for entry in sector %u", sectors_.Index(sector));
621 reserved_addresses[i] = sectors_.NextWritableAddress(*sector);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700622 }
623
624 // Write the entry at the first address that was found.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700625 Entry entry = CreateEntry(reserved_addresses[0], key, value, new_state);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700626 TRY(AppendEntry(entry, key, value));
627
628 // After writing the first entry successfully, update the key descriptors.
629 // Once a single new the entry is written, the old entries are invalidated.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700630 EntryMetadata new_metadata =
631 UpdateKeyDescriptor(entry, key, prior_metadata, prior_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700632
633 // Write the additional copies of the entry, if redundancy is greater than 1.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700634 for (size_t i = 1; i < redundancy(); ++i) {
635 entry.set_address(reserved_addresses[i]);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700636 TRY(AppendEntry(entry, key, value));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700637 new_metadata.AddNewAddress(reserved_addresses[i]);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700638 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800639 return Status::OK;
640}
641
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700642KeyValueStore::EntryMetadata KeyValueStore::UpdateKeyDescriptor(
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700643 const Entry& entry,
644 string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700645 EntryMetadata* prior_metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700646 size_t prior_size) {
647 // If there is no prior descriptor, create a new one.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700648 if (prior_metadata == nullptr) {
649 return entry_cache_.AddNew(entry.descriptor(key), entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800650 }
651
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700652 // Remove valid bytes for the old entry and its copies, which are now stale.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700653 for (Address address : prior_metadata->addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700654 sectors_.FromAddress(address).RemoveValidBytes(prior_size);
David Rogersa2562b52020-03-05 15:30:05 -0800655 }
656
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700657 prior_metadata->Reset(entry.descriptor(key), entry.address());
658 return *prior_metadata;
David Rogersa2562b52020-03-05 15:30:05 -0800659}
660
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700661// Finds a sector to use for writing a new entry to. Does automatic garbage
David Rogersa2562b52020-03-05 15:30:05 -0800662// collection if needed and allowed.
663//
664// OK: Sector found with needed space.
665// RESOURCE_EXHAUSTED: No sector available with the needed space.
666Status KeyValueStore::GetSectorForWrite(SectorDescriptor** sector,
667 size_t entry_size,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700668 span<const Address> reserved) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700669 Status result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800670
David Rogersf3884eb2020-03-08 19:21:40 -0700671 size_t gc_sector_count = 0;
David Rogersa2562b52020-03-05 15:30:05 -0800672 bool do_auto_gc = options_.gc_on_write != GargbageCollectOnWrite::kDisabled;
673
674 // Do garbage collection as needed, so long as policy allows.
675 while (result == Status::RESOURCE_EXHAUSTED && do_auto_gc) {
676 if (options_.gc_on_write == GargbageCollectOnWrite::kOneSector) {
677 // If GC config option is kOneSector clear the flag to not do any more
678 // GC after this try.
679 do_auto_gc = false;
680 }
681 // Garbage collect and then try again to find the best sector.
David Rogers9abe3c72020-03-24 19:03:13 -0700682 Status gc_status = GarbageCollect(reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800683 if (!gc_status.ok()) {
684 if (gc_status == Status::NOT_FOUND) {
685 // Not enough space, and no reclaimable bytes, this KVS is full!
686 return Status::RESOURCE_EXHAUSTED;
687 }
688 return gc_status;
689 }
690
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700691 result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersf3884eb2020-03-08 19:21:40 -0700692
693 gc_sector_count++;
694 // Allow total sectors + 2 number of GC cycles so that once reclaimable
695 // bytes in all the sectors have been reclaimed can try and free up space by
696 // moving entries for keys other than the one being worked on in to sectors
697 // that have copies of the key trying to be written.
698 if (gc_sector_count > (partition_.sector_count() + 2)) {
699 ERR("Did more GC sectors than total sectors!!!!");
700 return Status::RESOURCE_EXHAUSTED;
701 }
David Rogersa2562b52020-03-05 15:30:05 -0800702 }
703
704 if (!result.ok()) {
705 WRN("Unable to find sector to write %zu B", entry_size);
706 }
707 return result;
708}
709
David Rogers9abe3c72020-03-24 19:03:13 -0700710Status KeyValueStore::MarkSectorCorruptIfNotOk(Status status,
711 SectorDescriptor* sector) {
712 if (!status.ok()) {
713 DBG(" Sector %u corrupt", sectors_.Index(sector));
714 sector->mark_corrupt();
715 error_detected_ = true;
716 }
717 return status;
718}
719
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700720Status KeyValueStore::AppendEntry(const Entry& entry,
David Rogersa2562b52020-03-05 15:30:05 -0800721 string_view key,
722 span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700723 const StatusWithSize result = entry.Write(key, value);
David Rogersa2562b52020-03-05 15:30:05 -0800724
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700725 SectorDescriptor& sector = sectors_.FromAddress(entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800726
727 if (!result.ok()) {
728 ERR("Failed to write %zu bytes at %#zx. %zu actually written",
729 entry.size(),
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700730 size_t(entry.address()),
David Rogersa2562b52020-03-05 15:30:05 -0800731 result.size());
David Rogers9abe3c72020-03-24 19:03:13 -0700732 TRY(MarkSectorCorruptIfNotOk(result.status(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800733 }
734
735 if (options_.verify_on_write) {
David Rogers9abe3c72020-03-24 19:03:13 -0700736 TRY(MarkSectorCorruptIfNotOk(entry.VerifyChecksumInFlash(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800737 }
738
David Rogers98fea472020-04-01 15:43:48 -0700739 sector.RemoveWritableBytes(result.size());
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700740 sector.AddValidBytes(result.size());
David Rogersa2562b52020-03-05 15:30:05 -0800741 return Status::OK;
742}
743
David Rogers98fea472020-04-01 15:43:48 -0700744StatusWithSize KeyValueStore::CopyEntryToSector(Entry& entry,
745 SectorDescriptor* new_sector,
746 Address& new_address) {
747 new_address = sectors_.NextWritableAddress(*new_sector);
748 const StatusWithSize result = entry.Copy(new_address);
749
750 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(result.status(), new_sector));
751
752 if (options_.verify_on_write) {
753 TRY_WITH_SIZE(
754 MarkSectorCorruptIfNotOk(entry.VerifyChecksumInFlash(), new_sector));
755 }
756 // Entry was written successfully; update descriptor's address and the sector
757 // descriptors to reflect the new entry.
758 new_sector->RemoveWritableBytes(result.size());
759 new_sector->AddValidBytes(result.size());
760
761 return result;
762}
763
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700764Status KeyValueStore::RelocateEntry(const EntryMetadata& metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700765 KeyValueStore::Address& address,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700766 span<const Address> reserved_addresses) {
David Rogersa2562b52020-03-05 15:30:05 -0800767 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700768 TRY(ReadEntry(metadata, entry));
David Rogersa2562b52020-03-05 15:30:05 -0800769
770 // Find a new sector for the entry and write it to the new location. For
771 // relocation the find should not not be a sector already containing the key
772 // but can be the always empty sector, since this is part of the GC process
773 // that will result in a new empty sector. Also find a sector that does not
774 // have reclaimable space (mostly for the full GC, where that would result in
775 // an immediate extra relocation).
776 SectorDescriptor* new_sector;
777
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700778 TRY(sectors_.FindSpaceDuringGarbageCollection(
779 &new_sector, entry.size(), metadata.addresses(), reserved_addresses));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700780
David Rogers98fea472020-04-01 15:43:48 -0700781 Address new_address;
782 TRY_ASSIGN(const size_t result_size,
783 CopyEntryToSector(entry, new_sector, new_address));
784 sectors_.FromAddress(address).RemoveValidBytes(result_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700785 address = new_address;
David Rogersa2562b52020-03-05 15:30:05 -0800786
787 return Status::OK;
788}
789
David Rogers9abe3c72020-03-24 19:03:13 -0700790Status KeyValueStore::FullMaintenance() {
791 if (initialized_ == InitializationState::kNotInitialized) {
792 return Status::FAILED_PRECONDITION;
793 }
794
795 DBG("Do full maintenance");
David Rogers98fea472020-04-01 15:43:48 -0700796 CheckForErrors();
David Rogers9abe3c72020-03-24 19:03:13 -0700797
798 if (error_detected_) {
799 TRY(Repair());
800 }
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700801
802 SectorDescriptor* sector = sectors_.last_new();
David Rogerscd87c322020-02-27 14:04:08 -0800803
804 // TODO: look in to making an iterator method for cycling through sectors
805 // starting from last_new_sector_.
806 for (size_t j = 0; j < sectors_.size(); j++) {
807 sector += 1;
808 if (sector == sectors_.end()) {
809 sector = sectors_.begin();
810 }
811
812 if (sector->RecoverableBytes(partition_.sector_size_bytes()) > 0) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700813 TRY(GarbageCollectSector(*sector, {}));
David Rogerscd87c322020-02-27 14:04:08 -0800814 }
815 }
816
David Rogers9abe3c72020-03-24 19:03:13 -0700817 DBG("Full maintenance complete");
David Rogerscd87c322020-02-27 14:04:08 -0800818 return Status::OK;
819}
820
David Rogers9abe3c72020-03-24 19:03:13 -0700821Status KeyValueStore::GarbageCollect(span<const Address> reserved_addresses) {
822 if (initialized_ == InitializationState::kNotInitialized) {
823 return Status::FAILED_PRECONDITION;
824 }
825
826 // Do automatic repair, if KVS options allow for it.
827 if (error_detected_ && options_.recovery != ErrorRecovery::kManual) {
828 TRY(Repair());
829 }
830
David Rogersc9d545e2020-03-11 17:47:43 -0700831 DBG("Garbage Collect a single sector");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700832 for (Address address : reserved_addresses) {
David Rogersc9d545e2020-03-11 17:47:43 -0700833 DBG(" Avoid address %u", unsigned(address));
834 }
David Rogers67f4b6c2020-02-06 16:17:09 -0800835
David Rogersa12786b2020-01-31 16:02:33 -0800836 // Step 1: Find the sector to garbage collect
David Rogersc9d545e2020-03-11 17:47:43 -0700837 SectorDescriptor* sector_to_gc =
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700838 sectors_.FindSectorToGarbageCollect(reserved_addresses);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800839
David Rogersa12786b2020-01-31 16:02:33 -0800840 if (sector_to_gc == nullptr) {
David Rogersa2562b52020-03-05 15:30:05 -0800841 // Nothing to GC.
842 return Status::NOT_FOUND;
David Rogersa12786b2020-01-31 16:02:33 -0800843 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800844
David Rogersc9d545e2020-03-11 17:47:43 -0700845 // Step 2: Garbage collect the selected sector.
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700846 return GarbageCollectSector(*sector_to_gc, reserved_addresses);
David Rogerscd87c322020-02-27 14:04:08 -0800847}
848
David Rogersf3884eb2020-03-08 19:21:40 -0700849Status KeyValueStore::RelocateKeyAddressesInSector(
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700850 SectorDescriptor& sector_to_gc,
David Rogers98fea472020-04-01 15:43:48 -0700851 EntryMetadata& metadata,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700852 span<const Address> reserved_addresses) {
853 for (FlashPartition::Address& address : metadata.addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700854 if (sectors_.AddressInSector(sector_to_gc, address)) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700855 DBG(" Relocate entry for Key 0x%08" PRIx32 ", sector %u",
856 metadata.hash(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700857 sectors_.Index(sectors_.FromAddress(address)));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700858 TRY(RelocateEntry(metadata, address, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700859 }
860 }
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700861
David Rogersf3884eb2020-03-08 19:21:40 -0700862 return Status::OK;
863};
864
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700865Status KeyValueStore::GarbageCollectSector(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700866 SectorDescriptor& sector_to_gc, span<const Address> reserved_addresses) {
David Rogers9abe3c72020-03-24 19:03:13 -0700867 DBG(" Garbage Collect sector %u", sectors_.Index(sector_to_gc));
David Rogersf3884eb2020-03-08 19:21:40 -0700868 // Step 1: Move any valid entries in the GC sector to other sectors
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700869 if (sector_to_gc.valid_bytes() != 0) {
David Rogers98fea472020-04-01 15:43:48 -0700870 for (EntryMetadata& metadata : entry_cache_) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700871 TRY(RelocateKeyAddressesInSector(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700872 sector_to_gc, metadata, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700873 }
874 }
875
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700876 if (sector_to_gc.valid_bytes() != 0) {
David Rogers67f4b6c2020-02-06 16:17:09 -0800877 ERR(" Failed to relocate valid entries from sector being garbage "
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800878 "collected, %zu valid bytes remain",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700879 sector_to_gc.valid_bytes());
Wyatt Heplerb7609542020-01-24 10:29:54 -0800880 return Status::INTERNAL;
881 }
882
David Rogerscd87c322020-02-27 14:04:08 -0800883 // Step 2: Reinitialize the sector
David Rogers9abe3c72020-03-24 19:03:13 -0700884 sector_to_gc.mark_corrupt();
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700885 TRY(partition_.Erase(sectors_.BaseAddress(sector_to_gc), 1));
886 sector_to_gc.set_writable_bytes(partition_.sector_size_bytes());
Wyatt Heplerb7609542020-01-24 10:29:54 -0800887
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700888 DBG(" Garbage Collect sector %u complete", sectors_.Index(sector_to_gc));
David Rogersa12786b2020-01-31 16:02:33 -0800889 return Status::OK;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800890}
891
David Rogers9abe3c72020-03-24 19:03:13 -0700892// Add any missing redundant entries/copies for a key.
893Status KeyValueStore::AddRedundantEntries(EntryMetadata& metadata) {
894 SectorDescriptor* new_sector;
895
896 Entry entry;
897
David Rogers98fea472020-04-01 15:43:48 -0700898 TRY(ReadEntry(metadata, entry));
David Rogers9abe3c72020-03-24 19:03:13 -0700899 TRY(entry.VerifyChecksumInFlash());
900
901 for (size_t i = metadata.addresses().size();
902 metadata.addresses().size() < redundancy();
903 i++) {
904 TRY(sectors_.FindSpace(&new_sector, entry.size(), metadata.addresses()));
905
David Rogers98fea472020-04-01 15:43:48 -0700906 Address new_address;
907 TRY(CopyEntryToSector(entry, new_sector, new_address));
David Rogers9abe3c72020-03-24 19:03:13 -0700908
909 metadata.AddNewAddress(new_address);
910 }
911 return Status::OK;
912}
913
914Status KeyValueStore::RepairCorruptSectors() {
915 // Try to GC each corrupt sector, even if previous sectors fail. If GC of a
916 // sector failed on the first pass, then do a second pass, since a later
917 // sector might have cleared up space or otherwise unblocked the earlier
918 // failed sector.
919 Status repair_status = Status::OK;
920
921 size_t loop_count = 0;
922 do {
923 loop_count++;
924 // Error of RESOURCE_EXHAUSTED indicates no space found for relocation.
925 // Reset back to OK for the next pass.
926 if (repair_status == Status::RESOURCE_EXHAUSTED) {
927 repair_status = Status::OK;
928 }
929
930 DBG(" Pass %u", unsigned(loop_count));
931 for (SectorDescriptor& sector : sectors_) {
932 if (sector.corrupt()) {
933 DBG(" Found sector %u with corruption", sectors_.Index(sector));
934 Status sector_status = GarbageCollectSector(sector, {});
935 if (sector_status.ok()) {
936 error_stats_.corrupt_sectors_recovered += 1;
937 } else if (repair_status.ok() ||
938 repair_status == Status::RESOURCE_EXHAUSTED) {
939 repair_status = sector_status;
940 }
941 }
942 }
943 DBG(" Pass %u complete", unsigned(loop_count));
944 } while (!repair_status.ok() && loop_count < 2);
945
946 return repair_status;
947}
948
949Status KeyValueStore::EnsureFreeSectorExists() {
950 Status repair_status = Status::OK;
951 bool empty_sector_found = false;
952
953 DBG(" Find empty sector");
954 for (SectorDescriptor& sector : sectors_) {
955 if (sector.Empty(partition_.sector_size_bytes())) {
956 empty_sector_found = true;
957 DBG(" Empty sector found");
958 break;
959 }
960 }
961 if (empty_sector_found == false) {
962 DBG(" No empty sector found, attempting to GC a free sector");
963 Status sector_status = GarbageCollect(span<const Address, 0>());
964 if (repair_status.ok() && !sector_status.ok()) {
965 DBG(" Unable to free an empty sector");
966 repair_status = sector_status;
967 }
968 }
969
970 return repair_status;
971}
972
973Status KeyValueStore::EnsureEntryRedundancy() {
974 Status repair_status = Status::OK;
975
976 if (redundancy() == 1) {
David Rogers98fea472020-04-01 15:43:48 -0700977 DBG(" Redundancy not in use, nothting to check");
David Rogers9abe3c72020-03-24 19:03:13 -0700978 return Status::OK;
979 }
980
David Rogers98fea472020-04-01 15:43:48 -0700981 DBG(" Write any needed additional duplicate copies of keys to fulfill %u"
982 " redundancy",
David Rogers9abe3c72020-03-24 19:03:13 -0700983 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -0700984 for (EntryMetadata& metadata : entry_cache_) {
David Rogers9abe3c72020-03-24 19:03:13 -0700985 if (metadata.addresses().size() >= redundancy()) {
986 continue;
987 }
988
989 DBG(" Key with %u of %u copies found, adding missing copies",
990 unsigned(metadata.addresses().size()),
991 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -0700992 Status fill_status = AddRedundantEntries(metadata);
David Rogers9abe3c72020-03-24 19:03:13 -0700993 if (fill_status.ok()) {
994 error_stats_.missing_redundant_entries_recovered += 1;
995 DBG(" Key missing copies added");
996 } else {
997 DBG(" Failed to add key missing copies");
998 if (repair_status.ok()) {
999 repair_status = fill_status;
1000 }
1001 }
1002 }
1003
1004 return repair_status;
1005}
1006
1007Status KeyValueStore::Repair() {
1008 // Collect and return the first error encountered.
1009 Status overall_status = Status::OK;
1010
1011 DBG("KVS repair");
1012
1013 // Step 1: Garbage collect any sectors marked as corrupt.
1014 Status repair_status = RepairCorruptSectors();
1015 if (overall_status.ok()) {
1016 overall_status = repair_status;
1017 }
1018
1019 // Step 2: Make sure there is at least 1 empty sector. This needs to be a
1020 // seperate check of sectors from step 1, because a found empty sector might
1021 // get written to by a later GC that fails and does not result in a free
1022 // sector.
1023 repair_status = EnsureFreeSectorExists();
1024 if (overall_status.ok()) {
1025 overall_status = repair_status;
1026 }
1027
1028 // Step 3: Make sure each stored key has the full number of redundant
1029 // entries.
1030 repair_status = EnsureEntryRedundancy();
1031 if (overall_status.ok()) {
1032 overall_status = repair_status;
1033 }
1034
1035 if (overall_status.ok()) {
1036 error_detected_ = false;
1037 initialized_ = InitializationState::kReady;
1038 }
1039 return overall_status;
1040}
1041
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -08001042KeyValueStore::Entry KeyValueStore::CreateEntry(Address address,
Wyatt Heplerab3b2492020-03-11 16:15:16 -07001043 string_view key,
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -08001044 span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001045 EntryState state) {
Keir Mierle9e38b402020-02-21 13:06:21 -08001046 // Always bump the transaction ID when creating a new entry.
1047 //
1048 // Burning transaction IDs prevents inconsistencies between flash and memory
1049 // that which could happen if a write succeeds, but for some reason the read
1050 // and verify step fails. Here's how this would happen:
1051 //
1052 // 1. The entry is written but for some reason the flash reports failure OR
1053 // The write succeeds, but the read / verify operation fails.
1054 // 2. The transaction ID is NOT incremented, because of the failure
1055 // 3. (later) A new entry is written, re-using the transaction ID (oops)
1056 //
1057 // By always burning transaction IDs, the above problem can't happen.
1058 last_transaction_id_ += 1;
1059
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001060 if (state == EntryState::kDeleted) {
Wyatt Hepler7465be32020-02-21 15:30:53 -08001061 return Entry::Tombstone(
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -08001062 partition_, address, formats_.primary(), key, last_transaction_id_);
Wyatt Hepler1fc11042020-02-19 17:17:51 -08001063 }
1064 return Entry::Valid(partition_,
1065 address,
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -08001066 formats_.primary(),
Wyatt Hepler1fc11042020-02-19 17:17:51 -08001067 key,
1068 value,
Keir Mierle9e38b402020-02-21 13:06:21 -08001069 last_transaction_id_);
Wyatt Heplerd2298282020-02-20 17:12:45 -08001070}
1071
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001072void KeyValueStore::LogDebugInfo() const {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001073 const size_t sector_size_bytes = partition_.sector_size_bytes();
1074 DBG("====================== KEY VALUE STORE DUMP =========================");
1075 DBG(" ");
1076 DBG("Flash partition:");
Wyatt Heplerad0a7932020-02-06 08:20:38 -08001077 DBG(" Sector count = %zu", partition_.sector_count());
Wyatt Hepler38ce30f2020-02-19 11:48:31 -08001078 DBG(" Sector max count = %zu", sectors_.max_size());
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001079 DBG(" Sectors in use = %zu", sectors_.size());
Keir Mierle8c352dc2020-02-02 13:58:19 -08001080 DBG(" Sector size = %zu", sector_size_bytes);
1081 DBG(" Total size = %zu", partition_.size_bytes());
1082 DBG(" Alignment = %zu", partition_.alignment_bytes());
1083 DBG(" ");
1084 DBG("Key descriptors:");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001085 DBG(" Entry count = %zu", entry_cache_.total_entries());
1086 DBG(" Max entry count = %zu", entry_cache_.max_entries());
Keir Mierle8c352dc2020-02-02 13:58:19 -08001087 DBG(" ");
1088 DBG(" # hash version address address (hex)");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001089 size_t i = 0;
1090 for (const EntryMetadata& metadata : entry_cache_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001091 DBG(" |%3zu: | %8zx |%8zu | %8zu | %8zx",
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001092 i++,
1093 size_t(metadata.hash()),
1094 size_t(metadata.transaction_id()),
1095 size_t(metadata.first_address()),
1096 size_t(metadata.first_address()));
Keir Mierle8c352dc2020-02-02 13:58:19 -08001097 }
1098 DBG(" ");
1099
1100 DBG("Sector descriptors:");
1101 DBG(" # tail free valid has_space");
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001102 for (const SectorDescriptor& sd : sectors_) {
1103 DBG(" |%3u: | %8zu |%8zu | %s",
1104 sectors_.Index(sd),
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001105 size_t(sd.writable_bytes()),
1106 sd.valid_bytes(),
1107 sd.writable_bytes() ? "YES" : "");
Keir Mierle8c352dc2020-02-02 13:58:19 -08001108 }
1109 DBG(" ");
1110
1111 // TODO: This should stop logging after some threshold.
1112 // size_t dumped_bytes = 0;
1113 DBG("Sector raw data:");
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001114 for (size_t sector_id = 0; sector_id < sectors_.size(); ++sector_id) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001115 // Read sector data. Yes, this will blow the stack on embedded.
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001116 std::array<byte, 500> raw_sector_data; // TODO!!!
Keir Mierle8c352dc2020-02-02 13:58:19 -08001117 StatusWithSize sws =
1118 partition_.Read(sector_id * sector_size_bytes, raw_sector_data);
1119 DBG("Read: %zu bytes", sws.size());
1120
1121 DBG(" base addr offs 0 1 2 3 4 5 6 7");
1122 for (size_t i = 0; i < sector_size_bytes; i += 8) {
1123 DBG(" %3zu %8zx %5zu | %02x %02x %02x %02x %02x %02x %02x %02x",
1124 sector_id,
1125 (sector_id * sector_size_bytes) + i,
1126 i,
1127 static_cast<unsigned int>(raw_sector_data[i + 0]),
1128 static_cast<unsigned int>(raw_sector_data[i + 1]),
1129 static_cast<unsigned int>(raw_sector_data[i + 2]),
1130 static_cast<unsigned int>(raw_sector_data[i + 3]),
1131 static_cast<unsigned int>(raw_sector_data[i + 4]),
1132 static_cast<unsigned int>(raw_sector_data[i + 5]),
1133 static_cast<unsigned int>(raw_sector_data[i + 6]),
1134 static_cast<unsigned int>(raw_sector_data[i + 7]));
1135
1136 // TODO: Fix exit condition.
1137 if (i > 128) {
1138 break;
1139 }
1140 }
1141 DBG(" ");
1142 }
1143
1144 DBG("////////////////////// KEY VALUE STORE DUMP END /////////////////////");
1145}
1146
David Rogerscf680ab2020-02-12 23:28:32 -08001147void KeyValueStore::LogSectors() const {
1148 DBG("Sector descriptors: count %zu", sectors_.size());
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001149 for (auto& sector : sectors_) {
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001150 DBG(" - Sector %u: valid %zu, recoverable %zu, free %zu",
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001151 sectors_.Index(sector),
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001152 sector.valid_bytes(),
1153 sector.RecoverableBytes(partition_.sector_size_bytes()),
1154 sector.writable_bytes());
David Rogers50185ad2020-02-07 00:02:46 -08001155 }
1156}
1157
David Rogerscf680ab2020-02-12 23:28:32 -08001158void KeyValueStore::LogKeyDescriptor() const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001159 DBG("Key descriptors: count %zu", entry_cache_.total_entries());
David Rogers9abe3c72020-03-24 19:03:13 -07001160 for (const EntryMetadata& metadata : entry_cache_) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001161 DBG(" - Key: %s, hash %#zx, transaction ID %zu, first address %#zx",
Wyatt Hepler02946272020-03-18 10:36:22 -07001162 metadata.state() == EntryState::kDeleted ? "Deleted" : "Valid",
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001163 static_cast<size_t>(metadata.hash()),
1164 static_cast<size_t>(metadata.transaction_id()),
1165 static_cast<size_t>(metadata.first_address()));
David Rogerscf680ab2020-02-12 23:28:32 -08001166 }
1167}
1168
Wyatt Hepler2ad60672020-01-21 08:00:16 -08001169} // namespace pw::kvs