blob: 9fa93a263724fd94e35f32611099f9894199fdac [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
31using std::string_view;
32
33constexpr FlashPartition::Address kNoAddress = FlashPartition::Address(-1);
34
35} // namespace
36
David Rogers9abe3c72020-03-24 19:03:13 -070037void EntryMetadata::RemoveAddress(Address address_to_remove) {
38 // Find the index of the address to remove.
39 for (Address& address : addresses_) {
40 if (address == address_to_remove) {
41 // Move the address at the back of the list to the slot of the address
42 // being removed. Do this unconditionally, even if the address to remove
43 // is the last slot since the logic still works.
44 address = addresses_.back();
45
46 // Remove the back entry of the address list.
47 addresses_.back() = kNoAddress;
Wyatt Heplere2cbadf2020-06-22 11:21:45 -070048 addresses_ = std::span(addresses_.begin(), addresses_.size() - 1);
David Rogers9abe3c72020-03-24 19:03:13 -070049 break;
50 }
51 }
52}
53
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070054void EntryMetadata::Reset(const KeyDescriptor& descriptor, Address address) {
55 *descriptor_ = descriptor;
56
57 addresses_[0] = address;
58 for (size_t i = 1; i < addresses_.size(); ++i) {
59 addresses_[i] = kNoAddress;
60 }
61 addresses_ = addresses_.first(1);
62}
63
David Rogers98fea472020-04-01 15:43:48 -070064StatusWithSize EntryCache::Find(FlashPartition& partition,
65 const Sectors& sectors,
66 const EntryFormats& formats,
67 string_view key,
68 EntryMetadata* metadata) const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070069 const uint32_t hash = internal::Hash(key);
70 Entry::KeyBuffer key_buffer;
David Rogers98fea472020-04-01 15:43:48 -070071 bool error_detected = false;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070072
73 for (size_t i = 0; i < descriptors_.size(); ++i) {
74 if (descriptors_[i].key_hash == hash) {
David Rogers98fea472020-04-01 15:43:48 -070075 bool key_found = false;
76 string_view read_key;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -070077
David Rogers98fea472020-04-01 15:43:48 -070078 for (Address address : addresses(i)) {
79 Status read_result =
80 Entry::ReadKey(partition, address, key.size(), key_buffer.data());
81
82 read_key = string_view(key_buffer.data(), key.size());
83
84 if (read_result.ok() && hash == internal::Hash(read_key)) {
85 key_found = true;
86 break;
87 } else {
88 // A hash mismatch can be caused by reading invalid data or a key hash
89 // collision of keys with differing size. To verify the data read from
90 // flash is good, validate the entry.
91 Entry entry;
92 read_result = Entry::Read(partition, address, formats, &entry);
93 if (read_result.ok() && entry.VerifyChecksumInFlash().ok()) {
94 key_found = true;
95 break;
96 }
97
98 PW_LOG_WARN(
99 " Found corrupt entry, invalidating this copy of the key");
100 error_detected = true;
101 sectors.FromAddress(address).mark_corrupt();
102 }
103 }
104 size_t error_val = error_detected ? 1 : 0;
105
106 if (!key_found) {
107 PW_LOG_ERROR("No valid entries for key. Data has been lost!");
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700108 return StatusWithSize::DataLoss(error_val);
David Rogers98fea472020-04-01 15:43:48 -0700109 } else if (key == read_key) {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700110 PW_LOG_DEBUG("Found match for key hash 0x%08" PRIx32, hash);
111 *metadata = EntryMetadata(descriptors_[i], addresses(i));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700112 return StatusWithSize::Ok(error_val);
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700113 } else {
114 PW_LOG_WARN("Found key hash collision for 0x%08" PRIx32, hash);
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700115 return StatusWithSize::AlreadyExists(error_val);
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700116 }
117 }
118 }
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700119 return StatusWithSize::NotFound();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700120}
121
122EntryMetadata EntryCache::AddNew(const KeyDescriptor& descriptor,
Wyatt Hepler883e7402020-04-06 09:31:18 -0700123 Address entry_address) const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700124 // TODO(hepler): DCHECK(!full());
125 Address* first_address = ResetAddresses(descriptors_.size(), entry_address);
126 descriptors_.push_back(descriptor);
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700127 return EntryMetadata(descriptors_.back(), std::span(first_address, 1));
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700128}
129
130// TODO: This method is the trigger of the O(valid_entries * all_entries) time
131// complexity for reading. At some cost to memory, this could be optimized by
132// using a hash table instead of scanning, but in practice this should be fine
133// for a small number of keys
134Status EntryCache::AddNewOrUpdateExisting(const KeyDescriptor& descriptor,
135 Address address,
Wyatt Hepler883e7402020-04-06 09:31:18 -0700136 size_t sector_size_bytes) const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700137 // With the new key descriptor, either add it to the descriptor table or
138 // overwrite an existing entry with an older version of the key.
139 const int index = FindIndex(descriptor.key_hash);
140
141 // Write a new entry if there is room.
142 if (index == -1) {
143 if (full()) {
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700144 return Status::ResourceExhausted();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700145 }
146 AddNew(descriptor, address);
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700147 return Status::Ok();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700148 }
149
150 // Existing entry is old; replace the existing entry with the new one.
151 if (descriptor.transaction_id > descriptors_[index].transaction_id) {
152 descriptors_[index] = descriptor;
153 ResetAddresses(index, address);
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700154 return Status::Ok();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700155 }
156
157 // If the entries have a duplicate transaction ID, add the new (redundant)
158 // entry to the existing descriptor.
159 if (descriptors_[index].transaction_id == descriptor.transaction_id) {
160 if (descriptors_[index].key_hash != descriptor.key_hash) {
161 PW_LOG_ERROR("Duplicate entry for key 0x%08" PRIx32
162 " with transaction ID %" PRIu32 " has non-matching hash",
163 descriptor.key_hash,
164 descriptor.transaction_id);
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700165 return Status::DataLoss();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700166 }
167
168 // Verify that this entry is not in the same sector as an existing copy of
169 // this same key.
170 for (Address existing_address : addresses(index)) {
171 if (existing_address / sector_size_bytes == address / sector_size_bytes) {
Wyatt Hepler09b3a2a2020-03-25 13:18:37 -0700172 PW_LOG_DEBUG("Multiple Redundant entries in same sector %u",
173 unsigned(address / sector_size_bytes));
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700174 return Status::DataLoss();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700175 }
176 }
177
178 AddAddressIfRoom(index, address);
179 } else {
180 PW_LOG_DEBUG("Found stale entry when appending; ignoring");
181 }
Wyatt Heplerd78f7c62020-09-28 14:27:32 -0700182 return Status::Ok();
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700183}
184
185size_t EntryCache::present_entries() const {
186 size_t present_entries = 0;
187
188 for (const KeyDescriptor& descriptor : descriptors_) {
189 if (descriptor.state != EntryState::kDeleted) {
190 present_entries += 1;
191 }
192 }
193
194 return present_entries;
195}
196
197int EntryCache::FindIndex(uint32_t key_hash) const {
198 for (size_t i = 0; i < descriptors_.size(); ++i) {
199 if (descriptors_[i].key_hash == key_hash) {
200 return i;
201 }
202 }
203 return -1;
204}
205
Wyatt Hepler883e7402020-04-06 09:31:18 -0700206void EntryCache::AddAddressIfRoom(size_t descriptor_index,
207 Address address) const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700208 Address* const existing = first_address(descriptor_index);
209
210 for (size_t i = 0; i < redundancy(); ++i) {
211 if (existing[i] == kNoAddress) {
212 existing[i] = address;
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700213 return;
214 }
215 }
216}
217
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700218std::span<EntryCache::Address> EntryCache::addresses(
219 size_t descriptor_index) const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700220 Address* const addresses = first_address(descriptor_index);
221
222 size_t size = 0;
223 while (size < redundancy() && addresses[size] != kNoAddress) {
224 size += 1;
225 }
226
Wyatt Heplere2cbadf2020-06-22 11:21:45 -0700227 return std::span(addresses, size);
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700228}
229
230EntryCache::Address* EntryCache::ResetAddresses(size_t descriptor_index,
Wyatt Hepler883e7402020-04-06 09:31:18 -0700231 Address address) const {
Wyatt Hepler7ded6da2020-03-11 18:24:43 -0700232 Address* first = first_address(descriptor_index);
233 *first = address;
234
235 // Clear the additional addresses, if any.
236 for (size_t i = 1; i < redundancy_; ++i) {
237 first[i] = kNoAddress;
238 }
239
240 return first;
241}
242
243} // namespace pw::kvs::internal