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

Commit eb7eb4be authored by David Anderson's avatar David Anderson
Browse files

libsnapshot: Use the COW size from the update manifest.

When Virtual A/B Compression is enabled, the manifest contains the
predicted COW size. Use this instead of the algorithm based on the
kernel COW format.

Bug: 168554689
Test: vts_libsnapshot_test
Change-Id: I545679b4834957ff80a930d91cb44afbadebb66c
parent 2eb7b922
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ cc_defaults {
    header_libs: [
        "libchrome",
        "libfiemap_headers",
        "libstorage_literals_headers",
        "libupdate_engine_headers",
    ],
    export_static_lib_headers: [
+19 −2
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@

#include <android-base/logging.h>
#include <android/snapshot/snapshot.pb.h>
#include <storage_literals/storage_literals.h>

#include "dm_snapshot_internals.h"
#include "utility.h"
@@ -34,6 +35,8 @@ using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
namespace android {
namespace snapshot {

using namespace android::storage_literals;

// Intersect two linear extents. If no intersection, return an extent with length 0.
static std::unique_ptr<Extent> Intersect(Extent* target_extent, Extent* existing_extent) {
    // Convert target_extent and existing_extent to linear extents. Zero extents
@@ -138,6 +141,17 @@ void WriteExtent(DmSnapCowSizeCalculator* sc, const chromeos_update_engine::Exte
}

uint64_t PartitionCowCreator::GetCowSize() {
    if (compression_enabled) {
        if (update == nullptr || !update->has_estimate_cow_size()) {
            LOG(ERROR) << "Update manifest does not include a COW size";
            return 0;
        }

        // Add an extra 2MB of wiggle room for any minor differences in labels/metadata
        // that might come up.
        return update->estimate_cow_size() + 2_MiB;
    }

    // WARNING: The origin partition should be READ-ONLY
    const uint64_t logical_block_size = current_metadata->logical_block_size();
    const unsigned int sectors_per_block = logical_block_size / kSectorSize;
@@ -149,9 +163,9 @@ uint64_t PartitionCowCreator::GetCowSize() {
        WriteExtent(&sc, de, sectors_per_block);
    }

    if (operations == nullptr) return sc.cow_size_bytes();
    if (update == nullptr) return sc.cow_size_bytes();

    for (const auto& iop : *operations) {
    for (const auto& iop : update->operations()) {
        const InstallOperation* written_op = &iop;
        InstallOperation buf;
        // Do not allocate space for extents that are going to be skipped
@@ -213,6 +227,9 @@ std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {

    LOG(INFO) << "Remaining free space for COW: " << free_region_length << " bytes";
    auto cow_size = GetCowSize();
    if (!cow_size) {
        return {};
    }

    // Compute the COW partition size.
    uint64_t cow_partition_size = std::min(cow_size, free_region_length);
+5 −2
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ struct PartitionCowCreator {
    using MetadataBuilder = android::fs_mgr::MetadataBuilder;
    using Partition = android::fs_mgr::Partition;
    using InstallOperation = chromeos_update_engine::InstallOperation;
    using PartitionUpdate = chromeos_update_engine::PartitionUpdate;
    template <typename T>
    using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;

@@ -50,11 +51,13 @@ struct PartitionCowCreator {
    MetadataBuilder* current_metadata = nullptr;
    // The suffix of the current slot.
    std::string current_suffix;
    // List of operations to be applied on the partition.
    const RepeatedPtrField<InstallOperation>* operations = nullptr;
    // Partition information from the OTA manifest.
    const PartitionUpdate* update = nullptr;
    // Extra extents that are going to be invalidated during the update
    // process.
    std::vector<ChromeOSExtent> extra_extents = {};
    // True if compression is enabled.
    bool compression_enabled = false;

    struct Return {
        SnapshotStatus snapshot_status;
+57 −3
Original line number Diff line number Diff line
@@ -145,13 +145,15 @@ TEST_F(PartitionCowCreatorTest, CowSize) {

    auto cow_device_size = [](const std::vector<InstallOperation>& iopv, MetadataBuilder* builder_a,
                              MetadataBuilder* builder_b, Partition* system_b) {
        RepeatedInstallOperationPtr riop(iopv.begin(), iopv.end());
        PartitionUpdate update;
        *update.mutable_operations() = RepeatedInstallOperationPtr(iopv.begin(), iopv.end());

        PartitionCowCreator creator{.target_metadata = builder_b,
                                    .target_suffix = "_b",
                                    .target_partition = system_b,
                                    .current_metadata = builder_a,
                                    .current_suffix = "_a",
                                    .operations = &riop};
                                    .update = &update};

        auto ret = creator.Run();

@@ -218,7 +220,7 @@ TEST_F(PartitionCowCreatorTest, Zero) {
                                .target_partition = system_b,
                                .current_metadata = builder_a.get(),
                                .current_suffix = "_a",
                                .operations = nullptr};
                                .update = nullptr};

    auto ret = creator.Run();

@@ -228,6 +230,58 @@ TEST_F(PartitionCowCreatorTest, Zero) {
    ASSERT_EQ(0u, ret->snapshot_status.cow_partition_size());
}

TEST_F(PartitionCowCreatorTest, CompressionEnabled) {
    constexpr uint64_t super_size = 1_MiB;
    auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
    ASSERT_NE(builder_a, nullptr);

    auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
    ASSERT_NE(builder_b, nullptr);
    auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
    ASSERT_NE(system_b, nullptr);
    ASSERT_TRUE(builder_b->ResizePartition(system_b, 128_KiB));

    PartitionUpdate update;
    update.set_estimate_cow_size(256_KiB);

    PartitionCowCreator creator{.target_metadata = builder_b.get(),
                                .target_suffix = "_b",
                                .target_partition = system_b,
                                .current_metadata = builder_a.get(),
                                .current_suffix = "_a",
                                .compression_enabled = true,
                                .update = &update};

    auto ret = creator.Run();
    ASSERT_TRUE(ret.has_value());
    ASSERT_EQ(ret->snapshot_status.cow_file_size(), 1458176);
}

TEST_F(PartitionCowCreatorTest, CompressionWithNoManifest) {
    constexpr uint64_t super_size = 1_MiB;
    auto builder_a = MetadataBuilder::New(super_size, 1_KiB, 2);
    ASSERT_NE(builder_a, nullptr);

    auto builder_b = MetadataBuilder::New(super_size, 1_KiB, 2);
    ASSERT_NE(builder_b, nullptr);
    auto system_b = builder_b->AddPartition("system_b", LP_PARTITION_ATTR_READONLY);
    ASSERT_NE(system_b, nullptr);
    ASSERT_TRUE(builder_b->ResizePartition(system_b, 128_KiB));

    PartitionUpdate update;

    PartitionCowCreator creator{.target_metadata = builder_b.get(),
                                .target_suffix = "_b",
                                .target_partition = system_b,
                                .current_metadata = builder_a.get(),
                                .current_suffix = "_a",
                                .compression_enabled = true,
                                .update = nullptr};

    auto ret = creator.Run();
    ASSERT_FALSE(ret.has_value());
}

TEST(DmSnapshotInternals, CowSizeCalculator) {
    SKIP_IF_NON_VIRTUAL_AB();

+10 −13
Original line number Diff line number Diff line
@@ -75,7 +75,7 @@ using android::hardware::boot::V1_1::MergeStatus;
using chromeos_update_engine::DeltaArchiveManifest;
using chromeos_update_engine::Extent;
using chromeos_update_engine::FileDescriptor;
using chromeos_update_engine::InstallOperation;
using chromeos_update_engine::PartitionUpdate;
template <typename T>
using RepeatedPtrField = google::protobuf::RepeatedPtrField<T>;
using std::chrono::duration_cast;
@@ -116,10 +116,6 @@ SnapshotManager::SnapshotManager(IDeviceInfo* device) : device_(device) {
    metadata_dir_ = device_->GetMetadataDir();
}

static inline bool IsCompressionEnabled() {
    return android::base::GetBoolProperty("ro.virtual_ab.compression.enabled", false);
}

static std::string GetCowName(const std::string& snapshot_name) {
    return snapshot_name + "-cow";
}
@@ -2527,8 +2523,9 @@ Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manife
            .target_partition = nullptr,
            .current_metadata = current_metadata.get(),
            .current_suffix = current_suffix,
            .operations = nullptr,
            .update = nullptr,
            .extra_extents = {},
            .compression_enabled = IsCompressionEnabled(),
    };

    auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
@@ -2572,12 +2569,11 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal(
        return Return::Error();
    }

    std::map<std::string, const RepeatedPtrField<InstallOperation>*> install_operation_map;
    std::map<std::string, const PartitionUpdate*> partition_map;
    std::map<std::string, std::vector<Extent>> extra_extents_map;
    for (const auto& partition_update : manifest.partitions()) {
        auto suffixed_name = partition_update.partition_name() + target_suffix;
        auto&& [it, inserted] =
                install_operation_map.emplace(suffixed_name, &partition_update.operations());
        auto&& [it, inserted] = partition_map.emplace(suffixed_name, &partition_update);
        if (!inserted) {
            LOG(ERROR) << "Duplicated partition " << partition_update.partition_name()
                       << " in update manifest.";
@@ -2595,10 +2591,10 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal(

    for (auto* target_partition : ListPartitionsWithSuffix(target_metadata, target_suffix)) {
        cow_creator->target_partition = target_partition;
        cow_creator->operations = nullptr;
        auto operations_it = install_operation_map.find(target_partition->name());
        if (operations_it != install_operation_map.end()) {
            cow_creator->operations = operations_it->second;
        cow_creator->update = nullptr;
        auto iter = partition_map.find(target_partition->name());
        if (iter != partition_map.end()) {
            cow_creator->update = iter->second;
        } else {
            LOG(INFO) << target_partition->name()
                      << " isn't included in the payload, skipping the cow creation.";
@@ -2614,6 +2610,7 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal(
        // Compute the device sizes for the partition.
        auto cow_creator_ret = cow_creator->Run();
        if (!cow_creator_ret.has_value()) {
            LOG(ERROR) << "PartitionCowCreator returned no value for " << target_partition->name();
            return Return::Error();
        }

Loading