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

Commit e1af25c6 authored by Daniel Rosenberg's avatar Daniel Rosenberg Committed by Automerger Merge Worker
Browse files

Merge changes from topic "CowSequenceOp" am: b19c333f

Original change: https://android-review.googlesource.com/c/platform/system/core/+/1749489

Change-Id: I0d01f7c88a04338b89a281373d494211eb05d004
parents 6d1c951f b19c333f
Loading
Loading
Loading
Loading
+131 −0
Original line number Diff line number Diff line
@@ -981,6 +981,137 @@ TEST_F(CowTest, DeleteMidCluster) {
    ASSERT_EQ(num_clusters, 1);
}

TEST_F(CowTest, BigSeqOp) {
    CowOptions options;
    CowWriter writer(options);
    const int seq_len = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t) + 1;
    uint32_t sequence[seq_len];
    for (int i = 0; i < seq_len; i++) {
        sequence[i] = i + 1;
    }

    ASSERT_TRUE(writer.Initialize(cow_->fd));

    ASSERT_TRUE(writer.AddSequenceData(seq_len, sequence));
    ASSERT_TRUE(writer.AddZeroBlocks(1, seq_len));
    ASSERT_TRUE(writer.Finalize());

    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);

    CowReader reader;
    ASSERT_TRUE(reader.Parse(cow_->fd));
    auto iter = reader.GetRevMergeOpIter();

    for (int i = 0; i < seq_len; i++) {
        ASSERT_TRUE(!iter->Done());
        const auto& op = iter->Get();

        ASSERT_EQ(op.new_block, seq_len - i);

        iter->Next();
    }
    ASSERT_TRUE(iter->Done());
}

TEST_F(CowTest, RevMergeOpItrTest) {
    CowOptions options;
    options.cluster_ops = 5;
    options.num_merge_ops = 1;
    CowWriter writer(options);
    uint32_t sequence[] = {2, 10, 6, 7, 3, 5};

    ASSERT_TRUE(writer.Initialize(cow_->fd));

    ASSERT_TRUE(writer.AddSequenceData(6, sequence));
    ASSERT_TRUE(writer.AddCopy(6, 3));
    ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
    ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
    ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
    ASSERT_TRUE(writer.AddCopy(3, 5));
    ASSERT_TRUE(writer.AddCopy(2, 1));
    ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
    ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
    ASSERT_TRUE(writer.AddCopy(5, 6));
    ASSERT_TRUE(writer.AddZeroBlocks(1, 1));
    ASSERT_TRUE(writer.AddCopy(10, 2));
    ASSERT_TRUE(writer.AddCopy(7, 4));
    ASSERT_TRUE(writer.Finalize());

    // New block in cow order is 6, 12, 8, 11, 3, 2, 4, 9, 5, 1, 10, 7
    // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1
    // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2
    // new block 2 is "already merged", so will be left out.

    std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};

    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);

    CowReader reader;
    ASSERT_TRUE(reader.Parse(cow_->fd));
    auto iter = reader.GetRevMergeOpIter();
    auto expected_new_block = revMergeOpSequence.begin();

    while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
        const auto& op = iter->Get();

        ASSERT_EQ(op.new_block, *expected_new_block);

        iter->Next();
        expected_new_block++;
    }
    ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
    ASSERT_TRUE(iter->Done());
}

TEST_F(CowTest, LegacyRevMergeOpItrTest) {
    CowOptions options;
    options.cluster_ops = 5;
    options.num_merge_ops = 1;
    CowWriter writer(options);

    ASSERT_TRUE(writer.Initialize(cow_->fd));

    ASSERT_TRUE(writer.AddCopy(2, 1));
    ASSERT_TRUE(writer.AddCopy(10, 2));
    ASSERT_TRUE(writer.AddCopy(6, 3));
    ASSERT_TRUE(writer.AddCopy(7, 4));
    ASSERT_TRUE(writer.AddCopy(3, 5));
    ASSERT_TRUE(writer.AddCopy(5, 6));
    ASSERT_TRUE(writer.AddZeroBlocks(12, 1));
    ASSERT_TRUE(writer.AddZeroBlocks(8, 1));
    ASSERT_TRUE(writer.AddZeroBlocks(11, 1));
    ASSERT_TRUE(writer.AddZeroBlocks(4, 1));
    ASSERT_TRUE(writer.AddZeroBlocks(9, 1));
    ASSERT_TRUE(writer.AddZeroBlocks(1, 1));

    ASSERT_TRUE(writer.Finalize());

    // New block in cow order is 2, 10, 6, 7, 3, 5, 12, 8, 11, 4, 9, 1
    // New block in merge order is 2, 10, 6, 7, 3, 5, 12, 11, 9, 8, 4, 1
    // RevMergeOrder is 1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10, 2
    // new block 2 is "already merged", so will be left out.

    std::vector<uint64_t> revMergeOpSequence = {1, 4, 8, 9, 11, 12, 5, 3, 7, 6, 10};

    ASSERT_EQ(lseek(cow_->fd, 0, SEEK_SET), 0);

    CowReader reader;
    ASSERT_TRUE(reader.Parse(cow_->fd));
    auto iter = reader.GetRevMergeOpIter();
    auto expected_new_block = revMergeOpSequence.begin();

    while (!iter->Done() && expected_new_block != revMergeOpSequence.end()) {
        const auto& op = iter->Get();

        ASSERT_EQ(op.new_block, *expected_new_block);

        iter->Next();
        expected_new_block++;
    }
    ASSERT_EQ(expected_new_block, revMergeOpSequence.end());
    ASSERT_TRUE(iter->Done());
}

}  // namespace snapshot
}  // namespace android

+12 −0
Original line number Diff line number Diff line
@@ -37,6 +37,8 @@ std::ostream& operator<<(std::ostream& os, CowOperation const& op) {
        os << "kCowLabelOp,   ";
    else if (op.type == kCowClusterOp)
        os << "kCowClusterOp  ";
    else if (op.type == kCowSequenceOp)
        os << "kCowSequenceOp ";
    else if (op.type == kCowFooterOp)
        os << "kCowFooterOp  ";
    else
@@ -81,6 +83,16 @@ bool IsMetadataOp(const CowOperation& op) {
        case kCowLabelOp:
        case kCowClusterOp:
        case kCowFooterOp:
        case kCowSequenceOp:
            return true;
        default:
            return false;
    }
}

bool IsOrderedOp(const CowOperation& op) {
    switch (op.type) {
        case kCowCopyOp:
            return true;
        default:
            return false;
+186 −142
Original line number Diff line number Diff line
@@ -19,6 +19,9 @@

#include <limits>
#include <optional>
#include <set>
#include <unordered_map>
#include <unordered_set>
#include <vector>

#include <android-base/file.h>
@@ -127,7 +130,10 @@ bool CowReader::Parse(android::base::borrowed_fd fd, std::optional<uint64_t> lab
        return false;
    }

    return ParseOps(label);
    if (!ParseOps(label)) {
        return false;
    }
    return PrepMergeOps();
}

bool CowReader::ParseOps(std::optional<uint64_t> label) {
@@ -201,6 +207,8 @@ bool CowReader::ParseOps(std::optional<uint64_t> label) {
                current_op_num--;
                done = true;
                break;
            } else if (current_op.type == kCowSequenceOp) {
                has_seq_ops_ = true;
            }
        }

@@ -251,7 +259,7 @@ bool CowReader::ParseOps(std::optional<uint64_t> label) {
            LOG(ERROR) << "ops checksum does not match";
            return false;
        }
        SHA256(ops_buffer.get()->data(), footer_->op.ops_size, csum);
        SHA256(ops_buffer->data(), footer_->op.ops_size, csum);
        if (memcmp(csum, footer_->data.ops_checksum, sizeof(csum)) != 0) {
            LOG(ERROR) << "ops checksum does not match";
            return false;
@@ -264,15 +272,15 @@ bool CowReader::ParseOps(std::optional<uint64_t> label) {
    return true;
}

void CowReader::InitializeMerge() {
    uint64_t num_copy_ops = 0;

    // Remove all the metadata operations
    ops_->erase(std::remove_if(ops_.get()->begin(), ops_.get()->end(),
                               [](CowOperation& op) { return IsMetadataOp(op); }),
                ops_.get()->end());

    set_total_data_ops(ops_->size());
//
// This sets up the data needed for MergeOpIter. MergeOpIter presents
// data in the order we intend to merge in.
//
// We merge all order sensitive ops up front, and sort the rest to allow for
// batch merging. Order sensitive ops can either be presented in their proper
// order in the cow, or be ordered by sequence ops (kCowSequenceOp), in which
// case we want to merge those ops first, followed by any ops not specified by
// new_block value by the sequence op, in sorted order.
// We will re-arrange the vector in such a way that
// kernel can batch merge. Ex:
//
@@ -368,34 +376,61 @@ void CowReader::InitializeMerge() {
// Merge-2 - Batch-merge {Replace-op-7, Replace-op-6, Zero-op-8,
//                        Replace-op-4, Zero-op-9, Replace-op-5 }
//==============================================================
bool CowReader::PrepMergeOps() {
    auto merge_op_blocks = std::make_shared<std::vector<uint32_t>>();
    std::set<int, std::greater<int>> other_ops;
    auto seq_ops_set = std::unordered_set<uint32_t>();
    auto block_map = std::make_shared<std::unordered_map<uint32_t, int>>();
    int num_seqs = 0;
    size_t read;

    for (int i = 0; i < ops_->size(); i++) {
        auto& current_op = ops_->data()[i];

    num_copy_ops = FindNumCopyops();

    std::sort(ops_.get()->begin() + num_copy_ops, ops_.get()->end(),
              [](CowOperation& op1, CowOperation& op2) -> bool {
                  return op1.new_block > op2.new_block;
              });
        if (current_op.type == kCowSequenceOp) {
            size_t seq_len = current_op.data_length / sizeof(uint32_t);

    if (header_.num_merge_ops > 0) {
        ops_->erase(ops_.get()->begin(), ops_.get()->begin() + header_.num_merge_ops);
            merge_op_blocks->resize(merge_op_blocks->size() + seq_len);
            if (!GetRawBytes(current_op.source, &merge_op_blocks->data()[num_seqs],
                             current_op.data_length, &read)) {
                PLOG(ERROR) << "Failed to read sequence op!";
                return false;
            }

    num_copy_ops = FindNumCopyops();
    set_copy_ops(num_copy_ops);
            for (int j = num_seqs; j < num_seqs + seq_len; j++) {
                seq_ops_set.insert(merge_op_blocks->data()[j]);
            }
            num_seqs += seq_len;
        }

uint64_t CowReader::FindNumCopyops() {
    uint64_t num_copy_ops = 0;
        if (IsMetadataOp(current_op)) {
            continue;
        }

    for (uint64_t i = 0; i < ops_->size(); i++) {
        auto& current_op = ops_->data()[i];
        if (current_op.type != kCowCopyOp) {
            break;
        if (!has_seq_ops_ && IsOrderedOp(current_op)) {
            merge_op_blocks->emplace_back(current_op.new_block);
        } else if (seq_ops_set.count(current_op.new_block) == 0) {
            other_ops.insert(current_op.new_block);
        }
        block_map->insert({current_op.new_block, i});
    }
        num_copy_ops += 1;
    if (merge_op_blocks->size() > header_.num_merge_ops)
        num_ordered_ops_to_merge_ = merge_op_blocks->size() - header_.num_merge_ops;
    else
        num_ordered_ops_to_merge_ = 0;
    merge_op_blocks->reserve(merge_op_blocks->size() + other_ops.size());
    for (auto block : other_ops) {
        merge_op_blocks->emplace_back(block);
    }

    return num_copy_ops;
    num_total_data_ops_ = merge_op_blocks->size();
    if (header_.num_merge_ops > 0) {
        merge_op_blocks->erase(merge_op_blocks->begin(),
                               merge_op_blocks->begin() + header_.num_merge_ops);
    }

    block_map_ = block_map;
    merge_op_blocks_ = merge_op_blocks;
    return true;
}

bool CowReader::GetHeader(CowHeader* header) {
@@ -430,11 +465,11 @@ class CowOpIter final : public ICowOpIter {

CowOpIter::CowOpIter(std::shared_ptr<std::vector<CowOperation>>& ops) {
    ops_ = ops;
    op_iter_ = ops_.get()->begin();
    op_iter_ = ops_->begin();
}

bool CowOpIter::Done() {
    return op_iter_ == ops_.get()->end();
    return op_iter_ == ops_->end();
}

void CowOpIter::Next() {
@@ -447,9 +482,11 @@ const CowOperation& CowOpIter::Get() {
    return (*op_iter_);
}

class CowOpReverseIter final : public ICowOpReverseIter {
class CowRevMergeOpIter final : public ICowOpIter {
  public:
    explicit CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops);
    explicit CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
                               std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
                               std::shared_ptr<std::unordered_map<uint32_t, int>> map);

    bool Done() override;
    const CowOperation& Get() override;
@@ -457,34 +494,41 @@ class CowOpReverseIter final : public ICowOpReverseIter {

  private:
    std::shared_ptr<std::vector<CowOperation>> ops_;
    std::vector<CowOperation>::reverse_iterator op_riter_;
    std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_;
    std::shared_ptr<std::unordered_map<uint32_t, int>> map_;
    std::vector<uint32_t>::reverse_iterator block_riter_;
};

CowOpReverseIter::CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops) {
CowRevMergeOpIter::CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
                                     std::shared_ptr<std::vector<uint32_t>> merge_op_blocks,
                                     std::shared_ptr<std::unordered_map<uint32_t, int>> map) {
    ops_ = ops;
    op_riter_ = ops_.get()->rbegin();
    merge_op_blocks_ = merge_op_blocks;
    map_ = map;

    block_riter_ = merge_op_blocks->rbegin();
}

bool CowOpReverseIter::Done() {
    return op_riter_ == ops_.get()->rend();
bool CowRevMergeOpIter::Done() {
    return block_riter_ == merge_op_blocks_->rend();
}

void CowOpReverseIter::Next() {
void CowRevMergeOpIter::Next() {
    CHECK(!Done());
    op_riter_++;
    block_riter_++;
}

const CowOperation& CowOpReverseIter::Get() {
const CowOperation& CowRevMergeOpIter::Get() {
    CHECK(!Done());
    return (*op_riter_);
    return ops_->data()[map_->at(*block_riter_)];
}

std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
    return std::make_unique<CowOpIter>(ops_);
}

std::unique_ptr<ICowOpReverseIter> CowReader::GetRevOpIter() {
    return std::make_unique<CowOpReverseIter>(ops_);
std::unique_ptr<ICowOpIter> CowReader::GetRevMergeOpIter() {
    return std::make_unique<CowRevMergeOpIter>(ops_, merge_op_blocks_, block_map_);
}

bool CowReader::GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read) {
+23 −4
Original line number Diff line number Diff line
@@ -76,9 +76,8 @@ bool ICowWriter::AddLabel(uint64_t label) {
    return EmitLabel(label);
}

bool ICowWriter::AddSequenceData(size_t /*num_ops*/, const uint32_t* /*data*/) {
    LOG(ERROR) << "AddSequenceData not yet implemented";
    return false;
bool ICowWriter::AddSequenceData(size_t num_ops, const uint32_t* data) {
    return EmitSequenceData(num_ops, data);
}

bool ICowWriter::ValidateNewBlock(uint64_t new_block) {
@@ -103,7 +102,7 @@ void CowWriter::SetupHeaders() {
    header_.footer_size = sizeof(CowFooter);
    header_.op_size = sizeof(CowOperation);
    header_.block_size = options_.block_size;
    header_.num_merge_ops = 0;
    header_.num_merge_ops = options_.num_merge_ops;
    header_.cluster_ops = options_.cluster_ops;
    header_.buffer_size = 0;
    footer_ = {};
@@ -337,6 +336,26 @@ bool CowWriter::EmitLabel(uint64_t label) {
    return WriteOperation(op) && Sync();
}

bool CowWriter::EmitSequenceData(size_t num_ops, const uint32_t* data) {
    CHECK(!merge_in_progress_);
    size_t to_add = 0;
    size_t max_ops = std::numeric_limits<uint16_t>::max() / sizeof(uint32_t);
    while (num_ops > 0) {
        CowOperation op = {};
        op.type = kCowSequenceOp;
        op.source = next_data_pos_;
        to_add = std::min(num_ops, max_ops);
        op.data_length = static_cast<uint16_t>(to_add * sizeof(uint32_t));
        if (!WriteOperation(op, data, op.data_length)) {
            PLOG(ERROR) << "AddSequenceData: write failed";
            return false;
        }
        num_ops -= to_add;
        data += to_add;
    }
    return true;
}

bool CowWriter::EmitCluster() {
    CowOperation op = {};
    op.type = kCowClusterOp;
+4 −0
Original line number Diff line number Diff line
@@ -148,6 +148,7 @@ static constexpr uint8_t kCowReplaceOp = 2;
static constexpr uint8_t kCowZeroOp = 3;
static constexpr uint8_t kCowLabelOp = 4;
static constexpr uint8_t kCowClusterOp = 5;
static constexpr uint8_t kCowSequenceOp = 7;
static constexpr uint8_t kCowFooterOp = -1;

static constexpr uint8_t kCowCompressNone = 0;
@@ -184,7 +185,10 @@ std::ostream& operator<<(std::ostream& os, CowOperation const& arg);
int64_t GetNextOpOffset(const CowOperation& op, uint32_t cluster_size);
int64_t GetNextDataOffset(const CowOperation& op, uint32_t cluster_size);

// Ops that are internal to the Cow Format and not OTA data
bool IsMetadataOp(const CowOperation& op);
// Ops that have dependencies on old blocks, and must take care in their merge order
bool IsOrderedOp(const CowOperation& op);

}  // namespace snapshot
}  // namespace android
Loading