blob: 80bb8cc0b51feb82e5f099bad8d0052f064e3500 [file] [log] [blame]
Wyatt Heplerb7609542020-01-24 10:29:54 -08001// Copyright 2020 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
Armando Montanez28ecccb2020-05-04 15:35:54 -070015#define PW_LOG_MODULE_NAME "KVS"
16
Wyatt Heplerb7609542020-01-24 10:29:54 -080017#include "pw_kvs/key_value_store.h"
18
Wyatt Heplerbab0e202020-02-04 07:40:08 -080019#include <algorithm>
Wyatt Hepler5a33d8c2020-02-06 09:32:58 -080020#include <cinttypes>
Wyatt Heplerb7609542020-01-24 10:29:54 -080021#include <cstring>
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080022#include <type_traits>
Wyatt Heplerb7609542020-01-24 10:29:54 -080023
Keir Mierle8c352dc2020-02-02 13:58:19 -080024#define PW_LOG_USE_ULTRA_SHORT_NAMES 1
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080025#include "pw_kvs_private/macros.h"
Keir Mierle8c352dc2020-02-02 13:58:19 -080026#include "pw_log/log.h"
Wyatt Heplerb7609542020-01-24 10:29:54 -080027
Wyatt Hepler2ad60672020-01-21 08:00:16 -080028namespace pw::kvs {
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080029namespace {
Wyatt Heplerb7609542020-01-24 10:29:54 -080030
Wyatt Hepleracaacf92020-01-24 10:58:30 -080031using std::byte;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080032using std::string_view;
Wyatt Hepleracaacf92020-01-24 10:58:30 -080033
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080034constexpr bool InvalidKey(std::string_view key) {
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -080035 return key.empty() || (key.size() > internal::Entry::kMaxKeyLength);
Wyatt Heplera00d1ef2020-02-14 14:31:26 -080036}
37
38} // namespace
39
Wyatt Heplerad0a7932020-02-06 08:20:38 -080040KeyValueStore::KeyValueStore(FlashPartition* partition,
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080041 span<const EntryFormat> formats,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070042 const Options& options,
43 size_t redundancy,
44 Vector<SectorDescriptor>& sector_descriptor_list,
45 const SectorDescriptor** temp_sectors_to_skip,
46 Vector<KeyDescriptor>& key_descriptor_list,
47 Address* addresses)
Wyatt Heplerad0a7932020-02-06 08:20:38 -080048 : partition_(*partition),
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -080049 formats_(formats),
Wyatt Heplerc84393f2020-03-20 11:23:24 -070050 sectors_(sector_descriptor_list, *partition, temp_sectors_to_skip),
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070051 entry_cache_(key_descriptor_list, addresses, redundancy),
David Rogers49766d92020-03-20 10:55:54 -070052 options_(options),
David Rogers9abe3c72020-03-24 19:03:13 -070053 initialized_(InitializationState::kNotInitialized),
David Rogers49766d92020-03-20 10:55:54 -070054 error_detected_(false),
David Rogers9abe3c72020-03-24 19:03:13 -070055 error_stats_({}),
David Rogers49766d92020-03-20 10:55:54 -070056 last_transaction_id_(0) {}
Wyatt Heplerad0a7932020-02-06 08:20:38 -080057
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080058Status KeyValueStore::Init() {
David Rogers9abe3c72020-03-24 19:03:13 -070059 initialized_ = InitializationState::kNotInitialized;
David Rogers49766d92020-03-20 10:55:54 -070060 error_detected_ = false;
Keir Mierlebf904812020-03-11 17:28:22 -070061 last_transaction_id_ = 0;
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
David Rogerscd134352020-04-17 19:37:15 -070083 Status metadata_result = InitializeMetadata();
David Rogersfcea3252020-04-07 14:56:35 -070084
85 if (!error_detected_) {
86 initialized_ = InitializationState::kReady;
87 } else {
David Rogers0f8a1bb2020-04-21 18:50:19 -070088 initialized_ = InitializationState::kNeedsMaintenance;
89
David Rogersfcea3252020-04-07 14:56:35 -070090 if (options_.recovery != ErrorRecovery::kManual) {
Armando Montanezf8419ae2020-04-21 10:03:05 -070091 size_t pre_fix_redundancy_errors =
92 error_stats_.missing_redundant_entries_recovered;
David Rogersfcea3252020-04-07 14:56:35 -070093 Status recovery_status = FixErrors();
94
95 if (recovery_status.ok()) {
David Rogerscd134352020-04-17 19:37:15 -070096 if (metadata_result == Status::OUT_OF_RANGE) {
Armando Montanezf8419ae2020-04-21 10:03:05 -070097 error_stats_.missing_redundant_entries_recovered =
98 pre_fix_redundancy_errors;
David Rogerscd134352020-04-17 19:37:15 -070099 INF("KVS init: Redundancy level successfully updated");
100 } else {
101 WRN("KVS init: Corruption detected and fully repaired");
102 }
David Rogersfcea3252020-04-07 14:56:35 -0700103 initialized_ = InitializationState::kReady;
104 } else if (recovery_status == Status::RESOURCE_EXHAUSTED) {
105 WRN("KVS init: Unable to maintain required free sector");
David Rogersfcea3252020-04-07 14:56:35 -0700106 } else {
107 WRN("KVS init: Corruption detected and unable repair");
David Rogersfcea3252020-04-07 14:56:35 -0700108 }
109 } else {
110 WRN("KVS init: Corruption detected, no repair attempted due to options");
David Rogersfcea3252020-04-07 14:56:35 -0700111 }
112 }
113
114 INF("KeyValueStore init complete: active keys %zu, deleted keys %zu, sectors "
115 "%zu, logical sector size %zu bytes",
116 size(),
117 (entry_cache_.total_entries() - size()),
118 sectors_.size(),
119 partition_.sector_size_bytes());
120
121 // Report any corruption was not repaired.
122 if (error_detected_) {
123 WRN("KVS init: Corruption found but not repaired, KVS unavailable until "
124 "successful maintenance.");
125 return Status::DATA_LOSS;
126 }
127
128 return Status::OK;
129}
130
David Rogerscd134352020-04-17 19:37:15 -0700131Status KeyValueStore::InitializeMetadata() {
David Rogersfcea3252020-04-07 14:56:35 -0700132 const size_t sector_size_bytes = partition_.sector_size_bytes();
133
134 sectors_.Reset();
135 entry_cache_.Reset();
136
Keir Mierle8c352dc2020-02-02 13:58:19 -0800137 DBG("First pass: Read all entries from all sectors");
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800138 Address sector_address = 0;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800139
Alexei Frolovd4adf912020-02-21 13:29:15 -0800140 size_t total_corrupt_bytes = 0;
David Rogerscd134352020-04-17 19:37:15 -0700141 size_t corrupt_entries = 0;
David Rogers91627482020-02-27 17:38:12 -0800142 bool empty_sector_found = false;
David Rogerscd134352020-04-17 19:37:15 -0700143 size_t entry_copies_missing = 0;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800144
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800145 for (SectorDescriptor& sector : sectors_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800146 Address entry_address = sector_address;
147
Alexei Frolovd4adf912020-02-21 13:29:15 -0800148 size_t sector_corrupt_bytes = 0;
149
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800150 for (int num_entries_in_sector = 0; true; num_entries_in_sector++) {
David Rogersfcea3252020-04-07 14:56:35 -0700151 DBG("Load entry: sector=%u, entry#=%d, address=%u",
152 unsigned(sector_address),
Keir Mierle8c352dc2020-02-02 13:58:19 -0800153 num_entries_in_sector,
David Rogersfcea3252020-04-07 14:56:35 -0700154 unsigned(entry_address));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800155
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700156 if (!sectors_.AddressInSector(sector, entry_address)) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800157 DBG("Fell off end of sector; moving to the next sector");
158 break;
159 }
160
161 Address next_entry_address;
162 Status status = LoadEntry(entry_address, &next_entry_address);
163 if (status == Status::NOT_FOUND) {
164 DBG("Hit un-written data in sector; moving to the next sector");
165 break;
David Rogersfcea3252020-04-07 14:56:35 -0700166 } else if (!status.ok()) {
167 // The entry could not be read, indicating likely data corruption within
168 // the sector. Try to scan the remainder of the sector for other
169 // entries.
Alexei Frolovd4adf912020-02-21 13:29:15 -0800170
David Rogers49766d92020-03-20 10:55:54 -0700171 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800172 corrupt_entries++;
173
174 status = ScanForEntry(sector,
175 entry_address + Entry::kMinAlignmentBytes,
176 &next_entry_address);
David Rogersfcea3252020-04-07 14:56:35 -0700177 if (!status.ok()) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800178 // No further entries in this sector. Mark the remaining bytes in the
179 // sector as corrupt (since we can't reliably know the size of the
180 // corrupt entry).
181 sector_corrupt_bytes +=
182 sector_size_bytes - (entry_address - sector_address);
183 break;
184 }
185
Alexei Frolovd4adf912020-02-21 13:29:15 -0800186 sector_corrupt_bytes += next_entry_address - entry_address;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800187 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800188
189 // Entry loaded successfully; so get ready to load the next one.
190 entry_address = next_entry_address;
191
192 // Update of the number of writable bytes in this sector.
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800193 sector.set_writable_bytes(sector_size_bytes -
194 (entry_address - sector_address));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800195 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800196
Alexei Frolovd4adf912020-02-21 13:29:15 -0800197 if (sector_corrupt_bytes > 0) {
198 // If the sector contains corrupt data, prevent any further entries from
199 // being written to it by indicating that it has no space. This should
200 // also make it a decent GC candidate. Valid keys in the sector are still
201 // readable as normal.
David Rogers49766d92020-03-20 10:55:54 -0700202 sector.mark_corrupt();
203 error_detected_ = true;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800204
205 WRN("Sector %u contains %zuB of corrupt data",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700206 sectors_.Index(sector),
Alexei Frolovd4adf912020-02-21 13:29:15 -0800207 sector_corrupt_bytes);
208 }
209
David Rogers91627482020-02-27 17:38:12 -0800210 if (sector.Empty(sector_size_bytes)) {
211 empty_sector_found = true;
212 }
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800213 sector_address += sector_size_bytes;
Alexei Frolovd4adf912020-02-21 13:29:15 -0800214 total_corrupt_bytes += sector_corrupt_bytes;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800215 }
216
217 DBG("Second pass: Count valid bytes in each sector");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700218 Address newest_key = 0;
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800219
David Rogers98fea472020-04-01 15:43:48 -0700220 // For every valid entry, for each address, count the valid bytes in that
221 // sector. If the address fails to read, remove the address and mark the
222 // sector as corrupt. Track which entry has the newest transaction ID for
223 // initializing last_new_sector_.
224 for (EntryMetadata& metadata : entry_cache_) {
David Rogers49766d92020-03-20 10:55:54 -0700225 if (metadata.addresses().size() < redundancy()) {
David Rogers31b358b2020-04-15 05:00:50 -0700226 DBG("Key 0x%08x missing copies, has %u, needs %u",
227 unsigned(metadata.hash()),
228 unsigned(metadata.addresses().size()),
229 unsigned(redundancy()));
David Rogerscd134352020-04-17 19:37:15 -0700230 entry_copies_missing++;
David Rogers49766d92020-03-20 10:55:54 -0700231 }
David Rogers98fea472020-04-01 15:43:48 -0700232 size_t index = 0;
233 while (index < metadata.addresses().size()) {
234 Address address = metadata.addresses()[index];
David Rogersf56131c2020-03-04 10:19:22 -0800235 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700236
237 Status read_result = Entry::Read(partition_, address, formats_, &entry);
238
239 SectorDescriptor& sector = sectors_.FromAddress(address);
240
241 if (read_result.ok()) {
242 sector.AddValidBytes(entry.size());
243 index++;
244 } else {
245 corrupt_entries++;
246 total_corrupt_bytes += sector.writable_bytes();
247 error_detected_ = true;
248 sector.mark_corrupt();
249
250 // Remove the bad address and stay at this index. The removal
251 // replaces out the removed address with the back address so
252 // this index needs to be rechecked with the new address.
253 metadata.RemoveAddress(address);
254 }
David Rogersf56131c2020-03-04 10:19:22 -0800255 }
David Rogers98fea472020-04-01 15:43:48 -0700256
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700257 if (metadata.IsNewerThan(last_transaction_id_)) {
258 last_transaction_id_ = metadata.transaction_id();
259 newest_key = metadata.addresses().back();
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800260 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800261 }
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800262
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700263 sectors_.set_last_new_sector(newest_key);
Wyatt Hepler1fc11042020-02-19 17:17:51 -0800264
David Rogers91627482020-02-27 17:38:12 -0800265 if (!empty_sector_found) {
David Rogers31b358b2020-04-15 05:00:50 -0700266 DBG("No empty sector found");
David Rogers9abe3c72020-03-24 19:03:13 -0700267 error_detected_ = true;
David Rogers91627482020-02-27 17:38:12 -0800268 }
269
David Rogerscd134352020-04-17 19:37:15 -0700270 if (entry_copies_missing > 0) {
271 bool other_errors = error_detected_;
272 error_detected_ = true;
273
Armando Montanez344814b2020-04-24 15:05:42 -0700274 if (!other_errors && entry_copies_missing == entry_cache_.total_entries()) {
David Rogerscd134352020-04-17 19:37:15 -0700275 INF("KVS configuration changed to redundancy of %zu total copies per key",
276 redundancy());
277 return Status::OUT_OF_RANGE;
278 }
David Rogers9abe3c72020-03-24 19:03:13 -0700279 }
David Rogerscd134352020-04-17 19:37:15 -0700280
281 if (error_detected_) {
282 WRN("Corruption detected. Found %zu corrupt bytes, %zu corrupt entries, "
283 "and %zu keys missing redundant copies.",
284 total_corrupt_bytes,
285 corrupt_entries,
286 entry_copies_missing);
287 return Status::FAILED_PRECONDITION;
288 }
289 return Status::OK;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800290}
291
Alexei Frolov9e235832020-02-24 12:44:45 -0800292KeyValueStore::StorageStats KeyValueStore::GetStorageStats() const {
David Rogers9abe3c72020-03-24 19:03:13 -0700293 StorageStats stats{};
Alexei Frolov9e235832020-02-24 12:44:45 -0800294 const size_t sector_size = partition_.sector_size_bytes();
295 bool found_empty_sector = false;
David Rogers9abe3c72020-03-24 19:03:13 -0700296 stats.corrupt_sectors_recovered = error_stats_.corrupt_sectors_recovered;
297 stats.missing_redundant_entries_recovered =
298 error_stats_.missing_redundant_entries_recovered;
Alexei Frolov9e235832020-02-24 12:44:45 -0800299
300 for (const SectorDescriptor& sector : sectors_) {
301 stats.in_use_bytes += sector.valid_bytes();
302 stats.reclaimable_bytes += sector.RecoverableBytes(sector_size);
303
304 if (!found_empty_sector && sector.Empty(sector_size)) {
305 // The KVS tries to always keep an empty sector for GC, so don't count
306 // the first empty sector seen as writable space. However, a free sector
307 // cannot always be assumed to exist; if a GC operation fails, all sectors
308 // may be partially written, in which case the space reported might be
309 // inaccurate.
310 found_empty_sector = true;
311 continue;
312 }
313
314 stats.writable_bytes += sector.writable_bytes();
315 }
316
317 return stats;
318}
319
David Rogers98fea472020-04-01 15:43:48 -0700320// Check KVS for any error conditions. Primarily intended for test and
321// internal use.
David Rogers9abe3c72020-03-24 19:03:13 -0700322bool KeyValueStore::CheckForErrors() {
323 // Check for corrupted sectors
324 for (SectorDescriptor& sector : sectors_) {
325 if (sector.corrupt()) {
326 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700327 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700328 }
329 }
330
331 // Check for missing redundancy.
332 if (redundancy() > 1) {
333 for (const EntryMetadata& metadata : entry_cache_) {
334 if (metadata.addresses().size() < redundancy()) {
335 error_detected_ = true;
David Rogers98fea472020-04-01 15:43:48 -0700336 return error_detected();
David Rogers9abe3c72020-03-24 19:03:13 -0700337 }
338 }
339 }
340
341 return error_detected();
342}
343
Keir Mierle8c352dc2020-02-02 13:58:19 -0800344Status KeyValueStore::LoadEntry(Address entry_address,
345 Address* next_entry_address) {
Wyatt Heplere541e072020-02-14 09:10:53 -0800346 Entry entry;
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800347 TRY(Entry::Read(partition_, entry_address, formats_, &entry));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800348
349 // Read the key from flash & validate the entry (which reads the value).
Wyatt Heplera00d1ef2020-02-14 14:31:26 -0800350 Entry::KeyBuffer key_buffer;
Wyatt Heplere541e072020-02-14 09:10:53 -0800351 TRY_ASSIGN(size_t key_length, entry.ReadKey(key_buffer));
352 const string_view key(key_buffer.data(), key_length);
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800353
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800354 TRY(entry.VerifyChecksumInFlash());
David Rogersf56131c2020-03-04 10:19:22 -0800355
356 // A valid entry was found, so update the next entry address before doing any
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700357 // of the checks that happen in AddNewOrUpdateExisting.
David Rogersf56131c2020-03-04 10:19:22 -0800358 *next_entry_address = entry.next_address();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700359 return entry_cache_.AddNewOrUpdateExisting(
360 entry.descriptor(key), entry.address(), partition_.sector_size_bytes());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800361}
362
Alexei Frolovd4adf912020-02-21 13:29:15 -0800363// Scans flash memory within a sector to find a KVS entry magic.
Alexei Frolovd4adf912020-02-21 13:29:15 -0800364Status KeyValueStore::ScanForEntry(const SectorDescriptor& sector,
365 Address start_address,
366 Address* next_entry_address) {
David Rogersfcea3252020-04-07 14:56:35 -0700367 DBG("Scanning sector %u for entries starting from address %u",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700368 sectors_.Index(sector),
David Rogersfcea3252020-04-07 14:56:35 -0700369 unsigned(start_address));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800370
371 // Entries must start at addresses which are aligned on a multiple of
372 // Entry::kMinAlignmentBytes. However, that multiple can vary between entries.
373 // When scanning, we don't have an entry to tell us what the current alignment
374 // is, so the minimum alignment is used to be exhaustive.
375 for (Address address = AlignUp(start_address, Entry::kMinAlignmentBytes);
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700376 sectors_.AddressInSector(sector, address);
Alexei Frolovd4adf912020-02-21 13:29:15 -0800377 address += Entry::kMinAlignmentBytes) {
Alexei Frolovd4adf912020-02-21 13:29:15 -0800378 uint32_t magic;
David Rogersfcea3252020-04-07 14:56:35 -0700379 StatusWithSize read_result =
380 partition_.Read(address, as_writable_bytes(span(&magic, 1)));
381 if (!read_result.ok()) {
382 continue;
383 }
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800384 if (formats_.KnownMagic(magic)) {
David Rogersfcea3252020-04-07 14:56:35 -0700385 DBG("Found entry magic at address %u", unsigned(address));
Alexei Frolovd4adf912020-02-21 13:29:15 -0800386 *next_entry_address = address;
387 return Status::OK;
388 }
389 }
390
391 return Status::NOT_FOUND;
392}
393
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800394StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Hepler5f6efc02020-02-18 16:54:31 -0800395 span<byte> value_buffer,
396 size_t offset_bytes) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700397 TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800398
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700399 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700400 TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800401
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700402 return Get(key, metadata, value_buffer, offset_bytes);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800403}
404
Wyatt Heplerfac81132020-02-27 17:26:33 -0800405Status KeyValueStore::PutBytes(string_view key, span<const byte> value) {
David Rogers9abe3c72020-03-24 19:03:13 -0700406 TRY(CheckWriteOperation(key));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800407 DBG("Writing key/value; key length=%zu, value length=%zu",
408 key.size(),
409 value.size());
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800410
Wyatt Hepler5406a672020-02-18 15:42:38 -0800411 if (Entry::size(partition_, key, value) > partition_.sector_size_bytes()) {
412 DBG("%zu B value with %zu B key cannot fit in one sector",
413 value.size(),
414 key.size());
415 return Status::INVALID_ARGUMENT;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800416 }
417
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700418 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700419 Status status = FindEntry(key, &metadata);
Wyatt Hepler2d401692020-02-13 16:01:23 -0800420
421 if (status.ok()) {
David Rogersf56131c2020-03-04 10:19:22 -0800422 // TODO: figure out logging how to support multiple addresses.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700423 DBG("Overwriting entry for key 0x%08" PRIx32 " in %zu sectors including %u",
424 metadata.hash(),
425 metadata.addresses().size(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700426 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700427 return WriteEntryForExistingKey(metadata, EntryState::kValid, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800428 }
David Rogers2761aeb2020-01-31 17:09:00 -0800429
Wyatt Hepler2d401692020-02-13 16:01:23 -0800430 if (status == Status::NOT_FOUND) {
431 return WriteEntryForNewKey(key, value);
432 }
433
434 return status;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800435}
436
437Status KeyValueStore::Delete(string_view key) {
David Rogers9abe3c72020-03-24 19:03:13 -0700438 TRY(CheckWriteOperation(key));
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800439
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700440 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700441 TRY(FindExisting(key, &metadata));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800442
David Rogersf56131c2020-03-04 10:19:22 -0800443 // TODO: figure out logging how to support multiple addresses.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700444 DBG("Writing tombstone for key 0x%08" PRIx32 " in %zu sectors including %u",
445 metadata.hash(),
446 metadata.addresses().size(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700447 sectors_.Index(metadata.first_address()));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700448 return WriteEntryForExistingKey(metadata, EntryState::kDeleted, key, {});
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800449}
450
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800451void KeyValueStore::Item::ReadKey() {
452 key_buffer_.fill('\0');
453
454 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700455 if (kvs_.ReadEntry(*iterator_, entry).ok()) {
Wyatt Hepler08d37d82020-02-27 15:45:37 -0800456 entry.ReadKey(key_buffer_);
457 }
458}
459
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800460KeyValueStore::iterator& KeyValueStore::iterator::operator++() {
461 // Skip to the next entry that is valid (not deleted).
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700462 while (++item_.iterator_ != item_.kvs_.entry_cache_.end() &&
Wyatt Hepler02946272020-03-18 10:36:22 -0700463 item_.iterator_->state() != EntryState::kValid) {
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800464 }
465 return *this;
466}
467
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800468KeyValueStore::iterator KeyValueStore::begin() const {
Wyatt Heplerbfc6a522020-04-01 16:30:24 -0700469 internal::EntryCache::const_iterator cache_iterator = entry_cache_.begin();
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800470 // Skip over any deleted entries at the start of the descriptor list.
Wyatt Hepler02946272020-03-18 10:36:22 -0700471 while (cache_iterator != entry_cache_.end() &&
472 cache_iterator->state() != EntryState::kValid) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700473 ++cache_iterator;
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800474 }
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700475 return iterator(*this, cache_iterator);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800476}
477
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700478StatusWithSize KeyValueStore::ValueSize(string_view key) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700479 TRY_WITH_SIZE(CheckReadOperation(key));
Wyatt Heplered163b02020-02-03 17:49:32 -0800480
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700481 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700482 TRY_WITH_SIZE(FindExisting(key, &metadata));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800483
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700484 return ValueSize(metadata);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800485}
Wyatt Heplered163b02020-02-03 17:49:32 -0800486
David Rogers98fea472020-04-01 15:43:48 -0700487Status KeyValueStore::ReadEntry(const EntryMetadata& metadata,
488 Entry& entry) const {
489 // Try to read an entry
490 Status read_result = Status::DATA_LOSS;
491 for (Address address : metadata.addresses()) {
492 read_result = Entry::Read(partition_, address, formats_, &entry);
493 if (read_result.ok()) {
494 return read_result;
495 }
496
497 // Found a bad address. Set the sector as corrupt.
498 error_detected_ = true;
499 sectors_.FromAddress(address).mark_corrupt();
500 }
501
502 ERR("No valid entries for key. Data has been lost!");
503 return read_result;
504}
505
506Status KeyValueStore::FindEntry(string_view key,
507 EntryMetadata* found_entry) const {
508 StatusWithSize find_result =
509 entry_cache_.Find(partition_, sectors_, formats_, key, found_entry);
510
511 if (find_result.size() > 0u) {
512 error_detected_ = true;
513 }
514 return find_result.status();
515}
516
517Status KeyValueStore::FindExisting(string_view key,
518 EntryMetadata* metadata) const {
519 Status status = FindEntry(key, metadata);
520
521 // If the key's hash collides with an existing key or if the key is deleted,
522 // treat it as if it is not in the KVS.
523 if (status == Status::ALREADY_EXISTS ||
524 (status.ok() && metadata->state() == EntryState::kDeleted)) {
525 return Status::NOT_FOUND;
526 }
527 return status;
528}
529
Wyatt Heplerfac81132020-02-27 17:26:33 -0800530StatusWithSize KeyValueStore::Get(string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700531 const EntryMetadata& metadata,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800532 span<std::byte> value_buffer,
533 size_t offset_bytes) const {
534 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700535
536 TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800537
538 StatusWithSize result = entry.ReadValue(value_buffer, offset_bytes);
539 if (result.ok() && options_.verify_on_read && offset_bytes == 0u) {
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -0800540 Status verify_result =
541 entry.VerifyChecksum(key, value_buffer.first(result.size()));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800542 if (!verify_result.ok()) {
543 std::memset(value_buffer.data(), 0, result.size());
544 return StatusWithSize(verify_result, 0);
545 }
546
547 return StatusWithSize(verify_result, result.size());
548 }
549 return result;
Wyatt Heplered163b02020-02-03 17:49:32 -0800550}
551
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800552Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800553 void* value,
554 size_t size_bytes) const {
David Rogers9abe3c72020-03-24 19:03:13 -0700555 TRY(CheckWriteOperation(key));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800556
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700557 EntryMetadata metadata;
David Rogers98fea472020-04-01 15:43:48 -0700558 TRY(FindExisting(key, &metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800559
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700560 return FixedSizeGet(key, metadata, value, size_bytes);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800561}
562
563Status KeyValueStore::FixedSizeGet(std::string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700564 const EntryMetadata& metadata,
Wyatt Heplerfac81132020-02-27 17:26:33 -0800565 void* value,
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800566 size_t size_bytes) const {
567 // Ensure that the size of the stored value matches the size of the type.
568 // Otherwise, report error. This check avoids potential memory corruption.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700569 TRY_ASSIGN(const size_t actual_size, ValueSize(metadata));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800570
571 if (actual_size != size_bytes) {
572 DBG("Requested %zu B read, but value is %zu B", size_bytes, actual_size);
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800573 return Status::INVALID_ARGUMENT;
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800574 }
Wyatt Heplerfac81132020-02-27 17:26:33 -0800575
576 StatusWithSize result =
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700577 Get(key, metadata, span(static_cast<byte*>(value), size_bytes), 0);
Wyatt Heplerfac81132020-02-27 17:26:33 -0800578
579 return result.status();
580}
581
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700582StatusWithSize KeyValueStore::ValueSize(const EntryMetadata& metadata) const {
Wyatt Heplerfac81132020-02-27 17:26:33 -0800583 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700584 TRY_WITH_SIZE(ReadEntry(metadata, entry));
Wyatt Heplerfac81132020-02-27 17:26:33 -0800585
586 return StatusWithSize(entry.value_size());
Keir Mierle8c352dc2020-02-02 13:58:19 -0800587}
588
David Rogers9abe3c72020-03-24 19:03:13 -0700589Status KeyValueStore::CheckWriteOperation(string_view key) const {
Wyatt Hepleracaacf92020-01-24 10:58:30 -0800590 if (InvalidKey(key)) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800591 return Status::INVALID_ARGUMENT;
592 }
David Rogers9abe3c72020-03-24 19:03:13 -0700593
594 // For normal write operation the KVS must be fully ready.
Wyatt Heplerd2298282020-02-20 17:12:45 -0800595 if (!initialized()) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800596 return Status::FAILED_PRECONDITION;
597 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800598 return Status::OK;
599}
600
David Rogers9abe3c72020-03-24 19:03:13 -0700601Status KeyValueStore::CheckReadOperation(string_view key) const {
602 if (InvalidKey(key)) {
603 return Status::INVALID_ARGUMENT;
604 }
605
606 // Operations that are explicitly read-only can be done after init() has been
607 // called but not fully ready (when needing maintenance).
608 if (initialized_ == InitializationState::kNotInitialized) {
609 return Status::FAILED_PRECONDITION;
610 }
611 return Status::OK;
612}
613
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700614Status KeyValueStore::WriteEntryForExistingKey(EntryMetadata& metadata,
615 EntryState new_state,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800616 string_view key,
617 span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700618 // Read the original entry to get the size for sector accounting purposes.
619 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700620 TRY(ReadEntry(metadata, entry));
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800621
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700622 return WriteEntry(key, value, new_state, &metadata, entry.size());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800623}
624
625Status KeyValueStore::WriteEntryForNewKey(string_view key,
626 span<const byte> value) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700627 if (entry_cache_.full()) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800628 WRN("KVS full: trying to store a new entry, but can't. Have %zu entries",
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700629 entry_cache_.total_entries());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800630 return Status::RESOURCE_EXHAUSTED;
631 }
632
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700633 return WriteEntry(key, value, EntryState::kValid);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700634}
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800635
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700636Status KeyValueStore::WriteEntry(string_view key,
637 span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700638 EntryState new_state,
639 EntryMetadata* prior_metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700640 size_t prior_size) {
641 const size_t entry_size = Entry::size(partition_, key, value);
642
643 // List of addresses for sectors with space for this entry.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700644 Address* reserved_addresses = entry_cache_.TempReservedAddressesForWrite();
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700645
David Rogers31b358b2020-04-15 05:00:50 -0700646 // Find addresses to write the entry to. This may involve garbage collecting
647 // one or more sectors.
648 TRY(GetAddressesForWrite(reserved_addresses, entry_size));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700649
650 // Write the entry at the first address that was found.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700651 Entry entry = CreateEntry(reserved_addresses[0], key, value, new_state);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700652 TRY(AppendEntry(entry, key, value));
653
654 // After writing the first entry successfully, update the key descriptors.
655 // Once a single new the entry is written, the old entries are invalidated.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700656 EntryMetadata new_metadata =
David Rogers31b358b2020-04-15 05:00:50 -0700657 CreateOrUpdateKeyDescriptor(entry, key, prior_metadata, prior_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700658
659 // Write the additional copies of the entry, if redundancy is greater than 1.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700660 for (size_t i = 1; i < redundancy(); ++i) {
661 entry.set_address(reserved_addresses[i]);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700662 TRY(AppendEntry(entry, key, value));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700663 new_metadata.AddNewAddress(reserved_addresses[i]);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700664 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800665 return Status::OK;
666}
667
David Rogers31b358b2020-04-15 05:00:50 -0700668KeyValueStore::EntryMetadata KeyValueStore::CreateOrUpdateKeyDescriptor(
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700669 const Entry& entry,
670 string_view key,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700671 EntryMetadata* prior_metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700672 size_t prior_size) {
673 // If there is no prior descriptor, create a new one.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700674 if (prior_metadata == nullptr) {
675 return entry_cache_.AddNew(entry.descriptor(key), entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800676 }
677
David Rogers31b358b2020-04-15 05:00:50 -0700678 return UpdateKeyDescriptor(
679 entry, entry.address(), prior_metadata, prior_size);
680}
681
682KeyValueStore::EntryMetadata KeyValueStore::UpdateKeyDescriptor(
683 const Entry& entry,
684 Address new_address,
685 EntryMetadata* prior_metadata,
686 size_t prior_size) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700687 // Remove valid bytes for the old entry and its copies, which are now stale.
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700688 for (Address address : prior_metadata->addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700689 sectors_.FromAddress(address).RemoveValidBytes(prior_size);
David Rogersa2562b52020-03-05 15:30:05 -0800690 }
691
David Rogers31b358b2020-04-15 05:00:50 -0700692 prior_metadata->Reset(entry.descriptor(prior_metadata->hash()), new_address);
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700693 return *prior_metadata;
David Rogersa2562b52020-03-05 15:30:05 -0800694}
695
David Rogers31b358b2020-04-15 05:00:50 -0700696Status KeyValueStore::GetAddressesForWrite(Address* write_addresses,
697 size_t write_size) {
698 for (size_t i = 0; i < redundancy(); i++) {
699 SectorDescriptor* sector;
700 TRY(GetSectorForWrite(&sector, write_size, span(write_addresses, i)));
701 write_addresses[i] = sectors_.NextWritableAddress(*sector);
702
703 DBG("Found space for entry in sector %u at address %u",
704 sectors_.Index(sector),
705 unsigned(write_addresses[i]));
706 }
707
708 return Status::OK;
709}
710
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700711// Finds a sector to use for writing a new entry to. Does automatic garbage
David Rogersa2562b52020-03-05 15:30:05 -0800712// collection if needed and allowed.
713//
714// OK: Sector found with needed space.
715// RESOURCE_EXHAUSTED: No sector available with the needed space.
716Status KeyValueStore::GetSectorForWrite(SectorDescriptor** sector,
717 size_t entry_size,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700718 span<const Address> reserved) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700719 Status result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800720
David Rogersf3884eb2020-03-08 19:21:40 -0700721 size_t gc_sector_count = 0;
David Rogersa2562b52020-03-05 15:30:05 -0800722 bool do_auto_gc = options_.gc_on_write != GargbageCollectOnWrite::kDisabled;
723
724 // Do garbage collection as needed, so long as policy allows.
725 while (result == Status::RESOURCE_EXHAUSTED && do_auto_gc) {
726 if (options_.gc_on_write == GargbageCollectOnWrite::kOneSector) {
727 // If GC config option is kOneSector clear the flag to not do any more
728 // GC after this try.
729 do_auto_gc = false;
730 }
731 // Garbage collect and then try again to find the best sector.
David Rogers9abe3c72020-03-24 19:03:13 -0700732 Status gc_status = GarbageCollect(reserved);
David Rogersa2562b52020-03-05 15:30:05 -0800733 if (!gc_status.ok()) {
734 if (gc_status == Status::NOT_FOUND) {
735 // Not enough space, and no reclaimable bytes, this KVS is full!
736 return Status::RESOURCE_EXHAUSTED;
737 }
738 return gc_status;
739 }
740
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700741 result = sectors_.FindSpace(sector, entry_size, reserved);
David Rogersf3884eb2020-03-08 19:21:40 -0700742
743 gc_sector_count++;
744 // Allow total sectors + 2 number of GC cycles so that once reclaimable
745 // bytes in all the sectors have been reclaimed can try and free up space by
746 // moving entries for keys other than the one being worked on in to sectors
747 // that have copies of the key trying to be written.
748 if (gc_sector_count > (partition_.sector_count() + 2)) {
749 ERR("Did more GC sectors than total sectors!!!!");
750 return Status::RESOURCE_EXHAUSTED;
751 }
David Rogersa2562b52020-03-05 15:30:05 -0800752 }
753
754 if (!result.ok()) {
755 WRN("Unable to find sector to write %zu B", entry_size);
756 }
757 return result;
758}
759
David Rogers9abe3c72020-03-24 19:03:13 -0700760Status KeyValueStore::MarkSectorCorruptIfNotOk(Status status,
761 SectorDescriptor* sector) {
762 if (!status.ok()) {
763 DBG(" Sector %u corrupt", sectors_.Index(sector));
764 sector->mark_corrupt();
765 error_detected_ = true;
766 }
767 return status;
768}
769
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700770Status KeyValueStore::AppendEntry(const Entry& entry,
David Rogersa2562b52020-03-05 15:30:05 -0800771 string_view key,
772 span<const byte> value) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700773 const StatusWithSize result = entry.Write(key, value);
David Rogersa2562b52020-03-05 15:30:05 -0800774
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700775 SectorDescriptor& sector = sectors_.FromAddress(entry.address());
David Rogersa2562b52020-03-05 15:30:05 -0800776
777 if (!result.ok()) {
778 ERR("Failed to write %zu bytes at %#zx. %zu actually written",
779 entry.size(),
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700780 size_t(entry.address()),
David Rogersa2562b52020-03-05 15:30:05 -0800781 result.size());
David Rogers9abe3c72020-03-24 19:03:13 -0700782 TRY(MarkSectorCorruptIfNotOk(result.status(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800783 }
784
785 if (options_.verify_on_write) {
David Rogers9abe3c72020-03-24 19:03:13 -0700786 TRY(MarkSectorCorruptIfNotOk(entry.VerifyChecksumInFlash(), &sector));
David Rogersa2562b52020-03-05 15:30:05 -0800787 }
788
David Rogers98fea472020-04-01 15:43:48 -0700789 sector.RemoveWritableBytes(result.size());
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700790 sector.AddValidBytes(result.size());
David Rogersa2562b52020-03-05 15:30:05 -0800791 return Status::OK;
792}
793
David Rogers98fea472020-04-01 15:43:48 -0700794StatusWithSize KeyValueStore::CopyEntryToSector(Entry& entry,
795 SectorDescriptor* new_sector,
David Rogers31b358b2020-04-15 05:00:50 -0700796 Address new_address) {
David Rogers98fea472020-04-01 15:43:48 -0700797 const StatusWithSize result = entry.Copy(new_address);
798
799 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(result.status(), new_sector));
800
801 if (options_.verify_on_write) {
David Rogers31b358b2020-04-15 05:00:50 -0700802 Entry new_entry;
803 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(
804 Entry::Read(partition_, new_address, formats_, &new_entry),
805 new_sector));
806 // TODO: add test that catches doing the verify on the old entry.
807 TRY_WITH_SIZE(MarkSectorCorruptIfNotOk(new_entry.VerifyChecksumInFlash(),
808 new_sector));
David Rogers98fea472020-04-01 15:43:48 -0700809 }
810 // Entry was written successfully; update descriptor's address and the sector
811 // descriptors to reflect the new entry.
812 new_sector->RemoveWritableBytes(result.size());
813 new_sector->AddValidBytes(result.size());
814
815 return result;
816}
817
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700818Status KeyValueStore::RelocateEntry(const EntryMetadata& metadata,
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700819 KeyValueStore::Address& address,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700820 span<const Address> reserved_addresses) {
David Rogersa2562b52020-03-05 15:30:05 -0800821 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -0700822 TRY(ReadEntry(metadata, entry));
David Rogersa2562b52020-03-05 15:30:05 -0800823
824 // Find a new sector for the entry and write it to the new location. For
825 // relocation the find should not not be a sector already containing the key
826 // but can be the always empty sector, since this is part of the GC process
827 // that will result in a new empty sector. Also find a sector that does not
828 // have reclaimable space (mostly for the full GC, where that would result in
829 // an immediate extra relocation).
830 SectorDescriptor* new_sector;
831
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700832 TRY(sectors_.FindSpaceDuringGarbageCollection(
833 &new_sector, entry.size(), metadata.addresses(), reserved_addresses));
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700834
David Rogers31b358b2020-04-15 05:00:50 -0700835 Address new_address = sectors_.NextWritableAddress(*new_sector);
David Rogers98fea472020-04-01 15:43:48 -0700836 TRY_ASSIGN(const size_t result_size,
837 CopyEntryToSector(entry, new_sector, new_address));
838 sectors_.FromAddress(address).RemoveValidBytes(result_size);
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700839 address = new_address;
David Rogersa2562b52020-03-05 15:30:05 -0800840
841 return Status::OK;
842}
843
David Rogers9abe3c72020-03-24 19:03:13 -0700844Status KeyValueStore::FullMaintenance() {
845 if (initialized_ == InitializationState::kNotInitialized) {
846 return Status::FAILED_PRECONDITION;
847 }
848
Armando Montanez17083bb2020-04-24 10:18:26 -0700849 // Full maintenance can be a potentially heavy operation, and should be
850 // relatively infrequent, so log start/end at INFO level.
851 INF("Beginning full maintenance");
David Rogers98fea472020-04-01 15:43:48 -0700852 CheckForErrors();
David Rogers9abe3c72020-03-24 19:03:13 -0700853
854 if (error_detected_) {
855 TRY(Repair());
856 }
David Rogers35c3f842020-04-22 15:34:05 -0700857 StatusWithSize update_status = UpdateEntriesToPrimaryFormat();
858 Status overall_status = update_status.status();
859
David Rogers31b358b2020-04-15 05:00:50 -0700860 // Make sure all the entries are on the primary format.
Armando Montanez17083bb2020-04-24 10:18:26 -0700861 if (!overall_status.ok()) {
862 ERR("Failed to update all entries to the primary format");
863 }
David Rogers31b358b2020-04-15 05:00:50 -0700864
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700865 SectorDescriptor* sector = sectors_.last_new();
David Rogerscd87c322020-02-27 14:04:08 -0800866
David Rogers35c3f842020-04-22 15:34:05 -0700867 // Calculate number of bytes for the threshold.
868 size_t threshold_bytes =
869 (partition_.size_bytes() * kGcUsageThresholdPercentage) / 100;
870
871 // Is bytes in use over the threshold.
872 StorageStats stats = GetStorageStats();
873 bool over_usage_threshold = stats.in_use_bytes > threshold_bytes;
874 bool force_gc = over_usage_threshold || (update_status.size() > 0);
875
David Rogerscd87c322020-02-27 14:04:08 -0800876 // TODO: look in to making an iterator method for cycling through sectors
877 // starting from last_new_sector_.
Armando Montanez17083bb2020-04-24 10:18:26 -0700878 Status gc_status;
David Rogerscd87c322020-02-27 14:04:08 -0800879 for (size_t j = 0; j < sectors_.size(); j++) {
880 sector += 1;
881 if (sector == sectors_.end()) {
882 sector = sectors_.begin();
883 }
884
David Rogers35c3f842020-04-22 15:34:05 -0700885 if (sector->RecoverableBytes(partition_.sector_size_bytes()) > 0 &&
886 (force_gc || sector->valid_bytes() == 0)) {
Armando Montanez17083bb2020-04-24 10:18:26 -0700887 gc_status = GarbageCollectSector(*sector, {});
888 if (!gc_status.ok()) {
889 ERR("Failed to garbage collect all sectors");
890 break;
891 }
David Rogerscd87c322020-02-27 14:04:08 -0800892 }
893 }
Armando Montanez17083bb2020-04-24 10:18:26 -0700894 if (overall_status.ok()) {
895 overall_status = gc_status;
896 }
David Rogerscd87c322020-02-27 14:04:08 -0800897
Armando Montanez17083bb2020-04-24 10:18:26 -0700898 if (overall_status.ok()) {
899 INF("Full maintenance complete");
900 } else {
901 ERR("Full maintenance finished with some errors");
902 }
903 return overall_status;
David Rogerscd87c322020-02-27 14:04:08 -0800904}
905
David Rogers0f8a1bb2020-04-21 18:50:19 -0700906Status KeyValueStore::PartialMaintenance() {
David Rogers9abe3c72020-03-24 19:03:13 -0700907 if (initialized_ == InitializationState::kNotInitialized) {
908 return Status::FAILED_PRECONDITION;
909 }
910
David Rogers0f8a1bb2020-04-21 18:50:19 -0700911 CheckForErrors();
David Rogersfcea3252020-04-07 14:56:35 -0700912 // Do automatic repair, if KVS options allow for it.
913 if (error_detected_ && options_.recovery != ErrorRecovery::kManual) {
914 TRY(Repair());
915 }
David Rogers0f8a1bb2020-04-21 18:50:19 -0700916 return GarbageCollect(span<const Address>());
917}
918
919Status KeyValueStore::GarbageCollect(span<const Address> reserved_addresses) {
920 DBG("Garbage Collect a single sector");
921 for (Address address : reserved_addresses) {
922 DBG(" Avoid address %u", unsigned(address));
923 }
David Rogersfcea3252020-04-07 14:56:35 -0700924
David Rogersa12786b2020-01-31 16:02:33 -0800925 // Step 1: Find the sector to garbage collect
David Rogersc9d545e2020-03-11 17:47:43 -0700926 SectorDescriptor* sector_to_gc =
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700927 sectors_.FindSectorToGarbageCollect(reserved_addresses);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800928
David Rogersa12786b2020-01-31 16:02:33 -0800929 if (sector_to_gc == nullptr) {
David Rogersa2562b52020-03-05 15:30:05 -0800930 // Nothing to GC.
931 return Status::NOT_FOUND;
David Rogersa12786b2020-01-31 16:02:33 -0800932 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800933
David Rogersc9d545e2020-03-11 17:47:43 -0700934 // Step 2: Garbage collect the selected sector.
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700935 return GarbageCollectSector(*sector_to_gc, reserved_addresses);
David Rogerscd87c322020-02-27 14:04:08 -0800936}
937
David Rogersf3884eb2020-03-08 19:21:40 -0700938Status KeyValueStore::RelocateKeyAddressesInSector(
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700939 SectorDescriptor& sector_to_gc,
David Rogersfcea3252020-04-07 14:56:35 -0700940 const EntryMetadata& metadata,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700941 span<const Address> reserved_addresses) {
942 for (FlashPartition::Address& address : metadata.addresses()) {
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700943 if (sectors_.AddressInSector(sector_to_gc, address)) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700944 DBG(" Relocate entry for Key 0x%08" PRIx32 ", sector %u",
945 metadata.hash(),
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700946 sectors_.Index(sectors_.FromAddress(address)));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700947 TRY(RelocateEntry(metadata, address, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700948 }
949 }
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700950
David Rogersf3884eb2020-03-08 19:21:40 -0700951 return Status::OK;
952};
953
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700954Status KeyValueStore::GarbageCollectSector(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700955 SectorDescriptor& sector_to_gc, span<const Address> reserved_addresses) {
David Rogers9abe3c72020-03-24 19:03:13 -0700956 DBG(" Garbage Collect sector %u", sectors_.Index(sector_to_gc));
David Rogersf3884eb2020-03-08 19:21:40 -0700957 // Step 1: Move any valid entries in the GC sector to other sectors
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700958 if (sector_to_gc.valid_bytes() != 0) {
David Rogers98fea472020-04-01 15:43:48 -0700959 for (EntryMetadata& metadata : entry_cache_) {
Wyatt Heplerab3b2492020-03-11 16:15:16 -0700960 TRY(RelocateKeyAddressesInSector(
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700961 sector_to_gc, metadata, reserved_addresses));
David Rogersf3884eb2020-03-08 19:21:40 -0700962 }
963 }
964
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700965 if (sector_to_gc.valid_bytes() != 0) {
David Rogers67f4b6c2020-02-06 16:17:09 -0800966 ERR(" Failed to relocate valid entries from sector being garbage "
Wyatt Hepler2c7eca02020-02-18 16:01:42 -0800967 "collected, %zu valid bytes remain",
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700968 sector_to_gc.valid_bytes());
Wyatt Heplerb7609542020-01-24 10:29:54 -0800969 return Status::INTERNAL;
970 }
971
David Rogerscd87c322020-02-27 14:04:08 -0800972 // Step 2: Reinitialize the sector
David Rogers9abe3c72020-03-24 19:03:13 -0700973 sector_to_gc.mark_corrupt();
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700974 TRY(partition_.Erase(sectors_.BaseAddress(sector_to_gc), 1));
975 sector_to_gc.set_writable_bytes(partition_.sector_size_bytes());
Wyatt Heplerb7609542020-01-24 10:29:54 -0800976
Wyatt Heplerc84393f2020-03-20 11:23:24 -0700977 DBG(" Garbage Collect sector %u complete", sectors_.Index(sector_to_gc));
David Rogersa12786b2020-01-31 16:02:33 -0800978 return Status::OK;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800979}
980
David Rogers35c3f842020-04-22 15:34:05 -0700981StatusWithSize KeyValueStore::UpdateEntriesToPrimaryFormat() {
982 size_t entries_updated = 0;
David Rogers31b358b2020-04-15 05:00:50 -0700983 for (EntryMetadata& prior_metadata : entry_cache_) {
984 Entry entry;
David Rogers35c3f842020-04-22 15:34:05 -0700985 TRY_WITH_SIZE(ReadEntry(prior_metadata, entry));
David Rogers31b358b2020-04-15 05:00:50 -0700986 if (formats_.primary().magic == entry.magic()) {
987 // Ignore entries that are already on the primary format.
988 continue;
989 }
990
991 DBG("Updating entry 0x%08x from old format [0x%08x] to new format "
992 "[0x%08x]",
993 unsigned(prior_metadata.hash()),
994 unsigned(entry.magic()),
995 unsigned(formats_.primary().magic));
996
David Rogers35c3f842020-04-22 15:34:05 -0700997 entries_updated++;
998
David Rogers31b358b2020-04-15 05:00:50 -0700999 last_transaction_id_ += 1;
David Rogers35c3f842020-04-22 15:34:05 -07001000 TRY_WITH_SIZE(entry.Update(formats_.primary(), last_transaction_id_));
David Rogers31b358b2020-04-15 05:00:50 -07001001
1002 // List of addresses for sectors with space for this entry.
1003 Address* reserved_addresses = entry_cache_.TempReservedAddressesForWrite();
1004
1005 // Find addresses to write the entry to. This may involve garbage collecting
1006 // one or more sectors.
David Rogers35c3f842020-04-22 15:34:05 -07001007 TRY_WITH_SIZE(GetAddressesForWrite(reserved_addresses, entry.size()));
David Rogers31b358b2020-04-15 05:00:50 -07001008
David Rogers35c3f842020-04-22 15:34:05 -07001009 TRY_WITH_SIZE(
1010 CopyEntryToSector(entry,
David Rogers31b358b2020-04-15 05:00:50 -07001011 &sectors_.FromAddress(reserved_addresses[0]),
1012 reserved_addresses[0]));
1013
1014 // After writing the first entry successfully, update the key descriptors.
1015 // Once a single new the entry is written, the old entries are invalidated.
1016 EntryMetadata new_metadata = UpdateKeyDescriptor(
1017 entry, reserved_addresses[0], &prior_metadata, entry.size());
1018
1019 // Write the additional copies of the entry, if redundancy is greater
1020 // than 1.
1021 for (size_t i = 1; i < redundancy(); ++i) {
David Rogers35c3f842020-04-22 15:34:05 -07001022 TRY_WITH_SIZE(
1023 CopyEntryToSector(entry,
David Rogers31b358b2020-04-15 05:00:50 -07001024 &sectors_.FromAddress(reserved_addresses[i]),
1025 reserved_addresses[i]));
1026 new_metadata.AddNewAddress(reserved_addresses[i]);
1027 }
1028 }
David Rogers35c3f842020-04-22 15:34:05 -07001029
1030 return StatusWithSize(entries_updated);
David Rogers31b358b2020-04-15 05:00:50 -07001031}
1032
David Rogers9abe3c72020-03-24 19:03:13 -07001033// Add any missing redundant entries/copies for a key.
1034Status KeyValueStore::AddRedundantEntries(EntryMetadata& metadata) {
David Rogers9abe3c72020-03-24 19:03:13 -07001035 Entry entry;
David Rogers98fea472020-04-01 15:43:48 -07001036 TRY(ReadEntry(metadata, entry));
David Rogers9abe3c72020-03-24 19:03:13 -07001037 TRY(entry.VerifyChecksumInFlash());
1038
David Rogers0f8a1bb2020-04-21 18:50:19 -07001039 while (metadata.addresses().size() < redundancy()) {
1040 SectorDescriptor* new_sector;
1041 TRY(GetSectorForWrite(&new_sector, entry.size(), metadata.addresses()));
David Rogers9abe3c72020-03-24 19:03:13 -07001042
David Rogers31b358b2020-04-15 05:00:50 -07001043 Address new_address = sectors_.NextWritableAddress(*new_sector);
David Rogers98fea472020-04-01 15:43:48 -07001044 TRY(CopyEntryToSector(entry, new_sector, new_address));
David Rogers9abe3c72020-03-24 19:03:13 -07001045
1046 metadata.AddNewAddress(new_address);
1047 }
1048 return Status::OK;
1049}
1050
1051Status KeyValueStore::RepairCorruptSectors() {
1052 // Try to GC each corrupt sector, even if previous sectors fail. If GC of a
1053 // sector failed on the first pass, then do a second pass, since a later
1054 // sector might have cleared up space or otherwise unblocked the earlier
1055 // failed sector.
1056 Status repair_status = Status::OK;
1057
1058 size_t loop_count = 0;
1059 do {
1060 loop_count++;
1061 // Error of RESOURCE_EXHAUSTED indicates no space found for relocation.
1062 // Reset back to OK for the next pass.
1063 if (repair_status == Status::RESOURCE_EXHAUSTED) {
1064 repair_status = Status::OK;
1065 }
1066
1067 DBG(" Pass %u", unsigned(loop_count));
1068 for (SectorDescriptor& sector : sectors_) {
1069 if (sector.corrupt()) {
1070 DBG(" Found sector %u with corruption", sectors_.Index(sector));
1071 Status sector_status = GarbageCollectSector(sector, {});
1072 if (sector_status.ok()) {
1073 error_stats_.corrupt_sectors_recovered += 1;
1074 } else if (repair_status.ok() ||
1075 repair_status == Status::RESOURCE_EXHAUSTED) {
1076 repair_status = sector_status;
1077 }
1078 }
1079 }
1080 DBG(" Pass %u complete", unsigned(loop_count));
1081 } while (!repair_status.ok() && loop_count < 2);
1082
1083 return repair_status;
1084}
1085
1086Status KeyValueStore::EnsureFreeSectorExists() {
1087 Status repair_status = Status::OK;
1088 bool empty_sector_found = false;
1089
1090 DBG(" Find empty sector");
1091 for (SectorDescriptor& sector : sectors_) {
1092 if (sector.Empty(partition_.sector_size_bytes())) {
1093 empty_sector_found = true;
1094 DBG(" Empty sector found");
1095 break;
1096 }
1097 }
1098 if (empty_sector_found == false) {
1099 DBG(" No empty sector found, attempting to GC a free sector");
1100 Status sector_status = GarbageCollect(span<const Address, 0>());
1101 if (repair_status.ok() && !sector_status.ok()) {
1102 DBG(" Unable to free an empty sector");
1103 repair_status = sector_status;
1104 }
1105 }
1106
1107 return repair_status;
1108}
1109
1110Status KeyValueStore::EnsureEntryRedundancy() {
1111 Status repair_status = Status::OK;
1112
1113 if (redundancy() == 1) {
David Rogers98fea472020-04-01 15:43:48 -07001114 DBG(" Redundancy not in use, nothting to check");
David Rogers9abe3c72020-03-24 19:03:13 -07001115 return Status::OK;
1116 }
1117
David Rogers98fea472020-04-01 15:43:48 -07001118 DBG(" Write any needed additional duplicate copies of keys to fulfill %u"
1119 " redundancy",
David Rogers9abe3c72020-03-24 19:03:13 -07001120 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -07001121 for (EntryMetadata& metadata : entry_cache_) {
David Rogers9abe3c72020-03-24 19:03:13 -07001122 if (metadata.addresses().size() >= redundancy()) {
1123 continue;
1124 }
1125
1126 DBG(" Key with %u of %u copies found, adding missing copies",
1127 unsigned(metadata.addresses().size()),
1128 unsigned(redundancy()));
David Rogers98fea472020-04-01 15:43:48 -07001129 Status fill_status = AddRedundantEntries(metadata);
David Rogers9abe3c72020-03-24 19:03:13 -07001130 if (fill_status.ok()) {
1131 error_stats_.missing_redundant_entries_recovered += 1;
1132 DBG(" Key missing copies added");
1133 } else {
1134 DBG(" Failed to add key missing copies");
1135 if (repair_status.ok()) {
1136 repair_status = fill_status;
1137 }
1138 }
1139 }
1140
1141 return repair_status;
1142}
1143
David Rogersfcea3252020-04-07 14:56:35 -07001144Status KeyValueStore::FixErrors() {
1145 DBG("Fixing KVS errors");
David Rogers9abe3c72020-03-24 19:03:13 -07001146
1147 // Step 1: Garbage collect any sectors marked as corrupt.
David Rogersfcea3252020-04-07 14:56:35 -07001148 Status overall_status = RepairCorruptSectors();
David Rogers9abe3c72020-03-24 19:03:13 -07001149
1150 // Step 2: Make sure there is at least 1 empty sector. This needs to be a
1151 // seperate check of sectors from step 1, because a found empty sector might
1152 // get written to by a later GC that fails and does not result in a free
1153 // sector.
David Rogersfcea3252020-04-07 14:56:35 -07001154 Status repair_status = EnsureFreeSectorExists();
David Rogers9abe3c72020-03-24 19:03:13 -07001155 if (overall_status.ok()) {
1156 overall_status = repair_status;
1157 }
1158
1159 // Step 3: Make sure each stored key has the full number of redundant
1160 // entries.
1161 repair_status = EnsureEntryRedundancy();
1162 if (overall_status.ok()) {
1163 overall_status = repair_status;
1164 }
1165
1166 if (overall_status.ok()) {
1167 error_detected_ = false;
1168 initialized_ = InitializationState::kReady;
1169 }
1170 return overall_status;
1171}
1172
David Rogersfcea3252020-04-07 14:56:35 -07001173Status KeyValueStore::Repair() {
1174 // If errors have been detected, just reinit the KVS metadata. This does a
1175 // full deep error check and any needed repairs. Then repair any errors.
1176 INF("Starting KVS repair");
1177
1178 DBG("Reinitialize KVS metadata");
1179 InitializeMetadata();
1180
1181 return FixErrors();
1182}
1183
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -08001184KeyValueStore::Entry KeyValueStore::CreateEntry(Address address,
Wyatt Heplerab3b2492020-03-11 16:15:16 -07001185 string_view key,
Wyatt Heplerbdd8e5a2020-02-20 19:27:26 -08001186 span<const byte> value,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001187 EntryState state) {
Keir Mierle9e38b402020-02-21 13:06:21 -08001188 // Always bump the transaction ID when creating a new entry.
1189 //
1190 // Burning transaction IDs prevents inconsistencies between flash and memory
1191 // that which could happen if a write succeeds, but for some reason the read
1192 // and verify step fails. Here's how this would happen:
1193 //
1194 // 1. The entry is written but for some reason the flash reports failure OR
1195 // The write succeeds, but the read / verify operation fails.
1196 // 2. The transaction ID is NOT incremented, because of the failure
1197 // 3. (later) A new entry is written, re-using the transaction ID (oops)
1198 //
1199 // By always burning transaction IDs, the above problem can't happen.
1200 last_transaction_id_ += 1;
1201
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001202 if (state == EntryState::kDeleted) {
Wyatt Hepler7465be32020-02-21 15:30:53 -08001203 return Entry::Tombstone(
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -08001204 partition_, address, formats_.primary(), key, last_transaction_id_);
Wyatt Hepler1fc11042020-02-19 17:17:51 -08001205 }
1206 return Entry::Valid(partition_,
1207 address,
Wyatt Hepler22d0d9f2020-03-05 14:57:11 -08001208 formats_.primary(),
Wyatt Hepler1fc11042020-02-19 17:17:51 -08001209 key,
1210 value,
Keir Mierle9e38b402020-02-21 13:06:21 -08001211 last_transaction_id_);
Wyatt Heplerd2298282020-02-20 17:12:45 -08001212}
1213
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001214void KeyValueStore::LogDebugInfo() const {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001215 const size_t sector_size_bytes = partition_.sector_size_bytes();
1216 DBG("====================== KEY VALUE STORE DUMP =========================");
1217 DBG(" ");
1218 DBG("Flash partition:");
Wyatt Heplerad0a7932020-02-06 08:20:38 -08001219 DBG(" Sector count = %zu", partition_.sector_count());
Wyatt Hepler38ce30f2020-02-19 11:48:31 -08001220 DBG(" Sector max count = %zu", sectors_.max_size());
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001221 DBG(" Sectors in use = %zu", sectors_.size());
Keir Mierle8c352dc2020-02-02 13:58:19 -08001222 DBG(" Sector size = %zu", sector_size_bytes);
1223 DBG(" Total size = %zu", partition_.size_bytes());
1224 DBG(" Alignment = %zu", partition_.alignment_bytes());
1225 DBG(" ");
1226 DBG("Key descriptors:");
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001227 DBG(" Entry count = %zu", entry_cache_.total_entries());
1228 DBG(" Max entry count = %zu", entry_cache_.max_entries());
Keir Mierle8c352dc2020-02-02 13:58:19 -08001229 DBG(" ");
1230 DBG(" # hash version address address (hex)");
Armando Montanez888370d2020-05-01 18:29:22 -07001231 size_t count = 0;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001232 for (const EntryMetadata& metadata : entry_cache_) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001233 DBG(" |%3zu: | %8zx |%8zu | %8zu | %8zx",
Armando Montanez888370d2020-05-01 18:29:22 -07001234 count++,
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001235 size_t(metadata.hash()),
1236 size_t(metadata.transaction_id()),
1237 size_t(metadata.first_address()),
1238 size_t(metadata.first_address()));
Keir Mierle8c352dc2020-02-02 13:58:19 -08001239 }
1240 DBG(" ");
1241
1242 DBG("Sector descriptors:");
1243 DBG(" # tail free valid has_space");
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001244 for (const SectorDescriptor& sd : sectors_) {
1245 DBG(" |%3u: | %8zu |%8zu | %s",
1246 sectors_.Index(sd),
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001247 size_t(sd.writable_bytes()),
1248 sd.valid_bytes(),
1249 sd.writable_bytes() ? "YES" : "");
Keir Mierle8c352dc2020-02-02 13:58:19 -08001250 }
1251 DBG(" ");
1252
1253 // TODO: This should stop logging after some threshold.
1254 // size_t dumped_bytes = 0;
1255 DBG("Sector raw data:");
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001256 for (size_t sector_id = 0; sector_id < sectors_.size(); ++sector_id) {
Keir Mierle8c352dc2020-02-02 13:58:19 -08001257 // Read sector data. Yes, this will blow the stack on embedded.
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001258 std::array<byte, 500> raw_sector_data; // TODO!!!
Keir Mierle8c352dc2020-02-02 13:58:19 -08001259 StatusWithSize sws =
1260 partition_.Read(sector_id * sector_size_bytes, raw_sector_data);
1261 DBG("Read: %zu bytes", sws.size());
1262
1263 DBG(" base addr offs 0 1 2 3 4 5 6 7");
1264 for (size_t i = 0; i < sector_size_bytes; i += 8) {
1265 DBG(" %3zu %8zx %5zu | %02x %02x %02x %02x %02x %02x %02x %02x",
1266 sector_id,
1267 (sector_id * sector_size_bytes) + i,
1268 i,
1269 static_cast<unsigned int>(raw_sector_data[i + 0]),
1270 static_cast<unsigned int>(raw_sector_data[i + 1]),
1271 static_cast<unsigned int>(raw_sector_data[i + 2]),
1272 static_cast<unsigned int>(raw_sector_data[i + 3]),
1273 static_cast<unsigned int>(raw_sector_data[i + 4]),
1274 static_cast<unsigned int>(raw_sector_data[i + 5]),
1275 static_cast<unsigned int>(raw_sector_data[i + 6]),
1276 static_cast<unsigned int>(raw_sector_data[i + 7]));
1277
1278 // TODO: Fix exit condition.
1279 if (i > 128) {
1280 break;
1281 }
1282 }
1283 DBG(" ");
1284 }
1285
1286 DBG("////////////////////// KEY VALUE STORE DUMP END /////////////////////");
1287}
1288
David Rogerscf680ab2020-02-12 23:28:32 -08001289void KeyValueStore::LogSectors() const {
1290 DBG("Sector descriptors: count %zu", sectors_.size());
Wyatt Hepler1c329ca2020-02-07 18:07:23 -08001291 for (auto& sector : sectors_) {
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001292 DBG(" - Sector %u: valid %zu, recoverable %zu, free %zu",
Wyatt Heplerc84393f2020-03-20 11:23:24 -07001293 sectors_.Index(sector),
Wyatt Hepler2c7eca02020-02-18 16:01:42 -08001294 sector.valid_bytes(),
1295 sector.RecoverableBytes(partition_.sector_size_bytes()),
1296 sector.writable_bytes());
David Rogers50185ad2020-02-07 00:02:46 -08001297 }
1298}
1299
David Rogerscf680ab2020-02-12 23:28:32 -08001300void KeyValueStore::LogKeyDescriptor() const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001301 DBG("Key descriptors: count %zu", entry_cache_.total_entries());
David Rogers9abe3c72020-03-24 19:03:13 -07001302 for (const EntryMetadata& metadata : entry_cache_) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001303 DBG(" - Key: %s, hash %#zx, transaction ID %zu, first address %#zx",
Wyatt Hepler02946272020-03-18 10:36:22 -07001304 metadata.state() == EntryState::kDeleted ? "Deleted" : "Valid",
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001305 static_cast<size_t>(metadata.hash()),
1306 static_cast<size_t>(metadata.transaction_id()),
1307 static_cast<size_t>(metadata.first_address()));
David Rogerscf680ab2020-02-12 23:28:32 -08001308 }
1309}
1310
Wyatt Hepler2ad60672020-01-21 08:00:16 -08001311} // namespace pw::kvs