blob: a778851516a9e7350a8d98d682922b038caac107 [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 -080031namespace {
32
Wyatt Heplerbab0e202020-02-04 07:40:08 -080033using Address = FlashPartition::Address;
34
35constexpr union {
36 byte bytes[4];
37 uint32_t value;
38} kNoChecksum{};
39
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080040// constexpr uint32_t SixFiveFiveNineNine(std::string_view string) {
41constexpr uint32_t HashKey(std::string_view string) {
42 uint32_t hash = 0;
43 uint32_t coefficient = 65599u;
44
45 for (char ch : string) {
46 hash += coefficient * unsigned(ch);
47 coefficient *= 65599u;
Wyatt Heplerb7609542020-01-24 10:29:54 -080048 }
49
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080050 return hash;
Wyatt Heplerb7609542020-01-24 10:29:54 -080051}
52
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080053constexpr size_t EntrySize(string_view key, span<const byte> value) {
54 return sizeof(EntryHeader) + key.size() + value.size();
55}
56
57} // namespace
58
59Status KeyValueStore::Init() {
Keir Mierle8c352dc2020-02-02 13:58:19 -080060 // Reset the number of occupied key descriptors; we will fill them later.
61 key_descriptor_list_size_ = 0;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080062
Keir Mierle8c352dc2020-02-02 13:58:19 -080063 const size_t sector_size_bytes = partition_.sector_size_bytes();
64 const size_t sector_count = partition_.sector_count();
65
66 DBG("First pass: Read all entries from all sectors");
67 for (size_t sector_id = 0; sector_id < sector_count; ++sector_id) {
68 // Track writable bytes in this sector. Updated after reading each entry.
69 sector_map_[sector_id].tail_free_bytes = sector_size_bytes;
70
71 const Address sector_address = sector_id * sector_size_bytes;
72 Address entry_address = sector_address;
73
74 for (int num_entries_in_sector = 0;; num_entries_in_sector++) {
75 DBG("Load entry: sector=%zu, entry#=%d, address=%zu",
76 sector_id,
77 num_entries_in_sector,
78 size_t(entry_address));
79
80 if (!AddressInSector(sector_map_[sector_id], entry_address)) {
81 DBG("Fell off end of sector; moving to the next sector");
82 break;
83 }
84
85 Address next_entry_address;
86 Status status = LoadEntry(entry_address, &next_entry_address);
87 if (status == Status::NOT_FOUND) {
88 DBG("Hit un-written data in sector; moving to the next sector");
89 break;
90 }
91 if (status == Status::DATA_LOSS) {
92 // It's not clear KVS can make a unilateral decision about what to do
93 // in corruption cases. It's an application decision, for which we
94 // should offer some configurability. For now, entirely bail out of
95 // loading and give up.
96 //
97 // Later, scan for remaining valid keys; since it's entirely possible
98 // that there is a duplicate of the key elsewhere and everything is
99 // fine. Later, we can wipe and maybe recover the sector.
100 //
101 // TODO: Implement rest-of-sector scanning for valid entries.
102 return Status::DATA_LOSS;
103 }
104 TRY(status);
105
106 // Entry loaded successfully; so get ready to load the next one.
107 entry_address = next_entry_address;
108
109 // Update of the number of writable bytes in this sector.
110 sector_map_[sector_id].tail_free_bytes =
111 sector_size_bytes - (entry_address - sector_address);
112 }
113 }
114
115 DBG("Second pass: Count valid bytes in each sector");
116 // Initialize the sector sizes.
117 for (size_t sector_id = 0; sector_id < sector_count; ++sector_id) {
118 sector_map_[sector_id].valid_bytes = 0;
119 }
120 // For every valid key, increment the valid bytes for that sector.
121 for (size_t key_id = 0; key_id < key_descriptor_list_size_; ++key_id) {
122 uint32_t sector_id =
123 key_descriptor_list_[key_id].address / sector_size_bytes;
124 KeyDescriptor key_descriptor;
125 key_descriptor.address = key_descriptor_list_[key_id].address;
126 EntryHeader header;
127 TRY(ReadEntryHeader(key_descriptor, &header));
128 sector_map_[sector_id].valid_bytes += header.entry_size();
129 }
130 enabled_ = true;
131 return Status::OK;
132}
133
134Status KeyValueStore::LoadEntry(Address entry_address,
135 Address* next_entry_address) {
136 const size_t alignment_bytes = partition_.alignment_bytes();
137
138 KeyDescriptor key_descriptor;
139 key_descriptor.address = entry_address;
140
141 EntryHeader header;
142 TRY(ReadEntryHeader(key_descriptor, &header));
143 // TODO: Should likely add a "LogHeader" method or similar.
144 DBG("Header: ");
145 DBG(" Address = 0x%zx", size_t(entry_address));
146 DBG(" Magic = 0x%zx", size_t(header.magic()));
147 DBG(" Checksum = 0x%zx", size_t(header.checksum_as_uint32()));
148 DBG(" Key length = 0x%zx", size_t(header.key_length()));
149 DBG(" Value length = 0x%zx", size_t(header.value_length()));
150 DBG(" Entry size = 0x%zx", size_t(header.entry_size()));
151 DBG(" Padded size = 0x%zx",
152 size_t(AlignUp(header.entry_size(), alignment_bytes)));
153
154 if (HeaderLooksLikeUnwrittenData(header)) {
155 return Status::NOT_FOUND;
156 }
157 key_descriptor.key_version = header.key_version();
158
159 // TODO: Handle multiple magics for formats that have changed.
160 if (header.magic() != entry_header_format_.magic) {
161 // TODO: It may be cleaner to have some logging helpers for these cases.
162 CRT("Found corrupt magic: %zx; expecting %zx; at address %zx",
163 size_t(header.magic()),
164 size_t(entry_header_format_.magic),
165 size_t(entry_address));
166 return Status::DATA_LOSS;
167 }
168
169 // Read the key from flash & validate the entry (which reads the value).
170 KeyBuffer key_buffer;
171 TRY(ReadEntryKey(key_descriptor, header.key_length(), key_buffer.data()));
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800172 const string_view key(key_buffer.data(), header.key_length());
173
174 TRY(ValidateEntryChecksumInFlash(header, key, key_descriptor));
175 key_descriptor.key_hash = HashKey(key);
Keir Mierle8c352dc2020-02-02 13:58:19 -0800176
177 DBG("Key hash: %zx (%zu)",
178 size_t(key_descriptor.key_hash),
179 size_t(key_descriptor.key_hash));
180
181 TRY(AppendNewOrOverwriteStaleExistingDescriptor(key_descriptor));
182
183 // TODO: Extract this to something like "NextValidEntryAddress".
184 *next_entry_address =
185 AlignUp(key_descriptor.address + header.entry_size(), alignment_bytes);
186
187 return Status::OK;
188}
189
190// TODO: This method is the trigger of the O(valid_entries * all_entries) time
191// complexity for reading. At some cost to memory, this could be optimized by
192// using a hash table instead of scanning, but in practice this should be fine
193// for a small number of keys
194Status KeyValueStore::AppendNewOrOverwriteStaleExistingDescriptor(
195 const KeyDescriptor& key_descriptor) {
196 // With the new key descriptor, either add it to the descriptor table or
197 // overwrite an existing entry with an older version of the key.
198 KeyDescriptor* existing_descriptor = FindDescriptor(key_descriptor.key_hash);
199 if (existing_descriptor) {
200 if (existing_descriptor->key_version < key_descriptor.key_version) {
201 // Existing entry is old; replace the existing entry with the new one.
202 *existing_descriptor = key_descriptor;
203 } else {
204 // Otherwise, check for data integrity and leave the existing entry.
205 if (existing_descriptor->key_version == key_descriptor.key_version) {
206 ERR("Data loss: Duplicated old(=%zu) and new(=%zu) version",
207 size_t(existing_descriptor->key_version),
208 size_t(key_descriptor.key_version));
209 return Status::DATA_LOSS;
210 }
211 DBG("Found stale entry when appending; ignoring");
212 }
213 return Status::OK;
214 }
215 // Write new entry.
216 KeyDescriptor* newly_allocated_key_descriptor;
217 TRY(AppendEmptyDescriptor(&newly_allocated_key_descriptor));
218 *newly_allocated_key_descriptor = key_descriptor;
219 return Status::OK;
220}
221
222// TODO: Need a better name.
223Status KeyValueStore::AppendEmptyDescriptor(KeyDescriptor** new_descriptor) {
224 if (KeyListFull()) {
225 // TODO: Is this the right return code?
226 return Status::RESOURCE_EXHAUSTED;
227 }
228 *new_descriptor = &key_descriptor_list_[key_descriptor_list_size_++];
229 return Status::OK;
230}
231
232// TODO: Finish.
233bool KeyValueStore::HeaderLooksLikeUnwrittenData(
234 const EntryHeader& header) const {
235 // TODO: This is not correct; it should call through to flash memory.
236 return header.magic() == 0xffffffff;
237}
238
239KeyValueStore::KeyDescriptor* KeyValueStore::FindDescriptor(uint32_t hash) {
240 for (size_t key_id = 0; key_id < key_descriptor_list_size_; key_id++) {
241 if (key_descriptor_list_[key_id].key_hash == hash) {
242 return &(key_descriptor_list_[key_id]);
243 }
244 }
245 return nullptr;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800246}
247
248StatusWithSize KeyValueStore::Get(string_view key,
249 span<byte> value_buffer) const {
250 TRY(InvalidOperation(key));
251
David Rogers2761aeb2020-01-31 17:09:00 -0800252 const KeyDescriptor* key_descriptor;
253 TRY(FindKeyDescriptor(key, &key_descriptor));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800254
255 EntryHeader header;
David Rogers2761aeb2020-01-31 17:09:00 -0800256 TRY(ReadEntryHeader(*key_descriptor, &header));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800257
Keir Mierle8c352dc2020-02-02 13:58:19 -0800258 StatusWithSize result = ReadEntryValue(*key_descriptor, header, value_buffer);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800259 if (result.ok() && options_.verify_on_read) {
260 return ValidateEntryChecksum(header, key, value_buffer);
261 }
262 return result;
263}
264
265Status KeyValueStore::Put(string_view key, span<const byte> value) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800266 DBG("Writing key/value; key length=%zu, value length=%zu",
267 key.size(),
268 value.size());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800269 TRY(InvalidOperation(key));
270
271 if (value.size() > (1 << 24)) {
272 // TODO: Reject sizes that are larger than the maximum?
273 }
274
David Rogers2761aeb2020-01-31 17:09:00 -0800275 KeyDescriptor* key_descriptor;
276 if (FindKeyDescriptor(key, &key_descriptor).ok()) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800277 DBG("Writing over existing entry");
David Rogers2761aeb2020-01-31 17:09:00 -0800278 return WriteEntryForExistingKey(key_descriptor, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800279 }
David Rogers2761aeb2020-01-31 17:09:00 -0800280
Keir Mierle8c352dc2020-02-02 13:58:19 -0800281 DBG("Writing new entry");
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800282 return WriteEntryForNewKey(key, value);
283}
284
285Status KeyValueStore::Delete(string_view key) {
286 TRY(InvalidOperation(key));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800287 return Status::UNIMPLEMENTED;
288}
289
290const KeyValueStore::Entry& KeyValueStore::Iterator::operator*() {
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800291 const KeyDescriptor& descriptor = entry_.kvs_.key_descriptor_list_[index_];
292
293 std::memset(entry_.key_buffer_.data(), 0, sizeof(entry_.key_buffer_));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800294
295 EntryHeader header;
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800296 if (entry_.kvs_.ReadEntryHeader(descriptor, &header).ok()) {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800297 entry_.kvs_.ReadEntryKey(
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800298 descriptor, header.key_length(), entry_.key_buffer_.data());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800299 }
300
301 return entry_;
302}
303
Wyatt Heplered163b02020-02-03 17:49:32 -0800304StatusWithSize KeyValueStore::ValueSize(std::string_view key) const {
305 TRY(InvalidOperation(key));
306
307 const KeyDescriptor* key_descriptor;
308 TRY(FindKeyDescriptor(key, &key_descriptor));
309
310 EntryHeader header;
311 TRY(ReadEntryHeader(*key_descriptor, &header));
312
313 return StatusWithSize(header.value_length());
314}
315
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800316Status KeyValueStore::ValidateEntryChecksumInFlash(
317 const EntryHeader& header,
318 const string_view key,
319 const KeyDescriptor& entry) const {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800320 if (entry_header_format_.checksum == nullptr) {
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800321 return header.checksum_as_uint32() == 0 ? Status::OK : Status::DATA_LOSS;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800322 }
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800323
324 auto& checksum = *entry_header_format_.checksum;
325 checksum.Reset();
326 checksum.Update(header.DataForChecksum());
327 checksum.Update(as_bytes(span(key)));
328
329 // Read the value piece-by-piece into a small buffer.
330 // TODO: This read may be unaligned. The partition can handle this, but
331 // consider creating a API that skips the intermediate buffering.
332 byte buffer[32];
333
334 size_t bytes_to_read = header.value_length();
335 Address address = entry.address + sizeof(header) + header.key_length();
336
337 while (bytes_to_read > 0u) {
338 const size_t read_size = std::min(sizeof(buffer), bytes_to_read);
339 TRY(partition_.Read(address, read_size, buffer));
340 address += read_size;
341 checksum.Update(buffer, read_size);
342 }
343
Keir Mierle8c352dc2020-02-02 13:58:19 -0800344 return Status::OK;
345}
346
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800347Status KeyValueStore::InvalidOperation(string_view key) const {
Wyatt Hepleracaacf92020-01-24 10:58:30 -0800348 if (InvalidKey(key)) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800349 return Status::INVALID_ARGUMENT;
350 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800351 if (!enabled_) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800352 return Status::FAILED_PRECONDITION;
353 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800354 return Status::OK;
355}
356
David Rogers2761aeb2020-01-31 17:09:00 -0800357Status KeyValueStore::FindKeyDescriptor(string_view key,
358 const KeyDescriptor** result) const {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800359 char key_buffer[kMaxKeyLength];
360 const uint32_t hash = HashKey(key);
Wyatt Heplerb7609542020-01-24 10:29:54 -0800361
David Rogers2761aeb2020-01-31 17:09:00 -0800362 for (auto& descriptor : key_descriptors()) {
363 if (descriptor.key_hash == hash) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800364 DBG("Found match! For hash: %zx", size_t(hash));
David Rogers2761aeb2020-01-31 17:09:00 -0800365 TRY(ReadEntryKey(descriptor, key.size(), key_buffer));
Wyatt Heplerb7609542020-01-24 10:29:54 -0800366
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800367 if (key == string_view(key_buffer, key.size())) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800368 DBG("Keys matched too");
David Rogers2761aeb2020-01-31 17:09:00 -0800369 *result = &descriptor;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800370 return Status::OK;
371 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800372 }
373 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800374 return Status::NOT_FOUND;
375}
376
David Rogers2761aeb2020-01-31 17:09:00 -0800377Status KeyValueStore::ReadEntryHeader(const KeyDescriptor& descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800378 EntryHeader* header) const {
David Rogers2761aeb2020-01-31 17:09:00 -0800379 return partition_.Read(descriptor.address, sizeof(*header), header).status();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800380}
381
David Rogers2761aeb2020-01-31 17:09:00 -0800382Status KeyValueStore::ReadEntryKey(const KeyDescriptor& descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800383 size_t key_length,
384 char* key) const {
385 // TODO: This check probably shouldn't be here; this is like
386 // checking that the Cortex M's RAM isn't corrupt. This should be
387 // done at boot time.
388 // ^^ This argument sometimes comes from EntryHeader::key_value_len,
389 // which is read directly from flash. If it's corrupted, we shouldn't try
390 // to read a bunch of extra data.
391 if (key_length == 0u || key_length > kMaxKeyLength) {
392 return Status::DATA_LOSS;
393 }
394 // The key is immediately after the entry header.
David Rogers2761aeb2020-01-31 17:09:00 -0800395 return partition_
396 .Read(descriptor.address + sizeof(EntryHeader), key_length, key)
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800397 .status();
398}
399
David Rogers2761aeb2020-01-31 17:09:00 -0800400StatusWithSize KeyValueStore::ReadEntryValue(
401 const KeyDescriptor& key_descriptor,
402 const EntryHeader& header,
403 span<byte> value) const {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800404 const size_t read_size = std::min(header.value_length(), value.size());
David Rogers2761aeb2020-01-31 17:09:00 -0800405 StatusWithSize result = partition_.Read(
406 key_descriptor.address + sizeof(header) + header.key_length(),
Keir Mierle8c352dc2020-02-02 13:58:19 -0800407 value.subspan(0, read_size));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800408 TRY(result);
409 if (read_size != header.value_length()) {
410 return StatusWithSize(Status::RESOURCE_EXHAUSTED, read_size);
411 }
412 return StatusWithSize(read_size);
413}
414
415Status KeyValueStore::ValidateEntryChecksum(const EntryHeader& header,
416 string_view key,
417 span<const byte> value) const {
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800418 if (entry_header_format_.checksum == nullptr) {
419 return header.checksum_as_uint32() == kNoChecksum.value ? Status::OK
420 : Status::DATA_LOSS;
421 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800422
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800423 CalculateEntryChecksum(header, key, value);
424 return entry_header_format_.checksum->Verify(header.checksum());
425}
426
David Rogers2761aeb2020-01-31 17:09:00 -0800427Status KeyValueStore::WriteEntryForExistingKey(KeyDescriptor* key_descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800428 string_view key,
429 span<const byte> value) {
David Rogers2761aeb2020-01-31 17:09:00 -0800430 SectorDescriptor* sector;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800431 TRY(FindOrRecoverSectorWithSpace(&sector, EntrySize(key, value)));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800432 DBG("Writing existing entry; found sector: %d",
433 static_cast<int>(sector - sector_map_.data()));
David Rogers2761aeb2020-01-31 17:09:00 -0800434 return AppendEntry(sector, key_descriptor, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800435}
436
437Status KeyValueStore::WriteEntryForNewKey(string_view key,
438 span<const byte> value) {
David Rogers2761aeb2020-01-31 17:09:00 -0800439 if (KeyListFull()) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800440 WRN("KVS full: trying to store a new entry, but can't. Have %zu entries",
441 key_descriptor_list_size_);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800442 return Status::RESOURCE_EXHAUSTED;
443 }
444
David Rogers2761aeb2020-01-31 17:09:00 -0800445 // Modify the key descriptor at the end of the array, without bumping the map
446 // size so the key descriptor is prepared and written without committing
447 // first.
448 KeyDescriptor& key_descriptor =
449 key_descriptor_list_[key_descriptor_list_size_];
450 key_descriptor.key_hash = HashKey(key);
451 key_descriptor.key_version = 0; // will be incremented by AppendEntry()
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800452
David Rogers2761aeb2020-01-31 17:09:00 -0800453 SectorDescriptor* sector;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800454 TRY(FindOrRecoverSectorWithSpace(&sector, EntrySize(key, value)));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800455 DBG("Writing new entry; found sector: %d",
456 static_cast<int>(sector - sector_map_.data()));
David Rogers2761aeb2020-01-31 17:09:00 -0800457 TRY(AppendEntry(sector, &key_descriptor, key, value));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800458
Keir Mierle8c352dc2020-02-02 13:58:19 -0800459 // Only increment bump our size when we are certain the write succeeded.
David Rogers2761aeb2020-01-31 17:09:00 -0800460 key_descriptor_list_size_ += 1;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800461 return Status::OK;
462}
463
David Rogers2761aeb2020-01-31 17:09:00 -0800464Status KeyValueStore::RelocateEntry(KeyDescriptor& key_descriptor) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800465 // TODO: Implement this function!
David Rogers2761aeb2020-01-31 17:09:00 -0800466 (void)key_descriptor;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800467 return Status::OK;
David Rogersa12786b2020-01-31 16:02:33 -0800468 return Status::UNIMPLEMENTED;
469}
470
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800471// Find either an existing sector with enough space, or an empty sector.
472// Maintains the invariant that there is always at least 1 empty sector.
David Rogers2761aeb2020-01-31 17:09:00 -0800473KeyValueStore::SectorDescriptor* KeyValueStore::FindSectorWithSpace(
474 size_t size) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800475 const size_t sector_count = partition_.sector_count();
476
477 // TODO: Ignore last written sector for now and scan from the beginning.
478 last_written_sector_ = sector_count - 1;
479
480 size_t start = (last_written_sector_ + 1) % sector_count;
David Rogers2761aeb2020-01-31 17:09:00 -0800481 SectorDescriptor* first_empty_sector = nullptr;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800482 bool at_least_two_empty_sectors = false;
483
484 for (size_t i = start; i != last_written_sector_;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800485 i = (i + 1) % sector_count) {
486 DBG("Examining sector %zu", i);
David Rogers2761aeb2020-01-31 17:09:00 -0800487 SectorDescriptor& sector = sector_map_[i];
Keir Mierle8c352dc2020-02-02 13:58:19 -0800488
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800489 if (!SectorEmpty(sector) && sector.HasSpace(size)) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800490 DBG("Partially occupied sector with enough space; done!");
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800491 return &sector;
492 }
493
494 if (SectorEmpty(sector)) {
495 if (first_empty_sector == nullptr) {
496 first_empty_sector = &sector;
497 } else {
498 at_least_two_empty_sectors = true;
Wyatt Hepler2ad60672020-01-21 08:00:16 -0800499 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800500 }
501 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800502
503 if (at_least_two_empty_sectors) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800504 DBG("Found a empty sector and a spare one; returning the first found (%d)",
505 static_cast<int>(first_empty_sector - sector_map_.data()));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800506 return first_empty_sector;
507 }
508 return nullptr;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800509}
510
David Rogers2761aeb2020-01-31 17:09:00 -0800511Status KeyValueStore::FindOrRecoverSectorWithSpace(SectorDescriptor** sector,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800512 size_t size) {
513 *sector = FindSectorWithSpace(size);
514 if (*sector != nullptr) {
515 return Status::OK;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800516 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800517 if (options_.partial_gc_on_write) {
518 return GarbageCollectOneSector(sector);
519 }
520 return Status::RESOURCE_EXHAUSTED;
521}
522
David Rogers2761aeb2020-01-31 17:09:00 -0800523KeyValueStore::SectorDescriptor* KeyValueStore::FindSectorToGarbageCollect() {
524 SectorDescriptor* sector_candidate = nullptr;
David Rogersa12786b2020-01-31 16:02:33 -0800525 size_t candidate_bytes = 0;
526
527 // Step 1: Try to find a sectors with stale keys and no valid keys (no
528 // relocation needed). If any such sectors are found, use the sector with the
529 // most reclaimable bytes.
530 for (auto& sector : sector_map_) {
531 if ((sector.valid_bytes == 0) &&
532 (RecoverableBytes(sector) > candidate_bytes)) {
533 sector_candidate = &sector;
534 candidate_bytes = RecoverableBytes(sector);
535 }
536 }
537
538 // Step 2: If step 1 yields no sectors, just find the sector with the most
539 // reclaimable bytes.
540 if (sector_candidate == nullptr) {
541 for (auto& sector : sector_map_) {
542 if (RecoverableBytes(sector) > candidate_bytes) {
543 sector_candidate = &sector;
544 candidate_bytes = RecoverableBytes(sector);
545 }
546 }
547 }
548
549 return sector_candidate;
550}
551
David Rogers2761aeb2020-01-31 17:09:00 -0800552Status KeyValueStore::GarbageCollectOneSector(SectorDescriptor** sector) {
David Rogersa12786b2020-01-31 16:02:33 -0800553 // Step 1: Find the sector to garbage collect
David Rogers2761aeb2020-01-31 17:09:00 -0800554 SectorDescriptor* sector_to_gc = FindSectorToGarbageCollect();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800555
David Rogersa12786b2020-01-31 16:02:33 -0800556 if (sector_to_gc == nullptr) {
557 return Status::RESOURCE_EXHAUSTED;
558 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800559
David Rogersa12786b2020-01-31 16:02:33 -0800560 // Step 2: Move any valid entries in the GC sector to other sectors
561 if (sector_to_gc->valid_bytes != 0) {
David Rogers2761aeb2020-01-31 17:09:00 -0800562 for (auto& descriptor : key_descriptors()) {
563 if (AddressInSector(*sector_to_gc, descriptor.address)) {
564 TRY(RelocateEntry(descriptor));
David Rogersa12786b2020-01-31 16:02:33 -0800565 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800566 }
567 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800568
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800569 if (sector_to_gc->valid_bytes != 0) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800570 return Status::INTERNAL;
571 }
572
David Rogersa12786b2020-01-31 16:02:33 -0800573 // Step 3: Reinitialize the sector
574 sector_to_gc->tail_free_bytes = 0;
575 TRY(partition_.Erase(SectorBaseAddress(sector_to_gc), 1));
576 sector_to_gc->tail_free_bytes = partition_.sector_size_bytes();
Wyatt Heplerb7609542020-01-24 10:29:54 -0800577
David Rogersa12786b2020-01-31 16:02:33 -0800578 *sector = sector_to_gc;
579 return Status::OK;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800580}
581
David Rogers2761aeb2020-01-31 17:09:00 -0800582Status KeyValueStore::AppendEntry(SectorDescriptor* sector,
583 KeyDescriptor* key_descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800584 const string_view key,
585 span<const byte> value) {
586 // write header, key, and value
587 const EntryHeader header(entry_header_format_.magic,
588 CalculateEntryChecksum(header, key, value),
589 key.size(),
590 value.size(),
David Rogers2761aeb2020-01-31 17:09:00 -0800591 key_descriptor->key_version + 1);
Keir Mierle8c352dc2020-02-02 13:58:19 -0800592 DBG("Appending entry with key version: %zx", size_t(header.key_version()));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800593
594 // Handles writing multiple concatenated buffers, while breaking up the writes
595 // into alignment-sized blocks.
596 Address address = NextWritableAddress(sector);
Keir Mierle8c352dc2020-02-02 13:58:19 -0800597 DBG("Appending to address: %zx", size_t(address));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800598 TRY_ASSIGN(
599 size_t written,
600 partition_.Write(
601 address, {as_bytes(span(&header, 1)), as_bytes(span(key)), value}));
602
603 if (options_.verify_on_write) {
David Rogers2761aeb2020-01-31 17:09:00 -0800604 TRY(VerifyEntry(sector, key_descriptor));
Wyatt Heplerb7609542020-01-24 10:29:54 -0800605 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800606
607 // TODO: UPDATE last_written_sector_ appropriately
608
David Rogers2761aeb2020-01-31 17:09:00 -0800609 key_descriptor->address = address;
610 key_descriptor->key_version = header.key_version();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800611 sector->valid_bytes += written;
612 sector->tail_free_bytes -= written;
613 return Status::OK;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800614}
615
David Rogers2761aeb2020-01-31 17:09:00 -0800616Status KeyValueStore::VerifyEntry(SectorDescriptor* sector,
617 KeyDescriptor* key_descriptor) {
Wyatt Heplered163b02020-02-03 17:49:32 -0800618 // TODO: Remove this once checksums are fully implemented.
619 return Status::OK;
620
Keir Mierle8c352dc2020-02-02 13:58:19 -0800621 if (entry_header_format_.checksum == nullptr) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800622 return Status::OK;
623 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800624 // TODO: Implement me!
625 (void)sector;
David Rogers2761aeb2020-01-31 17:09:00 -0800626 (void)key_descriptor;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800627 return Status::UNIMPLEMENTED;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800628}
629
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800630span<const byte> KeyValueStore::CalculateEntryChecksum(
631 const EntryHeader& header,
632 const string_view key,
633 span<const byte> value) const {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800634 if (entry_header_format_.checksum == nullptr) {
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800635 return kNoChecksum.bytes;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800636 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800637
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800638 auto& checksum = *entry_header_format_.checksum;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800639 checksum.Reset();
640 checksum.Update(header.DataForChecksum());
641 checksum.Update(as_bytes(span(key)));
642 checksum.Update(value.data(), value.size_bytes());
643 return checksum.state();
Wyatt Heplerb7609542020-01-24 10:29:54 -0800644}
645
Keir Mierle8c352dc2020-02-02 13:58:19 -0800646void KeyValueStore::LogDebugInfo() {
647 const size_t sector_count = partition_.sector_count();
648 const size_t sector_size_bytes = partition_.sector_size_bytes();
649 DBG("====================== KEY VALUE STORE DUMP =========================");
650 DBG(" ");
651 DBG("Flash partition:");
652 DBG(" Sector count = %zu", sector_count);
653 DBG(" Sector max count = %zu", kUsableSectors);
654 DBG(" Sector size = %zu", sector_size_bytes);
655 DBG(" Total size = %zu", partition_.size_bytes());
656 DBG(" Alignment = %zu", partition_.alignment_bytes());
657 DBG(" ");
658 DBG("Key descriptors:");
659 DBG(" Entry count = %zu", key_descriptor_list_size_);
660 DBG(" Max entry count = %zu", kMaxEntries);
661 DBG(" ");
662 DBG(" # hash version address address (hex)");
663 for (size_t i = 0; i < key_descriptor_list_size_; ++i) {
664 const KeyDescriptor& kd = key_descriptor_list_[i];
665 DBG(" |%3zu: | %8zx |%8zu | %8zu | %8zx",
666 i,
667 size_t(kd.key_hash),
668 size_t(kd.key_version),
669 size_t(kd.address),
670 size_t(kd.address));
671 }
672 DBG(" ");
673
674 DBG("Sector descriptors:");
675 DBG(" # tail free valid has_space");
676 for (size_t sector_id = 0; sector_id < sector_count; ++sector_id) {
677 const SectorDescriptor& sd = sector_map_[sector_id];
678 DBG(" |%3zu: | %8zu |%8zu | %s",
679 sector_id,
680 size_t(sd.tail_free_bytes),
681 size_t(sd.valid_bytes),
682 sd.tail_free_bytes ? "YES" : "");
683 }
684 DBG(" ");
685
686 // TODO: This should stop logging after some threshold.
687 // size_t dumped_bytes = 0;
688 DBG("Sector raw data:");
689 for (size_t sector_id = 0; sector_id < sector_count; ++sector_id) {
690 // Read sector data. Yes, this will blow the stack on embedded.
691 std::array<byte, 500> raw_sector_data; // TODO
692 StatusWithSize sws =
693 partition_.Read(sector_id * sector_size_bytes, raw_sector_data);
694 DBG("Read: %zu bytes", sws.size());
695
696 DBG(" base addr offs 0 1 2 3 4 5 6 7");
697 for (size_t i = 0; i < sector_size_bytes; i += 8) {
698 DBG(" %3zu %8zx %5zu | %02x %02x %02x %02x %02x %02x %02x %02x",
699 sector_id,
700 (sector_id * sector_size_bytes) + i,
701 i,
702 static_cast<unsigned int>(raw_sector_data[i + 0]),
703 static_cast<unsigned int>(raw_sector_data[i + 1]),
704 static_cast<unsigned int>(raw_sector_data[i + 2]),
705 static_cast<unsigned int>(raw_sector_data[i + 3]),
706 static_cast<unsigned int>(raw_sector_data[i + 4]),
707 static_cast<unsigned int>(raw_sector_data[i + 5]),
708 static_cast<unsigned int>(raw_sector_data[i + 6]),
709 static_cast<unsigned int>(raw_sector_data[i + 7]));
710
711 // TODO: Fix exit condition.
712 if (i > 128) {
713 break;
714 }
715 }
716 DBG(" ");
717 }
718
719 DBG("////////////////////// KEY VALUE STORE DUMP END /////////////////////");
720}
721
Wyatt Hepler2ad60672020-01-21 08:00:16 -0800722} // namespace pw::kvs