pw_kvs: Add free space accounting for write errors
Add accounting of byte written to flash when write operation has an error.
Change-Id: I4268cf36de16648fa10117fb6a828327685d4417
diff --git a/pw_kvs/alignment.cc b/pw_kvs/alignment.cc
index 90418e6..cba0e40 100644
--- a/pw_kvs/alignment.cc
+++ b/pw_kvs/alignment.cc
@@ -16,7 +16,7 @@
namespace pw {
-Status AlignedWriter::Write(span<const std::byte> data) {
+StatusWithSize AlignedWriter::Write(span<const std::byte> data) {
while (!data.empty()) {
size_t to_copy = std::min(write_size_ - bytes_in_buffer_, data.size());
@@ -26,16 +26,20 @@
// If the buffer is full, write it out.
if (bytes_in_buffer_ == write_size_) {
- if (auto result = output_.Write(buffer_, write_size_); !result.ok()) {
- return result.status();
+ StatusWithSize result = output_.Write(buffer_, write_size_);
+
+ // Always use write_size_ for the bytes written. If there was an error
+ // assume the space was written or at least disturbed.
+ bytes_written_ += write_size_;
+ if (!result.ok()) {
+ return StatusWithSize(result.status(), bytes_written_);
}
- bytes_written_ += write_size_;
bytes_in_buffer_ = 0;
}
}
- return Status::OK;
+ return StatusWithSize(bytes_written_);
}
StatusWithSize AlignedWriter::Flush() {
diff --git a/pw_kvs/alignment_test.cc b/pw_kvs/alignment_test.cc
index d5bf620..542428d 100644
--- a/pw_kvs/alignment_test.cc
+++ b/pw_kvs/alignment_test.cc
@@ -138,23 +138,23 @@
AlignedWriterBuffer<32> writer(kAlignment, output);
// Write values smaller than the alignment.
- EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(0, 1)));
- EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(1, 9)));
+ EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(0, 1)).status());
+ EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(1, 9)).status());
// Write values larger than the alignment but smaller than the buffer.
- EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(10, 11)));
+ EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(10, 11)).status());
// Exactly fill the remainder of the buffer.
- EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(21, 11)));
+ EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(21, 11)).status());
// Fill the buffer more than once.
- EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(32, 66)));
+ EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(32, 66)).status());
// Write nothing.
- EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(98, 0)));
+ EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(98, 0)).status());
// Write the remaining data.
- EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(98, 2)));
+ EXPECT_EQ(Status::OK, writer.Write(kBytes.subspan(98, 2)).status());
auto result = writer.Flush();
EXPECT_EQ(Status::OK, result.status());
diff --git a/pw_kvs/key_value_store.cc b/pw_kvs/key_value_store.cc
index 934b296..4268ede 100644
--- a/pw_kvs/key_value_store.cc
+++ b/pw_kvs/key_value_store.cc
@@ -708,14 +708,27 @@
entry.transaction_id(),
size_t(address));
- TRY_ASSIGN(const size_t written, entry.Write(key, value));
+ StatusWithSize result = entry.Write(key, value);
+ // Remove any bytes that were written, even if the write was not successful.
+ sector->RemoveWritableBytes(result.size());
+
+ if (!result.ok()) {
+ // TODO: add testing coverage for this once flash errors are supported in
+ // tests.
+ ERR("Failed to write %zu bytes. %zu actually written",
+ entry.size(),
+ result.size());
+ return result.status();
+ }
if (options_.verify_on_write) {
TRY(entry.VerifyChecksumInFlash(entry_header_format_.checksum));
}
+ // Entry was written successfully, update the key descriptor and the sector
+ // descriptor to reflect the new entry.
entry.UpdateDescriptor(key_descriptor);
- sector->MarkValidBytesWritten(written);
+ sector->AddValidBytes(result.size());
return Status::OK;
}
diff --git a/pw_kvs/public/pw_kvs/alignment.h b/pw_kvs/public/pw_kvs/alignment.h
index 2663846..08965ad 100644
--- a/pw_kvs/public/pw_kvs/alignment.h
+++ b/pw_kvs/public/pw_kvs/alignment.h
@@ -58,10 +58,11 @@
~AlignedWriter() { Flush(); }
// Writes bytes to the AlignedWriter. The output may be called if the internal
- // buffer is filled.
- Status Write(span<const std::byte> data);
+ // buffer is filled. The size in the return value represents the total number
+ // of bytes written since flush/reset.
+ StatusWithSize Write(span<const std::byte> data);
- Status Write(const void* data, size_t size) {
+ StatusWithSize Write(const void* data, size_t size) {
return Write(span(static_cast<const std::byte*>(data), size));
}
@@ -106,8 +107,8 @@
AlignedWriterBuffer<kBufferSize> buffer(alignment_bytes, output);
for (const span<const std::byte>& chunk : data) {
- if (Status status = buffer.Write(chunk); !status.ok()) {
- return StatusWithSize(status);
+ if (StatusWithSize status = buffer.Write(chunk); !status.ok()) {
+ return status;
}
}
diff --git a/pw_kvs/public/pw_kvs/internal/sector_descriptor.h b/pw_kvs/public/pw_kvs/internal/sector_descriptor.h
index 96f4be3..79b79b5 100644
--- a/pw_kvs/public/pw_kvs/internal/sector_descriptor.h
+++ b/pw_kvs/public/pw_kvs/internal/sector_descriptor.h
@@ -50,15 +50,13 @@
}
}
- // Adds valid bytes and removes writable bytes.
- void MarkValidBytesWritten(size_t written) {
- valid_bytes_ += written;
-
- if (written > tail_free_bytes_) {
+ // Removes writable bytes without updating the valid bytes.
+ void RemoveWritableBytes(uint16_t bytes) {
+ if (bytes > writable_bytes()) {
// TODO: use a DCHECK instead -- this is a programming error
tail_free_bytes_ = 0;
} else {
- tail_free_bytes_ -= written;
+ tail_free_bytes_ -= bytes;
}
}