Handle resume of VABC updates by emitting labels
To support resuming an update with Virtual AB Compression, we emit
labels in between operations. After writing all SOURCE_COPY, we
emit label 0. Each time we finished writing an InstallOp, we emit
a label incremented by 1. When resuming, we pass the label to CowWriter.
Test: treehugger
1. update_device.py ota.zip
--extra-headers="SWITCH_SLOT_ON_REBOOT=0"
2. update_device.py ota.zip
3. Verify that 2 did not re-start the entire update,
only fs verification and postinstall may re-run.
Change-Id: I785cd04a35457181621ed7b8c0be9a46b6004b7b
diff --git a/payload_consumer/vabc_partition_writer.cc b/payload_consumer/vabc_partition_writer.cc
index a869509..5cb7989 100644
--- a/payload_consumer/vabc_partition_writer.cc
+++ b/payload_consumer/vabc_partition_writer.cc
@@ -31,8 +31,33 @@
#include "update_engine/payload_consumer/snapshot_extent_writer.h"
namespace chromeos_update_engine {
+// Expected layout of COW file:
+// === Beginning of Cow Image ===
+// All Source Copy Operations
+// ========== Label 0 ==========
+// Operation 0 in PartitionUpdate
+// ========== Label 1 ==========
+// Operation 1 in PartitionUpdate
+// ========== label 2 ==========
+// Operation 2 in PartitionUpdate
+// ========== label 3 ==========
+// .
+// .
+// .
+
+// When resuming, pass |next_op_index_| as label to
+// |InitializeWithAppend|.
+// For example, suppose we finished writing SOURCE_COPY, and we finished writing
+// operation 2 completely. Update is suspended when we are half way through
+// operation 3.
+// |cnext_op_index_| would be 3, so we pass 3 as
+// label to |InitializeWithAppend|. The CowWriter will retain all data before
+// label 3, Which contains all operation 2's data, but none of operation 3's
+// data.
+
bool VABCPartitionWriter::Init(const InstallPlan* install_plan,
- bool source_may_exist) {
+ bool source_may_exist,
+ size_t next_op_index) {
TEST_AND_RETURN_FALSE(install_plan != nullptr);
TEST_AND_RETURN_FALSE(
OpenSourcePartition(install_plan->source_slot, source_may_exist));
@@ -45,11 +70,19 @@
install_part_.name, source_path, install_plan->is_resume);
TEST_AND_RETURN_FALSE(cow_writer_ != nullptr);
- // TODO(zhangkelvin) Emit a label before writing SOURCE_COPY. When resuming,
- // use pref or CowWriter::GetLastLabel to determine if the SOURCE_COPY ops are
- // written. No need to handle SOURCE_COPY operations when resuming.
-
// ===== Resume case handling code goes here ====
+ // It is possible that the SOURCE_COPY are already written but
+ // |next_op_index_| is still 0. In this case we discard previously written
+ // SOURCE_COPY, and start over.
+ if (install_plan->is_resume && next_op_index > 0) {
+ LOG(INFO) << "Resuming update on partition `"
+ << partition_update_.partition_name() << "` op index "
+ << next_op_index;
+ TEST_AND_RETURN_FALSE(cow_writer_->InitializeAppend(next_op_index));
+ return true;
+ } else {
+ TEST_AND_RETURN_FALSE(cow_writer_->Initialize());
+ }
// ==============================================
@@ -90,6 +123,7 @@
break;
}
}
+
return true;
}
@@ -113,9 +147,10 @@
return true;
}
-bool VABCPartitionWriter::Flush() {
- // No need to do anything, as CowWriter automatically flushes every OP added.
- return true;
+void VABCPartitionWriter::CheckpointUpdateProgress(size_t next_op_index) {
+ // No need to call fsync/sync, as CowWriter flushes after a label is added
+ // added.
+ cow_writer_->AddLabel(next_op_index);
}
[[nodiscard]] bool VABCPartitionWriter::FinishedInstallOps() {