blob: c4c154ca86f56d651beecdfd1e1f8d831919ecf8 [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 Heplerb7609542020-01-24 10:29:54 -080018#include <cstring>
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080019#include <type_traits>
Wyatt Heplerb7609542020-01-24 10:29:54 -080020
Keir Mierle8c352dc2020-02-02 13:58:19 -080021#define PW_LOG_USE_ULTRA_SHORT_NAMES 1
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080022#include "pw_kvs_private/format.h"
23#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 Heplerb7609542020-01-24 10:29:54 -080027
Wyatt Hepleracaacf92020-01-24 10:58:30 -080028using std::byte;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080029using std::string_view;
Wyatt Hepleracaacf92020-01-24 10:58:30 -080030
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080031Status KeyValueStore::Init() {
Keir Mierle8c352dc2020-02-02 13:58:19 -080032 // Reset the number of occupied key descriptors; we will fill them later.
33 key_descriptor_list_size_ = 0;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080034
David Rogers8ce55cd2020-02-04 19:41:48 -080035 // TODO: init last_new_sector_ to a random sector. Since the on-flash stored
36 // information does not allow recovering the previous last_new_sector_ after
37 // clean start, random is a good second choice.
38
Keir Mierle8c352dc2020-02-02 13:58:19 -080039 const size_t sector_size_bytes = partition_.sector_size_bytes();
40 const size_t sector_count = partition_.sector_count();
41
David Rogersf0a35442020-02-04 12:16:38 -080042 if (working_buffer_.size() < sector_size_bytes) {
43 CRT("ERROR: working_buffer_ (%zu bytes) is smaller than sector "
44 "size (%zu bytes)",
45 working_buffer_.size(),
46 sector_size_bytes);
47 return Status::INVALID_ARGUMENT;
48 }
49
Keir Mierle8c352dc2020-02-02 13:58:19 -080050 DBG("First pass: Read all entries from all sectors");
51 for (size_t sector_id = 0; sector_id < sector_count; ++sector_id) {
52 // Track writable bytes in this sector. Updated after reading each entry.
53 sector_map_[sector_id].tail_free_bytes = sector_size_bytes;
54
55 const Address sector_address = sector_id * sector_size_bytes;
56 Address entry_address = sector_address;
57
58 for (int num_entries_in_sector = 0;; num_entries_in_sector++) {
59 DBG("Load entry: sector=%zu, entry#=%d, address=%zu",
60 sector_id,
61 num_entries_in_sector,
62 size_t(entry_address));
63
64 if (!AddressInSector(sector_map_[sector_id], entry_address)) {
65 DBG("Fell off end of sector; moving to the next sector");
66 break;
67 }
68
69 Address next_entry_address;
70 Status status = LoadEntry(entry_address, &next_entry_address);
71 if (status == Status::NOT_FOUND) {
72 DBG("Hit un-written data in sector; moving to the next sector");
73 break;
74 }
75 if (status == Status::DATA_LOSS) {
76 // It's not clear KVS can make a unilateral decision about what to do
77 // in corruption cases. It's an application decision, for which we
78 // should offer some configurability. For now, entirely bail out of
79 // loading and give up.
80 //
81 // Later, scan for remaining valid keys; since it's entirely possible
82 // that there is a duplicate of the key elsewhere and everything is
83 // fine. Later, we can wipe and maybe recover the sector.
84 //
85 // TODO: Implement rest-of-sector scanning for valid entries.
86 return Status::DATA_LOSS;
87 }
88 TRY(status);
89
90 // Entry loaded successfully; so get ready to load the next one.
91 entry_address = next_entry_address;
92
93 // Update of the number of writable bytes in this sector.
94 sector_map_[sector_id].tail_free_bytes =
95 sector_size_bytes - (entry_address - sector_address);
96 }
97 }
98
99 DBG("Second pass: Count valid bytes in each sector");
100 // Initialize the sector sizes.
101 for (size_t sector_id = 0; sector_id < sector_count; ++sector_id) {
102 sector_map_[sector_id].valid_bytes = 0;
103 }
104 // For every valid key, increment the valid bytes for that sector.
105 for (size_t key_id = 0; key_id < key_descriptor_list_size_; ++key_id) {
106 uint32_t sector_id =
107 key_descriptor_list_[key_id].address / sector_size_bytes;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800108 EntryHeader header;
Wyatt Hepler4d78cd62020-02-05 13:05:58 -0800109 TRY(ReadEntryHeader(key_descriptor_list_[key_id].address, &header));
Wyatt Hepler93b889d2020-02-05 09:01:18 -0800110 sector_map_[sector_id].valid_bytes += header.size();
Keir Mierle8c352dc2020-02-02 13:58:19 -0800111 }
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800112 initialized_ = true;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800113 return Status::OK;
114}
115
116Status KeyValueStore::LoadEntry(Address entry_address,
117 Address* next_entry_address) {
118 const size_t alignment_bytes = partition_.alignment_bytes();
119
Keir Mierle8c352dc2020-02-02 13:58:19 -0800120 EntryHeader header;
Wyatt Hepler4d78cd62020-02-05 13:05:58 -0800121 TRY(ReadEntryHeader(entry_address, &header));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800122 // TODO: Should likely add a "LogHeader" method or similar.
123 DBG("Header: ");
124 DBG(" Address = 0x%zx", size_t(entry_address));
125 DBG(" Magic = 0x%zx", size_t(header.magic()));
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800126 DBG(" Checksum = 0x%zx", size_t(header.checksum()));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800127 DBG(" Key length = 0x%zx", size_t(header.key_length()));
128 DBG(" Value length = 0x%zx", size_t(header.value_length()));
Wyatt Hepler93b889d2020-02-05 09:01:18 -0800129 DBG(" Entry size = 0x%zx", size_t(header.size()));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800130 DBG(" Padded size = 0x%zx",
Wyatt Hepler93b889d2020-02-05 09:01:18 -0800131 size_t(AlignUp(header.size(), alignment_bytes)));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800132
133 if (HeaderLooksLikeUnwrittenData(header)) {
134 return Status::NOT_FOUND;
135 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800136
137 // TODO: Handle multiple magics for formats that have changed.
138 if (header.magic() != entry_header_format_.magic) {
139 // TODO: It may be cleaner to have some logging helpers for these cases.
140 CRT("Found corrupt magic: %zx; expecting %zx; at address %zx",
141 size_t(header.magic()),
142 size_t(entry_header_format_.magic),
143 size_t(entry_address));
144 return Status::DATA_LOSS;
145 }
146
147 // Read the key from flash & validate the entry (which reads the value).
148 KeyBuffer key_buffer;
Wyatt Hepler4d78cd62020-02-05 13:05:58 -0800149 TRY(ReadEntryKey(entry_address, header.key_length(), key_buffer.data()));
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800150 const string_view key(key_buffer.data(), header.key_length());
151
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800152 TRY(header.VerifyChecksumInFlash(
Wyatt Hepler4d78cd62020-02-05 13:05:58 -0800153 &partition_, entry_address, entry_header_format_.checksum));
154
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800155 KeyDescriptor key_descriptor(
156 key,
157 header.key_version(),
158 entry_address,
159 header.deleted() ? KeyDescriptor::kDeleted : KeyDescriptor::kValid);
Keir Mierle8c352dc2020-02-02 13:58:19 -0800160
161 DBG("Key hash: %zx (%zu)",
162 size_t(key_descriptor.key_hash),
163 size_t(key_descriptor.key_hash));
164
165 TRY(AppendNewOrOverwriteStaleExistingDescriptor(key_descriptor));
166
167 // TODO: Extract this to something like "NextValidEntryAddress".
168 *next_entry_address =
Wyatt Hepler93b889d2020-02-05 09:01:18 -0800169 AlignUp(key_descriptor.address + header.size(), alignment_bytes);
Keir Mierle8c352dc2020-02-02 13:58:19 -0800170
171 return Status::OK;
172}
173
174// TODO: This method is the trigger of the O(valid_entries * all_entries) time
175// complexity for reading. At some cost to memory, this could be optimized by
176// using a hash table instead of scanning, but in practice this should be fine
177// for a small number of keys
178Status KeyValueStore::AppendNewOrOverwriteStaleExistingDescriptor(
179 const KeyDescriptor& key_descriptor) {
180 // With the new key descriptor, either add it to the descriptor table or
181 // overwrite an existing entry with an older version of the key.
182 KeyDescriptor* existing_descriptor = FindDescriptor(key_descriptor.key_hash);
183 if (existing_descriptor) {
184 if (existing_descriptor->key_version < key_descriptor.key_version) {
185 // Existing entry is old; replace the existing entry with the new one.
186 *existing_descriptor = key_descriptor;
187 } else {
188 // Otherwise, check for data integrity and leave the existing entry.
189 if (existing_descriptor->key_version == key_descriptor.key_version) {
190 ERR("Data loss: Duplicated old(=%zu) and new(=%zu) version",
191 size_t(existing_descriptor->key_version),
192 size_t(key_descriptor.key_version));
193 return Status::DATA_LOSS;
194 }
195 DBG("Found stale entry when appending; ignoring");
196 }
197 return Status::OK;
198 }
199 // Write new entry.
200 KeyDescriptor* newly_allocated_key_descriptor;
201 TRY(AppendEmptyDescriptor(&newly_allocated_key_descriptor));
202 *newly_allocated_key_descriptor = key_descriptor;
203 return Status::OK;
204}
205
206// TODO: Need a better name.
207Status KeyValueStore::AppendEmptyDescriptor(KeyDescriptor** new_descriptor) {
208 if (KeyListFull()) {
209 // TODO: Is this the right return code?
210 return Status::RESOURCE_EXHAUSTED;
211 }
212 *new_descriptor = &key_descriptor_list_[key_descriptor_list_size_++];
213 return Status::OK;
214}
215
216// TODO: Finish.
217bool KeyValueStore::HeaderLooksLikeUnwrittenData(
218 const EntryHeader& header) const {
219 // TODO: This is not correct; it should call through to flash memory.
220 return header.magic() == 0xffffffff;
221}
222
223KeyValueStore::KeyDescriptor* KeyValueStore::FindDescriptor(uint32_t hash) {
224 for (size_t key_id = 0; key_id < key_descriptor_list_size_; key_id++) {
225 if (key_descriptor_list_[key_id].key_hash == hash) {
226 return &(key_descriptor_list_[key_id]);
227 }
228 }
229 return nullptr;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800230}
231
232StatusWithSize KeyValueStore::Get(string_view key,
233 span<byte> value_buffer) const {
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800234 TRY(CheckOperation(key));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800235
David Rogers2761aeb2020-01-31 17:09:00 -0800236 const KeyDescriptor* key_descriptor;
237 TRY(FindKeyDescriptor(key, &key_descriptor));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800238
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800239 if (key_descriptor->deleted()) {
240 return Status::NOT_FOUND;
241 }
242
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800243 EntryHeader header;
Wyatt Hepler4d78cd62020-02-05 13:05:58 -0800244 TRY(ReadEntryHeader(key_descriptor->address, &header));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800245
Keir Mierle8c352dc2020-02-02 13:58:19 -0800246 StatusWithSize result = ReadEntryValue(*key_descriptor, header, value_buffer);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800247 if (result.ok() && options_.verify_on_read) {
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800248 return header.VerifyChecksum(entry_header_format_.checksum,
249 key,
250 value_buffer.subspan(0, result.size()));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800251 }
252 return result;
253}
254
255Status KeyValueStore::Put(string_view key, span<const byte> value) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800256 DBG("Writing key/value; key length=%zu, value length=%zu",
257 key.size(),
258 value.size());
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800259
260 TRY(CheckOperation(key));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800261
262 if (value.size() > (1 << 24)) {
263 // TODO: Reject sizes that are larger than the maximum?
264 }
265
David Rogers2761aeb2020-01-31 17:09:00 -0800266 KeyDescriptor* key_descriptor;
267 if (FindKeyDescriptor(key, &key_descriptor).ok()) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800268 DBG("Writing over existing entry");
David Rogers2761aeb2020-01-31 17:09:00 -0800269 return WriteEntryForExistingKey(key_descriptor, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800270 }
David Rogers2761aeb2020-01-31 17:09:00 -0800271
Keir Mierle8c352dc2020-02-02 13:58:19 -0800272 DBG("Writing new entry");
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800273 return WriteEntryForNewKey(key, value);
274}
275
276Status KeyValueStore::Delete(string_view key) {
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800277 TRY(CheckOperation(key));
278
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800279 KeyDescriptor* key_descriptor;
280 TRY(FindKeyDescriptor(key, &key_descriptor));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800281
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800282 if (key_descriptor->deleted()) {
283 return Status::NOT_FOUND;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800284 }
285
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800286 key_descriptor->state = KeyDescriptor::kDeleted;
287
288 SectorDescriptor* sector;
289 TRY(FindOrRecoverSectorWithSpace(&sector, EntryHeader::size(key, {})));
290
291 DBG("Writing tombstone; found sector: %zu", SectorIndex(sector));
292 return AppendEntry(sector, key_descriptor, key, {});
293}
294
295KeyValueStore::iterator& KeyValueStore::iterator::operator++() {
296 // Skip to the next entry that is valid (not deleted).
297 while (++index_ < item_.kvs_.key_descriptor_list_size_ &&
298 descriptor().deleted()) {
299 }
300 return *this;
301}
302
303const KeyValueStore::Item& KeyValueStore::iterator::operator*() {
304 std::memset(item_.key_buffer_.data(), 0, item_.key_buffer_.size());
305
306 EntryHeader header;
307 if (item_.kvs_.ReadEntryHeader(descriptor().address, &header).ok()) {
308 item_.kvs_.ReadEntryKey(
309 descriptor().address, header.key_length(), item_.key_buffer_.data());
310 }
311
312 return item_;
313}
314
315KeyValueStore::iterator KeyValueStore::begin() const {
316 size_t i = 0;
317 // Skip over any deleted entries at the start of the descriptor list.
318 while (i < key_descriptor_list_size_ && key_descriptor_list_[i].deleted()) {
319 i += 1;
320 }
321 return iterator(*this, i);
322}
323
324// TODO(hepler): The valid entry count could be tracked in the KVS to avoid the
325// need for this for-loop.
326size_t KeyValueStore::size() const {
327 size_t valid_entries = 0;
328
329 for (size_t i = 0; i < key_descriptor_list_size_; ++i) {
330 if (!key_descriptor_list_[i].deleted()) {
331 valid_entries += 1;
332 }
333 }
334
335 return valid_entries;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800336}
337
Wyatt Heplered163b02020-02-03 17:49:32 -0800338StatusWithSize KeyValueStore::ValueSize(std::string_view key) const {
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800339 TRY(CheckOperation(key));
Wyatt Heplered163b02020-02-03 17:49:32 -0800340
341 const KeyDescriptor* key_descriptor;
342 TRY(FindKeyDescriptor(key, &key_descriptor));
343
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800344 if (key_descriptor->deleted()) {
345 return Status::NOT_FOUND;
346 }
347
Wyatt Heplered163b02020-02-03 17:49:32 -0800348 EntryHeader header;
Wyatt Hepler4d78cd62020-02-05 13:05:58 -0800349 TRY(ReadEntryHeader(key_descriptor->address, &header));
Wyatt Heplered163b02020-02-03 17:49:32 -0800350
351 return StatusWithSize(header.value_length());
352}
353
Wyatt Hepler4d78cd62020-02-05 13:05:58 -0800354uint32_t KeyValueStore::HashKey(string_view string) {
355 uint32_t hash = 0;
356 uint32_t coefficient = 65599u;
357
358 for (char ch : string) {
359 hash += coefficient * unsigned(ch);
360 coefficient *= 65599u;
361 }
362
363 return hash;
364}
365
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800366Status KeyValueStore::FixedSizeGet(std::string_view key,
367 byte* value,
368 size_t size_bytes) const {
369 // Ensure that the size of the stored value matches the size of the type.
370 // Otherwise, report error. This check avoids potential memory corruption.
371 StatusWithSize result = ValueSize(key);
372 if (!result.ok()) {
373 return result.status();
Keir Mierle8c352dc2020-02-02 13:58:19 -0800374 }
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800375 if (result.size() != size_bytes) {
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800376 DBG("Requested %zu B read, but value is %zu B", size_bytes, result.size());
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800377 return Status::INVALID_ARGUMENT;
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800378 }
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800379 return Get(key, span(value, size_bytes)).status();
Keir Mierle8c352dc2020-02-02 13:58:19 -0800380}
381
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800382Status KeyValueStore::CheckOperation(string_view key) const {
Wyatt Hepleracaacf92020-01-24 10:58:30 -0800383 if (InvalidKey(key)) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800384 return Status::INVALID_ARGUMENT;
385 }
Wyatt Hepler729f28c2020-02-05 09:46:00 -0800386 if (!initialized_) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800387 return Status::FAILED_PRECONDITION;
388 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800389 return Status::OK;
390}
391
David Rogers2761aeb2020-01-31 17:09:00 -0800392Status KeyValueStore::FindKeyDescriptor(string_view key,
393 const KeyDescriptor** result) const {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800394 char key_buffer[kMaxKeyLength];
395 const uint32_t hash = HashKey(key);
Wyatt Heplerb7609542020-01-24 10:29:54 -0800396
David Rogers2761aeb2020-01-31 17:09:00 -0800397 for (auto& descriptor : key_descriptors()) {
398 if (descriptor.key_hash == hash) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800399 DBG("Found match! For hash: %zx", size_t(hash));
Wyatt Hepler4d78cd62020-02-05 13:05:58 -0800400 TRY(ReadEntryKey(descriptor.address, key.size(), key_buffer));
Wyatt Heplerb7609542020-01-24 10:29:54 -0800401
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800402 if (key == string_view(key_buffer, key.size())) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800403 DBG("Keys matched too");
David Rogers2761aeb2020-01-31 17:09:00 -0800404 *result = &descriptor;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800405 return Status::OK;
406 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800407 }
408 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800409 return Status::NOT_FOUND;
410}
411
Wyatt Hepler4d78cd62020-02-05 13:05:58 -0800412Status KeyValueStore::ReadEntryHeader(Address address,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800413 EntryHeader* header) const {
Wyatt Hepler4d78cd62020-02-05 13:05:58 -0800414 return partition_.Read(address, sizeof(*header), header).status();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800415}
416
Wyatt Hepler4d78cd62020-02-05 13:05:58 -0800417Status KeyValueStore::ReadEntryKey(Address address,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800418 size_t key_length,
419 char* key) const {
420 // TODO: This check probably shouldn't be here; this is like
421 // checking that the Cortex M's RAM isn't corrupt. This should be
422 // done at boot time.
423 // ^^ This argument sometimes comes from EntryHeader::key_value_len,
424 // which is read directly from flash. If it's corrupted, we shouldn't try
425 // to read a bunch of extra data.
426 if (key_length == 0u || key_length > kMaxKeyLength) {
427 return Status::DATA_LOSS;
428 }
429 // The key is immediately after the entry header.
Wyatt Hepler4d78cd62020-02-05 13:05:58 -0800430 return partition_.Read(address + sizeof(EntryHeader), key_length, key)
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800431 .status();
432}
433
David Rogers2761aeb2020-01-31 17:09:00 -0800434StatusWithSize KeyValueStore::ReadEntryValue(
435 const KeyDescriptor& key_descriptor,
436 const EntryHeader& header,
437 span<byte> value) const {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800438 const size_t read_size = std::min(header.value_length(), value.size());
David Rogers2761aeb2020-01-31 17:09:00 -0800439 StatusWithSize result = partition_.Read(
440 key_descriptor.address + sizeof(header) + header.key_length(),
Keir Mierle8c352dc2020-02-02 13:58:19 -0800441 value.subspan(0, read_size));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800442 TRY(result);
443 if (read_size != header.value_length()) {
444 return StatusWithSize(Status::RESOURCE_EXHAUSTED, read_size);
445 }
446 return StatusWithSize(read_size);
447}
448
David Rogers2761aeb2020-01-31 17:09:00 -0800449Status KeyValueStore::WriteEntryForExistingKey(KeyDescriptor* key_descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800450 string_view key,
451 span<const byte> value) {
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800452 key_descriptor->state = KeyDescriptor::kValid;
453
David Rogers2761aeb2020-01-31 17:09:00 -0800454 SectorDescriptor* sector;
Wyatt Hepler93b889d2020-02-05 09:01:18 -0800455 TRY(FindOrRecoverSectorWithSpace(&sector, EntryHeader::size(key, value)));
David Rogers8ce55cd2020-02-04 19:41:48 -0800456 DBG("Writing existing entry; found sector: %zu", SectorIndex(sector));
David Rogers2761aeb2020-01-31 17:09:00 -0800457 return AppendEntry(sector, key_descriptor, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800458}
459
460Status KeyValueStore::WriteEntryForNewKey(string_view key,
461 span<const byte> value) {
David Rogers2761aeb2020-01-31 17:09:00 -0800462 if (KeyListFull()) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800463 WRN("KVS full: trying to store a new entry, but can't. Have %zu entries",
464 key_descriptor_list_size_);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800465 return Status::RESOURCE_EXHAUSTED;
466 }
467
David Rogers2761aeb2020-01-31 17:09:00 -0800468 // Modify the key descriptor at the end of the array, without bumping the map
469 // size so the key descriptor is prepared and written without committing
470 // first.
471 KeyDescriptor& key_descriptor =
472 key_descriptor_list_[key_descriptor_list_size_];
473 key_descriptor.key_hash = HashKey(key);
474 key_descriptor.key_version = 0; // will be incremented by AppendEntry()
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800475 key_descriptor.state = KeyDescriptor::kValid;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800476
David Rogers2761aeb2020-01-31 17:09:00 -0800477 SectorDescriptor* sector;
Wyatt Hepler93b889d2020-02-05 09:01:18 -0800478 TRY(FindOrRecoverSectorWithSpace(&sector, EntryHeader::size(key, value)));
David Rogers8ce55cd2020-02-04 19:41:48 -0800479 DBG("Writing new entry; found sector: %zu", SectorIndex(sector));
David Rogers2761aeb2020-01-31 17:09:00 -0800480 TRY(AppendEntry(sector, &key_descriptor, key, value));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800481
Keir Mierle8c352dc2020-02-02 13:58:19 -0800482 // Only increment bump our size when we are certain the write succeeded.
David Rogers2761aeb2020-01-31 17:09:00 -0800483 key_descriptor_list_size_ += 1;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800484 return Status::OK;
485}
486
David Rogers2761aeb2020-01-31 17:09:00 -0800487Status KeyValueStore::RelocateEntry(KeyDescriptor& key_descriptor) {
David Rogersf0a35442020-02-04 12:16:38 -0800488 struct TempEntry {
489 std::array<char, kMaxKeyLength + 1> key;
490 std::array<char, sizeof(working_buffer_) - sizeof(key)> value;
491 };
492 TempEntry* entry = reinterpret_cast<TempEntry*>(working_buffer_.data());
493
494 // Read the entry to be relocated. Store the header in a local variable and
495 // store the key and value in the TempEntry stored in the static allocated
496 // working_buffer_.
497 EntryHeader header;
Wyatt Hepler4d78cd62020-02-05 13:05:58 -0800498 TRY(ReadEntryHeader(key_descriptor.address, &header));
499 TRY(ReadEntryKey(
500 key_descriptor.address, header.key_length(), entry->key.data()));
David Rogersf0a35442020-02-04 12:16:38 -0800501 string_view key = string_view(entry->key.data(), header.key_length());
502 StatusWithSize result = ReadEntryValue(
503 key_descriptor, header, as_writable_bytes(span(entry->value)));
504 if (!result.status().ok()) {
505 return Status::INTERNAL;
506 }
507
508 auto value = span(entry->value.data(), result.size());
509
510 TRY(header.VerifyChecksum(
511 entry_header_format_.checksum, key, as_bytes(value)));
512
513 SectorDescriptor* old_sector = SectorFromAddress(key_descriptor.address);
514 if (old_sector == nullptr) {
515 return Status::INTERNAL;
516 }
517
518 // Find a new sector for the entry and write it to the new location.
David Rogers8ce55cd2020-02-04 19:41:48 -0800519 SectorDescriptor* new_sector;
520 TRY(FindSectorWithSpace(&new_sector, header.size(), old_sector, true));
David Rogersf0a35442020-02-04 12:16:38 -0800521 return AppendEntry(new_sector, &key_descriptor, key, as_bytes(value));
David Rogersa12786b2020-01-31 16:02:33 -0800522}
523
David Rogers8db5a722020-02-03 18:28:34 -0800524// Find either an existing sector with enough space that is not the sector to
525// skip, or an empty sector. Maintains the invariant that there is always at
526// least 1 empty sector unless set to bypass the rule.
David Rogers8ce55cd2020-02-04 19:41:48 -0800527Status KeyValueStore::FindSectorWithSpace(SectorDescriptor** found_sector,
528 size_t size,
529 SectorDescriptor* sector_to_skip,
530 bool bypass_empty_sector_rule) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800531 const size_t sector_count = partition_.sector_count();
532
David Rogers8ce55cd2020-02-04 19:41:48 -0800533 // The last_new_sector_ is the sector that was last selected as the "new empty
534 // sector" to write to. This last new sector is used as the starting point for
535 // the next "find a new empty sector to write to" operation. By using the last
536 // new sector as the start point we will cycle which empty sector is selected
537 // next, spreading the wear across all the empty sectors and get a wear
538 // leveling benefit, rather than putting more wear on the lower number
539 // sectors.
540 //
541 // Locally use the sector index for ease of iterating through the sectors. For
542 // the persistent storage use SectorDescriptor* rather than sector index
543 // because SectorDescriptor* is the standard way to identify a sector.
544 size_t last_new_sector_index_ = SectorIndex(last_new_sector_);
545 size_t start = (last_new_sector_index_ + 1) % sector_count;
David Rogers2761aeb2020-01-31 17:09:00 -0800546 SectorDescriptor* first_empty_sector = nullptr;
David Rogers8db5a722020-02-03 18:28:34 -0800547 bool at_least_two_empty_sectors = bypass_empty_sector_rule;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800548
David Rogers8ce55cd2020-02-04 19:41:48 -0800549 // Look for a partial sector to use with enough space. Immediately use the
550 // first one of those that is found. While scanning for a partial sector, keep
551 // track of the first empty sector and if a second sector was seen.
552 for (size_t i = start; i != last_new_sector_index_;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800553 i = (i + 1) % sector_count) {
554 DBG("Examining sector %zu", i);
David Rogers2761aeb2020-01-31 17:09:00 -0800555 SectorDescriptor& sector = sector_map_[i];
Keir Mierle8c352dc2020-02-02 13:58:19 -0800556
David Rogers8db5a722020-02-03 18:28:34 -0800557 if (sector_to_skip == &sector) {
558 DBG("Skipping the skip sector");
559 continue;
560 }
561
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800562 if (!SectorEmpty(sector) && sector.HasSpace(size)) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800563 DBG("Partially occupied sector with enough space; done!");
David Rogers8ce55cd2020-02-04 19:41:48 -0800564 *found_sector = &sector;
565 return Status::OK;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800566 }
567
568 if (SectorEmpty(sector)) {
569 if (first_empty_sector == nullptr) {
570 first_empty_sector = &sector;
571 } else {
572 at_least_two_empty_sectors = true;
Wyatt Hepler2ad60672020-01-21 08:00:16 -0800573 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800574 }
575 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800576
David Rogers8ce55cd2020-02-04 19:41:48 -0800577 // If the scan for a partial sector does not find a suitable sector, use the
578 // first empty sector that was found. Normally it is required to keep 1 empty
579 // sector after the sector found here, but that rule can be bypassed in
580 // special circumstances (such as during garbage collection).
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800581 if (at_least_two_empty_sectors) {
David Rogers8ce55cd2020-02-04 19:41:48 -0800582 DBG("Found a usable empty sector; returning the first found (%zu)",
583 SectorIndex(first_empty_sector));
584 last_new_sector_ = first_empty_sector;
585 *found_sector = first_empty_sector;
586 return Status::OK;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800587 }
David Rogers8ce55cd2020-02-04 19:41:48 -0800588
589 // No sector was found.
590 *found_sector = nullptr;
591 return Status::RESOURCE_EXHAUSTED;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800592}
593
David Rogers2761aeb2020-01-31 17:09:00 -0800594Status KeyValueStore::FindOrRecoverSectorWithSpace(SectorDescriptor** sector,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800595 size_t size) {
David Rogers8ce55cd2020-02-04 19:41:48 -0800596 Status result = FindSectorWithSpace(sector, size);
597 if (result.ok()) {
598 return result;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800599 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800600 if (options_.partial_gc_on_write) {
601 return GarbageCollectOneSector(sector);
602 }
David Rogers8ce55cd2020-02-04 19:41:48 -0800603 return result;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800604}
605
David Rogers2761aeb2020-01-31 17:09:00 -0800606KeyValueStore::SectorDescriptor* KeyValueStore::FindSectorToGarbageCollect() {
607 SectorDescriptor* sector_candidate = nullptr;
David Rogersa12786b2020-01-31 16:02:33 -0800608 size_t candidate_bytes = 0;
609
610 // Step 1: Try to find a sectors with stale keys and no valid keys (no
611 // relocation needed). If any such sectors are found, use the sector with the
612 // most reclaimable bytes.
613 for (auto& sector : sector_map_) {
614 if ((sector.valid_bytes == 0) &&
615 (RecoverableBytes(sector) > candidate_bytes)) {
616 sector_candidate = &sector;
617 candidate_bytes = RecoverableBytes(sector);
618 }
619 }
620
621 // Step 2: If step 1 yields no sectors, just find the sector with the most
622 // reclaimable bytes.
623 if (sector_candidate == nullptr) {
624 for (auto& sector : sector_map_) {
625 if (RecoverableBytes(sector) > candidate_bytes) {
626 sector_candidate = &sector;
627 candidate_bytes = RecoverableBytes(sector);
628 }
629 }
630 }
631
632 return sector_candidate;
633}
634
David Rogers2761aeb2020-01-31 17:09:00 -0800635Status KeyValueStore::GarbageCollectOneSector(SectorDescriptor** sector) {
David Rogersa12786b2020-01-31 16:02:33 -0800636 // Step 1: Find the sector to garbage collect
David Rogers2761aeb2020-01-31 17:09:00 -0800637 SectorDescriptor* sector_to_gc = FindSectorToGarbageCollect();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800638
David Rogersa12786b2020-01-31 16:02:33 -0800639 if (sector_to_gc == nullptr) {
640 return Status::RESOURCE_EXHAUSTED;
641 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800642
David Rogersa12786b2020-01-31 16:02:33 -0800643 // Step 2: Move any valid entries in the GC sector to other sectors
644 if (sector_to_gc->valid_bytes != 0) {
David Rogers2761aeb2020-01-31 17:09:00 -0800645 for (auto& descriptor : key_descriptors()) {
646 if (AddressInSector(*sector_to_gc, descriptor.address)) {
647 TRY(RelocateEntry(descriptor));
David Rogersa12786b2020-01-31 16:02:33 -0800648 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800649 }
650 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800651
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800652 if (sector_to_gc->valid_bytes != 0) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800653 return Status::INTERNAL;
654 }
655
David Rogersa12786b2020-01-31 16:02:33 -0800656 // Step 3: Reinitialize the sector
657 sector_to_gc->tail_free_bytes = 0;
658 TRY(partition_.Erase(SectorBaseAddress(sector_to_gc), 1));
659 sector_to_gc->tail_free_bytes = partition_.sector_size_bytes();
Wyatt Heplerb7609542020-01-24 10:29:54 -0800660
David Rogersa12786b2020-01-31 16:02:33 -0800661 *sector = sector_to_gc;
662 return Status::OK;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800663}
664
David Rogers2761aeb2020-01-31 17:09:00 -0800665Status KeyValueStore::AppendEntry(SectorDescriptor* sector,
666 KeyDescriptor* key_descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800667 const string_view key,
668 span<const byte> value) {
669 // write header, key, and value
Wyatt Hepler6c24c062020-02-05 15:30:49 -0800670 EntryHeader header;
671
672 if (key_descriptor->deleted()) {
673 header = EntryHeader::Tombstone(entry_header_format_.magic,
674 entry_header_format_.checksum,
675 key,
676 key_descriptor->key_version + 1);
677 } else {
678 header = EntryHeader::Valid(entry_header_format_.magic,
679 entry_header_format_.checksum,
680 key,
681 value,
682 key_descriptor->key_version + 1);
683 }
684
Keir Mierle8c352dc2020-02-02 13:58:19 -0800685 DBG("Appending entry with key version: %zx", size_t(header.key_version()));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800686
687 // Handles writing multiple concatenated buffers, while breaking up the writes
688 // into alignment-sized blocks.
689 Address address = NextWritableAddress(sector);
Keir Mierle8c352dc2020-02-02 13:58:19 -0800690 DBG("Appending to address: %zx", size_t(address));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800691 TRY_ASSIGN(
692 size_t written,
693 partition_.Write(
694 address, {as_bytes(span(&header, 1)), as_bytes(span(key)), value}));
695
696 if (options_.verify_on_write) {
Wyatt Hepler0a223582020-02-04 17:47:40 -0800697 TRY(header.VerifyChecksumInFlash(
698 &partition_, address, entry_header_format_.checksum));
Wyatt Heplerb7609542020-01-24 10:29:54 -0800699 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800700
David Rogers2761aeb2020-01-31 17:09:00 -0800701 key_descriptor->address = address;
702 key_descriptor->key_version = header.key_version();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800703 sector->valid_bytes += written;
704 sector->tail_free_bytes -= written;
705 return Status::OK;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800706}
707
Keir Mierle8c352dc2020-02-02 13:58:19 -0800708void KeyValueStore::LogDebugInfo() {
709 const size_t sector_count = partition_.sector_count();
710 const size_t sector_size_bytes = partition_.sector_size_bytes();
711 DBG("====================== KEY VALUE STORE DUMP =========================");
712 DBG(" ");
713 DBG("Flash partition:");
714 DBG(" Sector count = %zu", sector_count);
715 DBG(" Sector max count = %zu", kUsableSectors);
716 DBG(" Sector size = %zu", sector_size_bytes);
717 DBG(" Total size = %zu", partition_.size_bytes());
718 DBG(" Alignment = %zu", partition_.alignment_bytes());
719 DBG(" ");
720 DBG("Key descriptors:");
721 DBG(" Entry count = %zu", key_descriptor_list_size_);
722 DBG(" Max entry count = %zu", kMaxEntries);
723 DBG(" ");
724 DBG(" # hash version address address (hex)");
725 for (size_t i = 0; i < key_descriptor_list_size_; ++i) {
726 const KeyDescriptor& kd = key_descriptor_list_[i];
727 DBG(" |%3zu: | %8zx |%8zu | %8zu | %8zx",
728 i,
729 size_t(kd.key_hash),
730 size_t(kd.key_version),
731 size_t(kd.address),
732 size_t(kd.address));
733 }
734 DBG(" ");
735
736 DBG("Sector descriptors:");
737 DBG(" # tail free valid has_space");
738 for (size_t sector_id = 0; sector_id < sector_count; ++sector_id) {
739 const SectorDescriptor& sd = sector_map_[sector_id];
740 DBG(" |%3zu: | %8zu |%8zu | %s",
741 sector_id,
742 size_t(sd.tail_free_bytes),
743 size_t(sd.valid_bytes),
744 sd.tail_free_bytes ? "YES" : "");
745 }
746 DBG(" ");
747
748 // TODO: This should stop logging after some threshold.
749 // size_t dumped_bytes = 0;
750 DBG("Sector raw data:");
751 for (size_t sector_id = 0; sector_id < sector_count; ++sector_id) {
752 // Read sector data. Yes, this will blow the stack on embedded.
753 std::array<byte, 500> raw_sector_data; // TODO
754 StatusWithSize sws =
755 partition_.Read(sector_id * sector_size_bytes, raw_sector_data);
756 DBG("Read: %zu bytes", sws.size());
757
758 DBG(" base addr offs 0 1 2 3 4 5 6 7");
759 for (size_t i = 0; i < sector_size_bytes; i += 8) {
760 DBG(" %3zu %8zx %5zu | %02x %02x %02x %02x %02x %02x %02x %02x",
761 sector_id,
762 (sector_id * sector_size_bytes) + i,
763 i,
764 static_cast<unsigned int>(raw_sector_data[i + 0]),
765 static_cast<unsigned int>(raw_sector_data[i + 1]),
766 static_cast<unsigned int>(raw_sector_data[i + 2]),
767 static_cast<unsigned int>(raw_sector_data[i + 3]),
768 static_cast<unsigned int>(raw_sector_data[i + 4]),
769 static_cast<unsigned int>(raw_sector_data[i + 5]),
770 static_cast<unsigned int>(raw_sector_data[i + 6]),
771 static_cast<unsigned int>(raw_sector_data[i + 7]));
772
773 // TODO: Fix exit condition.
774 if (i > 128) {
775 break;
776 }
777 }
778 DBG(" ");
779 }
780
781 DBG("////////////////////// KEY VALUE STORE DUMP END /////////////////////");
782}
783
Wyatt Hepler2ad60672020-01-21 08:00:16 -0800784} // namespace pw::kvs