diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp index 57d108f0f..b3198a092 100644 --- a/fs_mgr/libsnapshot/cow_reader.cpp +++ b/fs_mgr/libsnapshot/cow_reader.cpp @@ -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> ops); - - bool Done() override; - const CowOperation& Get() override; - void Next() override; - - private: - std::shared_ptr> ops_; - std::vector::reverse_iterator op_riter_; -}; - -CowOpReverseIter::CowOpReverseIter(std::shared_ptr> 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> ops, @@ -687,10 +527,6 @@ std::unique_ptr CowReader::GetOpIter() { return std::make_unique(ops_); } -std::unique_ptr CowReader::GetRevOpIter() { - return std::make_unique(ops_); -} - std::unique_ptr CowReader::GetRevMergeOpIter() { return std::make_unique(ops_, merge_op_blocks_, block_map_); } diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h index cdfe2f196..05f70662c 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h @@ -75,9 +75,6 @@ class ICowReader { // Return an iterator for retrieving CowOperation entries. virtual std::unique_ptr GetOpIter() = 0; - // Return an reverse iterator for retrieving CowOperation entries. - virtual std::unique_ptr GetRevOpIter() = 0; - // Return an iterator for retrieving CowOperation entries in merge order virtual std::unique_ptr 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 GetOpIter() override; - std::unique_ptr GetRevOpIter() override; std::unique_ptr 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> ops_; std::shared_ptr> merge_op_blocks_; std::shared_ptr> 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_; }; diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp index fc1a9b729..e578a7676 100644 --- a/fs_mgr/libsnapshot/snapuserd.cpp +++ b/fs_mgr/libsnapshot/snapuserd.cpp @@ -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(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 cowop_riter = reader_->GetRevOpIter(); + std::unique_ptr 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((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 map; std::set 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);