1*61c4878aSAndroid Build Coastguard Worker // Copyright 2020 The Pigweed Authors
2*61c4878aSAndroid Build Coastguard Worker //
3*61c4878aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License"); you may not
4*61c4878aSAndroid Build Coastguard Worker // use this file except in compliance with the License. You may obtain a copy of
5*61c4878aSAndroid Build Coastguard Worker // the License at
6*61c4878aSAndroid Build Coastguard Worker //
7*61c4878aSAndroid Build Coastguard Worker // https://www.apache.org/licenses/LICENSE-2.0
8*61c4878aSAndroid Build Coastguard Worker //
9*61c4878aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*61c4878aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11*61c4878aSAndroid Build Coastguard Worker // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12*61c4878aSAndroid Build Coastguard Worker // License for the specific language governing permissions and limitations under
13*61c4878aSAndroid Build Coastguard Worker // the License.
14*61c4878aSAndroid Build Coastguard Worker
15*61c4878aSAndroid Build Coastguard Worker #define PW_LOG_MODULE_NAME "KVS"
16*61c4878aSAndroid Build Coastguard Worker #define PW_LOG_LEVEL PW_KVS_LOG_LEVEL
17*61c4878aSAndroid Build Coastguard Worker
18*61c4878aSAndroid Build Coastguard Worker #include "pw_kvs/internal/entry_cache.h"
19*61c4878aSAndroid Build Coastguard Worker
20*61c4878aSAndroid Build Coastguard Worker #include <cinttypes>
21*61c4878aSAndroid Build Coastguard Worker
22*61c4878aSAndroid Build Coastguard Worker #include "pw_assert/check.h"
23*61c4878aSAndroid Build Coastguard Worker #include "pw_kvs/flash_memory.h"
24*61c4878aSAndroid Build Coastguard Worker #include "pw_kvs/internal/entry.h"
25*61c4878aSAndroid Build Coastguard Worker #include "pw_kvs/internal/hash.h"
26*61c4878aSAndroid Build Coastguard Worker #include "pw_kvs_private/config.h"
27*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
28*61c4878aSAndroid Build Coastguard Worker
29*61c4878aSAndroid Build Coastguard Worker namespace pw::kvs::internal {
30*61c4878aSAndroid Build Coastguard Worker namespace {
31*61c4878aSAndroid Build Coastguard Worker
32*61c4878aSAndroid Build Coastguard Worker constexpr FlashPartition::Address kNoAddress = FlashPartition::Address(-1);
33*61c4878aSAndroid Build Coastguard Worker
34*61c4878aSAndroid Build Coastguard Worker } // namespace
35*61c4878aSAndroid Build Coastguard Worker
RemoveAddress(Address address_to_remove)36*61c4878aSAndroid Build Coastguard Worker void EntryMetadata::RemoveAddress(Address address_to_remove) {
37*61c4878aSAndroid Build Coastguard Worker // Find the index of the address to remove.
38*61c4878aSAndroid Build Coastguard Worker for (Address& address : addresses_) {
39*61c4878aSAndroid Build Coastguard Worker if (address == address_to_remove) {
40*61c4878aSAndroid Build Coastguard Worker // Move the address at the back of the list to the slot of the address
41*61c4878aSAndroid Build Coastguard Worker // being removed. Do this unconditionally, even if the address to remove
42*61c4878aSAndroid Build Coastguard Worker // is the last slot since the logic still works.
43*61c4878aSAndroid Build Coastguard Worker address = addresses_.back();
44*61c4878aSAndroid Build Coastguard Worker
45*61c4878aSAndroid Build Coastguard Worker // Remove the back entry of the address list.
46*61c4878aSAndroid Build Coastguard Worker addresses_.back() = kNoAddress;
47*61c4878aSAndroid Build Coastguard Worker addresses_ = span(addresses_.begin(), addresses_.size() - 1);
48*61c4878aSAndroid Build Coastguard Worker break;
49*61c4878aSAndroid Build Coastguard Worker }
50*61c4878aSAndroid Build Coastguard Worker }
51*61c4878aSAndroid Build Coastguard Worker }
52*61c4878aSAndroid Build Coastguard Worker
Reset(const KeyDescriptor & descriptor,Address address)53*61c4878aSAndroid Build Coastguard Worker void EntryMetadata::Reset(const KeyDescriptor& descriptor, Address address) {
54*61c4878aSAndroid Build Coastguard Worker *descriptor_ = descriptor;
55*61c4878aSAndroid Build Coastguard Worker
56*61c4878aSAndroid Build Coastguard Worker addresses_[0] = address;
57*61c4878aSAndroid Build Coastguard Worker for (size_t i = 1; i < addresses_.size(); ++i) {
58*61c4878aSAndroid Build Coastguard Worker addresses_[i] = kNoAddress;
59*61c4878aSAndroid Build Coastguard Worker }
60*61c4878aSAndroid Build Coastguard Worker addresses_ = addresses_.first(1);
61*61c4878aSAndroid Build Coastguard Worker }
62*61c4878aSAndroid Build Coastguard Worker
Find(FlashPartition & partition,const Sectors & sectors,const EntryFormats & formats,std::string_view key,EntryMetadata * metadata) const63*61c4878aSAndroid Build Coastguard Worker StatusWithSize EntryCache::Find(FlashPartition& partition,
64*61c4878aSAndroid Build Coastguard Worker const Sectors& sectors,
65*61c4878aSAndroid Build Coastguard Worker const EntryFormats& formats,
66*61c4878aSAndroid Build Coastguard Worker std::string_view key,
67*61c4878aSAndroid Build Coastguard Worker EntryMetadata* metadata) const {
68*61c4878aSAndroid Build Coastguard Worker const uint32_t hash = internal::Hash(key);
69*61c4878aSAndroid Build Coastguard Worker Entry::KeyBuffer key_buffer;
70*61c4878aSAndroid Build Coastguard Worker bool error_detected = false;
71*61c4878aSAndroid Build Coastguard Worker
72*61c4878aSAndroid Build Coastguard Worker for (size_t i = 0; i < descriptors_.size(); ++i) {
73*61c4878aSAndroid Build Coastguard Worker if (descriptors_[i].key_hash == hash) {
74*61c4878aSAndroid Build Coastguard Worker bool key_found = false;
75*61c4878aSAndroid Build Coastguard Worker std::string_view read_key;
76*61c4878aSAndroid Build Coastguard Worker
77*61c4878aSAndroid Build Coastguard Worker for (Address address : addresses(i)) {
78*61c4878aSAndroid Build Coastguard Worker Status read_result =
79*61c4878aSAndroid Build Coastguard Worker Entry::ReadKey(partition, address, key.size(), key_buffer.data());
80*61c4878aSAndroid Build Coastguard Worker
81*61c4878aSAndroid Build Coastguard Worker read_key = std::string_view(key_buffer.data(), key.size());
82*61c4878aSAndroid Build Coastguard Worker
83*61c4878aSAndroid Build Coastguard Worker if (read_result.ok() && hash == internal::Hash(read_key)) {
84*61c4878aSAndroid Build Coastguard Worker key_found = true;
85*61c4878aSAndroid Build Coastguard Worker break;
86*61c4878aSAndroid Build Coastguard Worker } else {
87*61c4878aSAndroid Build Coastguard Worker // A hash mismatch can be caused by reading invalid data or a key hash
88*61c4878aSAndroid Build Coastguard Worker // collision of keys with differing size. To verify the data read from
89*61c4878aSAndroid Build Coastguard Worker // flash is good, validate the entry.
90*61c4878aSAndroid Build Coastguard Worker Entry entry;
91*61c4878aSAndroid Build Coastguard Worker read_result = Entry::Read(partition, address, formats, &entry);
92*61c4878aSAndroid Build Coastguard Worker if (read_result.ok() && entry.VerifyChecksumInFlash().ok()) {
93*61c4878aSAndroid Build Coastguard Worker key_found = true;
94*61c4878aSAndroid Build Coastguard Worker break;
95*61c4878aSAndroid Build Coastguard Worker }
96*61c4878aSAndroid Build Coastguard Worker
97*61c4878aSAndroid Build Coastguard Worker PW_LOG_WARN(
98*61c4878aSAndroid Build Coastguard Worker " Found corrupt entry, invalidating this copy of the key");
99*61c4878aSAndroid Build Coastguard Worker error_detected = true;
100*61c4878aSAndroid Build Coastguard Worker sectors.FromAddress(address).mark_corrupt();
101*61c4878aSAndroid Build Coastguard Worker }
102*61c4878aSAndroid Build Coastguard Worker }
103*61c4878aSAndroid Build Coastguard Worker size_t error_val = error_detected ? 1 : 0;
104*61c4878aSAndroid Build Coastguard Worker
105*61c4878aSAndroid Build Coastguard Worker if (!key_found) {
106*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("No valid entries for key. Data has been lost!");
107*61c4878aSAndroid Build Coastguard Worker return StatusWithSize::DataLoss(error_val);
108*61c4878aSAndroid Build Coastguard Worker } else if (key == read_key) {
109*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG("Found match for key hash 0x%08" PRIx32, hash);
110*61c4878aSAndroid Build Coastguard Worker *metadata = EntryMetadata(descriptors_[i], addresses(i));
111*61c4878aSAndroid Build Coastguard Worker return StatusWithSize(error_val);
112*61c4878aSAndroid Build Coastguard Worker } else {
113*61c4878aSAndroid Build Coastguard Worker PW_LOG_WARN("Found key hash collision for 0x%08" PRIx32, hash);
114*61c4878aSAndroid Build Coastguard Worker return StatusWithSize::AlreadyExists(error_val);
115*61c4878aSAndroid Build Coastguard Worker }
116*61c4878aSAndroid Build Coastguard Worker }
117*61c4878aSAndroid Build Coastguard Worker }
118*61c4878aSAndroid Build Coastguard Worker return StatusWithSize::NotFound();
119*61c4878aSAndroid Build Coastguard Worker }
120*61c4878aSAndroid Build Coastguard Worker
AddNew(const KeyDescriptor & descriptor,Address address) const121*61c4878aSAndroid Build Coastguard Worker EntryMetadata EntryCache::AddNew(const KeyDescriptor& descriptor,
122*61c4878aSAndroid Build Coastguard Worker Address address) const {
123*61c4878aSAndroid Build Coastguard Worker // TODO(hepler): DCHECK(!full());
124*61c4878aSAndroid Build Coastguard Worker Address* first_address = ResetAddresses(descriptors_.size(), address);
125*61c4878aSAndroid Build Coastguard Worker descriptors_.push_back(descriptor);
126*61c4878aSAndroid Build Coastguard Worker return EntryMetadata(descriptors_.back(), span(first_address, 1));
127*61c4878aSAndroid Build Coastguard Worker }
128*61c4878aSAndroid Build Coastguard Worker
129*61c4878aSAndroid Build Coastguard Worker // Removes an existing entry from the cache
RemoveEntry(iterator & entry_it)130*61c4878aSAndroid Build Coastguard Worker EntryCache::iterator EntryCache::RemoveEntry(iterator& entry_it) {
131*61c4878aSAndroid Build Coastguard Worker PW_DCHECK_PTR_EQ(entry_it.entry_cache_, this);
132*61c4878aSAndroid Build Coastguard Worker
133*61c4878aSAndroid Build Coastguard Worker const unsigned int index_to_remove =
134*61c4878aSAndroid Build Coastguard Worker entry_it.metadata_.descriptor_ - &descriptors_.front();
135*61c4878aSAndroid Build Coastguard Worker const KeyDescriptor last_desc = descriptors_[descriptors_.size() - 1];
136*61c4878aSAndroid Build Coastguard Worker
137*61c4878aSAndroid Build Coastguard Worker // Since order is not important, this copies the last descriptor into the
138*61c4878aSAndroid Build Coastguard Worker // deleted descriptor's space and then pops the last entry.
139*61c4878aSAndroid Build Coastguard Worker Address* addresses_at_end = first_address(descriptors_.size() - 1);
140*61c4878aSAndroid Build Coastguard Worker
141*61c4878aSAndroid Build Coastguard Worker if (index_to_remove < descriptors_.size() - 1) {
142*61c4878aSAndroid Build Coastguard Worker Address* addresses_to_remove = first_address(index_to_remove);
143*61c4878aSAndroid Build Coastguard Worker for (unsigned int i = 0; i < redundancy_; i++) {
144*61c4878aSAndroid Build Coastguard Worker addresses_to_remove[i] = addresses_at_end[i];
145*61c4878aSAndroid Build Coastguard Worker }
146*61c4878aSAndroid Build Coastguard Worker descriptors_[index_to_remove] = last_desc;
147*61c4878aSAndroid Build Coastguard Worker }
148*61c4878aSAndroid Build Coastguard Worker
149*61c4878aSAndroid Build Coastguard Worker // Erase the last entry since it was copied over the entry being deleted.
150*61c4878aSAndroid Build Coastguard Worker descriptors_.pop_back();
151*61c4878aSAndroid Build Coastguard Worker
152*61c4878aSAndroid Build Coastguard Worker return {this, descriptors_.data() + index_to_remove};
153*61c4878aSAndroid Build Coastguard Worker }
154*61c4878aSAndroid Build Coastguard Worker
155*61c4878aSAndroid Build Coastguard Worker // TODO(hepler): This method is the trigger of the O(valid_entries *
156*61c4878aSAndroid Build Coastguard Worker // all_entries) time complexity for reading. At some cost to memory, this could
157*61c4878aSAndroid Build Coastguard Worker // be optimized by using a hash table instead of scanning, but in practice this
158*61c4878aSAndroid Build Coastguard Worker // should be fine for a small number of keys
AddNewOrUpdateExisting(const KeyDescriptor & descriptor,Address address,size_t sector_size_bytes) const159*61c4878aSAndroid Build Coastguard Worker Status EntryCache::AddNewOrUpdateExisting(const KeyDescriptor& descriptor,
160*61c4878aSAndroid Build Coastguard Worker Address address,
161*61c4878aSAndroid Build Coastguard Worker size_t sector_size_bytes) const {
162*61c4878aSAndroid Build Coastguard Worker // With the new key descriptor, either add it to the descriptor table or
163*61c4878aSAndroid Build Coastguard Worker // overwrite an existing entry with an older version of the key.
164*61c4878aSAndroid Build Coastguard Worker const int index = FindIndex(descriptor.key_hash);
165*61c4878aSAndroid Build Coastguard Worker
166*61c4878aSAndroid Build Coastguard Worker // Write a new entry if there is room.
167*61c4878aSAndroid Build Coastguard Worker if (index == -1) {
168*61c4878aSAndroid Build Coastguard Worker if (full()) {
169*61c4878aSAndroid Build Coastguard Worker return Status::ResourceExhausted();
170*61c4878aSAndroid Build Coastguard Worker }
171*61c4878aSAndroid Build Coastguard Worker AddNew(descriptor, address);
172*61c4878aSAndroid Build Coastguard Worker return OkStatus();
173*61c4878aSAndroid Build Coastguard Worker }
174*61c4878aSAndroid Build Coastguard Worker
175*61c4878aSAndroid Build Coastguard Worker // Existing entry is old; replace the existing entry with the new one.
176*61c4878aSAndroid Build Coastguard Worker if (descriptor.transaction_id > descriptors_[index].transaction_id) {
177*61c4878aSAndroid Build Coastguard Worker descriptors_[index] = descriptor;
178*61c4878aSAndroid Build Coastguard Worker ResetAddresses(index, address);
179*61c4878aSAndroid Build Coastguard Worker return OkStatus();
180*61c4878aSAndroid Build Coastguard Worker }
181*61c4878aSAndroid Build Coastguard Worker
182*61c4878aSAndroid Build Coastguard Worker // If the entries have a duplicate transaction ID, add the new (redundant)
183*61c4878aSAndroid Build Coastguard Worker // entry to the existing descriptor.
184*61c4878aSAndroid Build Coastguard Worker if (descriptors_[index].transaction_id == descriptor.transaction_id) {
185*61c4878aSAndroid Build Coastguard Worker if (descriptors_[index].key_hash != descriptor.key_hash) {
186*61c4878aSAndroid Build Coastguard Worker PW_LOG_ERROR("Duplicate entry for key 0x%08" PRIx32
187*61c4878aSAndroid Build Coastguard Worker " with transaction ID %" PRIu32 " has non-matching hash",
188*61c4878aSAndroid Build Coastguard Worker descriptor.key_hash,
189*61c4878aSAndroid Build Coastguard Worker descriptor.transaction_id);
190*61c4878aSAndroid Build Coastguard Worker return Status::DataLoss();
191*61c4878aSAndroid Build Coastguard Worker }
192*61c4878aSAndroid Build Coastguard Worker
193*61c4878aSAndroid Build Coastguard Worker // Verify that this entry is not in the same sector as an existing copy of
194*61c4878aSAndroid Build Coastguard Worker // this same key.
195*61c4878aSAndroid Build Coastguard Worker for (Address existing_address : addresses(index)) {
196*61c4878aSAndroid Build Coastguard Worker if (existing_address / sector_size_bytes == address / sector_size_bytes) {
197*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG("Multiple Redundant entries in same sector %u",
198*61c4878aSAndroid Build Coastguard Worker unsigned(address / sector_size_bytes));
199*61c4878aSAndroid Build Coastguard Worker return Status::DataLoss();
200*61c4878aSAndroid Build Coastguard Worker }
201*61c4878aSAndroid Build Coastguard Worker }
202*61c4878aSAndroid Build Coastguard Worker
203*61c4878aSAndroid Build Coastguard Worker AddAddressIfRoom(index, address);
204*61c4878aSAndroid Build Coastguard Worker } else {
205*61c4878aSAndroid Build Coastguard Worker PW_LOG_DEBUG("Found stale entry when appending; ignoring");
206*61c4878aSAndroid Build Coastguard Worker }
207*61c4878aSAndroid Build Coastguard Worker return OkStatus();
208*61c4878aSAndroid Build Coastguard Worker }
209*61c4878aSAndroid Build Coastguard Worker
present_entries() const210*61c4878aSAndroid Build Coastguard Worker size_t EntryCache::present_entries() const {
211*61c4878aSAndroid Build Coastguard Worker size_t present_entries = 0;
212*61c4878aSAndroid Build Coastguard Worker
213*61c4878aSAndroid Build Coastguard Worker for (const KeyDescriptor& descriptor : descriptors_) {
214*61c4878aSAndroid Build Coastguard Worker if (descriptor.state != EntryState::kDeleted) {
215*61c4878aSAndroid Build Coastguard Worker present_entries += 1;
216*61c4878aSAndroid Build Coastguard Worker }
217*61c4878aSAndroid Build Coastguard Worker }
218*61c4878aSAndroid Build Coastguard Worker
219*61c4878aSAndroid Build Coastguard Worker return present_entries;
220*61c4878aSAndroid Build Coastguard Worker }
221*61c4878aSAndroid Build Coastguard Worker
FindIndex(uint32_t key_hash) const222*61c4878aSAndroid Build Coastguard Worker int EntryCache::FindIndex(uint32_t key_hash) const {
223*61c4878aSAndroid Build Coastguard Worker for (size_t i = 0; i < descriptors_.size(); ++i) {
224*61c4878aSAndroid Build Coastguard Worker if (descriptors_[i].key_hash == key_hash) {
225*61c4878aSAndroid Build Coastguard Worker return i;
226*61c4878aSAndroid Build Coastguard Worker }
227*61c4878aSAndroid Build Coastguard Worker }
228*61c4878aSAndroid Build Coastguard Worker return -1;
229*61c4878aSAndroid Build Coastguard Worker }
230*61c4878aSAndroid Build Coastguard Worker
AddAddressIfRoom(size_t descriptor_index,Address address) const231*61c4878aSAndroid Build Coastguard Worker void EntryCache::AddAddressIfRoom(size_t descriptor_index,
232*61c4878aSAndroid Build Coastguard Worker Address address) const {
233*61c4878aSAndroid Build Coastguard Worker Address* const existing = first_address(descriptor_index);
234*61c4878aSAndroid Build Coastguard Worker
235*61c4878aSAndroid Build Coastguard Worker for (size_t i = 0; i < redundancy(); ++i) {
236*61c4878aSAndroid Build Coastguard Worker if (existing[i] == kNoAddress) {
237*61c4878aSAndroid Build Coastguard Worker existing[i] = address;
238*61c4878aSAndroid Build Coastguard Worker return;
239*61c4878aSAndroid Build Coastguard Worker }
240*61c4878aSAndroid Build Coastguard Worker }
241*61c4878aSAndroid Build Coastguard Worker }
242*61c4878aSAndroid Build Coastguard Worker
addresses(size_t descriptor_index) const243*61c4878aSAndroid Build Coastguard Worker span<EntryCache::Address> EntryCache::addresses(size_t descriptor_index) const {
244*61c4878aSAndroid Build Coastguard Worker Address* const addresses = first_address(descriptor_index);
245*61c4878aSAndroid Build Coastguard Worker
246*61c4878aSAndroid Build Coastguard Worker size_t size = 0;
247*61c4878aSAndroid Build Coastguard Worker while (size < redundancy() && addresses[size] != kNoAddress) {
248*61c4878aSAndroid Build Coastguard Worker size += 1;
249*61c4878aSAndroid Build Coastguard Worker }
250*61c4878aSAndroid Build Coastguard Worker
251*61c4878aSAndroid Build Coastguard Worker return span(addresses, size);
252*61c4878aSAndroid Build Coastguard Worker }
253*61c4878aSAndroid Build Coastguard Worker
ResetAddresses(size_t descriptor_index,Address address) const254*61c4878aSAndroid Build Coastguard Worker EntryCache::Address* EntryCache::ResetAddresses(size_t descriptor_index,
255*61c4878aSAndroid Build Coastguard Worker Address address) const {
256*61c4878aSAndroid Build Coastguard Worker Address* first = first_address(descriptor_index);
257*61c4878aSAndroid Build Coastguard Worker *first = address;
258*61c4878aSAndroid Build Coastguard Worker
259*61c4878aSAndroid Build Coastguard Worker // Clear the additional addresses, if any.
260*61c4878aSAndroid Build Coastguard Worker for (size_t i = 1; i < redundancy_; ++i) {
261*61c4878aSAndroid Build Coastguard Worker first[i] = kNoAddress;
262*61c4878aSAndroid Build Coastguard Worker }
263*61c4878aSAndroid Build Coastguard Worker
264*61c4878aSAndroid Build Coastguard Worker return first;
265*61c4878aSAndroid Build Coastguard Worker }
266*61c4878aSAndroid Build Coastguard Worker
267*61c4878aSAndroid Build Coastguard Worker } // namespace pw::kvs::internal
268