pw_kvs: Add garbage collection when fixing redundancy

- When adding redundancy make sure to garbage collect as part of finding
  a sector to write to.
- Move the KVS initialization check out of GarbageCollect.
- Do not repair during a sector GC to prevent infinite GC loops.

Change-Id: Ic16d32a30bbddfd89e2497a2ef0d5572a265189d
diff --git a/pw_kvs/key_value_store.cc b/pw_kvs/key_value_store.cc
index 1f3737e..539be83 100644
--- a/pw_kvs/key_value_store.cc
+++ b/pw_kvs/key_value_store.cc
@@ -83,6 +83,8 @@
   if (!error_detected_) {
     initialized_ = InitializationState::kReady;
   } else {
+    initialized_ = InitializationState::kNeedsMaintenance;
+
     if (options_.recovery != ErrorRecovery::kManual) {
       size_t pre_fix_redundancy_errors =
           error_stats_.missing_redundant_entries_recovered;
@@ -99,14 +101,11 @@
         initialized_ = InitializationState::kReady;
       } else if (recovery_status == Status::RESOURCE_EXHAUSTED) {
         WRN("KVS init: Unable to maintain required free sector");
-        initialized_ = InitializationState::kNeedsMaintenance;
       } else {
         WRN("KVS init: Corruption detected and unable repair");
-        initialized_ = InitializationState::kNeedsMaintenance;
       }
     } else {
       WRN("KVS init: Corruption detected, no repair attempted due to options");
-      initialized_ = InitializationState::kNeedsMaintenance;
     }
   }
 
@@ -874,20 +873,24 @@
   return Status::OK;
 }
 
-Status KeyValueStore::GarbageCollect(span<const Address> reserved_addresses) {
+Status KeyValueStore::PartialMaintenance() {
   if (initialized_ == InitializationState::kNotInitialized) {
     return Status::FAILED_PRECONDITION;
   }
 
-  DBG("Garbage Collect a single sector");
-  for (Address address : reserved_addresses) {
-    DBG("   Avoid address %u", unsigned(address));
-  }
-
+  CheckForErrors();
   // Do automatic repair, if KVS options allow for it.
   if (error_detected_ && options_.recovery != ErrorRecovery::kManual) {
     TRY(Repair());
   }
+  return GarbageCollect(span<const Address>());
+}
+
+Status KeyValueStore::GarbageCollect(span<const Address> reserved_addresses) {
+  DBG("Garbage Collect a single sector");
+  for (Address address : reserved_addresses) {
+    DBG("   Avoid address %u", unsigned(address));
+  }
 
   // Step 1: Find the sector to garbage collect
   SectorDescriptor* sector_to_gc =
@@ -993,17 +996,13 @@
 
 // Add any missing redundant entries/copies for a key.
 Status KeyValueStore::AddRedundantEntries(EntryMetadata& metadata) {
-  SectorDescriptor* new_sector;
-
   Entry entry;
-
   TRY(ReadEntry(metadata, entry));
   TRY(entry.VerifyChecksumInFlash());
 
-  for (size_t i = metadata.addresses().size();
-       metadata.addresses().size() < redundancy();
-       i++) {
-    TRY(sectors_.FindSpace(&new_sector, entry.size(), metadata.addresses()));
+  while (metadata.addresses().size() < redundancy()) {
+    SectorDescriptor* new_sector;
+    TRY(GetSectorForWrite(&new_sector, entry.size(), metadata.addresses()));
 
     Address new_address = sectors_.NextWritableAddress(*new_sector);
     TRY(CopyEntryToSector(entry, new_sector, new_address));