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

Commit 218059ff authored by Akilesh Kailash's avatar Akilesh Kailash
Browse files

libsnapshot:snapuserd: Snapshot merge support.



Handle write IO during snapshot merge. When merge
is completed, dm-snapshot-merge issues WRITE IO
to indicate the completion of merge for a specific
operation. Snapuserd daemon tracks the merge completion
process in the COW header; this is required to handle
resuming merge operation gracefully if there is a crash
during merge.

Bug: 168311203
Test: vts_libsnapshot_test, cow_snapuserd_test
Signed-off-by: default avatarAkilesh Kailash <akailash@google.com>
Change-Id: I4fc8cc23a6ec5b26b7ae3339212efdd3b9a367e8
parent 3e64e2ba
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
@@ -193,6 +193,26 @@ bool CowReader::ParseOps() {
    } else {
        LOG(INFO) << "No Footer, recovered data";
    }

    if (header_.num_merge_ops > 0) {
        uint64_t merge_ops = header_.num_merge_ops;
        uint64_t metadata_ops = 0;
        current_op_num = 0;

        CHECK(ops_buffer->size() >= merge_ops);
        while (merge_ops) {
            auto& current_op = ops_buffer->data()[current_op_num];
            if (current_op.type == kCowLabelOp || current_op.type == kCowFooterOp) {
                metadata_ops += 1;
            } else {
                merge_ops -= 1;
            }
            current_op_num += 1;
        }
        ops_buffer->erase(ops_buffer.get()->begin(),
                          ops_buffer.get()->begin() + header_.num_merge_ops + metadata_ops);
    }

    ops_ = ops_buffer;
    return true;
}
+29 −0
Original line number Diff line number Diff line
@@ -91,6 +91,7 @@ void CowWriter::SetupHeaders() {
    header_.header_size = sizeof(CowHeader);
    header_.footer_size = sizeof(CowFooter);
    header_.block_size = options_.block_size;
    header_.num_merge_ops = 0;
    footer_ = {};
    footer_.op.data_length = 64;
    footer_.op.type = kCowFooterOp;
@@ -125,6 +126,12 @@ bool CowWriter::SetFd(android::base::borrowed_fd fd) {
    return true;
}

void CowWriter::InitializeMerge(borrowed_fd fd, CowHeader* header) {
    fd_ = fd;
    memcpy(&header_, header, sizeof(CowHeader));
    merge_in_progress_ = true;
}

bool CowWriter::Initialize(unique_fd&& fd) {
    owned_fd_ = std::move(fd);
    return Initialize(borrowed_fd{owned_fd_});
@@ -223,6 +230,7 @@ bool CowWriter::OpenForAppend(uint64_t label) {
}

bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
    CHECK(!merge_in_progress_);
    CowOperation op = {};
    op.type = kCowCopyOp;
    op.new_block = new_block;
@@ -233,6 +241,7 @@ bool CowWriter::EmitCopy(uint64_t new_block, uint64_t old_block) {
bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t size) {
    const uint8_t* iter = reinterpret_cast<const uint8_t*>(data);
    uint64_t pos;
    CHECK(!merge_in_progress_);
    for (size_t i = 0; i < size / header_.block_size; i++) {
        CowOperation op = {};
        op.type = kCowReplaceOp;
@@ -271,6 +280,7 @@ bool CowWriter::EmitRawBlocks(uint64_t new_block_start, const void* data, size_t
}

bool CowWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
    CHECK(!merge_in_progress_);
    for (uint64_t i = 0; i < num_blocks; i++) {
        CowOperation op = {};
        op.type = kCowZeroOp;
@@ -282,6 +292,7 @@ bool CowWriter::EmitZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) {
}

bool CowWriter::EmitLabel(uint64_t label) {
    CHECK(!merge_in_progress_);
    CowOperation op = {};
    op.type = kCowLabelOp;
    op.source = label;
@@ -416,5 +427,23 @@ bool CowWriter::Sync() {
    return true;
}

bool CowWriter::CommitMerge(int merged_ops) {
    CHECK(merge_in_progress_);
    header_.num_merge_ops += merged_ops;

    if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
        PLOG(ERROR) << "lseek failed";
        return false;
    }

    if (!android::base::WriteFully(fd_, reinterpret_cast<const uint8_t*>(&header_),
                                   sizeof(header_))) {
        PLOG(ERROR) << "WriteFully failed";
        return false;
    }

    return Sync();
}

}  // namespace snapshot
}  // namespace android
+3 −0
Original line number Diff line number Diff line
@@ -59,6 +59,9 @@ struct CowHeader {

    // The size of block operations, in bytes.
    uint32_t block_size;

    // Tracks merge operations completed
    uint64_t num_merge_ops;
} __attribute__((packed));

// This structure is the same size of a normal Operation, but is repurposed for the footer.
+2 −0
Original line number Diff line number Diff line
@@ -135,6 +135,8 @@ class CowReader : public ICowReader {

    bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);

    void UpdateMergeProgress(uint64_t merge_ops) { header_.num_merge_ops += merge_ops; }

  private:
    bool ParseOps();

+4 −0
Original line number Diff line number Diff line
@@ -97,6 +97,9 @@ class CowWriter : public ICowWriter {
    bool InitializeAppend(android::base::unique_fd&&, uint64_t label);
    bool InitializeAppend(android::base::borrowed_fd fd, uint64_t label);

    void InitializeMerge(android::base::borrowed_fd fd, CowHeader* header);
    bool CommitMerge(int merged_ops);

    bool Finalize() override;

    uint64_t GetCowSize() override;
@@ -129,6 +132,7 @@ class CowWriter : public ICowWriter {
    int compression_ = 0;
    uint64_t next_op_pos_ = 0;
    bool is_dev_null_ = false;
    bool merge_in_progress_ = false;

    // :TODO: this is not efficient, but stringstream ubsan aborts because some
    // bytes overflow a signed char.
Loading