Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 18c6248f authored by Yifan Hong's avatar Yifan Hong Committed by Gerrit Code Review
Browse files

Merge changes from topic "wait_for_merge"

* changes:
  libsnapshot: add WaitForMerge
  libsnapshot: SnapshotUpdateTest::AddOperation
  libsnapshot: add GetCurrentSlot
parents bc58945c 1af515b5
Loading
Loading
Loading
Loading
+11 −0
Original line number Original line Diff line number Diff line
@@ -198,6 +198,13 @@ class SnapshotManager final {
    //   - other states indicating an error has occurred
    //   - other states indicating an error has occurred
    UpdateState InitiateMergeAndWait();
    UpdateState InitiateMergeAndWait();


    // Wait for the merge if rebooted into the new slot. Does NOT initiate a
    // merge. If the merge has not been initiated (but should be), wait.
    // Returns:
    //   - true there is no merge or merge finishes
    //   - false indicating an error has occurred
    bool WaitForMerge();

    // Find the status of the current update, if any.
    // Find the status of the current update, if any.
    //
    //
    // |progress| depends on the returned status:
    // |progress| depends on the returned status:
@@ -496,6 +503,10 @@ class SnapshotManager final {
    // as a sanity check.
    // as a sanity check.
    bool EnsureNoOverflowSnapshot(LockedFile* lock);
    bool EnsureNoOverflowSnapshot(LockedFile* lock);


    enum class Slot { Unknown, Source, Target };
    friend std::ostream& operator<<(std::ostream& os, SnapshotManager::Slot slot);
    Slot GetCurrentSlot();

    std::string gsid_dir_;
    std::string gsid_dir_;
    std::string metadata_dir_;
    std::string metadata_dir_;
    std::unique_ptr<IDeviceInfo> device_;
    std::unique_ptr<IDeviceInfo> device_;
+58 −41
Original line number Original line Diff line number Diff line
@@ -74,6 +74,7 @@ using namespace std::chrono_literals;
using namespace std::string_literals;
using namespace std::string_literals;


static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
static constexpr char kBootIndicatorPath[] = "/metadata/ota/snapshot-boot";
static constexpr auto kUpdateStateCheckInterval = 2s;


// Note: IImageManager is an incomplete type in the header, so the default
// Note: IImageManager is an incomplete type in the header, so the default
// destructor doesn't work.
// destructor doesn't work.
@@ -171,19 +172,27 @@ bool SnapshotManager::TryCancelUpdate(bool* needs_merge) {


    if (state == UpdateState::Unverified) {
    if (state == UpdateState::Unverified) {
        // We completed an update, but it can still be canceled if we haven't booted into it.
        // We completed an update, but it can still be canceled if we haven't booted into it.
        auto slot = GetCurrentSlot();
        if (slot != Slot::Target) {
            LOG(INFO) << "Canceling previously completed updates (if any)";
            return RemoveAllUpdateState(file.get());
        }
    }
    *needs_merge = true;
    return true;
}

SnapshotManager::Slot SnapshotManager::GetCurrentSlot() {
    auto boot_file = GetSnapshotBootIndicatorPath();
    auto boot_file = GetSnapshotBootIndicatorPath();
    std::string contents;
    std::string contents;
    if (!android::base::ReadFileToString(boot_file, &contents)) {
    if (!android::base::ReadFileToString(boot_file, &contents)) {
            PLOG(WARNING) << "Cannot read " << boot_file << ", proceed to canceling the update:";
        PLOG(WARNING) << "Cannot read " << boot_file;
            return RemoveAllUpdateState(file.get());
        return Slot::Unknown;
    }
    }
    if (device_->GetSlotSuffix() == contents) {
    if (device_->GetSlotSuffix() == contents) {
            LOG(INFO) << "Canceling a previously completed update";
        return Slot::Source;
            return RemoveAllUpdateState(file.get());
    }
    }
    }
    return Slot::Target;
    *needs_merge = true;
    return true;
}
}


bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
bool SnapshotManager::RemoveAllUpdateState(LockedFile* lock) {
@@ -505,15 +514,9 @@ bool SnapshotManager::InitiateMerge() {
        return false;
        return false;
    }
    }


    std::string old_slot;
    auto slot = GetCurrentSlot();
    auto boot_file = GetSnapshotBootIndicatorPath();
    if (slot != Slot::Target) {
    if (!android::base::ReadFileToString(boot_file, &old_slot)) {
        LOG(ERROR) << "Device cannot merge while not booting from new slot";
        LOG(ERROR) << "Could not determine the previous slot; aborting merge";
        return false;
    }
    auto new_slot = device_->GetSlotSuffix();
    if (new_slot == old_slot) {
        LOG(ERROR) << "Device cannot merge while booting off old slot " << old_slot;
        return false;
        return false;
    }
    }


@@ -729,7 +732,7 @@ UpdateState SnapshotManager::ProcessUpdateState(const std::function<void()>& cal


        // This wait is not super time sensitive, so we have a relatively
        // This wait is not super time sensitive, so we have a relatively
        // low polling frequency.
        // low polling frequency.
        std::this_thread::sleep_for(2s);
        std::this_thread::sleep_for(kUpdateStateCheckInterval);
    }
    }
}
}


@@ -1097,13 +1100,11 @@ bool SnapshotManager::CollapseSnapshotDevice(const std::string& name,
}
}


bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) {
bool SnapshotManager::HandleCancelledUpdate(LockedFile* lock) {
    std::string old_slot;
    auto slot = GetCurrentSlot();
    auto boot_file = GetSnapshotBootIndicatorPath();
    if (slot == Slot::Unknown) {
    if (!android::base::ReadFileToString(boot_file, &old_slot)) {
        PLOG(ERROR) << "Unable to read the snapshot indicator file: " << boot_file;
        return false;
        return false;
    }
    }
    if (device_->GetSlotSuffix() != old_slot) {
    if (slot == Slot::Target) {
        // We're booted into the target slot, which means we just rebooted
        // We're booted into the target slot, which means we just rebooted
        // after applying the update.
        // after applying the update.
        if (!HandleCancelledUpdateOnNewSlot(lock)) {
        if (!HandleCancelledUpdateOnNewSlot(lock)) {
@@ -1271,14 +1272,9 @@ bool SnapshotManager::NeedSnapshotsInFirstStageMount() {
    // ultimately we'll fail to boot. Why not make it a fatal error and have
    // ultimately we'll fail to boot. Why not make it a fatal error and have
    // the reason be clearer? Because the indicator file still exists, and
    // the reason be clearer? Because the indicator file still exists, and
    // if this was FATAL, reverting to the old slot would be broken.
    // if this was FATAL, reverting to the old slot would be broken.
    std::string old_slot;
    auto slot = GetCurrentSlot();
    auto boot_file = GetSnapshotBootIndicatorPath();
    if (slot != Slot::Target) {
    if (!android::base::ReadFileToString(boot_file, &old_slot)) {
        LOG(INFO) << "Not booting from new slot. Will not mount snapshots.";
        PLOG(ERROR) << "Unable to read the snapshot indicator file: " << boot_file;
        return false;
    }
    if (device_->GetSlotSuffix() == old_slot) {
        LOG(INFO) << "Detected slot rollback, will not mount snapshots.";
        return false;
        return false;
    }
    }


@@ -2156,6 +2152,17 @@ bool SnapshotManager::UnmapAllPartitions() {
    return ok;
    return ok;
}
}


std::ostream& operator<<(std::ostream& os, SnapshotManager::Slot slot) {
    switch (slot) {
        case SnapshotManager::Slot::Unknown:
            return os << "unknown";
        case SnapshotManager::Slot::Source:
            return os << "source";
        case SnapshotManager::Slot::Target:
            return os << "target";
    }
}

bool SnapshotManager::Dump(std::ostream& os) {
bool SnapshotManager::Dump(std::ostream& os) {
    // Don't actually lock. Dump() is for debugging purposes only, so it is okay
    // Don't actually lock. Dump() is for debugging purposes only, so it is okay
    // if it is racy.
    // if it is racy.
@@ -2166,11 +2173,8 @@ bool SnapshotManager::Dump(std::ostream& os) {


    ss << "Update state: " << ReadUpdateState(file.get()) << std::endl;
    ss << "Update state: " << ReadUpdateState(file.get()) << std::endl;


    auto boot_file = GetSnapshotBootIndicatorPath();
    ss << "Current slot: " << device_->GetSlotSuffix() << std::endl;
    std::string boot_indicator;
    ss << "Boot indicator: booting from " << GetCurrentSlot() << " slot" << std::endl;
    if (android::base::ReadFileToString(boot_file, &boot_indicator)) {
        ss << "Boot indicator: old slot = " << boot_indicator << std::endl;
    }


    bool ok = true;
    bool ok = true;
    std::vector<std::string> snapshots;
    std::vector<std::string> snapshots;
@@ -2238,6 +2242,21 @@ UpdateState SnapshotManager::InitiateMergeAndWait() {
    return state;
    return state;
}
}


bool SnapshotManager::WaitForMerge() {
    LOG(INFO) << "Waiting for any previous merge request to complete. "
              << "This can take up to several minutes.";
    while (true) {
        auto state = ProcessUpdateState();
        if (state == UpdateState::Unverified && GetCurrentSlot() == Slot::Target) {
            LOG(INFO) << "Wait for merge to be initiated.";
            std::this_thread::sleep_for(kUpdateStateCheckInterval);
            continue;
        }
        LOG(INFO) << "Wait for merge exits with state " << state;
        return state == UpdateState::None || state == UpdateState::MergeCompleted;
    }
}

bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) {
bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callback) {
    if (!device_->IsRecovery()) {
    if (!device_->IsRecovery()) {
        LOG(ERROR) << "Data wipes are only allowed in recovery.";
        LOG(ERROR) << "Data wipes are only allowed in recovery.";
@@ -2283,11 +2302,9 @@ bool SnapshotManager::HandleImminentDataWipe(const std::function<void()>& callba
            //
            //
            // Since the rollback is inevitable, we don't treat a HAL failure
            // Since the rollback is inevitable, we don't treat a HAL failure
            // as an error here.
            // as an error here.
            std::string old_slot;
            auto slot = GetCurrentSlot();
            auto boot_file = GetSnapshotBootIndicatorPath();
            if (slot == Slot::Target) {
            if (android::base::ReadFileToString(boot_file, &old_slot) &&
                LOG(ERROR) << "Reverting to old slot since update will be deleted.";
                device_->GetSlotSuffix() != old_slot) {
                LOG(ERROR) << "Reverting to slot " << old_slot << " since update will be deleted.";
                device_->SetSlotAsUnbootable(slot_number);
                device_->SetSlotAsUnbootable(slot_number);
            }
            }
            break;
            break;
+64 −25
Original line number Original line Diff line number Diff line
@@ -921,6 +921,25 @@ class SnapshotUpdateTest : public SnapshotTest {
        return AssertionSuccess();
        return AssertionSuccess();
    }
    }


    // Create fake install operations to grow the COW device size.
    void AddOperation(PartitionUpdate* partition_update, uint64_t size_bytes = 0) {
        auto e = partition_update->add_operations()->add_dst_extents();
        e->set_start_block(0);
        if (size_bytes == 0) {
            size_bytes = GetSize(partition_update);
        }
        e->set_num_blocks(size_bytes / manifest_.block_size());
    }

    void AddOperationForPartitions(std::vector<PartitionUpdate*> partitions = {}) {
        if (partitions.empty()) {
            partitions = {sys_, vnd_, prd_};
        }
        for (auto* partition : partitions) {
            AddOperation(partition);
        }
    }

    std::unique_ptr<TestPartitionOpener> opener_;
    std::unique_ptr<TestPartitionOpener> opener_;
    DeltaArchiveManifest manifest_;
    DeltaArchiveManifest manifest_;
    std::unique_ptr<MetadataBuilder> src_;
    std::unique_ptr<MetadataBuilder> src_;
@@ -948,12 +967,7 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
    SetSize(vnd_, partition_size);
    SetSize(vnd_, partition_size);
    SetSize(prd_, partition_size);
    SetSize(prd_, partition_size);


    // Create fake install operations to grow the COW device size.
    AddOperationForPartitions();
    for (auto& partition : {sys_, vnd_, prd_}) {
        auto e = partition->add_operations()->add_dst_extents();
        e->set_start_block(0);
        e->set_num_blocks(GetSize(partition) / manifest_.block_size());
    }


    // Execute the update.
    // Execute the update.
    ASSERT_TRUE(sm->BeginUpdate());
    ASSERT_TRUE(sm->BeginUpdate());
@@ -1089,12 +1103,7 @@ TEST_F(SnapshotUpdateTest, TestRollback) {
    ASSERT_TRUE(sm->BeginUpdate());
    ASSERT_TRUE(sm->BeginUpdate());
    ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));
    ASSERT_TRUE(sm->UnmapUpdateSnapshot("sys_b"));


    // Create fake install operations to grow the COW device size.
    AddOperationForPartitions();
    for (auto& partition : {sys_, vnd_, prd_}) {
        auto e = partition->add_operations()->add_dst_extents();
        e->set_start_block(0);
        e->set_num_blocks(GetSize(partition) / manifest_.block_size());
    }


    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));


@@ -1239,10 +1248,8 @@ TEST_F(SnapshotUpdateTest, RetrofitAfterRegularAb) {
    group_->set_size(kRetrofitGroupSize);
    group_->set_size(kRetrofitGroupSize);
    for (auto* partition : {sys_, vnd_, prd_}) {
    for (auto* partition : {sys_, vnd_, prd_}) {
        SetSize(partition, 2_MiB);
        SetSize(partition, 2_MiB);
        auto* e = partition->add_operations()->add_dst_extents();
        e->set_start_block(0);
        e->set_num_blocks(2_MiB / manifest_.block_size());
    }
    }
    AddOperationForPartitions();


    ASSERT_TRUE(sm->BeginUpdate());
    ASSERT_TRUE(sm->BeginUpdate());
    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
@@ -1285,9 +1292,7 @@ TEST_F(SnapshotUpdateTest, MergeCannotRemoveCow) {
    }
    }


    // Add operations for sys. The whole device is written.
    // Add operations for sys. The whole device is written.
    auto e = sys_->add_operations()->add_dst_extents();
    AddOperation(sys_);
    e->set_start_block(0);
    e->set_num_blocks(GetSize(sys_) / manifest_.block_size());


    // Execute the update.
    // Execute the update.
    ASSERT_TRUE(sm->BeginUpdate());
    ASSERT_TRUE(sm->BeginUpdate());
@@ -1477,10 +1482,7 @@ TEST_F(SnapshotUpdateTest, Hashtree) {


    const auto block_size = manifest_.block_size();
    const auto block_size = manifest_.block_size();
    SetSize(sys_, partition_size);
    SetSize(sys_, partition_size);

    AddOperation(sys_, data_size);
    auto e = sys_->add_operations()->add_dst_extents();
    e->set_start_block(0);
    e->set_num_blocks(data_size / block_size);


    // Set hastree extents.
    // Set hastree extents.
    sys_->mutable_hash_tree_data_extent()->set_start_block(0);
    sys_->mutable_hash_tree_data_extent()->set_start_block(0);
@@ -1525,9 +1527,7 @@ TEST_F(SnapshotUpdateTest, Overflow) {
    const auto actual_write_size = GetSize(sys_);
    const auto actual_write_size = GetSize(sys_);
    const auto declared_write_size = actual_write_size - 1_MiB;
    const auto declared_write_size = actual_write_size - 1_MiB;


    auto e = sys_->add_operations()->add_dst_extents();
    AddOperation(sys_, declared_write_size);
    e->set_start_block(0);
    e->set_num_blocks(declared_write_size / manifest_.block_size());


    // Execute the update.
    // Execute the update.
    ASSERT_TRUE(sm->BeginUpdate());
    ASSERT_TRUE(sm->BeginUpdate());
@@ -1546,6 +1546,45 @@ TEST_F(SnapshotUpdateTest, Overflow) {
            << "FinishedSnapshotWrites should detect overflow of CoW device.";
            << "FinishedSnapshotWrites should detect overflow of CoW device.";
}
}


TEST_F(SnapshotUpdateTest, WaitForMerge) {
    AddOperationForPartitions();

    // Execute the update.
    ASSERT_TRUE(sm->BeginUpdate());
    ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));

    // Write some data to target partitions.
    for (const auto& name : {"sys_b", "vnd_b", "prd_b"}) {
        ASSERT_TRUE(WriteSnapshotAndHash(name));
    }

    ASSERT_TRUE(sm->FinishedSnapshotWrites());

    // Simulate shutting down the device.
    ASSERT_TRUE(UnmapAll());

    // After reboot, init does first stage mount.
    {
        auto init = SnapshotManager::NewForFirstStageMount(new TestDeviceInfo(fake_super, "_b"));
        ASSERT_NE(nullptr, init);
        ASSERT_TRUE(init->CreateLogicalAndSnapshotPartitions("super"));
    }

    auto new_sm = SnapshotManager::New(new TestDeviceInfo(fake_super, "_b"));
    ASSERT_NE(nullptr, new_sm);

    auto waiter = std::async(std::launch::async, [&new_sm] { return new_sm->WaitForMerge(); });
    ASSERT_EQ(std::future_status::timeout, waiter.wait_for(1s))
            << "WaitForMerge should block when not initiated";

    auto merger =
            std::async(std::launch::async, [&new_sm] { return new_sm->InitiateMergeAndWait(); });
    // Small images, so should be merged pretty quickly.
    ASSERT_EQ(std::future_status::ready, waiter.wait_for(3s)) << "WaitForMerge did not finish";
    ASSERT_TRUE(waiter.get());
    ASSERT_THAT(merger.get(), AnyOf(UpdateState::None, UpdateState::MergeCompleted));
}

class FlashAfterUpdateTest : public SnapshotUpdateTest,
class FlashAfterUpdateTest : public SnapshotUpdateTest,
                             public WithParamInterface<std::tuple<uint32_t, bool>> {
                             public WithParamInterface<std::tuple<uint32_t, bool>> {
  public:
  public: