Implement ZERO and DISCARD operations in update_engine.
ZERO and DISCARD operations are implemented using the specific ioctl()
that allow SSD to do a faster operation when writing zeros. In case of
failure, such as when they are not supported, the operation will fall
back to write the zeros directly.
Bug: 23180264
Test: Added a unittest.
Change-Id: I0f12ab9864620b50250157c1de17b7fc58c7ea1c
diff --git a/delta_performer.cc b/delta_performer.cc
index 4025b1f..96850df 100644
--- a/delta_performer.cc
+++ b/delta_performer.cc
@@ -18,6 +18,7 @@
#include <endian.h>
#include <errno.h>
+#include <linux/fs.h>
#include <algorithm>
#include <cstring>
@@ -650,6 +651,10 @@
case InstallOperation::REPLACE_XZ:
op_result = PerformReplaceOperation(op, is_kernel_partition);
break;
+ case InstallOperation::ZERO:
+ case InstallOperation::DISCARD:
+ op_result = PerformZeroOrDiscardOperation(op, is_kernel_partition);
+ break;
case InstallOperation::MOVE:
op_result = PerformMoveOperation(op, is_kernel_partition);
break;
@@ -739,6 +744,43 @@
return true;
}
+bool DeltaPerformer::PerformZeroOrDiscardOperation(
+ const InstallOperation& operation,
+ bool is_kernel_partition) {
+ CHECK(operation.type() == InstallOperation::DISCARD ||
+ operation.type() == InstallOperation::ZERO);
+
+ // These operations have no blob.
+ TEST_AND_RETURN_FALSE(!operation.has_data_offset());
+ TEST_AND_RETURN_FALSE(!operation.has_data_length());
+
+ int request =
+ (operation.type() == InstallOperation::ZERO ? BLKZEROOUT : BLKDISCARD);
+
+ FileDescriptorPtr fd = is_kernel_partition ? kernel_fd_ : fd_;
+ bool attempt_ioctl = true;
+ chromeos::Blob zeros;
+ for (int i = 0; i < operation.dst_extents_size(); i++) {
+ Extent extent = operation.dst_extents(i);
+ const uint64_t start = extent.start_block() * block_size_;
+ const uint64_t length = extent.num_blocks() * block_size_;
+ if (attempt_ioctl) {
+ int result = 0;
+ if (fd->BlkIoctl(request, start, length, &result) && result == 0)
+ continue;
+ attempt_ioctl = false;
+ zeros.resize(16 * block_size_);
+ }
+ // In case of failure, we fall back to writing 0 to the selected region.
+ for (uint64_t offset = 0; offset < length; offset += zeros.size()) {
+ uint64_t chunk_length = min(length - offset, zeros.size());
+ TEST_AND_RETURN_FALSE(
+ utils::PWriteAll(fd, zeros.data(), chunk_length, start + offset));
+ }
+ }
+ return true;
+}
+
bool DeltaPerformer::PerformMoveOperation(const InstallOperation& operation,
bool is_kernel_partition) {
// Calculate buffer size. Note, this function doesn't do a sliding