blob: e71d42e09aadc82736bc48eb7874de022db0a16d [file] [log] [blame]
Wyatt Hepler7ded6da2020-03-11 18:24:43 -07001// Copyright 2020 The Pigweed Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License"); you may not
4// use this file except in compliance with the License. You may obtain a copy of
5// the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12// License for the specific language governing permissions and limitations under
13// the License.
14
Armando Montanez28ecccb2020-05-04 15:35:54 -070015#define PW_LOG_MODULE_NAME "KVS"
Wyatt Heplerae222dc2020-10-14 10:46:27 -070016#define PW_LOG_LEVEL PW_KVS_LOG_LEVEL
Armando Montanez28ecccb2020-05-04 15:35:54 -070017
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070018#include "pw_kvs/internal/entry_cache.h"
19
20#include <cinttypes>
21
22#include "pw_kvs/flash_memory.h"
23#include "pw_kvs/internal/entry.h"
24#include "pw_kvs/internal/hash.h"
Wyatt Heplerae222dc2020-10-14 10:46:27 -070025#include "pw_kvs_private/config.h"
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070026#include "pw_log/log.h"
27
28namespace pw::kvs::internal {
29namespace {
30
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070031constexpr FlashPartition::Address kNoAddress = FlashPartition::Address(-1);
32
33} // namespace
34
David Rogers9abe3c72020-03-24 19:03:13 -070035void EntryMetadata::RemoveAddress(Address address_to_remove) {
36 // Find the index of the address to remove.
37 for (Address& address : addresses_) {
38 if (address == address_to_remove) {
39 // Move the address at the back of the list to the slot of the address
40 // being removed. Do this unconditionally, even if the address to remove
41 // is the last slot since the logic still works.
42 address = addresses_.back();
43
44 // Remove the back entry of the address list.
45 addresses_.back() = kNoAddress;
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070046 addresses_ = std::span(addresses_.begin(), addresses_.size() - 1);
David Rogers9abe3c72020-03-24 19:03:13 -070047 break;
48 }
49 }
50}
51
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070052void EntryMetadata::Reset(const KeyDescriptor& descriptor, Address address) {
53 *descriptor_ = descriptor;
54
55 addresses_[0] = address;
56 for (size_t i = 1; i < addresses_.size(); ++i) {
57 addresses_[i] = kNoAddress;
58 }
59 addresses_ = addresses_.first(1);
60}
61
David Rogers98fea472020-04-01 15:43:48 -070062StatusWithSize EntryCache::Find(FlashPartition& partition,
63 const Sectors& sectors,
64 const EntryFormats& formats,
Rob Olivere64daf42020-11-24 11:50:03 -050065 Key key,
David Rogers98fea472020-04-01 15:43:48 -070066 EntryMetadata* metadata) const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070067 const uint32_t hash = internal::Hash(key);
68 Entry::KeyBuffer key_buffer;
David Rogers98fea472020-04-01 15:43:48 -070069 bool error_detected = false;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070070
71 for (size_t i = 0; i < descriptors_.size(); ++i) {
72 if (descriptors_[i].key_hash == hash) {
David Rogers98fea472020-04-01 15:43:48 -070073 bool key_found = false;
Rob Olivere64daf42020-11-24 11:50:03 -050074 Key read_key;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070075
David Rogers98fea472020-04-01 15:43:48 -070076 for (Address address : addresses(i)) {
77 Status read_result =
78 Entry::ReadKey(partition, address, key.size(), key_buffer.data());
79
Rob Olivere64daf42020-11-24 11:50:03 -050080 read_key = Key(key_buffer.data(), key.size());
David Rogers98fea472020-04-01 15:43:48 -070081
82 if (read_result.ok() && hash == internal::Hash(read_key)) {
83 key_found = true;
84 break;
85 } else {
86 // A hash mismatch can be caused by reading invalid data or a key hash
87 // collision of keys with differing size. To verify the data read from
88 // flash is good, validate the entry.
89 Entry entry;
90 read_result = Entry::Read(partition, address, formats, &entry);
91 if (read_result.ok() && entry.VerifyChecksumInFlash().ok()) {
92 key_found = true;
93 break;
94 }
95
96 PW_LOG_WARN(
97 " Found corrupt entry, invalidating this copy of the key");
98 error_detected = true;
99 sectors.FromAddress(address).mark_corrupt();
100 }
101 }
102 size_t error_val = error_detected ? 1 : 0;
103
104 if (!key_found) {
105 PW_LOG_ERROR("No valid entries for key. Data has been lost!");
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700106 return StatusWithSize::DataLoss(error_val);
David Rogers98fea472020-04-01 15:43:48 -0700107 } else if (key == read_key) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700108 PW_LOG_DEBUG("Found match for key hash 0x%08" PRIx32, hash);
109 *metadata = EntryMetadata(descriptors_[i], addresses(i));
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800110 return StatusWithSize(error_val);
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700111 } else {
112 PW_LOG_WARN("Found key hash collision for 0x%08" PRIx32, hash);
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700113 return StatusWithSize::AlreadyExists(error_val);
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700114 }
115 }
116 }
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700117 return StatusWithSize::NotFound();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700118}
119
120EntryMetadata EntryCache::AddNew(const KeyDescriptor& descriptor,
Wyatt Hepler883e7402020-04-06 09:31:18 -0700121 Address entry_address) const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700122 // TODO(hepler): DCHECK(!full());
123 Address* first_address = ResetAddresses(descriptors_.size(), entry_address);
124 descriptors_.push_back(descriptor);
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700125 return EntryMetadata(descriptors_.back(), std::span(first_address, 1));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700126}
127
128// TODO: This method is the trigger of the O(valid_entries * all_entries) time
129// complexity for reading. At some cost to memory, this could be optimized by
130// using a hash table instead of scanning, but in practice this should be fine
131// for a small number of keys
132Status EntryCache::AddNewOrUpdateExisting(const KeyDescriptor& descriptor,
133 Address address,
Wyatt Hepler883e7402020-04-06 09:31:18 -0700134 size_t sector_size_bytes) const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700135 // With the new key descriptor, either add it to the descriptor table or
136 // overwrite an existing entry with an older version of the key.
137 const int index = FindIndex(descriptor.key_hash);
138
139 // Write a new entry if there is room.
140 if (index == -1) {
141 if (full()) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700142 return Status::ResourceExhausted();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700143 }
144 AddNew(descriptor, address);
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800145 return OkStatus();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700146 }
147
148 // Existing entry is old; replace the existing entry with the new one.
149 if (descriptor.transaction_id > descriptors_[index].transaction_id) {
150 descriptors_[index] = descriptor;
151 ResetAddresses(index, address);
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800152 return OkStatus();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700153 }
154
155 // If the entries have a duplicate transaction ID, add the new (redundant)
156 // entry to the existing descriptor.
157 if (descriptors_[index].transaction_id == descriptor.transaction_id) {
158 if (descriptors_[index].key_hash != descriptor.key_hash) {
159 PW_LOG_ERROR("Duplicate entry for key 0x%08" PRIx32
160 " with transaction ID %" PRIu32 " has non-matching hash",
161 descriptor.key_hash,
162 descriptor.transaction_id);
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700163 return Status::DataLoss();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700164 }
165
166 // Verify that this entry is not in the same sector as an existing copy of
167 // this same key.
168 for (Address existing_address : addresses(index)) {
169 if (existing_address / sector_size_bytes == address / sector_size_bytes) {
Wyatt Hepler09b3a2a2020-03-25 13:18:37 -0700170 PW_LOG_DEBUG("Multiple Redundant entries in same sector %u",
171 unsigned(address / sector_size_bytes));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700172 return Status::DataLoss();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700173 }
174 }
175
176 AddAddressIfRoom(index, address);
177 } else {
178 PW_LOG_DEBUG("Found stale entry when appending; ignoring");
179 }
Wyatt Hepler1b3da3a2021-01-07 13:26:57 -0800180 return OkStatus();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700181}
182
183size_t EntryCache::present_entries() const {
184 size_t present_entries = 0;
185
186 for (const KeyDescriptor& descriptor : descriptors_) {
187 if (descriptor.state != EntryState::kDeleted) {
188 present_entries += 1;
189 }
190 }
191
192 return present_entries;
193}
194
195int EntryCache::FindIndex(uint32_t key_hash) const {
196 for (size_t i = 0; i < descriptors_.size(); ++i) {
197 if (descriptors_[i].key_hash == key_hash) {
198 return i;
199 }
200 }
201 return -1;
202}
203
Wyatt Hepler883e7402020-04-06 09:31:18 -0700204void EntryCache::AddAddressIfRoom(size_t descriptor_index,
205 Address address) const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700206 Address* const existing = first_address(descriptor_index);
207
208 for (size_t i = 0; i < redundancy(); ++i) {
209 if (existing[i] == kNoAddress) {
210 existing[i] = address;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700211 return;
212 }
213 }
214}
215
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700216std::span<EntryCache::Address> EntryCache::addresses(
217 size_t descriptor_index) const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700218 Address* const addresses = first_address(descriptor_index);
219
220 size_t size = 0;
221 while (size < redundancy() && addresses[size] != kNoAddress) {
222 size += 1;
223 }
224
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700225 return std::span(addresses, size);
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700226}
227
228EntryCache::Address* EntryCache::ResetAddresses(size_t descriptor_index,
Wyatt Hepler883e7402020-04-06 09:31:18 -0700229 Address address) const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700230 Address* first = first_address(descriptor_index);
231 *first = address;
232
233 // Clear the additional addresses, if any.
234 for (size_t i = 1; i < redundancy_; ++i) {
235 first[i] = kNoAddress;
236 }
237
238 return first;
239}
240
241} // namespace pw::kvs::internal