xref: /aosp_15_r20/external/pigweed/pw_kvs/sectors.cc (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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/sectors.h"
19*61c4878aSAndroid Build Coastguard Worker 
20*61c4878aSAndroid Build Coastguard Worker #include "pw_kvs_private/config.h"
21*61c4878aSAndroid Build Coastguard Worker #include "pw_log/log.h"
22*61c4878aSAndroid Build Coastguard Worker 
23*61c4878aSAndroid Build Coastguard Worker namespace pw::kvs::internal {
24*61c4878aSAndroid Build Coastguard Worker namespace {
25*61c4878aSAndroid Build Coastguard Worker 
26*61c4878aSAndroid Build Coastguard Worker // Returns true if the container conatins the value.
27*61c4878aSAndroid Build Coastguard Worker // TODO(hepler): At some point move this to pw_containers, along with adding
28*61c4878aSAndroid Build Coastguard Worker // tests.
29*61c4878aSAndroid Build Coastguard Worker template <typename Container, typename T>
Contains(const Container & container,const T & value)30*61c4878aSAndroid Build Coastguard Worker bool Contains(const Container& container, const T& value) {
31*61c4878aSAndroid Build Coastguard Worker   return std::find(std::begin(container), std::end(container), value) !=
32*61c4878aSAndroid Build Coastguard Worker          std::end(container);
33*61c4878aSAndroid Build Coastguard Worker }
34*61c4878aSAndroid Build Coastguard Worker 
35*61c4878aSAndroid Build Coastguard Worker }  // namespace
36*61c4878aSAndroid Build Coastguard Worker 
Find(FindMode find_mode,SectorDescriptor ** found_sector,size_t size,span<const Address> addresses_to_skip,span<const Address> reserved_addresses)37*61c4878aSAndroid Build Coastguard Worker Status Sectors::Find(FindMode find_mode,
38*61c4878aSAndroid Build Coastguard Worker                      SectorDescriptor** found_sector,
39*61c4878aSAndroid Build Coastguard Worker                      size_t size,
40*61c4878aSAndroid Build Coastguard Worker                      span<const Address> addresses_to_skip,
41*61c4878aSAndroid Build Coastguard Worker                      span<const Address> reserved_addresses) {
42*61c4878aSAndroid Build Coastguard Worker   SectorDescriptor* first_empty_sector = nullptr;
43*61c4878aSAndroid Build Coastguard Worker   bool at_least_two_empty_sectors = (find_mode == kGarbageCollect);
44*61c4878aSAndroid Build Coastguard Worker 
45*61c4878aSAndroid Build Coastguard Worker   // Used for the GC reclaimable bytes check
46*61c4878aSAndroid Build Coastguard Worker   SectorDescriptor* non_empty_least_reclaimable_sector = nullptr;
47*61c4878aSAndroid Build Coastguard Worker   const size_t sector_size_bytes = partition_.sector_size_bytes();
48*61c4878aSAndroid Build Coastguard Worker 
49*61c4878aSAndroid Build Coastguard Worker   // Build a list of sectors to avoid.
50*61c4878aSAndroid Build Coastguard Worker   //
51*61c4878aSAndroid Build Coastguard Worker   // This is overly strict. reserved_addresses is populated when there are
52*61c4878aSAndroid Build Coastguard Worker   // sectors reserved for a new entry. It is safe to garbage collect into
53*61c4878aSAndroid Build Coastguard Worker   // these sectors, as long as there remains room for the pending entry. These
54*61c4878aSAndroid Build Coastguard Worker   // reserved sectors could also be garbage collected if they have recoverable
55*61c4878aSAndroid Build Coastguard Worker   // space. For simplicitly, avoid both the relocating key's redundant entries
56*61c4878aSAndroid Build Coastguard Worker   // (addresses_to_skip) and the sectors reserved for pending writes
57*61c4878aSAndroid Build Coastguard Worker   // (reserved_addresses).
58*61c4878aSAndroid Build Coastguard Worker   // TODO(hepler): Look into improving garbage collection.
59*61c4878aSAndroid Build Coastguard Worker   size_t sectors_to_skip = 0;
60*61c4878aSAndroid Build Coastguard Worker   for (Address address : addresses_to_skip) {
61*61c4878aSAndroid Build Coastguard Worker     temp_sectors_to_skip_[sectors_to_skip++] = &FromAddress(address);
62*61c4878aSAndroid Build Coastguard Worker   }
63*61c4878aSAndroid Build Coastguard Worker   for (Address address : reserved_addresses) {
64*61c4878aSAndroid Build Coastguard Worker     temp_sectors_to_skip_[sectors_to_skip++] = &FromAddress(address);
65*61c4878aSAndroid Build Coastguard Worker   }
66*61c4878aSAndroid Build Coastguard Worker 
67*61c4878aSAndroid Build Coastguard Worker   PW_LOG_DEBUG(
68*61c4878aSAndroid Build Coastguard Worker       "Find sector with %u bytes available, starting with sector %u, %s",
69*61c4878aSAndroid Build Coastguard Worker       unsigned(size),
70*61c4878aSAndroid Build Coastguard Worker       Index(last_new_),
71*61c4878aSAndroid Build Coastguard Worker       (find_mode == kAppendEntry) ? "Append" : "GC");
72*61c4878aSAndroid Build Coastguard Worker   for (size_t i = 0; i < sectors_to_skip; ++i) {
73*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG("  Skip sector %u", Index(temp_sectors_to_skip_[i]));
74*61c4878aSAndroid Build Coastguard Worker   }
75*61c4878aSAndroid Build Coastguard Worker 
76*61c4878aSAndroid Build Coastguard Worker   // last_new_ is the sector that was last selected as the "new empty sector" to
77*61c4878aSAndroid Build Coastguard Worker   // write to. This last new sector is used as the starting point for the next
78*61c4878aSAndroid Build Coastguard Worker   // "find a new empty sector to write to" operation. By using the last new
79*61c4878aSAndroid Build Coastguard Worker   // sector as the start point we will cycle which empty sector is selected
80*61c4878aSAndroid Build Coastguard Worker   // next, spreading the wear across all the empty sectors and get a wear
81*61c4878aSAndroid Build Coastguard Worker   // leveling benefit, rather than putting more wear on the lower number
82*61c4878aSAndroid Build Coastguard Worker   // sectors.
83*61c4878aSAndroid Build Coastguard Worker   SectorDescriptor* sector = last_new_;
84*61c4878aSAndroid Build Coastguard Worker 
85*61c4878aSAndroid Build Coastguard Worker   // Look for a sector to use with enough space. The search uses a 3 priority
86*61c4878aSAndroid Build Coastguard Worker   // tier process.
87*61c4878aSAndroid Build Coastguard Worker   //
88*61c4878aSAndroid Build Coastguard Worker   // Tier 1 is sector that already has valid data. During GC only select a
89*61c4878aSAndroid Build Coastguard Worker   // sector that has no reclaimable bytes. Immediately use the first matching
90*61c4878aSAndroid Build Coastguard Worker   // sector that is found.
91*61c4878aSAndroid Build Coastguard Worker   //
92*61c4878aSAndroid Build Coastguard Worker   // Tier 2 is find sectors that are empty/erased. While scanning for a partial
93*61c4878aSAndroid Build Coastguard Worker   // sector, keep track of the first empty sector and if a second empty sector
94*61c4878aSAndroid Build Coastguard Worker   // was seen. If during GC then count the second empty sector as always seen.
95*61c4878aSAndroid Build Coastguard Worker   //
96*61c4878aSAndroid Build Coastguard Worker   // Tier 3 is during garbage collection, find sectors with enough space that
97*61c4878aSAndroid Build Coastguard Worker   // are not empty but have recoverable bytes. Pick the sector with the least
98*61c4878aSAndroid Build Coastguard Worker   // recoverable bytes to minimize the likelyhood of this sector needing to be
99*61c4878aSAndroid Build Coastguard Worker   // garbage collected soon.
100*61c4878aSAndroid Build Coastguard Worker   for (size_t j = 0; j < descriptors_.size(); j++) {
101*61c4878aSAndroid Build Coastguard Worker     sector += 1;
102*61c4878aSAndroid Build Coastguard Worker     if (sector == descriptors_.end()) {
103*61c4878aSAndroid Build Coastguard Worker       sector = descriptors_.begin();
104*61c4878aSAndroid Build Coastguard Worker     }
105*61c4878aSAndroid Build Coastguard Worker 
106*61c4878aSAndroid Build Coastguard Worker     // Skip sectors in the skip list.
107*61c4878aSAndroid Build Coastguard Worker     if (Contains(span(temp_sectors_to_skip_, sectors_to_skip), sector)) {
108*61c4878aSAndroid Build Coastguard Worker       continue;
109*61c4878aSAndroid Build Coastguard Worker     }
110*61c4878aSAndroid Build Coastguard Worker 
111*61c4878aSAndroid Build Coastguard Worker     if (!sector->Empty(sector_size_bytes) && sector->HasSpace(size)) {
112*61c4878aSAndroid Build Coastguard Worker       if ((find_mode == kAppendEntry) ||
113*61c4878aSAndroid Build Coastguard Worker           (sector->RecoverableBytes(sector_size_bytes) == 0)) {
114*61c4878aSAndroid Build Coastguard Worker         *found_sector = sector;
115*61c4878aSAndroid Build Coastguard Worker         return OkStatus();
116*61c4878aSAndroid Build Coastguard Worker       } else {
117*61c4878aSAndroid Build Coastguard Worker         if ((non_empty_least_reclaimable_sector == nullptr) ||
118*61c4878aSAndroid Build Coastguard Worker             (non_empty_least_reclaimable_sector->RecoverableBytes(
119*61c4878aSAndroid Build Coastguard Worker                  sector_size_bytes) <
120*61c4878aSAndroid Build Coastguard Worker              sector->RecoverableBytes(sector_size_bytes))) {
121*61c4878aSAndroid Build Coastguard Worker           non_empty_least_reclaimable_sector = sector;
122*61c4878aSAndroid Build Coastguard Worker         }
123*61c4878aSAndroid Build Coastguard Worker       }
124*61c4878aSAndroid Build Coastguard Worker     }
125*61c4878aSAndroid Build Coastguard Worker 
126*61c4878aSAndroid Build Coastguard Worker     if (sector->Empty(sector_size_bytes)) {
127*61c4878aSAndroid Build Coastguard Worker       if (first_empty_sector == nullptr) {
128*61c4878aSAndroid Build Coastguard Worker         first_empty_sector = sector;
129*61c4878aSAndroid Build Coastguard Worker       } else {
130*61c4878aSAndroid Build Coastguard Worker         at_least_two_empty_sectors = true;
131*61c4878aSAndroid Build Coastguard Worker       }
132*61c4878aSAndroid Build Coastguard Worker     }
133*61c4878aSAndroid Build Coastguard Worker   }
134*61c4878aSAndroid Build Coastguard Worker 
135*61c4878aSAndroid Build Coastguard Worker   // Tier 2 check: If the scan for a partial sector does not find a suitable
136*61c4878aSAndroid Build Coastguard Worker   // sector, use the first empty sector that was found. Normally it is required
137*61c4878aSAndroid Build Coastguard Worker   // to keep 1 empty sector after the sector found here, but that rule does not
138*61c4878aSAndroid Build Coastguard Worker   // apply during GC.
139*61c4878aSAndroid Build Coastguard Worker   if (first_empty_sector != nullptr && at_least_two_empty_sectors) {
140*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG(
141*61c4878aSAndroid Build Coastguard Worker         "  Found a usable empty sector; returning the first found (%u)",
142*61c4878aSAndroid Build Coastguard Worker         Index(first_empty_sector));
143*61c4878aSAndroid Build Coastguard Worker     last_new_ = first_empty_sector;
144*61c4878aSAndroid Build Coastguard Worker     *found_sector = first_empty_sector;
145*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
146*61c4878aSAndroid Build Coastguard Worker   }
147*61c4878aSAndroid Build Coastguard Worker 
148*61c4878aSAndroid Build Coastguard Worker   // Tier 3 check: If we got this far, use the sector with least recoverable
149*61c4878aSAndroid Build Coastguard Worker   // bytes
150*61c4878aSAndroid Build Coastguard Worker   if (non_empty_least_reclaimable_sector != nullptr) {
151*61c4878aSAndroid Build Coastguard Worker     *found_sector = non_empty_least_reclaimable_sector;
152*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG(
153*61c4878aSAndroid Build Coastguard Worker         "  Found a usable sector %u, with %u B recoverable, in GC",
154*61c4878aSAndroid Build Coastguard Worker         Index(*found_sector),
155*61c4878aSAndroid Build Coastguard Worker         unsigned((*found_sector)->RecoverableBytes(sector_size_bytes)));
156*61c4878aSAndroid Build Coastguard Worker     return OkStatus();
157*61c4878aSAndroid Build Coastguard Worker   }
158*61c4878aSAndroid Build Coastguard Worker 
159*61c4878aSAndroid Build Coastguard Worker   // No sector was found.
160*61c4878aSAndroid Build Coastguard Worker   PW_LOG_DEBUG("  Unable to find a usable sector");
161*61c4878aSAndroid Build Coastguard Worker   *found_sector = nullptr;
162*61c4878aSAndroid Build Coastguard Worker   return Status::ResourceExhausted();
163*61c4878aSAndroid Build Coastguard Worker }
164*61c4878aSAndroid Build Coastguard Worker 
WearLeveledSectorFromIndex(size_t idx) const165*61c4878aSAndroid Build Coastguard Worker SectorDescriptor& Sectors::WearLeveledSectorFromIndex(size_t idx) const {
166*61c4878aSAndroid Build Coastguard Worker   return descriptors_[(Index(last_new_) + 1 + idx) % descriptors_.size()];
167*61c4878aSAndroid Build Coastguard Worker }
168*61c4878aSAndroid Build Coastguard Worker 
169*61c4878aSAndroid Build Coastguard Worker // TODO(hepler): Consider breaking this function into smaller sub-chunks.
FindSectorToGarbageCollect(span<const Address> reserved_addresses) const170*61c4878aSAndroid Build Coastguard Worker SectorDescriptor* Sectors::FindSectorToGarbageCollect(
171*61c4878aSAndroid Build Coastguard Worker     span<const Address> reserved_addresses) const {
172*61c4878aSAndroid Build Coastguard Worker   const size_t sector_size_bytes = partition_.sector_size_bytes();
173*61c4878aSAndroid Build Coastguard Worker   SectorDescriptor* sector_candidate = nullptr;
174*61c4878aSAndroid Build Coastguard Worker   size_t candidate_bytes = 0;
175*61c4878aSAndroid Build Coastguard Worker 
176*61c4878aSAndroid Build Coastguard Worker   // Build a vector of sectors to avoid.
177*61c4878aSAndroid Build Coastguard Worker   for (size_t i = 0; i < reserved_addresses.size(); ++i) {
178*61c4878aSAndroid Build Coastguard Worker     temp_sectors_to_skip_[i] = &FromAddress(reserved_addresses[i]);
179*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG("    Skip sector %u", Index(reserved_addresses[i]));
180*61c4878aSAndroid Build Coastguard Worker   }
181*61c4878aSAndroid Build Coastguard Worker   const span sectors_to_skip(temp_sectors_to_skip_, reserved_addresses.size());
182*61c4878aSAndroid Build Coastguard Worker 
183*61c4878aSAndroid Build Coastguard Worker   // Step 1: Try to find a sectors with stale keys and no valid keys (no
184*61c4878aSAndroid Build Coastguard Worker   // relocation needed). Use the first such sector found, as that will help the
185*61c4878aSAndroid Build Coastguard Worker   // KVS "rotate" around the partition. Initially this would select the sector
186*61c4878aSAndroid Build Coastguard Worker   // with the most reclaimable space, but that can cause GC sector selection to
187*61c4878aSAndroid Build Coastguard Worker   // "ping-pong" between two sectors when updating large keys.
188*61c4878aSAndroid Build Coastguard Worker   for (size_t i = 0; i < descriptors_.size(); ++i) {
189*61c4878aSAndroid Build Coastguard Worker     SectorDescriptor& sector = WearLeveledSectorFromIndex(i);
190*61c4878aSAndroid Build Coastguard Worker     if ((sector.valid_bytes() == 0) &&
191*61c4878aSAndroid Build Coastguard Worker         (sector.RecoverableBytes(sector_size_bytes) > 0) &&
192*61c4878aSAndroid Build Coastguard Worker         !Contains(sectors_to_skip, &sector)) {
193*61c4878aSAndroid Build Coastguard Worker       sector_candidate = &sector;
194*61c4878aSAndroid Build Coastguard Worker       break;
195*61c4878aSAndroid Build Coastguard Worker     }
196*61c4878aSAndroid Build Coastguard Worker   }
197*61c4878aSAndroid Build Coastguard Worker 
198*61c4878aSAndroid Build Coastguard Worker   // Step 2: If step 1 yields no sectors, just find the sector with the most
199*61c4878aSAndroid Build Coastguard Worker   // reclaimable bytes but no addresses to avoid.
200*61c4878aSAndroid Build Coastguard Worker   if (sector_candidate == nullptr) {
201*61c4878aSAndroid Build Coastguard Worker     for (size_t i = 0; i < descriptors_.size(); ++i) {
202*61c4878aSAndroid Build Coastguard Worker       SectorDescriptor& sector = WearLeveledSectorFromIndex(i);
203*61c4878aSAndroid Build Coastguard Worker       if ((sector.RecoverableBytes(sector_size_bytes) > candidate_bytes) &&
204*61c4878aSAndroid Build Coastguard Worker           !Contains(sectors_to_skip, &sector)) {
205*61c4878aSAndroid Build Coastguard Worker         sector_candidate = &sector;
206*61c4878aSAndroid Build Coastguard Worker         candidate_bytes = sector.RecoverableBytes(sector_size_bytes);
207*61c4878aSAndroid Build Coastguard Worker       }
208*61c4878aSAndroid Build Coastguard Worker     }
209*61c4878aSAndroid Build Coastguard Worker   }
210*61c4878aSAndroid Build Coastguard Worker 
211*61c4878aSAndroid Build Coastguard Worker   // Step 3: If no sectors with reclaimable bytes, select the sector with the
212*61c4878aSAndroid Build Coastguard Worker   // most free bytes. This at least will allow entries of existing keys to get
213*61c4878aSAndroid Build Coastguard Worker   // spread to other sectors, including sectors that already have copies of the
214*61c4878aSAndroid Build Coastguard Worker   // current key being written.
215*61c4878aSAndroid Build Coastguard Worker   if (sector_candidate == nullptr) {
216*61c4878aSAndroid Build Coastguard Worker     for (size_t i = 0; i < descriptors_.size(); ++i) {
217*61c4878aSAndroid Build Coastguard Worker       SectorDescriptor& sector = WearLeveledSectorFromIndex(i);
218*61c4878aSAndroid Build Coastguard Worker       if ((sector.valid_bytes() > candidate_bytes) &&
219*61c4878aSAndroid Build Coastguard Worker           !Contains(sectors_to_skip, &sector)) {
220*61c4878aSAndroid Build Coastguard Worker         sector_candidate = &sector;
221*61c4878aSAndroid Build Coastguard Worker         candidate_bytes = sector.valid_bytes();
222*61c4878aSAndroid Build Coastguard Worker         PW_LOG_DEBUG("    Doing GC on sector with no reclaimable bytes!");
223*61c4878aSAndroid Build Coastguard Worker       }
224*61c4878aSAndroid Build Coastguard Worker     }
225*61c4878aSAndroid Build Coastguard Worker   }
226*61c4878aSAndroid Build Coastguard Worker 
227*61c4878aSAndroid Build Coastguard Worker   if (sector_candidate != nullptr) {
228*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG(
229*61c4878aSAndroid Build Coastguard Worker         "Found sector %u to Garbage Collect, %u recoverable bytes",
230*61c4878aSAndroid Build Coastguard Worker         Index(sector_candidate),
231*61c4878aSAndroid Build Coastguard Worker         unsigned(sector_candidate->RecoverableBytes(sector_size_bytes)));
232*61c4878aSAndroid Build Coastguard Worker   } else {
233*61c4878aSAndroid Build Coastguard Worker     PW_LOG_DEBUG("Unable to find sector to garbage collect!");
234*61c4878aSAndroid Build Coastguard Worker   }
235*61c4878aSAndroid Build Coastguard Worker   return sector_candidate;
236*61c4878aSAndroid Build Coastguard Worker }
237*61c4878aSAndroid Build Coastguard Worker 
238*61c4878aSAndroid Build Coastguard Worker }  // namespace pw::kvs::internal
239