1 // 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 #pragma once 15 16 #include <array> 17 #include <cstddef> 18 #include <cstdint> 19 #include <span> 20 #include <type_traits> 21 22 #include "pw_containers/vector.h" 23 #include "pw_kvs/checksum.h" 24 #include "pw_kvs/flash_memory.h" 25 #include "pw_kvs/format.h" 26 #include "pw_kvs/internal/entry.h" 27 #include "pw_kvs/internal/entry_cache.h" 28 #include "pw_kvs/internal/key_descriptor.h" 29 #include "pw_kvs/internal/sectors.h" 30 #include "pw_kvs/internal/span_traits.h" 31 #include "pw_kvs/key.h" 32 #include "pw_status/status.h" 33 #include "pw_status/status_with_size.h" 34 35 namespace pw { 36 namespace kvs { 37 38 enum class GargbageCollectOnWrite { 39 // Disable all automatic garbage collection on write. 40 kDisabled, 41 42 // Allow up to a single sector, if needed, to be garbage collected on write. 43 kOneSector, 44 45 // Allow as many sectors as needed be garbage collected on write. 46 kAsManySectorsNeeded, 47 }; 48 49 enum class ErrorRecovery { 50 // Immediately do full recovery of any errors that are detected. 51 kImmediate, 52 53 // Recover from errors, but delay time consuming recovery steps until later 54 // as part of other time consuming operations. Such as waiting to garbage 55 // collect sectors with corrupt entries until the next garbage collection. 56 kLazy, 57 58 // Only recover from errors when manually triggered as part of maintenance 59 // operations. This is not recommended for normal use, only for test or 60 // read-only use. 61 kManual, 62 }; 63 64 struct Options { 65 // Perform garbage collection if necessary when writing. If not kDisabled, 66 // garbage collection is attempted if space for an entry cannot be found. This 67 // is a relatively lengthy operation. If kDisabled, Put calls that would 68 // require garbage collection fail with RESOURCE_EXHAUSTED. 69 GargbageCollectOnWrite gc_on_write = 70 GargbageCollectOnWrite::kAsManySectorsNeeded; 71 72 // When the KVS handles errors that are discovered, such as corrupt entries, 73 // not enough redundant copys of an entry, etc. 74 ErrorRecovery recovery = ErrorRecovery::kLazy; 75 76 // Verify an entry's checksum after reading it from flash. 77 bool verify_on_read = true; 78 79 // Verify an in-flash entry's checksum after writing it. 80 bool verify_on_write = true; 81 }; 82 83 class KeyValueStore { 84 public: 85 // KeyValueStores are declared as instances of 86 // KeyValueStoreBuffer<MAX_ENTRIES, MAX_SECTORS>, which allocates buffers for 87 // tracking entries and flash sectors. 88 89 // Initializes the key-value store. Must be called before calling other 90 // functions. 91 // 92 // Return values: 93 // 94 // OK: KVS successfully initialized. 95 // DATA_LOSS: KVS initialized and is usable, but contains corrupt data. 96 // UNKNOWN: Unknown error. KVS is not initialized. 97 // 98 Status Init(); 99 initialized()100 bool initialized() const { 101 return initialized_ == InitializationState::kReady; 102 } 103 104 // Reads the value of an entry in the KVS. The value is read into the provided 105 // buffer and the number of bytes read is returned. If desired, the read can 106 // be started at an offset. 107 // 108 // If the output buffer is too small for the value, Get returns 109 // RESOURCE_EXHAUSTED with the number of bytes read. The remainder of the 110 // value can be read by calling get with an offset. 111 // 112 // OK: the entry was successfully read 113 // NOT_FOUND: the key is not present in the KVS 114 // DATA_LOSS: found the entry, but the data was corrupted 115 // RESOURCE_EXHAUSTED: the buffer could not fit the entire value, but as 116 // many bytes as possible were written to it 117 // FAILED_PRECONDITION: the KVS is not initialized 118 // INVALID_ARGUMENT: key is empty or too long or value is too large 119 // 120 StatusWithSize Get(Key key, 121 std::span<std::byte> value, 122 size_t offset_bytes = 0) const; 123 124 // This overload of Get accepts a pointer to a trivially copyable object. 125 // If the value is an array, call Get with 126 // std::as_writable_bytes(std::span(array)), or pass a pointer to the array 127 // instead of the array itself. 128 template <typename Pointer, 129 typename = std::enable_if_t<std::is_pointer<Pointer>::value>> Get(const Key & key,const Pointer & pointer)130 Status Get(const Key& key, const Pointer& pointer) const { 131 using T = std::remove_reference_t<std::remove_pointer_t<Pointer>>; 132 CheckThatObjectCanBePutOrGet<T>(); 133 return FixedSizeGet(key, pointer, sizeof(T)); 134 } 135 136 // Adds a key-value entry to the KVS. If the key was already present, its 137 // value is overwritten. 138 // 139 // The value may be a std::span of bytes or a trivially copyable object. 140 // 141 // In the current implementation, all keys in the KVS must have a unique hash. 142 // If Put is called with a key whose hash matches an existing key, nothing 143 // is added and ALREADY_EXISTS is returned. 144 // 145 // OK: the entry was successfully added or updated 146 // DATA_LOSS: checksum validation failed after writing the data 147 // RESOURCE_EXHAUSTED: there is not enough space to add the entry 148 // ALREADY_EXISTS: the entry could not be added because a different key 149 // with the same hash is already in the KVS 150 // FAILED_PRECONDITION: the KVS is not initialized 151 // INVALID_ARGUMENT: key is empty or too long or value is too large 152 // 153 template <typename T, 154 typename std::enable_if_t<ConvertsToSpan<T>::value>* = nullptr> Put(const Key & key,const T & value)155 Status Put(const Key& key, const T& value) { 156 return PutBytes(key, std::as_bytes(internal::make_span(value))); 157 } 158 159 template <typename T, 160 typename std::enable_if_t<!ConvertsToSpan<T>::value>* = nullptr> Put(const Key & key,const T & value)161 Status Put(const Key& key, const T& value) { 162 CheckThatObjectCanBePutOrGet<T>(); 163 return PutBytes(key, std::as_bytes(std::span<const T>(&value, 1))); 164 } 165 166 // Removes a key-value entry from the KVS. 167 // 168 // OK: the entry was successfully added or updated 169 // NOT_FOUND: the key is not present in the KVS 170 // DATA_LOSS: checksum validation failed after recording the erase 171 // RESOURCE_EXHAUSTED: insufficient space to mark the entry as deleted 172 // FAILED_PRECONDITION: the KVS is not initialized 173 // INVALID_ARGUMENT: key is empty or too long 174 // 175 Status Delete(Key key); 176 177 // Returns the size of the value corresponding to the key. 178 // 179 // OK: the size was returned successfully 180 // NOT_FOUND: the key is not present in the KVS 181 // DATA_LOSS: checksum validation failed after reading the entry 182 // FAILED_PRECONDITION: the KVS is not initialized 183 // INVALID_ARGUMENT: key is empty or too long 184 // 185 StatusWithSize ValueSize(Key key) const; 186 187 // Perform all maintenance possible, including all neeeded repairing of 188 // corruption and garbage collection of reclaimable space in the KVS. When 189 // configured for manual recovery, this (along with FullMaintenance) is the 190 // only way KVS repair is triggered. 191 // 192 // - Heavy garbage collection of all reclaimable space, regardless of valid 193 // data in the sector. HeavyMaintenance()194 Status HeavyMaintenance() { 195 return FullMaintenanceHelper(MaintenanceType::kHeavy); 196 } 197 198 // Perform all maintenance possible, including all neeeded repairing of 199 // corruption and garbage collection of reclaimable space in the KVS. When 200 // configured for manual recovery, this (along with HeavyMaintenance) is the 201 // only way KVS repair is triggered. 202 // 203 // - Regular will not garbage collect sectors with valid data unless the KVS 204 // is mostly full. FullMaintenance()205 Status FullMaintenance() { 206 return FullMaintenanceHelper(MaintenanceType::kRegular); 207 } 208 209 // Perform a portion of KVS maintenance. If configured for at least lazy 210 // recovery, will do any needed repairing of corruption. Does garbage 211 // collection of part of the KVS, typically a single sector or similar unit 212 // that makes sense for the KVS implementation. 213 Status PartialMaintenance(); 214 215 void LogDebugInfo() const; 216 217 // Classes and functions to support STL-style iteration. 218 class iterator; 219 220 class Item { 221 public: 222 // The key as a null-terminated string. key()223 const char* key() const { return key_buffer_.data(); } 224 225 // Gets the value referred to by this iterator. Equivalent to 226 // KeyValueStore::Get. 227 StatusWithSize Get(std::span<std::byte> value_buffer, 228 size_t offset_bytes = 0) const { 229 return kvs_.Get(key(), *iterator_, value_buffer, offset_bytes); 230 } 231 232 template <typename Pointer, 233 typename = std::enable_if_t<std::is_pointer<Pointer>::value>> Get(const Pointer & pointer)234 Status Get(const Pointer& pointer) const { 235 using T = std::remove_reference_t<std::remove_pointer_t<Pointer>>; 236 CheckThatObjectCanBePutOrGet<T>(); 237 return kvs_.FixedSizeGet(key(), *iterator_, pointer, sizeof(T)); 238 } 239 240 // Reads the size of the value referred to by this iterator. Equivalent to 241 // KeyValueStore::ValueSize. ValueSize()242 StatusWithSize ValueSize() const { return kvs_.ValueSize(*iterator_); } 243 244 private: 245 friend class iterator; 246 Item(const KeyValueStore & kvs,const internal::EntryCache::const_iterator & item_iterator)247 constexpr Item(const KeyValueStore& kvs, 248 const internal::EntryCache::const_iterator& item_iterator) 249 : kvs_(kvs), iterator_(item_iterator), key_buffer_ {} 250 {} 251 252 void ReadKey(); 253 254 const KeyValueStore& kvs_; 255 internal::EntryCache::const_iterator iterator_; 256 257 // Buffer large enough for a null-terminated version of any valid key. 258 std::array<char, internal::Entry::kMaxKeyLength + 1> key_buffer_; 259 }; 260 261 class iterator { 262 public: 263 iterator& operator++(); 264 265 iterator operator++(int) { 266 const iterator original(item_.kvs_, item_.iterator_); 267 operator++(); 268 return original; 269 } 270 271 // Reads the entry's key from flash. 272 const Item& operator*() { 273 item_.ReadKey(); 274 return item_; 275 } 276 277 const Item* operator->() { 278 return &operator*(); // Read the key into the Item object. 279 } 280 281 constexpr bool operator==(const iterator& rhs) const { 282 return item_.iterator_ == rhs.item_.iterator_; 283 } 284 285 constexpr bool operator!=(const iterator& rhs) const { 286 return item_.iterator_ != rhs.item_.iterator_; 287 } 288 289 private: 290 friend class KeyValueStore; 291 iterator(const KeyValueStore & kvs,const internal::EntryCache::const_iterator & item_iterator)292 constexpr iterator( 293 const KeyValueStore& kvs, 294 const internal::EntryCache::const_iterator& item_iterator) 295 : item_(kvs, item_iterator) {} 296 297 Item item_; 298 }; 299 300 using const_iterator = iterator; // Standard alias for iterable types. 301 302 iterator begin() const; end()303 iterator end() const { return iterator(*this, entry_cache_.end()); } 304 305 // Returns the number of valid entries in the KeyValueStore. size()306 size_t size() const { return entry_cache_.present_entries(); } 307 max_size()308 size_t max_size() const { return entry_cache_.max_entries(); } 309 empty()310 size_t empty() const { return size() == 0u; } 311 312 // Returns the number of transactions that have occurred since the KVS was 313 // first used. This value is retained across initializations, but is reset if 314 // the underlying flash is erased. transaction_count()315 uint32_t transaction_count() const { return last_transaction_id_; } 316 317 struct StorageStats { 318 size_t writable_bytes; 319 size_t in_use_bytes; 320 size_t reclaimable_bytes; 321 size_t sector_erase_count; 322 size_t corrupt_sectors_recovered; 323 size_t missing_redundant_entries_recovered; 324 }; 325 326 StorageStats GetStorageStats() const; 327 328 // Level of redundancy to use for writing entries. redundancy()329 size_t redundancy() const { return entry_cache_.redundancy(); } 330 error_detected()331 bool error_detected() const { return error_detected_; } 332 333 // Maximum number of bytes allowed for a key-value combination. max_key_value_size_bytes()334 size_t max_key_value_size_bytes() const { 335 return max_key_value_size_bytes(partition_.sector_size_bytes()); 336 } 337 338 // Maximum number of bytes allowed for a given sector size for a key-value 339 // combination. max_key_value_size_bytes(size_t partition_sector_size_bytes)340 static constexpr size_t max_key_value_size_bytes( 341 size_t partition_sector_size_bytes) { 342 return partition_sector_size_bytes - Entry::entry_overhead(); 343 } 344 345 // Check KVS for any error conditions. Primarily intended for test and 346 // internal use. 347 bool CheckForErrors(); 348 349 protected: 350 using Address = FlashPartition::Address; 351 using Entry = internal::Entry; 352 using KeyDescriptor = internal::KeyDescriptor; 353 using SectorDescriptor = internal::SectorDescriptor; 354 355 // In the future, will be able to provide additional EntryFormats for 356 // backwards compatibility. 357 KeyValueStore(FlashPartition* partition, 358 std::span<const EntryFormat> formats, 359 const Options& options, 360 size_t redundancy, 361 Vector<SectorDescriptor>& sector_descriptor_list, 362 const SectorDescriptor** temp_sectors_to_skip, 363 Vector<KeyDescriptor>& key_descriptor_list, 364 Address* addresses); 365 366 private: 367 using EntryMetadata = internal::EntryMetadata; 368 using EntryState = internal::EntryState; 369 370 template <typename T> CheckThatObjectCanBePutOrGet()371 static constexpr void CheckThatObjectCanBePutOrGet() { 372 static_assert( 373 std::is_trivially_copyable<T>::value && !std::is_pointer<T>::value, 374 "Only trivially copyable, non-pointer objects may be Put and Get by " 375 "value. Any value may be stored by converting it to a byte std::span " 376 "with std::as_bytes(std::span(&value, 1)) or " 377 "std::as_writable_bytes(std::span(&value, 1))."); 378 } 379 380 Status InitializeMetadata(); 381 Status LoadEntry(Address entry_address, Address* next_entry_address); 382 Status ScanForEntry(const SectorDescriptor& sector, 383 Address start_address, 384 Address* next_entry_address); 385 386 Status PutBytes(Key key, std::span<const std::byte> value); 387 388 StatusWithSize ValueSize(const EntryMetadata& metadata) const; 389 390 Status ReadEntry(const EntryMetadata& metadata, Entry& entry) const; 391 392 // Finds the metadata for an entry matching a particular key. Searches for a 393 // KeyDescriptor that matches this key and sets *metadata_out to point to it 394 // if one is found. 395 // 396 // OK: there is a matching descriptor and *metadata is set 397 // NOT_FOUND: there is no descriptor that matches this key, but this key 398 // has a unique hash (and could potentially be added to the 399 // KVS) 400 // ALREADY_EXISTS: there is no descriptor that matches this key, but the 401 // key's hash collides with the hash for an existing 402 // descriptor 403 // 404 Status FindEntry(Key key, EntryMetadata* metadata_out) const; 405 406 // Searches for a KeyDescriptor that matches this key and sets *metadata_out 407 // to point to it if one is found. 408 // 409 // OK: there is a matching descriptor and *metadata_out is set 410 // NOT_FOUND: there is no descriptor that matches this key 411 // 412 Status FindExisting(Key key, EntryMetadata* metadata_out) const; 413 414 StatusWithSize Get(Key key, 415 const EntryMetadata& metadata, 416 std::span<std::byte> value_buffer, 417 size_t offset_bytes) const; 418 419 Status FixedSizeGet(Key key, void* value, size_t size_bytes) const; 420 421 Status FixedSizeGet(Key key, 422 const EntryMetadata& metadata, 423 void* value, 424 size_t size_bytes) const; 425 426 Status CheckWriteOperation(Key key) const; 427 Status CheckReadOperation(Key key) const; 428 429 Status WriteEntryForExistingKey(EntryMetadata& metadata, 430 EntryState new_state, 431 Key key, 432 std::span<const std::byte> value); 433 434 Status WriteEntryForNewKey(Key key, std::span<const std::byte> value); 435 436 Status WriteEntry(Key key, 437 std::span<const std::byte> value, 438 EntryState new_state, 439 EntryMetadata* prior_metadata = nullptr, 440 const internal::Entry* prior_entry = nullptr); 441 442 EntryMetadata CreateOrUpdateKeyDescriptor(const Entry& new_entry, 443 Key key, 444 EntryMetadata* prior_metadata, 445 size_t prior_size); 446 447 EntryMetadata UpdateKeyDescriptor(const Entry& entry, 448 Address new_address, 449 EntryMetadata* prior_metadata, 450 size_t prior_size); 451 452 Status GetAddressesForWrite(Address* write_addresses, size_t write_size); 453 454 Status GetSectorForWrite(SectorDescriptor** sector, 455 size_t entry_size, 456 std::span<const Address> reserved_addresses); 457 458 Status MarkSectorCorruptIfNotOk(Status status, SectorDescriptor* sector); 459 460 Status AppendEntry(const Entry& entry, 461 Key key, 462 std::span<const std::byte> value); 463 464 StatusWithSize CopyEntryToSector(Entry& entry, 465 SectorDescriptor* new_sector, 466 Address new_address); 467 468 Status RelocateEntry(const EntryMetadata& metadata, 469 KeyValueStore::Address& address, 470 std::span<const Address> reserved_addresses); 471 472 // Perform all maintenance possible, including all neeeded repairing of 473 // corruption and garbage collection of reclaimable space in the KVS. When 474 // configured for manual recovery, this is the only way KVS repair is 475 // triggered. 476 // 477 // - Regular will not garbage collect sectors with valid data unless the KVS 478 // is mostly full. 479 // - Heavy will garbage collect all reclaimable space regardless of valid data 480 // in the sector. 481 enum class MaintenanceType { 482 kRegular, 483 kHeavy, 484 }; 485 Status FullMaintenanceHelper(MaintenanceType maintenance_type); 486 487 // Find and garbage collect a singe sector that does not include a reserved 488 // address. 489 Status GarbageCollect(std::span<const Address> reserved_addresses); 490 491 Status RelocateKeyAddressesInSector( 492 SectorDescriptor& sector_to_gc, 493 const EntryMetadata& metadata, 494 std::span<const Address> reserved_addresses); 495 496 Status GarbageCollectSector(SectorDescriptor& sector_to_gc, 497 std::span<const Address> reserved_addresses); 498 499 // Ensure that all entries are on the primary (first) format. Entries that are 500 // not on the primary format are rewritten. 501 // 502 // Return: status + number of entries updated. 503 StatusWithSize UpdateEntriesToPrimaryFormat(); 504 505 Status AddRedundantEntries(EntryMetadata& metadata); 506 507 Status RepairCorruptSectors(); 508 509 Status EnsureFreeSectorExists(); 510 511 Status EnsureEntryRedundancy(); 512 513 Status FixErrors(); 514 515 Status Repair(); 516 517 internal::Entry CreateEntry(Address address, 518 Key key, 519 std::span<const std::byte> value, 520 EntryState state); 521 522 void LogSectors() const; 523 void LogKeyDescriptor() const; 524 525 FlashPartition& partition_; 526 const internal::EntryFormats formats_; 527 528 // List of sectors used by this KVS. 529 internal::Sectors sectors_; 530 531 // Unordered list of KeyDescriptors. Finding a key requires scanning and 532 // verifying a match by reading the actual entry. 533 internal::EntryCache entry_cache_; 534 535 Options options_; 536 537 // Threshold value for when to garbage collect all stale data. Above the 538 // threshold, GC all reclaimable bytes regardless of if valid data is in 539 // sector. Below the threshold, only GC sectors with reclaimable bytes and no 540 // valid bytes. The threshold is based on the portion of KVS capacity used by 541 // valid bytes. 542 static constexpr size_t kGcUsageThresholdPercentage = 70; 543 544 enum class InitializationState { 545 // KVS Init() has not been called and KVS is not usable. 546 kNotInitialized, 547 548 // KVS Init() run but not all the needed invariants are met for an operating 549 // KVS. KVS is not writable, but read operaions are possible. 550 kNeedsMaintenance, 551 552 // KVS is fully ready for use. 553 kReady, 554 }; 555 InitializationState initialized_; 556 557 // error_detected_ needs to be set from const KVS methods (such as Get), so 558 // make it mutable. 559 mutable bool error_detected_; 560 561 struct InternalStats { 562 size_t sector_erase_count; 563 size_t corrupt_sectors_recovered; 564 size_t missing_redundant_entries_recovered; 565 }; 566 InternalStats internal_stats_; 567 568 uint32_t last_transaction_id_; 569 }; 570 571 template <size_t kMaxEntries, 572 size_t kMaxUsableSectors, 573 size_t kRedundancy = 1, 574 size_t kEntryFormats = 1> 575 class KeyValueStoreBuffer : public KeyValueStore { 576 public: 577 // Constructs a KeyValueStore on the partition, with support for one 578 // EntryFormat (kEntryFormats must be 1). 579 KeyValueStoreBuffer(FlashPartition* partition, 580 const EntryFormat& format, 581 const Options& options = {}) KeyValueStoreBuffer(partition,std::span<const EntryFormat,kEntryFormats> (reinterpret_cast<const EntryFormat (&)[1]> (format)),options)582 : KeyValueStoreBuffer( 583 partition, 584 std::span<const EntryFormat, kEntryFormats>( 585 reinterpret_cast<const EntryFormat (&)[1]>(format)), 586 options) { 587 static_assert(kEntryFormats == 1, 588 "kEntryFormats EntryFormats must be specified"); 589 } 590 591 // Constructs a KeyValueStore on the partition. Supports multiple entry 592 // formats. The first EntryFormat is used for new entries. 593 KeyValueStoreBuffer(FlashPartition* partition, 594 std::span<const EntryFormat, kEntryFormats> formats, 595 const Options& options = {}) KeyValueStore(partition,formats_,options,kRedundancy,sectors_,temp_sectors_to_skip_,key_descriptors_,addresses_)596 : KeyValueStore(partition, 597 formats_, 598 options, 599 kRedundancy, 600 sectors_, 601 temp_sectors_to_skip_, 602 key_descriptors_, 603 addresses_) { 604 std::copy(formats.begin(), formats.end(), formats_.begin()); 605 } 606 607 private: 608 static_assert(kMaxEntries > 0u); 609 static_assert(kMaxUsableSectors > 0u); 610 static_assert(kRedundancy > 0u); 611 static_assert(kEntryFormats > 0u); 612 613 Vector<SectorDescriptor, kMaxUsableSectors> sectors_; 614 615 // Allocate space for an list of SectorDescriptors to avoid while searching 616 // for space to write entries. This is used to avoid changing sectors that are 617 // reserved for a new entry or marked as having other redundant copies of an 618 // entry. Up to kRedundancy sectors are reserved for a new entry, and up to 619 // kRedundancy - 1 sectors can have redundant copies of an entry, giving a 620 // maximum of 2 * kRedundancy - 1 sectors to avoid. 621 const SectorDescriptor* temp_sectors_to_skip_[2 * kRedundancy - 1]; 622 623 // KeyDescriptors for use by the KVS's EntryCache. 624 Vector<KeyDescriptor, kMaxEntries> key_descriptors_; 625 626 // An array of addresses associated with the KeyDescriptors for use with the 627 // EntryCache. To support having KeyValueStores with different redundancies, 628 // the addresses are stored in a parallel array instead of inside 629 // KeyDescriptors. 630 internal::EntryCache::AddressList<kRedundancy, kMaxEntries> addresses_; 631 632 // EntryFormats that can be read by this KeyValueStore. 633 std::array<EntryFormat, kEntryFormats> formats_; 634 }; 635 636 } // namespace kvs 637 } // namespace pw 638