libsnapshot: Switch merge to CowRevMergeOpItr
This switches merge code over from using the old RevOpItr to the new MergeRevOpItr. Since there are no other users, RevOpItr is no longer needed. Changed names of copy_ops_ and total_data_ops_ to more accurately reflect their meaning. Bug: 177104308 Test: cow_snapuserd_test Change-Id: Ic053be4877cfdc86656551f5a3d5d95f3825f937
This commit is contained in:
parent
fffb9ff8ee
commit
437a49ae66
3 changed files with 32 additions and 213 deletions
|
|
@ -413,154 +413,26 @@ bool CowReader::PrepMergeOps() {
|
|||
}
|
||||
block_map->insert({current_op.new_block, i});
|
||||
}
|
||||
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);
|
||||
}
|
||||
merge_op_blocks_ = merge_op_blocks;
|
||||
|
||||
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;
|
||||
|
||||
if (header_.num_merge_ops > 0) {
|
||||
merge_op_blocks_->erase(merge_op_blocks_->begin(),
|
||||
merge_op_blocks_->begin() + header_.num_merge_ops);
|
||||
}
|
||||
merge_op_blocks_ = merge_op_blocks;
|
||||
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());
|
||||
// We will re-arrange the vector in such a way that
|
||||
// kernel can batch merge. Ex:
|
||||
//
|
||||
// Existing COW format; All the copy operations
|
||||
// are at the beginning.
|
||||
// =======================================
|
||||
// Copy-op-1 - cow_op->new_block = 1
|
||||
// Copy-op-2 - cow_op->new_block = 2
|
||||
// Copy-op-3 - cow_op->new_block = 3
|
||||
// Replace-op-4 - cow_op->new_block = 6
|
||||
// Replace-op-5 - cow_op->new_block = 4
|
||||
// Replace-op-6 - cow_op->new_block = 8
|
||||
// Replace-op-7 - cow_op->new_block = 9
|
||||
// Zero-op-8 - cow_op->new_block = 7
|
||||
// Zero-op-9 - cow_op->new_block = 5
|
||||
// =======================================
|
||||
//
|
||||
// First find the operation which isn't a copy-op
|
||||
// and then sort all the operations in descending order
|
||||
// with the key being cow_op->new_block (source block)
|
||||
//
|
||||
// The data-structure will look like:
|
||||
//
|
||||
// =======================================
|
||||
// Copy-op-1 - cow_op->new_block = 1
|
||||
// Copy-op-2 - cow_op->new_block = 2
|
||||
// Copy-op-3 - cow_op->new_block = 3
|
||||
// Replace-op-7 - cow_op->new_block = 9
|
||||
// Replace-op-6 - cow_op->new_block = 8
|
||||
// Zero-op-8 - cow_op->new_block = 7
|
||||
// Replace-op-4 - cow_op->new_block = 6
|
||||
// Zero-op-9 - cow_op->new_block = 5
|
||||
// Replace-op-5 - cow_op->new_block = 4
|
||||
// =======================================
|
||||
//
|
||||
// Daemon will read the above data-structure in reverse-order
|
||||
// when reading metadata. Thus, kernel will get the metadata
|
||||
// in the following order:
|
||||
//
|
||||
// ========================================
|
||||
// Replace-op-5 - cow_op->new_block = 4
|
||||
// Zero-op-9 - cow_op->new_block = 5
|
||||
// Replace-op-4 - cow_op->new_block = 6
|
||||
// Zero-op-8 - cow_op->new_block = 7
|
||||
// Replace-op-6 - cow_op->new_block = 8
|
||||
// Replace-op-7 - cow_op->new_block = 9
|
||||
// Copy-op-3 - cow_op->new_block = 3
|
||||
// Copy-op-2 - cow_op->new_block = 2
|
||||
// Copy-op-1 - cow_op->new_block = 1
|
||||
// ===========================================
|
||||
//
|
||||
// When merging begins, kernel will start from the last
|
||||
// metadata which was read: In the above format, Copy-op-1
|
||||
// will be the first merge operation.
|
||||
//
|
||||
// Now, batching of the merge operations happens only when
|
||||
// 1: origin block numbers in the base device are contiguous
|
||||
// (cow_op->new_block) and,
|
||||
// 2: cow block numbers which are assigned by daemon in ReadMetadata()
|
||||
// are contiguous. These are monotonically increasing numbers.
|
||||
//
|
||||
// When both (1) and (2) are true, kernel will batch merge the operations.
|
||||
// In the above case, we have to ensure that the copy operations
|
||||
// are merged first before replace operations are done. Hence,
|
||||
// we will not change the order of copy operations. Since,
|
||||
// cow_op->new_block numbers are contiguous, we will ensure that the
|
||||
// cow block numbers assigned in ReadMetadata() for these respective copy
|
||||
// operations are not contiguous forcing kernel to issue merge for each
|
||||
// copy operations without batch merging.
|
||||
//
|
||||
// For all the other operations viz. Replace and Zero op, the cow block
|
||||
// numbers assigned by daemon will be contiguous allowing kernel to batch
|
||||
// merge.
|
||||
//
|
||||
// The final format after assiging COW block numbers by the daemon will
|
||||
// look something like:
|
||||
//
|
||||
// =========================================================
|
||||
// Replace-op-5 - cow_op->new_block = 4 cow-block-num = 2
|
||||
// Zero-op-9 - cow_op->new_block = 5 cow-block-num = 3
|
||||
// Replace-op-4 - cow_op->new_block = 6 cow-block-num = 4
|
||||
// Zero-op-8 - cow_op->new_block = 7 cow-block-num = 5
|
||||
// Replace-op-6 - cow_op->new_block = 8 cow-block-num = 6
|
||||
// Replace-op-7 - cow_op->new_block = 9 cow-block-num = 7
|
||||
// Copy-op-3 - cow_op->new_block = 3 cow-block-num = 9
|
||||
// Copy-op-2 - cow_op->new_block = 2 cow-block-num = 11
|
||||
// Copy-op-1 - cow_op->new_block = 1 cow-block-num = 13
|
||||
// ==========================================================
|
||||
//
|
||||
// Merge sequence will look like:
|
||||
//
|
||||
// Merge-1 - Batch-merge { Copy-op-1, Copy-op-2, Copy-op-3 }
|
||||
// Merge-2 - Batch-merge {Replace-op-7, Replace-op-6, Zero-op-8,
|
||||
// Replace-op-4, Zero-op-9, Replace-op-5 }
|
||||
//==============================================================
|
||||
|
||||
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 (header_.num_merge_ops > 0) {
|
||||
ops_->erase(ops_.get()->begin(), ops_.get()->begin() + header_.num_merge_ops);
|
||||
}
|
||||
|
||||
num_copy_ops = FindNumCopyops();
|
||||
set_copy_ops(num_copy_ops);
|
||||
}
|
||||
|
||||
uint64_t CowReader::FindNumCopyops() {
|
||||
uint64_t num_copy_ops = 0;
|
||||
|
||||
for (uint64_t i = 0; i < ops_->size(); i++) {
|
||||
auto& current_op = ops_->data()[i];
|
||||
if (current_op.type != kCowCopyOp) {
|
||||
break;
|
||||
}
|
||||
num_copy_ops += 1;
|
||||
}
|
||||
|
||||
return num_copy_ops;
|
||||
}
|
||||
|
||||
bool CowReader::GetHeader(CowHeader* header) {
|
||||
*header = header_;
|
||||
return true;
|
||||
|
|
@ -610,38 +482,6 @@ const CowOperation& CowOpIter::Get() {
|
|||
return (*op_iter_);
|
||||
}
|
||||
|
||||
class CowOpReverseIter final : public ICowOpIter {
|
||||
public:
|
||||
explicit CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops);
|
||||
|
||||
bool Done() override;
|
||||
const CowOperation& Get() override;
|
||||
void Next() override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<std::vector<CowOperation>> ops_;
|
||||
std::vector<CowOperation>::reverse_iterator op_riter_;
|
||||
};
|
||||
|
||||
CowOpReverseIter::CowOpReverseIter(std::shared_ptr<std::vector<CowOperation>> ops) {
|
||||
ops_ = ops;
|
||||
op_riter_ = ops_.get()->rbegin();
|
||||
}
|
||||
|
||||
bool CowOpReverseIter::Done() {
|
||||
return op_riter_ == ops_.get()->rend();
|
||||
}
|
||||
|
||||
void CowOpReverseIter::Next() {
|
||||
CHECK(!Done());
|
||||
op_riter_++;
|
||||
}
|
||||
|
||||
const CowOperation& CowOpReverseIter::Get() {
|
||||
CHECK(!Done());
|
||||
return (*op_riter_);
|
||||
}
|
||||
|
||||
class CowRevMergeOpIter final : public ICowOpIter {
|
||||
public:
|
||||
explicit CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops,
|
||||
|
|
@ -687,10 +527,6 @@ std::unique_ptr<ICowOpIter> CowReader::GetOpIter() {
|
|||
return std::make_unique<CowOpIter>(ops_);
|
||||
}
|
||||
|
||||
std::unique_ptr<ICowOpIter> 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_);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,9 +75,6 @@ class ICowReader {
|
|||
// Return an iterator for retrieving CowOperation entries.
|
||||
virtual std::unique_ptr<ICowOpIter> GetOpIter() = 0;
|
||||
|
||||
// Return an reverse iterator for retrieving CowOperation entries.
|
||||
virtual std::unique_ptr<ICowOpIter> GetRevOpIter() = 0;
|
||||
|
||||
// Return an iterator for retrieving CowOperation entries in merge order
|
||||
virtual std::unique_ptr<ICowOpIter> GetRevMergeOpIter() = 0;
|
||||
|
||||
|
|
@ -123,21 +120,15 @@ class CowReader : public ICowReader {
|
|||
// whose lifetime depends on the CowOpIter object; the return
|
||||
// value of these will never be null.
|
||||
std::unique_ptr<ICowOpIter> GetOpIter() override;
|
||||
std::unique_ptr<ICowOpIter> GetRevOpIter() override;
|
||||
std::unique_ptr<ICowOpIter> GetRevMergeOpIter() override;
|
||||
|
||||
bool ReadData(const CowOperation& op, IByteSink* sink) override;
|
||||
|
||||
bool GetRawBytes(uint64_t offset, void* buffer, size_t len, size_t* read);
|
||||
|
||||
void InitializeMerge();
|
||||
uint64_t get_num_total_data_ops() { return num_total_data_ops_; }
|
||||
|
||||
// Number of copy, replace, and zero ops. Set if InitializeMerge is called.
|
||||
void set_total_data_ops(uint64_t size) { total_data_ops_ = size; }
|
||||
uint64_t total_data_ops() { return total_data_ops_; }
|
||||
// Number of copy ops. Set if InitializeMerge is called.
|
||||
void set_copy_ops(uint64_t size) { copy_ops_ = size; }
|
||||
uint64_t total_copy_ops() { return copy_ops_; }
|
||||
uint64_t get_num_ordered_ops_to_merge() { return num_ordered_ops_to_merge_; }
|
||||
|
||||
void CloseCowFd() { owned_fd_ = {}; }
|
||||
|
||||
|
|
@ -155,8 +146,8 @@ class CowReader : public ICowReader {
|
|||
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>> block_map_;
|
||||
uint64_t total_data_ops_;
|
||||
uint64_t copy_ops_;
|
||||
uint64_t num_total_data_ops_;
|
||||
uint64_t num_ordered_ops_to_merge_;
|
||||
bool has_seq_ops_;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -266,14 +266,15 @@ chunk_t Snapuserd::GetNextAllocatableChunkId(chunk_t chunk) {
|
|||
|
||||
void Snapuserd::CheckMergeCompletionStatus() {
|
||||
if (!merge_initiated_) {
|
||||
SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: " << reader_->total_data_ops();
|
||||
SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: "
|
||||
<< reader_->get_num_total_data_ops();
|
||||
return;
|
||||
}
|
||||
|
||||
struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
|
||||
|
||||
SNAP_LOG(INFO) << "Merge-status: Total-Merged-ops: " << ch->num_merge_ops
|
||||
<< " Total-data-ops: " << reader_->total_data_ops();
|
||||
<< " Total-data-ops: " << reader_->get_num_total_data_ops();
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -352,7 +353,6 @@ bool Snapuserd::ReadMetadata() {
|
|||
return false;
|
||||
}
|
||||
|
||||
reader_->InitializeMerge();
|
||||
SNAP_LOG(DEBUG) << "Merge-ops: " << header.num_merge_ops;
|
||||
|
||||
if (!MmapMetadata()) {
|
||||
|
|
@ -361,7 +361,7 @@ bool Snapuserd::ReadMetadata() {
|
|||
}
|
||||
|
||||
// Initialize the iterator for reading metadata
|
||||
std::unique_ptr<ICowOpIter> cowop_riter = reader_->GetRevOpIter();
|
||||
std::unique_ptr<ICowOpIter> cowop_rm_iter = reader_->GetRevMergeOpIter();
|
||||
|
||||
exceptions_per_area_ = (CHUNK_SIZE << SECTOR_SHIFT) / sizeof(struct disk_exception);
|
||||
|
||||
|
|
@ -379,16 +379,11 @@ bool Snapuserd::ReadMetadata() {
|
|||
// this memset will ensure that metadata read is completed.
|
||||
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
|
||||
|
||||
while (!cowop_riter->Done()) {
|
||||
const CowOperation* cow_op = &cowop_riter->Get();
|
||||
while (!cowop_rm_iter->Done()) {
|
||||
const CowOperation* cow_op = &cowop_rm_iter->Get();
|
||||
struct disk_exception* de =
|
||||
reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
|
||||
|
||||
if (IsMetadataOp(*cow_op)) {
|
||||
cowop_riter->Next();
|
||||
continue;
|
||||
}
|
||||
|
||||
metadata_found = true;
|
||||
// This loop will handle all the replace and zero ops.
|
||||
// We will handle the copy ops later as it requires special
|
||||
|
|
@ -415,7 +410,7 @@ bool Snapuserd::ReadMetadata() {
|
|||
chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
|
||||
num_ops += 1;
|
||||
offset += sizeof(struct disk_exception);
|
||||
cowop_riter->Next();
|
||||
cowop_rm_iter->Next();
|
||||
|
||||
SNAP_LOG(DEBUG) << num_ops << ":"
|
||||
<< " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
|
||||
|
|
@ -432,7 +427,7 @@ bool Snapuserd::ReadMetadata() {
|
|||
sizeof(struct disk_exception));
|
||||
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
|
||||
|
||||
if (cowop_riter->Done()) {
|
||||
if (cowop_rm_iter->Done()) {
|
||||
vec_.push_back(std::move(de_ptr));
|
||||
}
|
||||
}
|
||||
|
|
@ -445,18 +440,15 @@ bool Snapuserd::ReadMetadata() {
|
|||
std::map<uint64_t, const CowOperation*> map;
|
||||
std::set<uint64_t> dest_blocks;
|
||||
size_t pending_copy_ops = exceptions_per_area_ - num_ops;
|
||||
uint64_t total_copy_ops = reader_->total_copy_ops();
|
||||
uint64_t total_copy_ops = reader_->get_num_ordered_ops_to_merge();
|
||||
|
||||
SNAP_LOG(DEBUG) << " Processing copy-ops at Area: " << vec_.size()
|
||||
<< " Number of replace/zero ops completed in this area: " << num_ops
|
||||
<< " Pending copy ops for this area: " << pending_copy_ops;
|
||||
while (!cowop_riter->Done()) {
|
||||
|
||||
while (!cowop_rm_iter->Done()) {
|
||||
do {
|
||||
const CowOperation* cow_op = &cowop_riter->Get();
|
||||
if (IsMetadataOp(*cow_op)) {
|
||||
cowop_riter->Next();
|
||||
continue;
|
||||
}
|
||||
const CowOperation* cow_op = &cowop_rm_iter->Get();
|
||||
|
||||
// We have two cases specific cases:
|
||||
//
|
||||
|
|
@ -572,8 +564,8 @@ bool Snapuserd::ReadMetadata() {
|
|||
map[cow_op->new_block] = cow_op;
|
||||
dest_blocks.insert(cow_op->source);
|
||||
prev_id = cow_op->new_block;
|
||||
cowop_riter->Next();
|
||||
} while (!cowop_riter->Done() && pending_copy_ops);
|
||||
cowop_rm_iter->Next();
|
||||
} while (!cowop_rm_iter->Done() && pending_copy_ops);
|
||||
|
||||
data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
|
||||
SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << map.size()
|
||||
|
|
@ -611,7 +603,7 @@ bool Snapuserd::ReadMetadata() {
|
|||
sizeof(struct disk_exception));
|
||||
memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
|
||||
|
||||
if (cowop_riter->Done()) {
|
||||
if (cowop_rm_iter->Done()) {
|
||||
vec_.push_back(std::move(de_ptr));
|
||||
SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
|
||||
}
|
||||
|
|
@ -661,7 +653,7 @@ bool Snapuserd::ReadMetadata() {
|
|||
<< " Replace-ops: " << replace_ops << " Zero-ops: " << zero_ops
|
||||
<< " Copy-ops: " << copy_ops << " Areas: " << vec_.size()
|
||||
<< " Num-ops-merged: " << header.num_merge_ops
|
||||
<< " Total-data-ops: " << reader_->total_data_ops();
|
||||
<< " Total-data-ops: " << reader_->get_num_total_data_ops();
|
||||
|
||||
// Total number of sectors required for creating dm-user device
|
||||
num_sectors_ = ChunkToSector(data_chunk_id);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue