blob: 0b6a6a3d4031227a3df2f4aa183a7bb9b48a474d [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
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080035// constexpr uint32_t SixFiveFiveNineNine(std::string_view string) {
36constexpr uint32_t HashKey(std::string_view string) {
37 uint32_t hash = 0;
38 uint32_t coefficient = 65599u;
39
40 for (char ch : string) {
41 hash += coefficient * unsigned(ch);
42 coefficient *= 65599u;
Wyatt Heplerb7609542020-01-24 10:29:54 -080043 }
44
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080045 return hash;
Wyatt Heplerb7609542020-01-24 10:29:54 -080046}
47
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080048constexpr size_t EntrySize(string_view key, span<const byte> value) {
49 return sizeof(EntryHeader) + key.size() + value.size();
50}
51
52} // namespace
53
54Status KeyValueStore::Init() {
Keir Mierle8c352dc2020-02-02 13:58:19 -080055 // Reset the number of occupied key descriptors; we will fill them later.
56 key_descriptor_list_size_ = 0;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -080057
Keir Mierle8c352dc2020-02-02 13:58:19 -080058 const size_t sector_size_bytes = partition_.sector_size_bytes();
59 const size_t sector_count = partition_.sector_count();
60
61 DBG("First pass: Read all entries from all sectors");
62 for (size_t sector_id = 0; sector_id < sector_count; ++sector_id) {
63 // Track writable bytes in this sector. Updated after reading each entry.
64 sector_map_[sector_id].tail_free_bytes = sector_size_bytes;
65
66 const Address sector_address = sector_id * sector_size_bytes;
67 Address entry_address = sector_address;
68
69 for (int num_entries_in_sector = 0;; num_entries_in_sector++) {
70 DBG("Load entry: sector=%zu, entry#=%d, address=%zu",
71 sector_id,
72 num_entries_in_sector,
73 size_t(entry_address));
74
75 if (!AddressInSector(sector_map_[sector_id], entry_address)) {
76 DBG("Fell off end of sector; moving to the next sector");
77 break;
78 }
79
80 Address next_entry_address;
81 Status status = LoadEntry(entry_address, &next_entry_address);
82 if (status == Status::NOT_FOUND) {
83 DBG("Hit un-written data in sector; moving to the next sector");
84 break;
85 }
86 if (status == Status::DATA_LOSS) {
87 // It's not clear KVS can make a unilateral decision about what to do
88 // in corruption cases. It's an application decision, for which we
89 // should offer some configurability. For now, entirely bail out of
90 // loading and give up.
91 //
92 // Later, scan for remaining valid keys; since it's entirely possible
93 // that there is a duplicate of the key elsewhere and everything is
94 // fine. Later, we can wipe and maybe recover the sector.
95 //
96 // TODO: Implement rest-of-sector scanning for valid entries.
97 return Status::DATA_LOSS;
98 }
99 TRY(status);
100
101 // Entry loaded successfully; so get ready to load the next one.
102 entry_address = next_entry_address;
103
104 // Update of the number of writable bytes in this sector.
105 sector_map_[sector_id].tail_free_bytes =
106 sector_size_bytes - (entry_address - sector_address);
107 }
108 }
109
110 DBG("Second pass: Count valid bytes in each sector");
111 // Initialize the sector sizes.
112 for (size_t sector_id = 0; sector_id < sector_count; ++sector_id) {
113 sector_map_[sector_id].valid_bytes = 0;
114 }
115 // For every valid key, increment the valid bytes for that sector.
116 for (size_t key_id = 0; key_id < key_descriptor_list_size_; ++key_id) {
117 uint32_t sector_id =
118 key_descriptor_list_[key_id].address / sector_size_bytes;
119 KeyDescriptor key_descriptor;
120 key_descriptor.address = key_descriptor_list_[key_id].address;
121 EntryHeader header;
122 TRY(ReadEntryHeader(key_descriptor, &header));
123 sector_map_[sector_id].valid_bytes += header.entry_size();
124 }
125 enabled_ = true;
126 return Status::OK;
127}
128
129Status KeyValueStore::LoadEntry(Address entry_address,
130 Address* next_entry_address) {
131 const size_t alignment_bytes = partition_.alignment_bytes();
132
133 KeyDescriptor key_descriptor;
134 key_descriptor.address = entry_address;
135
136 EntryHeader header;
137 TRY(ReadEntryHeader(key_descriptor, &header));
138 // TODO: Should likely add a "LogHeader" method or similar.
139 DBG("Header: ");
140 DBG(" Address = 0x%zx", size_t(entry_address));
141 DBG(" Magic = 0x%zx", size_t(header.magic()));
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800142 DBG(" Checksum = 0x%zx", size_t(header.checksum()));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800143 DBG(" Key length = 0x%zx", size_t(header.key_length()));
144 DBG(" Value length = 0x%zx", size_t(header.value_length()));
145 DBG(" Entry size = 0x%zx", size_t(header.entry_size()));
146 DBG(" Padded size = 0x%zx",
147 size_t(AlignUp(header.entry_size(), alignment_bytes)));
148
149 if (HeaderLooksLikeUnwrittenData(header)) {
150 return Status::NOT_FOUND;
151 }
152 key_descriptor.key_version = header.key_version();
153
154 // TODO: Handle multiple magics for formats that have changed.
155 if (header.magic() != entry_header_format_.magic) {
156 // TODO: It may be cleaner to have some logging helpers for these cases.
157 CRT("Found corrupt magic: %zx; expecting %zx; at address %zx",
158 size_t(header.magic()),
159 size_t(entry_header_format_.magic),
160 size_t(entry_address));
161 return Status::DATA_LOSS;
162 }
163
164 // Read the key from flash & validate the entry (which reads the value).
165 KeyBuffer key_buffer;
166 TRY(ReadEntryKey(key_descriptor, header.key_length(), key_buffer.data()));
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800167 const string_view key(key_buffer.data(), header.key_length());
168
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800169 TRY(header.VerifyChecksumInFlash(
170 &partition_, key_descriptor.address, entry_header_format_.checksum, key));
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800171 key_descriptor.key_hash = HashKey(key);
Keir Mierle8c352dc2020-02-02 13:58:19 -0800172
173 DBG("Key hash: %zx (%zu)",
174 size_t(key_descriptor.key_hash),
175 size_t(key_descriptor.key_hash));
176
177 TRY(AppendNewOrOverwriteStaleExistingDescriptor(key_descriptor));
178
179 // TODO: Extract this to something like "NextValidEntryAddress".
180 *next_entry_address =
181 AlignUp(key_descriptor.address + header.entry_size(), alignment_bytes);
182
183 return Status::OK;
184}
185
186// TODO: This method is the trigger of the O(valid_entries * all_entries) time
187// complexity for reading. At some cost to memory, this could be optimized by
188// using a hash table instead of scanning, but in practice this should be fine
189// for a small number of keys
190Status KeyValueStore::AppendNewOrOverwriteStaleExistingDescriptor(
191 const KeyDescriptor& key_descriptor) {
192 // With the new key descriptor, either add it to the descriptor table or
193 // overwrite an existing entry with an older version of the key.
194 KeyDescriptor* existing_descriptor = FindDescriptor(key_descriptor.key_hash);
195 if (existing_descriptor) {
196 if (existing_descriptor->key_version < key_descriptor.key_version) {
197 // Existing entry is old; replace the existing entry with the new one.
198 *existing_descriptor = key_descriptor;
199 } else {
200 // Otherwise, check for data integrity and leave the existing entry.
201 if (existing_descriptor->key_version == key_descriptor.key_version) {
202 ERR("Data loss: Duplicated old(=%zu) and new(=%zu) version",
203 size_t(existing_descriptor->key_version),
204 size_t(key_descriptor.key_version));
205 return Status::DATA_LOSS;
206 }
207 DBG("Found stale entry when appending; ignoring");
208 }
209 return Status::OK;
210 }
211 // Write new entry.
212 KeyDescriptor* newly_allocated_key_descriptor;
213 TRY(AppendEmptyDescriptor(&newly_allocated_key_descriptor));
214 *newly_allocated_key_descriptor = key_descriptor;
215 return Status::OK;
216}
217
218// TODO: Need a better name.
219Status KeyValueStore::AppendEmptyDescriptor(KeyDescriptor** new_descriptor) {
220 if (KeyListFull()) {
221 // TODO: Is this the right return code?
222 return Status::RESOURCE_EXHAUSTED;
223 }
224 *new_descriptor = &key_descriptor_list_[key_descriptor_list_size_++];
225 return Status::OK;
226}
227
228// TODO: Finish.
229bool KeyValueStore::HeaderLooksLikeUnwrittenData(
230 const EntryHeader& header) const {
231 // TODO: This is not correct; it should call through to flash memory.
232 return header.magic() == 0xffffffff;
233}
234
235KeyValueStore::KeyDescriptor* KeyValueStore::FindDescriptor(uint32_t hash) {
236 for (size_t key_id = 0; key_id < key_descriptor_list_size_; key_id++) {
237 if (key_descriptor_list_[key_id].key_hash == hash) {
238 return &(key_descriptor_list_[key_id]);
239 }
240 }
241 return nullptr;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800242}
243
244StatusWithSize KeyValueStore::Get(string_view key,
245 span<byte> value_buffer) const {
246 TRY(InvalidOperation(key));
247
David Rogers2761aeb2020-01-31 17:09:00 -0800248 const KeyDescriptor* key_descriptor;
249 TRY(FindKeyDescriptor(key, &key_descriptor));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800250
251 EntryHeader header;
David Rogers2761aeb2020-01-31 17:09:00 -0800252 TRY(ReadEntryHeader(*key_descriptor, &header));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800253
Keir Mierle8c352dc2020-02-02 13:58:19 -0800254 StatusWithSize result = ReadEntryValue(*key_descriptor, header, value_buffer);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800255 if (result.ok() && options_.verify_on_read) {
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800256 return header.VerifyChecksum(entry_header_format_.checksum,
257 key,
258 value_buffer.subspan(0, result.size()));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800259 }
260 return result;
261}
262
263Status KeyValueStore::Put(string_view key, span<const byte> value) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800264 DBG("Writing key/value; key length=%zu, value length=%zu",
265 key.size(),
266 value.size());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800267 TRY(InvalidOperation(key));
268
269 if (value.size() > (1 << 24)) {
270 // TODO: Reject sizes that are larger than the maximum?
271 }
272
David Rogers2761aeb2020-01-31 17:09:00 -0800273 KeyDescriptor* key_descriptor;
274 if (FindKeyDescriptor(key, &key_descriptor).ok()) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800275 DBG("Writing over existing entry");
David Rogers2761aeb2020-01-31 17:09:00 -0800276 return WriteEntryForExistingKey(key_descriptor, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800277 }
David Rogers2761aeb2020-01-31 17:09:00 -0800278
Keir Mierle8c352dc2020-02-02 13:58:19 -0800279 DBG("Writing new entry");
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800280 return WriteEntryForNewKey(key, value);
281}
282
283Status KeyValueStore::Delete(string_view key) {
284 TRY(InvalidOperation(key));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800285 return Status::UNIMPLEMENTED;
286}
287
288const KeyValueStore::Entry& KeyValueStore::Iterator::operator*() {
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800289 const KeyDescriptor& descriptor = entry_.kvs_.key_descriptor_list_[index_];
290
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800291 std::memset(entry_.key_buffer_.data(), 0, entry_.key_buffer_.size());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800292
293 EntryHeader header;
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800294 if (entry_.kvs_.ReadEntryHeader(descriptor, &header).ok()) {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800295 entry_.kvs_.ReadEntryKey(
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800296 descriptor, header.key_length(), entry_.key_buffer_.data());
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800297 }
298
299 return entry_;
300}
301
Wyatt Heplered163b02020-02-03 17:49:32 -0800302StatusWithSize KeyValueStore::ValueSize(std::string_view key) const {
303 TRY(InvalidOperation(key));
304
305 const KeyDescriptor* key_descriptor;
306 TRY(FindKeyDescriptor(key, &key_descriptor));
307
308 EntryHeader header;
309 TRY(ReadEntryHeader(*key_descriptor, &header));
310
311 return StatusWithSize(header.value_length());
312}
313
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800314Status KeyValueStore::FixedSizeGet(std::string_view key,
315 byte* value,
316 size_t size_bytes) const {
317 // Ensure that the size of the stored value matches the size of the type.
318 // Otherwise, report error. This check avoids potential memory corruption.
319 StatusWithSize result = ValueSize(key);
320 if (!result.ok()) {
321 return result.status();
Keir Mierle8c352dc2020-02-02 13:58:19 -0800322 }
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800323 if (result.size() != size_bytes) {
324 return Status::INVALID_ARGUMENT;
Wyatt Heplerbab0e202020-02-04 07:40:08 -0800325 }
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800326 return Get(key, span(value, size_bytes)).status();
Keir Mierle8c352dc2020-02-02 13:58:19 -0800327}
328
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800329Status KeyValueStore::InvalidOperation(string_view key) const {
Wyatt Hepleracaacf92020-01-24 10:58:30 -0800330 if (InvalidKey(key)) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800331 return Status::INVALID_ARGUMENT;
332 }
Keir Mierle8c352dc2020-02-02 13:58:19 -0800333 if (!enabled_) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800334 return Status::FAILED_PRECONDITION;
335 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800336 return Status::OK;
337}
338
David Rogers2761aeb2020-01-31 17:09:00 -0800339Status KeyValueStore::FindKeyDescriptor(string_view key,
340 const KeyDescriptor** result) const {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800341 char key_buffer[kMaxKeyLength];
342 const uint32_t hash = HashKey(key);
Wyatt Heplerb7609542020-01-24 10:29:54 -0800343
David Rogers2761aeb2020-01-31 17:09:00 -0800344 for (auto& descriptor : key_descriptors()) {
345 if (descriptor.key_hash == hash) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800346 DBG("Found match! For hash: %zx", size_t(hash));
David Rogers2761aeb2020-01-31 17:09:00 -0800347 TRY(ReadEntryKey(descriptor, key.size(), key_buffer));
Wyatt Heplerb7609542020-01-24 10:29:54 -0800348
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800349 if (key == string_view(key_buffer, key.size())) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800350 DBG("Keys matched too");
David Rogers2761aeb2020-01-31 17:09:00 -0800351 *result = &descriptor;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800352 return Status::OK;
353 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800354 }
355 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800356 return Status::NOT_FOUND;
357}
358
David Rogers2761aeb2020-01-31 17:09:00 -0800359Status KeyValueStore::ReadEntryHeader(const KeyDescriptor& descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800360 EntryHeader* header) const {
David Rogers2761aeb2020-01-31 17:09:00 -0800361 return partition_.Read(descriptor.address, sizeof(*header), header).status();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800362}
363
David Rogers2761aeb2020-01-31 17:09:00 -0800364Status KeyValueStore::ReadEntryKey(const KeyDescriptor& descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800365 size_t key_length,
366 char* key) const {
367 // TODO: This check probably shouldn't be here; this is like
368 // checking that the Cortex M's RAM isn't corrupt. This should be
369 // done at boot time.
370 // ^^ This argument sometimes comes from EntryHeader::key_value_len,
371 // which is read directly from flash. If it's corrupted, we shouldn't try
372 // to read a bunch of extra data.
373 if (key_length == 0u || key_length > kMaxKeyLength) {
374 return Status::DATA_LOSS;
375 }
376 // The key is immediately after the entry header.
David Rogers2761aeb2020-01-31 17:09:00 -0800377 return partition_
378 .Read(descriptor.address + sizeof(EntryHeader), key_length, key)
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800379 .status();
380}
381
David Rogers2761aeb2020-01-31 17:09:00 -0800382StatusWithSize KeyValueStore::ReadEntryValue(
383 const KeyDescriptor& key_descriptor,
384 const EntryHeader& header,
385 span<byte> value) const {
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800386 const size_t read_size = std::min(header.value_length(), value.size());
David Rogers2761aeb2020-01-31 17:09:00 -0800387 StatusWithSize result = partition_.Read(
388 key_descriptor.address + sizeof(header) + header.key_length(),
Keir Mierle8c352dc2020-02-02 13:58:19 -0800389 value.subspan(0, read_size));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800390 TRY(result);
391 if (read_size != header.value_length()) {
392 return StatusWithSize(Status::RESOURCE_EXHAUSTED, read_size);
393 }
394 return StatusWithSize(read_size);
395}
396
David Rogers2761aeb2020-01-31 17:09:00 -0800397Status KeyValueStore::WriteEntryForExistingKey(KeyDescriptor* key_descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800398 string_view key,
399 span<const byte> value) {
David Rogers2761aeb2020-01-31 17:09:00 -0800400 SectorDescriptor* sector;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800401 TRY(FindOrRecoverSectorWithSpace(&sector, EntrySize(key, value)));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800402 DBG("Writing existing entry; found sector: %d",
403 static_cast<int>(sector - sector_map_.data()));
David Rogers2761aeb2020-01-31 17:09:00 -0800404 return AppendEntry(sector, key_descriptor, key, value);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800405}
406
407Status KeyValueStore::WriteEntryForNewKey(string_view key,
408 span<const byte> value) {
David Rogers2761aeb2020-01-31 17:09:00 -0800409 if (KeyListFull()) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800410 WRN("KVS full: trying to store a new entry, but can't. Have %zu entries",
411 key_descriptor_list_size_);
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800412 return Status::RESOURCE_EXHAUSTED;
413 }
414
David Rogers2761aeb2020-01-31 17:09:00 -0800415 // Modify the key descriptor at the end of the array, without bumping the map
416 // size so the key descriptor is prepared and written without committing
417 // first.
418 KeyDescriptor& key_descriptor =
419 key_descriptor_list_[key_descriptor_list_size_];
420 key_descriptor.key_hash = HashKey(key);
421 key_descriptor.key_version = 0; // will be incremented by AppendEntry()
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800422
David Rogers2761aeb2020-01-31 17:09:00 -0800423 SectorDescriptor* sector;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800424 TRY(FindOrRecoverSectorWithSpace(&sector, EntrySize(key, value)));
Keir Mierle8c352dc2020-02-02 13:58:19 -0800425 DBG("Writing new entry; found sector: %d",
426 static_cast<int>(sector - sector_map_.data()));
David Rogers2761aeb2020-01-31 17:09:00 -0800427 TRY(AppendEntry(sector, &key_descriptor, key, value));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800428
Keir Mierle8c352dc2020-02-02 13:58:19 -0800429 // Only increment bump our size when we are certain the write succeeded.
David Rogers2761aeb2020-01-31 17:09:00 -0800430 key_descriptor_list_size_ += 1;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800431 return Status::OK;
432}
433
David Rogers2761aeb2020-01-31 17:09:00 -0800434Status KeyValueStore::RelocateEntry(KeyDescriptor& key_descriptor) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800435 // TODO: Implement this function!
David Rogers2761aeb2020-01-31 17:09:00 -0800436 (void)key_descriptor;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800437 return Status::OK;
David Rogersa12786b2020-01-31 16:02:33 -0800438 return Status::UNIMPLEMENTED;
439}
440
David Rogers8db5a722020-02-03 18:28:34 -0800441// Find either an existing sector with enough space that is not the sector to
442// skip, or an empty sector. Maintains the invariant that there is always at
443// least 1 empty sector unless set to bypass the rule.
David Rogers2761aeb2020-01-31 17:09:00 -0800444KeyValueStore::SectorDescriptor* KeyValueStore::FindSectorWithSpace(
David Rogers8db5a722020-02-03 18:28:34 -0800445 size_t size,
446 SectorDescriptor* sector_to_skip,
447 bool bypass_empty_sector_rule) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800448 const size_t sector_count = partition_.sector_count();
449
450 // TODO: Ignore last written sector for now and scan from the beginning.
451 last_written_sector_ = sector_count - 1;
452
453 size_t start = (last_written_sector_ + 1) % sector_count;
David Rogers2761aeb2020-01-31 17:09:00 -0800454 SectorDescriptor* first_empty_sector = nullptr;
David Rogers8db5a722020-02-03 18:28:34 -0800455 bool at_least_two_empty_sectors = bypass_empty_sector_rule;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800456
457 for (size_t i = start; i != last_written_sector_;
Keir Mierle8c352dc2020-02-02 13:58:19 -0800458 i = (i + 1) % sector_count) {
459 DBG("Examining sector %zu", i);
David Rogers2761aeb2020-01-31 17:09:00 -0800460 SectorDescriptor& sector = sector_map_[i];
Keir Mierle8c352dc2020-02-02 13:58:19 -0800461
David Rogers8db5a722020-02-03 18:28:34 -0800462 if (sector_to_skip == &sector) {
463 DBG("Skipping the skip sector");
464 continue;
465 }
466
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800467 if (!SectorEmpty(sector) && sector.HasSpace(size)) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800468 DBG("Partially occupied sector with enough space; done!");
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800469 return &sector;
470 }
471
472 if (SectorEmpty(sector)) {
473 if (first_empty_sector == nullptr) {
474 first_empty_sector = &sector;
475 } else {
476 at_least_two_empty_sectors = true;
Wyatt Hepler2ad60672020-01-21 08:00:16 -0800477 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800478 }
479 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800480
481 if (at_least_two_empty_sectors) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800482 DBG("Found a empty sector and a spare one; returning the first found (%d)",
483 static_cast<int>(first_empty_sector - sector_map_.data()));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800484 return first_empty_sector;
485 }
486 return nullptr;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800487}
488
David Rogers2761aeb2020-01-31 17:09:00 -0800489Status KeyValueStore::FindOrRecoverSectorWithSpace(SectorDescriptor** sector,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800490 size_t size) {
491 *sector = FindSectorWithSpace(size);
492 if (*sector != nullptr) {
493 return Status::OK;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800494 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800495 if (options_.partial_gc_on_write) {
496 return GarbageCollectOneSector(sector);
497 }
498 return Status::RESOURCE_EXHAUSTED;
499}
500
David Rogers2761aeb2020-01-31 17:09:00 -0800501KeyValueStore::SectorDescriptor* KeyValueStore::FindSectorToGarbageCollect() {
502 SectorDescriptor* sector_candidate = nullptr;
David Rogersa12786b2020-01-31 16:02:33 -0800503 size_t candidate_bytes = 0;
504
505 // Step 1: Try to find a sectors with stale keys and no valid keys (no
506 // relocation needed). If any such sectors are found, use the sector with the
507 // most reclaimable bytes.
508 for (auto& sector : sector_map_) {
509 if ((sector.valid_bytes == 0) &&
510 (RecoverableBytes(sector) > candidate_bytes)) {
511 sector_candidate = &sector;
512 candidate_bytes = RecoverableBytes(sector);
513 }
514 }
515
516 // Step 2: If step 1 yields no sectors, just find the sector with the most
517 // reclaimable bytes.
518 if (sector_candidate == nullptr) {
519 for (auto& sector : sector_map_) {
520 if (RecoverableBytes(sector) > candidate_bytes) {
521 sector_candidate = &sector;
522 candidate_bytes = RecoverableBytes(sector);
523 }
524 }
525 }
526
527 return sector_candidate;
528}
529
David Rogers2761aeb2020-01-31 17:09:00 -0800530Status KeyValueStore::GarbageCollectOneSector(SectorDescriptor** sector) {
David Rogersa12786b2020-01-31 16:02:33 -0800531 // Step 1: Find the sector to garbage collect
David Rogers2761aeb2020-01-31 17:09:00 -0800532 SectorDescriptor* sector_to_gc = FindSectorToGarbageCollect();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800533
David Rogersa12786b2020-01-31 16:02:33 -0800534 if (sector_to_gc == nullptr) {
535 return Status::RESOURCE_EXHAUSTED;
536 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800537
David Rogersa12786b2020-01-31 16:02:33 -0800538 // Step 2: Move any valid entries in the GC sector to other sectors
539 if (sector_to_gc->valid_bytes != 0) {
David Rogers2761aeb2020-01-31 17:09:00 -0800540 for (auto& descriptor : key_descriptors()) {
541 if (AddressInSector(*sector_to_gc, descriptor.address)) {
542 TRY(RelocateEntry(descriptor));
David Rogersa12786b2020-01-31 16:02:33 -0800543 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800544 }
545 }
Wyatt Heplerb7609542020-01-24 10:29:54 -0800546
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800547 if (sector_to_gc->valid_bytes != 0) {
Wyatt Heplerb7609542020-01-24 10:29:54 -0800548 return Status::INTERNAL;
549 }
550
David Rogersa12786b2020-01-31 16:02:33 -0800551 // Step 3: Reinitialize the sector
552 sector_to_gc->tail_free_bytes = 0;
553 TRY(partition_.Erase(SectorBaseAddress(sector_to_gc), 1));
554 sector_to_gc->tail_free_bytes = partition_.sector_size_bytes();
Wyatt Heplerb7609542020-01-24 10:29:54 -0800555
David Rogersa12786b2020-01-31 16:02:33 -0800556 *sector = sector_to_gc;
557 return Status::OK;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800558}
559
David Rogers2761aeb2020-01-31 17:09:00 -0800560Status KeyValueStore::AppendEntry(SectorDescriptor* sector,
561 KeyDescriptor* key_descriptor,
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800562 const string_view key,
563 span<const byte> value) {
564 // write header, key, and value
565 const EntryHeader header(entry_header_format_.magic,
Wyatt Hepler6e3a83b2020-02-04 07:36:45 -0800566 entry_header_format_.checksum,
567 key,
568 value,
David Rogers2761aeb2020-01-31 17:09:00 -0800569 key_descriptor->key_version + 1);
Keir Mierle8c352dc2020-02-02 13:58:19 -0800570 DBG("Appending entry with key version: %zx", size_t(header.key_version()));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800571
572 // Handles writing multiple concatenated buffers, while breaking up the writes
573 // into alignment-sized blocks.
574 Address address = NextWritableAddress(sector);
Keir Mierle8c352dc2020-02-02 13:58:19 -0800575 DBG("Appending to address: %zx", size_t(address));
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800576 TRY_ASSIGN(
577 size_t written,
578 partition_.Write(
579 address, {as_bytes(span(&header, 1)), as_bytes(span(key)), value}));
580
581 if (options_.verify_on_write) {
David Rogers2761aeb2020-01-31 17:09:00 -0800582 TRY(VerifyEntry(sector, key_descriptor));
Wyatt Heplerb7609542020-01-24 10:29:54 -0800583 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800584
585 // TODO: UPDATE last_written_sector_ appropriately
586
David Rogers2761aeb2020-01-31 17:09:00 -0800587 key_descriptor->address = address;
588 key_descriptor->key_version = header.key_version();
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800589 sector->valid_bytes += written;
590 sector->tail_free_bytes -= written;
591 return Status::OK;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800592}
593
David Rogers2761aeb2020-01-31 17:09:00 -0800594Status KeyValueStore::VerifyEntry(SectorDescriptor* sector,
595 KeyDescriptor* key_descriptor) {
Wyatt Heplered163b02020-02-03 17:49:32 -0800596 // TODO: Remove this once checksums are fully implemented.
597 return Status::OK;
598
Keir Mierle8c352dc2020-02-02 13:58:19 -0800599 if (entry_header_format_.checksum == nullptr) {
Keir Mierle8c352dc2020-02-02 13:58:19 -0800600 return Status::OK;
601 }
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800602 // TODO: Implement me!
603 (void)sector;
David Rogers2761aeb2020-01-31 17:09:00 -0800604 (void)key_descriptor;
Wyatt Hepler4da1fcb2020-01-30 17:32:18 -0800605 return Status::UNIMPLEMENTED;
Wyatt Heplerb7609542020-01-24 10:29:54 -0800606}
607
Keir Mierle8c352dc2020-02-02 13:58:19 -0800608void KeyValueStore::LogDebugInfo() {
609 const size_t sector_count = partition_.sector_count();
610 const size_t sector_size_bytes = partition_.sector_size_bytes();
611 DBG("====================== KEY VALUE STORE DUMP =========================");
612 DBG(" ");
613 DBG("Flash partition:");
614 DBG(" Sector count = %zu", sector_count);
615 DBG(" Sector max count = %zu", kUsableSectors);
616 DBG(" Sector size = %zu", sector_size_bytes);
617 DBG(" Total size = %zu", partition_.size_bytes());
618 DBG(" Alignment = %zu", partition_.alignment_bytes());
619 DBG(" ");
620 DBG("Key descriptors:");
621 DBG(" Entry count = %zu", key_descriptor_list_size_);
622 DBG(" Max entry count = %zu", kMaxEntries);
623 DBG(" ");
624 DBG(" # hash version address address (hex)");
625 for (size_t i = 0; i < key_descriptor_list_size_; ++i) {
626 const KeyDescriptor& kd = key_descriptor_list_[i];
627 DBG(" |%3zu: | %8zx |%8zu | %8zu | %8zx",
628 i,
629 size_t(kd.key_hash),
630 size_t(kd.key_version),
631 size_t(kd.address),
632 size_t(kd.address));
633 }
634 DBG(" ");
635
636 DBG("Sector descriptors:");
637 DBG(" # tail free valid has_space");
638 for (size_t sector_id = 0; sector_id < sector_count; ++sector_id) {
639 const SectorDescriptor& sd = sector_map_[sector_id];
640 DBG(" |%3zu: | %8zu |%8zu | %s",
641 sector_id,
642 size_t(sd.tail_free_bytes),
643 size_t(sd.valid_bytes),
644 sd.tail_free_bytes ? "YES" : "");
645 }
646 DBG(" ");
647
648 // TODO: This should stop logging after some threshold.
649 // size_t dumped_bytes = 0;
650 DBG("Sector raw data:");
651 for (size_t sector_id = 0; sector_id < sector_count; ++sector_id) {
652 // Read sector data. Yes, this will blow the stack on embedded.
653 std::array<byte, 500> raw_sector_data; // TODO
654 StatusWithSize sws =
655 partition_.Read(sector_id * sector_size_bytes, raw_sector_data);
656 DBG("Read: %zu bytes", sws.size());
657
658 DBG(" base addr offs 0 1 2 3 4 5 6 7");
659 for (size_t i = 0; i < sector_size_bytes; i += 8) {
660 DBG(" %3zu %8zx %5zu | %02x %02x %02x %02x %02x %02x %02x %02x",
661 sector_id,
662 (sector_id * sector_size_bytes) + i,
663 i,
664 static_cast<unsigned int>(raw_sector_data[i + 0]),
665 static_cast<unsigned int>(raw_sector_data[i + 1]),
666 static_cast<unsigned int>(raw_sector_data[i + 2]),
667 static_cast<unsigned int>(raw_sector_data[i + 3]),
668 static_cast<unsigned int>(raw_sector_data[i + 4]),
669 static_cast<unsigned int>(raw_sector_data[i + 5]),
670 static_cast<unsigned int>(raw_sector_data[i + 6]),
671 static_cast<unsigned int>(raw_sector_data[i + 7]));
672
673 // TODO: Fix exit condition.
674 if (i > 128) {
675 break;
676 }
677 }
678 DBG(" ");
679 }
680
681 DBG("////////////////////// KEY VALUE STORE DUMP END /////////////////////");
682}
683
Wyatt Hepler2ad60672020-01-21 08:00:16 -0800684} // namespace pw::kvs