From 09ccef4961659a724b860a5e166024a65757c15e Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 16 Jun 2023 13:47:56 -0700 Subject: [PATCH 1/2] libsnapshot: Add helpers for accessing CowOperation offsets. These helpers avoid manual inspection of CowOperation::source, and fiddling with block sizes. Bug: 280529365 Test: cow_api_test vts_libsnapshot_test apply OTA Change-Id: I1ba276e155eb8232a3115066caaa11030c171e11 --- .../include/libsnapshot/cow_reader.h | 13 ++++++++ .../libsnapshot_cow/cow_reader.cpp | 29 ++++++++++------ .../libsnapshot_cow/snapshot_reader.cpp | 14 ++++++-- .../dm-snapshot-merge/snapuserd_worker.cpp | 13 ++++---- .../user-space-merge/snapuserd_dm_user.cpp | 11 ++++--- .../user-space-merge/snapuserd_readahead.cpp | 33 +++++++++++-------- .../user-space-merge/snapuserd_readahead.h | 1 + 7 files changed, 77 insertions(+), 37 deletions(-) diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h index f5995015a..d0369e5d7 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h @@ -73,8 +73,20 @@ class ICowReader { // The operation pointer must derive from ICowOpIter::Get(). virtual ssize_t ReadData(const CowOperation* op, void* buffer, size_t buffer_size, size_t ignore_bytes = 0) = 0; + + // Get the absolute source offset, in bytes, of a CowOperation. Returns + // false if the operation does not read from source partitions. + virtual bool GetSourceOffset(const CowOperation* op, uint64_t* source_offset) = 0; }; +static constexpr uint64_t GetBlockFromOffset(const CowHeader& header, uint64_t offset) { + return offset / header.block_size; +} + +static constexpr uint64_t GetBlockRelativeOffset(const CowHeader& header, uint64_t offset) { + return offset % header.block_size; +} + // Iterate over a sequence of COW operations. The iterator is bidirectional. class ICowOpIter { public: @@ -119,6 +131,7 @@ class CowReader final : public ICowReader { bool VerifyMergeOps() override; bool GetFooter(CowFooter* footer) override; bool GetLastLabel(uint64_t* label) override; + bool GetSourceOffset(const CowOperation* op, uint64_t* source_offset) override; // Create a CowOpIter object which contains footer_.num_ops // CowOperation objects. Get() returns a unique CowOperation object diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp index 4ac89ae4f..cffc8dedb 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp @@ -312,19 +312,15 @@ bool CowReader::VerifyMergeOps() { std::unordered_map overwritten_blocks; while (!itr->AtEnd()) { const auto& op = itr->Get(); - uint64_t block; - bool offset; - if (op->type == kCowCopyOp) { - block = op->source; - offset = false; - } else if (op->type == kCowXorOp) { - block = op->source / header_.block_size; - offset = (op->source % header_.block_size) != 0; - } else { + uint64_t offset; + if (!GetSourceOffset(op, &offset)) { itr->Next(); continue; } + uint64_t block = GetBlockFromOffset(header_, offset); + bool misaligned = (GetBlockRelativeOffset(header_, offset) != 0); + const CowOperation* overwrite = nullptr; if (overwritten_blocks.count(block)) { overwrite = overwritten_blocks[block]; @@ -332,7 +328,7 @@ bool CowReader::VerifyMergeOps() { << op << "\noverwritten by previously merged op:\n" << *overwrite; } - if (offset && overwritten_blocks.count(block + 1)) { + if (misaligned && overwritten_blocks.count(block + 1)) { overwrite = overwritten_blocks[block + 1]; LOG(ERROR) << "Invalid Sequence! Block needed for op:\n" << op << "\noverwritten by previously merged op:\n" @@ -622,5 +618,18 @@ ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_ return decompressor->Decompress(buffer, buffer_size, header_.block_size, ignore_bytes); } +bool CowReader::GetSourceOffset(const CowOperation* op, uint64_t* source_offset) { + switch (op->type) { + case kCowCopyOp: + *source_offset = op->source * header_.block_size; + return true; + case kCowXorOp: + *source_offset = op->source; + return true; + default: + return false; + } +} + } // namespace snapshot } // namespace android diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp index f9cdbc0fe..a3e40d962 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/snapshot_reader.cpp @@ -155,7 +155,12 @@ ssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, size_t start_offset, } if (op) { - chunk = op->source; + uint64_t source_offset; + if (!cow_->GetSourceOffset(op, &source_offset)) { + LOG(ERROR) << "GetSourceOffset failed in CompressedSnapshotReader for op: " << *op; + return false; + } + chunk = GetBlockFromOffset(cow_->GetHeader(), source_offset); } off64_t offset = (chunk * block_size_) + start_offset; @@ -179,7 +184,12 @@ ssize_t CompressedSnapshotReader::ReadBlock(uint64_t chunk, size_t start_offset, return -1; } - off64_t offset = op->source + start_offset; + uint64_t source_offset; + if (!cow_->GetSourceOffset(op, &source_offset)) { + LOG(ERROR) << "GetSourceOffset failed in CompressedSnapshotReader for op: " << *op; + return false; + } + off64_t offset = source_offset + start_offset; std::string data(bytes_to_read, '\0'); if (!android::base::ReadFullyAtOffset(fd, data.data(), data.size(), offset)) { diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp index 922df3424..559dcc793 100644 --- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp +++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_worker.cpp @@ -115,12 +115,13 @@ bool WorkerThread::ReadFromBaseDevice(const CowOperation* cow_op) { SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer"; return false; } - SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block - << " Source: " << cow_op->source; - uint64_t offset = cow_op->source; - if (cow_op->type == kCowCopyOp) { - offset *= BLOCK_SZ; + uint64_t offset; + if (!reader_->GetSourceOffset(cow_op, &offset)) { + SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get source offset"; + return false; } + SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block + << " Source: " << offset; if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) { SNAP_PLOG(ERROR) << "Copy op failed. Read from backing store: " << backing_store_device_ << "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ; @@ -508,7 +509,7 @@ int WorkerThread::GetNumberOfMergedOps(void* merged_buffer, void* unmerged_buffe if (read_ahead_buffer_map.find(cow_op->new_block) == read_ahead_buffer_map.end()) { SNAP_LOG(ERROR) << " Block: " << cow_op->new_block << " not found in read-ahead cache" - << " Source: " << cow_op->source; + << " Op: " << *cow_op; return -1; } // If this is a final block merged in the read-ahead buffer diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp index 78582164f..44b731912 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp @@ -94,12 +94,13 @@ bool Worker::ReadFromSourceDevice(const CowOperation* cow_op) { SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer"; return false; } - SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block - << " Source: " << cow_op->source; - uint64_t offset = cow_op->source; - if (cow_op->type == kCowCopyOp) { - offset *= BLOCK_SZ; + uint64_t offset; + if (!reader_->GetSourceOffset(cow_op, &offset)) { + SNAP_LOG(ERROR) << "ReadFromSourceDevice: Failed to get source offset"; + return false; } + SNAP_LOG(DEBUG) << " ReadFromBaseDevice...: new-block: " << cow_op->new_block + << " Op: " << *cow_op; if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) { std::string op; if (cow_op->type == kCowCopyOp) diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp index af2428602..399f7b81b 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp @@ -34,14 +34,17 @@ ReadAhead::ReadAhead(const std::string& cow_device, const std::string& backing_d } void ReadAhead::CheckOverlap(const CowOperation* cow_op) { - uint64_t source_block = cow_op->source; - uint64_t source_offset = 0; - if (cow_op->type == kCowXorOp) { - source_block /= BLOCK_SZ; - source_offset = cow_op->source % BLOCK_SZ; + uint64_t source_offset; + if (!reader_->GetSourceOffset(cow_op, &source_offset)) { + SNAP_LOG(ERROR) << "ReadAhead operation has no source offset: " << *cow_op; + return; } + + uint64_t source_block = GetBlockFromOffset(header_, source_offset); + bool misaligned = (GetBlockRelativeOffset(header_, source_offset) != 0); + if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block) || - (source_offset > 0 && source_blocks_.count(source_block + 1))) { + (misaligned && source_blocks_.count(source_block + 1))) { overlap_ = true; } @@ -66,11 +69,12 @@ int ReadAhead::PrepareNextReadAhead(uint64_t* source_offset, int* pending_ops, // Get the first block with offset const CowOperation* cow_op = GetRAOpIter(); - *source_offset = cow_op->source; - if (cow_op->type == kCowCopyOp) { - *source_offset *= BLOCK_SZ; - } else if (cow_op->type == kCowXorOp) { + if (!reader_->GetSourceOffset(cow_op, source_offset)) { + SNAP_LOG(ERROR) << "PrepareNextReadAhead operation has no source offset: " << *cow_op; + return nr_consecutive; + } + if (cow_op->type == kCowXorOp) { xor_op_vec.push_back(cow_op); } @@ -88,10 +92,10 @@ int ReadAhead::PrepareNextReadAhead(uint64_t* source_offset, int* pending_ops, */ while (!RAIterDone() && num_ops) { const CowOperation* op = GetRAOpIter(); - uint64_t next_offset = op->source; - - if (cow_op->type == kCowCopyOp) { - next_offset *= BLOCK_SZ; + uint64_t next_offset; + if (!reader_->GetSourceOffset(op, &next_offset)) { + SNAP_LOG(ERROR) << "PrepareNextReadAhead operation has no source offset: " << *cow_op; + break; } // Check for consecutive blocks @@ -803,6 +807,7 @@ bool ReadAhead::InitReader() { if (!reader_->InitForMerge(std::move(cow_fd_))) { return false; } + header_ = reader_->GetHeader(); return true; } diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h index 5e94de005..d3ba1265b 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.h @@ -85,6 +85,7 @@ class ReadAhead { std::shared_ptr snapuserd_; std::unique_ptr reader_; + CowHeader header_; std::unordered_set dest_blocks_; std::unordered_set source_blocks_; From cf311bd00f72a4a36e7857beebc29369b9bcd4a0 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Fri, 16 Jun 2023 13:59:31 -0700 Subject: [PATCH 2/2] libsnapshot: Remove direct accesses of CowOperation::source and compression. For the remaining use cases of accessing CowOperation::source and compression, directly, introduce helper functions, since these fields will be removed in the v3 format. Bug: 280529365 Test: cow_api_test Change-Id: I07031e8968525a7c1314ca45c284e476b51d8104 --- .../libsnapshot/include/libsnapshot/cow_format.h | 7 +++++++ .../libsnapshot/include/libsnapshot/cow_reader.h | 1 + .../libsnapshot/libsnapshot_cow/cow_reader.cpp | 16 ++++++++++------ fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp | 16 ++++++++-------- .../snapuserd/dm-snapshot-merge/snapuserd.cpp | 2 +- .../dm-snapshot-merge/snapuserd_readahead.cpp | 6 +++--- 6 files changed, 30 insertions(+), 18 deletions(-) diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h index dd626bc46..3a81f6384 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_format.h @@ -166,6 +166,13 @@ static constexpr uint8_t kCowReadAheadNotStarted = 0; static constexpr uint8_t kCowReadAheadInProgress = 1; static constexpr uint8_t kCowReadAheadDone = 2; +static inline uint64_t GetCowOpSourceInfoData(const CowOperation* op) { + return op->source; +} +static inline bool GetCowOpSourceInfoCompression(const CowOperation* op) { + return op->compression != kCowCompressNone; +} + struct CowFooter { CowFooterOperation op; uint8_t unused[64]; diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h index d0369e5d7..f4ce52fa3 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h @@ -168,6 +168,7 @@ class CowReader final : public ICowReader { bool ParseOps(std::optional label); bool PrepMergeOps(); uint64_t FindNumCopyops(); + uint8_t GetCompressionType(const CowOperation* op); android::base::unique_fd owned_fd_; android::base::borrowed_fd fd_; diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp index cffc8dedb..489669ac0 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/cow_reader.cpp @@ -517,7 +517,7 @@ bool CowReader::GetRawBytes(const CowOperation* op, void* buffer, size_t len, si case kCowSequenceOp: case kCowReplaceOp: case kCowXorOp: - return GetRawBytes(op->source, buffer, len, read); + return GetRawBytes(GetCowOpSourceInfoData(op), buffer, len, read); default: LOG(ERROR) << "Cannot get raw bytes of non-data op: " << *op; return false; @@ -574,10 +574,14 @@ class CowDataStream final : public IByteStream { size_t remaining_; }; +uint8_t CowReader::GetCompressionType(const CowOperation* op) { + return op->compression; +} + ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_size, size_t ignore_bytes) { std::unique_ptr decompressor; - switch (op->compression) { + switch (GetCompressionType(op)) { case kCowCompressNone: break; case kCowCompressGz: @@ -597,7 +601,7 @@ ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_ } break; default: - LOG(ERROR) << "Unknown compression type: " << op->compression; + LOG(ERROR) << "Unknown compression type: " << GetCompressionType(op); return -1; } @@ -605,7 +609,7 @@ ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_ if (op->type == kCowXorOp) { offset = data_loc_->at(op->new_block); } else { - offset = op->source; + offset = GetCowOpSourceInfoData(op); } if (!decompressor) { @@ -621,10 +625,10 @@ ssize_t CowReader::ReadData(const CowOperation* op, void* buffer, size_t buffer_ bool CowReader::GetSourceOffset(const CowOperation* op, uint64_t* source_offset) { switch (op->type) { case kCowCopyOp: - *source_offset = op->source * header_.block_size; + *source_offset = GetCowOpSourceInfoData(op) * header_.block_size; return true; case kCowXorOp: - *source_offset = op->source; + *source_offset = GetCowOpSourceInfoData(op); return true; default: return false; diff --git a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp index 120b2c062..31b9a5840 100644 --- a/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp +++ b/fs_mgr/libsnapshot/libsnapshot_cow/test_v2.cpp @@ -145,7 +145,7 @@ TEST_F(CowTest, ReadWrite) { op = iter->Get(); ASSERT_EQ(op->type, kCowReplaceOp); - ASSERT_EQ(op->compression, kCowCompressNone); + ASSERT_FALSE(GetCowOpSourceInfoCompression(op)); ASSERT_EQ(op->data_length, 4096); ASSERT_EQ(op->new_block, 50); ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size())); @@ -224,10 +224,10 @@ TEST_F(CowTest, ReadWriteXor) { op = iter->Get(); ASSERT_EQ(op->type, kCowXorOp); - ASSERT_EQ(op->compression, kCowCompressNone); + ASSERT_FALSE(GetCowOpSourceInfoCompression(op)); ASSERT_EQ(op->data_length, 4096); ASSERT_EQ(op->new_block, 50); - ASSERT_EQ(op->source, 98314); // 4096 * 24 + 10 + ASSERT_EQ(GetCowOpSourceInfoData(op), 98314); // 4096 * 24 + 10 ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size())); ASSERT_EQ(sink, data); @@ -283,7 +283,7 @@ TEST_F(CowTest, CompressGz) { std::string sink(data.size(), '\0'); ASSERT_EQ(op->type, kCowReplaceOp); - ASSERT_EQ(op->compression, kCowCompressGz); + ASSERT_TRUE(GetCowOpSourceInfoCompression(op)); ASSERT_EQ(op->data_length, 56); // compressed! ASSERT_EQ(op->new_block, 50); ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size())); @@ -339,7 +339,7 @@ TEST_P(CompressionTest, ThreadedBatchWrites) { total_blocks += 1; std::string sink(xor_data.size(), '\0'); ASSERT_EQ(op->new_block, 50); - ASSERT_EQ(op->source, 98314); // 4096 * 24 + 10 + ASSERT_EQ(GetCowOpSourceInfoData(op), 98314); // 4096 * 24 + 10 ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size())); ASSERT_EQ(sink, xor_data); } @@ -528,7 +528,7 @@ TEST_F(CowTest, ClusterCompressGz) { std::string sink(data.size(), '\0'); ASSERT_EQ(op->type, kCowReplaceOp); - ASSERT_EQ(op->compression, kCowCompressGz); + ASSERT_TRUE(GetCowOpSourceInfoCompression(op)); ASSERT_EQ(op->data_length, 56); // compressed! ASSERT_EQ(op->new_block, 50); ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size())); @@ -546,7 +546,7 @@ TEST_F(CowTest, ClusterCompressGz) { sink = {}; sink.resize(data2.size(), '\0'); - ASSERT_EQ(op->compression, kCowCompressGz); + ASSERT_TRUE(GetCowOpSourceInfoCompression(op)); ASSERT_EQ(op->data_length, 41); // compressed! ASSERT_EQ(op->new_block, 51); ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size())); @@ -591,7 +591,7 @@ TEST_F(CowTest, CompressTwoBlocks) { auto op = iter->Get(); ASSERT_EQ(op->type, kCowReplaceOp); - ASSERT_EQ(op->compression, kCowCompressGz); + ASSERT_TRUE(GetCowOpSourceInfoCompression(op)); ASSERT_EQ(op->new_block, 51); ASSERT_TRUE(ReadData(reader, op, sink.data(), sink.size())); } diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp index da9bd113f..978a7f25c 100644 --- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp +++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd.cpp @@ -508,7 +508,7 @@ bool Snapuserd::ReadMetadata() { // the merge of operations are done based on the ops present // in the file. //=========================================================== - uint64_t block_source = cow_op->source; + uint64_t block_source = GetCowOpSourceInfoData(cow_op); if (prev_id.has_value()) { if (dest_blocks.count(cow_op->new_block) || source_blocks.count(block_source)) { break; diff --git a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp index a32c2bfdd..d5fbe91a3 100644 --- a/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp +++ b/fs_mgr/libsnapshot/snapuserd/dm-snapshot-merge/snapuserd_readahead.cpp @@ -172,7 +172,7 @@ ReadAheadThread::ReadAheadThread(const std::string& cow_device, const std::strin } void ReadAheadThread::CheckOverlap(const CowOperation* cow_op) { - uint64_t source_block = cow_op->source; + uint64_t source_block = GetCowOpSourceInfoData(cow_op); if (dest_blocks_.count(cow_op->new_block) || source_blocks_.count(source_block)) { overlap_ = true; } @@ -191,7 +191,7 @@ void ReadAheadThread::PrepareReadAhead(uint64_t* source_offset, int* pending_ops // Get the first block with offset const CowOperation* cow_op = GetRAOpIter(); CHECK_NE(cow_op, nullptr); - *source_offset = cow_op->source; + *source_offset = GetCowOpSourceInfoData(cow_op); if (cow_op->type == kCowCopyOp) { *source_offset *= BLOCK_SZ; } @@ -210,7 +210,7 @@ void ReadAheadThread::PrepareReadAhead(uint64_t* source_offset, int* pending_ops while (!RAIterDone() && num_ops) { const CowOperation* op = GetRAOpIter(); CHECK_NE(op, nullptr); - uint64_t next_offset = op->source; + uint64_t next_offset = GetCowOpSourceInfoData(op); if (op->type == kCowCopyOp) { next_offset *= BLOCK_SZ; }