Merge "libsnapshot: Remove GetSnapshotSize"
This commit is contained in:
commit
20ec0cd6ce
5 changed files with 61 additions and 68 deletions
|
|
@ -67,59 +67,6 @@ bool PartitionCowCreator::HasExtent(Partition* p, Extent* e) {
|
|||
return false;
|
||||
}
|
||||
|
||||
// Return the number of sectors, N, where |target_partition|[0..N] (from
|
||||
// |target_metadata|) are the sectors that should be snapshotted. N is computed
|
||||
// so that this range of sectors are used by partitions in |current_metadata|.
|
||||
//
|
||||
// The client code (update_engine) should have computed target_metadata by
|
||||
// resizing partitions of current_metadata, so only the first N sectors should
|
||||
// be snapshotted, not a range with start index != 0.
|
||||
//
|
||||
// Note that if partition A has shrunk and partition B has grown, the new
|
||||
// extents of partition B may use the empty space that was used by partition A.
|
||||
// In this case, that new extent cannot be written directly, as it may be used
|
||||
// by the running system. Hence, all extents of the new partition B must be
|
||||
// intersected with all old partitions (including old partition A and B) to get
|
||||
// the region that needs to be snapshotted.
|
||||
std::optional<uint64_t> PartitionCowCreator::GetSnapshotSize() {
|
||||
// Compute the number of sectors that needs to be snapshotted.
|
||||
uint64_t snapshot_sectors = 0;
|
||||
std::vector<std::unique_ptr<Extent>> intersections;
|
||||
for (const auto& extent : target_partition->extents()) {
|
||||
for (auto* existing_partition :
|
||||
ListPartitionsWithSuffix(current_metadata, current_suffix)) {
|
||||
for (const auto& existing_extent : existing_partition->extents()) {
|
||||
auto intersection = Intersect(extent.get(), existing_extent.get());
|
||||
if (intersection != nullptr && intersection->num_sectors() > 0) {
|
||||
snapshot_sectors += intersection->num_sectors();
|
||||
intersections.emplace_back(std::move(intersection));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
uint64_t snapshot_size = snapshot_sectors * kSectorSize;
|
||||
|
||||
// Sanity check that all recorded intersections are indeed within
|
||||
// target_partition[0..snapshot_sectors].
|
||||
Partition target_partition_snapshot = target_partition->GetBeginningExtents(snapshot_size);
|
||||
for (const auto& intersection : intersections) {
|
||||
if (!HasExtent(&target_partition_snapshot, intersection.get())) {
|
||||
auto linear_intersection = intersection->AsLinearExtent();
|
||||
LOG(ERROR) << "Extent "
|
||||
<< (linear_intersection
|
||||
? (std::to_string(linear_intersection->physical_sector()) + "," +
|
||||
std::to_string(linear_intersection->end_sector()))
|
||||
: "")
|
||||
<< " is not part of Partition " << target_partition->name() << "[0.."
|
||||
<< snapshot_size
|
||||
<< "]. The metadata wasn't constructed correctly. This should not happen.";
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
return snapshot_size;
|
||||
}
|
||||
|
||||
std::optional<uint64_t> PartitionCowCreator::GetCowSize(uint64_t snapshot_size) {
|
||||
// TODO: Use |operations|. to determine a minimum COW size.
|
||||
// kCowEstimateFactor is good for prototyping but we can't use that in production.
|
||||
|
|
@ -139,12 +86,11 @@ std::optional<PartitionCowCreator::Return> PartitionCowCreator::Run() {
|
|||
Return ret;
|
||||
ret.snapshot_status.device_size = target_partition->size();
|
||||
|
||||
auto snapshot_size = GetSnapshotSize();
|
||||
if (!snapshot_size.has_value()) return std::nullopt;
|
||||
// TODO(b/141889746): Optimize by using a smaller snapshot. Some ranges in target_partition
|
||||
// may be written directly.
|
||||
ret.snapshot_status.snapshot_size = target_partition->size();
|
||||
|
||||
ret.snapshot_status.snapshot_size = *snapshot_size;
|
||||
|
||||
auto cow_size = GetCowSize(*snapshot_size);
|
||||
auto cow_size = GetCowSize(ret.snapshot_status.snapshot_size);
|
||||
if (!cow_size.has_value()) return std::nullopt;
|
||||
|
||||
// Compute regions that are free in both current and target metadata. These are the regions
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ struct PartitionCowCreator {
|
|||
|
||||
private:
|
||||
bool HasExtent(Partition* p, Extent* e);
|
||||
std::optional<uint64_t> GetSnapshotSize();
|
||||
std::optional<uint64_t> GetCowSize(uint64_t snapshot_size);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#include "partition_cow_creator.h"
|
||||
#include "test_helpers.h"
|
||||
|
||||
using ::android::fs_mgr::MetadataBuilder;
|
||||
using namespace android::fs_mgr;
|
||||
|
||||
namespace android {
|
||||
namespace snapshot {
|
||||
|
|
@ -55,5 +55,46 @@ TEST_F(PartitionCowCreatorTest, IntersectSelf) {
|
|||
ASSERT_EQ(40 * 1024, ret->snapshot_status.snapshot_size);
|
||||
}
|
||||
|
||||
TEST_F(PartitionCowCreatorTest, Holes) {
|
||||
const auto& opener = test_device->GetPartitionOpener();
|
||||
|
||||
constexpr auto slack_space = 1_MiB;
|
||||
constexpr auto big_size = (kSuperSize - slack_space) / 2;
|
||||
constexpr auto small_size = big_size / 2;
|
||||
|
||||
BlockDeviceInfo super_device("super", kSuperSize, 0, 0, 4_KiB);
|
||||
std::vector<BlockDeviceInfo> devices = {super_device};
|
||||
auto source = MetadataBuilder::New(devices, "super", 1024, 2);
|
||||
auto system = source->AddPartition("system_a", 0);
|
||||
ASSERT_NE(nullptr, system);
|
||||
ASSERT_TRUE(source->ResizePartition(system, big_size));
|
||||
auto vendor = source->AddPartition("vendor_a", 0);
|
||||
ASSERT_NE(nullptr, vendor);
|
||||
ASSERT_TRUE(source->ResizePartition(vendor, big_size));
|
||||
// Create a hole between system and vendor
|
||||
ASSERT_TRUE(source->ResizePartition(system, small_size));
|
||||
auto source_metadata = source->Export();
|
||||
ASSERT_NE(nullptr, source_metadata);
|
||||
ASSERT_TRUE(FlashPartitionTable(opener, fake_super, *source_metadata.get()));
|
||||
|
||||
auto target = MetadataBuilder::NewForUpdate(opener, "super", 0, 1);
|
||||
// Shrink vendor
|
||||
vendor = target->FindPartition("vendor_b");
|
||||
ASSERT_NE(nullptr, vendor);
|
||||
ASSERT_TRUE(target->ResizePartition(vendor, small_size));
|
||||
// Grow system to take hole & saved space from vendor
|
||||
system = target->FindPartition("system_b");
|
||||
ASSERT_NE(nullptr, system);
|
||||
ASSERT_TRUE(target->ResizePartition(system, big_size * 2 - small_size));
|
||||
|
||||
PartitionCowCreator creator{.target_metadata = target.get(),
|
||||
.target_suffix = "_b",
|
||||
.target_partition = system,
|
||||
.current_metadata = source.get(),
|
||||
.current_suffix = "_a"};
|
||||
auto ret = creator.Run();
|
||||
ASSERT_TRUE(ret.has_value());
|
||||
}
|
||||
|
||||
} // namespace snapshot
|
||||
} // namespace android
|
||||
|
|
|
|||
|
|
@ -58,15 +58,11 @@ using namespace android::storage_literals;
|
|||
using namespace std::chrono_literals;
|
||||
using namespace std::string_literals;
|
||||
|
||||
// These are not reset between each test because it's expensive to create
|
||||
// these resources (starting+connecting to gsid, zero-filling images).
|
||||
// Global states. See test_helpers.h.
|
||||
std::unique_ptr<SnapshotManager> sm;
|
||||
TestDeviceInfo* test_device = nullptr;
|
||||
std::string fake_super;
|
||||
|
||||
static constexpr uint64_t kSuperSize = 16_MiB + 4_KiB;
|
||||
static constexpr uint64_t kGroupSize = 16_MiB;
|
||||
|
||||
class SnapshotTest : public ::testing::Test {
|
||||
public:
|
||||
SnapshotTest() : dm_(DeviceMapper::Instance()) {}
|
||||
|
|
@ -743,9 +739,9 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
|
|||
}
|
||||
|
||||
// Grow all partitions.
|
||||
SetSize(sys_, 4_MiB);
|
||||
SetSize(vnd_, 4_MiB);
|
||||
SetSize(prd_, 4_MiB);
|
||||
SetSize(sys_, 3788_KiB);
|
||||
SetSize(vnd_, 3788_KiB);
|
||||
SetSize(prd_, 3788_KiB);
|
||||
|
||||
// Execute the update.
|
||||
ASSERT_TRUE(sm->BeginUpdate());
|
||||
|
|
@ -810,6 +806,7 @@ TEST_F(SnapshotUpdateTest, FullUpdateFlow) {
|
|||
|
||||
// Test that if new system partitions uses empty space in super, that region is not snapshotted.
|
||||
TEST_F(SnapshotUpdateTest, DirectWriteEmptySpace) {
|
||||
GTEST_SKIP() << "b/141889746";
|
||||
SetSize(sys_, 4_MiB);
|
||||
// vnd_b and prd_b are unchanged.
|
||||
ASSERT_TRUE(sm->CreateUpdateSnapshots(manifest_));
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
#include <liblp/mock_property_fetcher.h>
|
||||
#include <liblp/partition_opener.h>
|
||||
#include <libsnapshot/snapshot.h>
|
||||
#include <storage_literals/storage_literals.h>
|
||||
#include <update_engine/update_metadata.pb.h>
|
||||
|
||||
namespace android {
|
||||
|
|
@ -38,8 +39,17 @@ using testing::AssertionResult;
|
|||
using testing::NiceMock;
|
||||
using testing::Return;
|
||||
|
||||
using namespace android::storage_literals;
|
||||
using namespace std::string_literals;
|
||||
|
||||
// These are not reset between each test because it's expensive to create
|
||||
// these resources (starting+connecting to gsid, zero-filling images).
|
||||
extern std::unique_ptr<SnapshotManager> sm;
|
||||
extern class TestDeviceInfo* test_device;
|
||||
extern std::string fake_super;
|
||||
static constexpr uint64_t kSuperSize = 16_MiB + 4_KiB;
|
||||
static constexpr uint64_t kGroupSize = 16_MiB;
|
||||
|
||||
// Redirect requests for "super" to our fake super partition.
|
||||
class TestPartitionOpener final : public android::fs_mgr::PartitionOpener {
|
||||
public:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue