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

Commit 34d084f8 authored by David Anderson's avatar David Anderson Committed by Automerger Merge Worker
Browse files

Merge "libsnapshot: Fix flaky low-space tests." am: 25613816

parents 762adad3 25613816
Loading
Loading
Loading
Loading
+0 −23
Original line number Diff line number Diff line
@@ -214,29 +214,6 @@ void SetSize(PartitionUpdate* partition_update, uint64_t size);
// Get partition size from update package metadata.
uint64_t GetSize(PartitionUpdate* partition_update);

// Util class for test cases on low space scenario. These tests assumes image manager
// uses /data as backup device.
class LowSpaceUserdata {
  public:
    // Set the maximum free space allowed for this test. If /userdata has more space than the given
    // number, a file is allocated to consume space.
    AssertionResult Init(uint64_t max_free_space);

    uint64_t free_space() const;
    uint64_t available_space() const;
    uint64_t bsize() const;

  private:
    AssertionResult ReadUserdataStats();

    static constexpr const char* kUserDataDevice = "/data";
    std::unique_ptr<TemporaryFile> big_file_;
    bool initialized_ = false;
    uint64_t free_space_ = 0;
    uint64_t available_space_ = 0;
    uint64_t bsize_ = 0;
};

bool IsVirtualAbEnabled();

#define SKIP_IF_NON_VIRTUAL_AB()                                                        \
+9 −2
Original line number Diff line number Diff line
@@ -3133,6 +3133,7 @@ static Return AddRequiredSpace(Return orig,
    for (auto&& [name, status] : all_snapshot_status) {
        sum += status.cow_file_size();
    }
    LOG(INFO) << "Calculated needed COW space: " << sum << " bytes";
    return Return::NoSpace(sum);
}

@@ -3279,7 +3280,10 @@ Return SnapshotManager::CreateUpdateSnapshots(const DeltaArchiveManifest& manife

    auto ret = CreateUpdateSnapshotsInternal(lock.get(), manifest, &cow_creator, &created_devices,
                                             &all_snapshot_status);
    if (!ret.is_ok()) return ret;
    if (!ret.is_ok()) {
        LOG(ERROR) << "CreateUpdateSnapshotsInternal failed: " << ret.string();
        return ret;
    }

    auto exported_target_metadata = target_metadata->Export();
    if (exported_target_metadata == nullptr) {
@@ -3496,7 +3500,10 @@ Return SnapshotManager::CreateUpdateSnapshotsInternal(
        // Create the backing COW image if necessary.
        if (snapshot_status.cow_file_size() > 0) {
            auto ret = CreateCowImage(lock, name);
            if (!ret.is_ok()) return AddRequiredSpace(ret, *all_snapshot_status);
            if (!ret.is_ok()) {
                LOG(ERROR) << "CreateCowImage failed: " << ret.string();
                return AddRequiredSpace(ret, *all_snapshot_status);
            }
        }

        LOG(INFO) << "Successfully created snapshot for " << name;
+46 −29
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@
#include <signal.h>
#include <sys/file.h>
#include <sys/stat.h>
#include <sys/statvfs.h>
#include <sys/types.h>

#include <chrono>
@@ -2371,20 +2372,44 @@ TEST_F(SnapshotUpdateTest, Overflow) {
            << "FinishedSnapshotWrites should detect overflow of CoW device.";
}

// Get max file size and free space.
std::pair<uint64_t, uint64_t> GetBigFileLimit() {
    struct statvfs fs;
    if (statvfs("/data", &fs) < 0) {
        PLOG(ERROR) << "statfs failed";
        return {0, 0};
    }

    auto fs_limit = static_cast<uint64_t>(fs.f_blocks) * (fs.f_bsize - 1);
    auto fs_free = static_cast<uint64_t>(fs.f_bfree) * fs.f_bsize;

    LOG(INFO) << "Big file limit: " << fs_limit << ", free space: " << fs_free;

    return {fs_limit, fs_free};
}

TEST_F(SnapshotUpdateTest, LowSpace) {
    static constexpr auto kMaxFree = 10_MiB;
    auto userdata = std::make_unique<LowSpaceUserdata>();
    ASSERT_TRUE(userdata->Init(kMaxFree));
    // To make the low space test more reliable, we force a large cow estimate.
    // However legacy VAB ignores the COW estimate and uses InstallOperations
    // to compute the exact size required for dm-snapshot. It's difficult to
    // make this work reliably (we'd need to somehow fake an extremely large
    // super partition, and we don't have that level of dependency injection).
    //
    // For now, just skip this test on legacy VAB.
    if (!snapuserd_required_) {
        GTEST_SKIP() << "Skipping test on legacy VAB";
    }

    auto fs = GetBigFileLimit();
    ASSERT_NE(fs.first, 0);

    // Grow all partitions to 10_MiB, total 30_MiB. This requires 30 MiB of CoW space. After
    // using the empty space in super (< 1 MiB), it uses 30 MiB of /userdata space.
    constexpr uint64_t partition_size = 10_MiB;
    SetSize(sys_, partition_size);
    SetSize(vnd_, partition_size);
    SetSize(prd_, partition_size);
    sys_->set_estimate_cow_size(partition_size);
    vnd_->set_estimate_cow_size(partition_size);
    prd_->set_estimate_cow_size(partition_size);
    sys_->set_estimate_cow_size(fs.first);
    vnd_->set_estimate_cow_size(fs.first);
    prd_->set_estimate_cow_size(fs.first);

    AddOperationForPartitions();

@@ -2393,8 +2418,12 @@ TEST_F(SnapshotUpdateTest, LowSpace) {
    auto res = sm->CreateUpdateSnapshots(manifest_);
    ASSERT_FALSE(res);
    ASSERT_EQ(Return::ErrorCode::NO_SPACE, res.error_code());
    ASSERT_GE(res.required_size(), 14_MiB);
    ASSERT_LT(res.required_size(), 40_MiB);

    // It's hard to predict exactly how much free space is needed, since /data
    // is writable and the test is not the only process running. Divide by two
    // as a rough lower bound, and adjust this in the future as necessary.
    auto expected_delta = fs.first - fs.second;
    ASSERT_GE(res.required_size(), expected_delta / 2);
}

TEST_F(SnapshotUpdateTest, AddPartition) {
@@ -2776,6 +2805,7 @@ class ImageManagerTest : public SnapshotTest {
    void TearDown() override {
        RETURN_IF_NON_VIRTUAL_AB();
        CleanUp();
        SnapshotTest::TearDown();
    }
    void CleanUp() {
        if (!image_manager_) {
@@ -2789,28 +2819,15 @@ class ImageManagerTest : public SnapshotTest {
};

TEST_F(ImageManagerTest, CreateImageNoSpace) {
    bool at_least_one_failure = false;
    for (uint64_t size = 1_MiB; size <= 512_MiB; size *= 2) {
        auto userdata = std::make_unique<LowSpaceUserdata>();
        ASSERT_TRUE(userdata->Init(size));

        uint64_t to_allocate = userdata->free_space() + userdata->bsize();
    auto fs = GetBigFileLimit();
    ASSERT_NE(fs.first, 0);

        auto res = image_manager_->CreateBackingImage(kImageName, to_allocate,
    auto res = image_manager_->CreateBackingImage(kImageName, fs.first,
                                                  IImageManager::CREATE_IMAGE_DEFAULT);
        if (!res) {
            at_least_one_failure = true;
        } else {
    ASSERT_FALSE(res);
    ASSERT_EQ(res.error_code(), FiemapStatus::ErrorCode::NO_SPACE) << res.string();
}

        CleanUp();
    }

    ASSERT_TRUE(at_least_one_failure)
            << "We should have failed to allocate at least one over-sized image";
}

bool Mkdir(const std::string& path) {
    if (mkdir(path.c_str(), 0700) && errno != EEXIST) {
        std::cerr << "Could not mkdir " << path << ": " << strerror(errno) << std::endl;
+0 −62
Original line number Diff line number Diff line
@@ -214,68 +214,6 @@ uint64_t GetSize(PartitionUpdate* partition_update) {
    return partition_update->mutable_new_partition_info()->size();
}

AssertionResult LowSpaceUserdata::Init(uint64_t max_free_space) {
    auto res = ReadUserdataStats();
    if (!res) return res;

    // Try to fill up the disk as much as possible until free_space_ <= max_free_space.
    big_file_ = std::make_unique<TemporaryFile>();
    if (big_file_->fd == -1) {
        return AssertionFailure() << strerror(errno);
    }
    if (!android::base::StartsWith(big_file_->path, kUserDataDevice)) {
        return AssertionFailure() << "Temp file allocated to " << big_file_->path << ", not in "
                                  << kUserDataDevice;
    }
    uint64_t next_consume = std::min(std::max(available_space_, max_free_space) - max_free_space,
                                     (uint64_t)std::numeric_limits<off_t>::max());
    off_t allocated = 0;
    while (next_consume > 0 && free_space_ > max_free_space) {
        int status = fallocate(big_file_->fd, 0, allocated, next_consume);
        if (status == -1 && errno == ENOSPC) {
            next_consume /= 2;
            continue;
        }
        if (status == -1) {
            return AssertionFailure() << strerror(errno);
        }
        allocated += next_consume;

        res = ReadUserdataStats();
        if (!res) return res;
    }

    LOG(INFO) << allocated << " bytes allocated to " << big_file_->path;
    initialized_ = true;
    return AssertionSuccess();
}

AssertionResult LowSpaceUserdata::ReadUserdataStats() {
    struct statvfs buf;
    if (statvfs(kUserDataDevice, &buf) == -1) {
        return AssertionFailure() << strerror(errno);
    }
    bsize_ = buf.f_bsize;
    free_space_ = bsize_ * buf.f_bfree;
    available_space_ = bsize_ * buf.f_bavail;
    return AssertionSuccess();
}

uint64_t LowSpaceUserdata::free_space() const {
    CHECK(initialized_);
    return free_space_;
}

uint64_t LowSpaceUserdata::available_space() const {
    CHECK(initialized_);
    return available_space_;
}

uint64_t LowSpaceUserdata::bsize() const {
    CHECK(initialized_);
    return bsize_;
}

bool IsVirtualAbEnabled() {
    return android::base::GetBoolProperty("ro.virtual_ab.enabled", false);
}