summaryrefslogtreecommitdiff
path: root/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp')
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp645
1 files changed, 0 insertions, 645 deletions
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
deleted file mode 100644
index 1e300d2d4..000000000
--- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_dm_user.cpp
+++ /dev/null
@@ -1,645 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "snapuserd_core.h"
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-Worker::Worker(const std::string& cow_device, const std::string& backing_device,
- const std::string& control_device, const std::string& misc_name,
- const std::string& base_path_merge, std::shared_ptr<SnapshotHandler> snapuserd) {
- cow_device_ = cow_device;
- backing_store_device_ = backing_device;
- control_device_ = control_device;
- misc_name_ = misc_name;
- base_path_merge_ = base_path_merge;
- snapuserd_ = snapuserd;
-}
-
-bool Worker::InitializeFds() {
- backing_store_fd_.reset(open(backing_store_device_.c_str(), O_RDONLY));
- if (backing_store_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << backing_store_device_;
- return false;
- }
-
- cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
- if (cow_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
- return false;
- }
-
- ctrl_fd_.reset(open(control_device_.c_str(), O_RDWR));
- if (ctrl_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Unable to open " << control_device_;
- return false;
- }
-
- // Base device used by merge thread
- base_path_merge_fd_.reset(open(base_path_merge_.c_str(), O_RDWR));
- if (base_path_merge_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << base_path_merge_;
- return false;
- }
-
- return true;
-}
-
-bool Worker::InitReader() {
- reader_ = snapuserd_->CloneReaderForWorker();
-
- if (!reader_->InitForMerge(std::move(cow_fd_))) {
- return false;
- }
- return true;
-}
-
-// Start the replace operation. This will read the
-// internal COW format and if the block is compressed,
-// it will be de-compressed.
-bool Worker::ProcessReplaceOp(const CowOperation* cow_op) {
- if (!reader_->ReadData(*cow_op, &bufsink_)) {
- SNAP_LOG(ERROR) << "ProcessReplaceOp failed for block " << cow_op->new_block;
- return false;
- }
-
- return true;
-}
-
-bool Worker::ReadFromSourceDevice(const CowOperation* cow_op) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- 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;
- }
- if (!android::base::ReadFullyAtOffset(backing_store_fd_, buffer, BLOCK_SZ, offset)) {
- std::string op;
- if (cow_op->type == kCowCopyOp)
- op = "Copy-op";
- else {
- op = "Xor-op";
- }
- SNAP_PLOG(ERROR) << op << " failed. Read from backing store: " << backing_store_device_
- << "at block :" << offset / BLOCK_SZ << " offset:" << offset % BLOCK_SZ;
- return false;
- }
-
- return true;
-}
-
-// Start the copy operation. This will read the backing
-// block device which is represented by cow_op->source.
-bool Worker::ProcessCopyOp(const CowOperation* cow_op) {
- if (!ReadFromSourceDevice(cow_op)) {
- return false;
- }
-
- return true;
-}
-
-bool Worker::ProcessXorOp(const CowOperation* cow_op) {
- if (!ReadFromSourceDevice(cow_op)) {
- return false;
- }
- xorsink_.Reset();
- if (!reader_->ReadData(*cow_op, &xorsink_)) {
- SNAP_LOG(ERROR) << "ProcessXorOp failed for block " << cow_op->new_block;
- return false;
- }
-
- return true;
-}
-
-bool Worker::ProcessZeroOp() {
- // Zero out the entire block
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ProcessZeroOp: Failed to get payload buffer";
- return false;
- }
-
- memset(buffer, 0, BLOCK_SZ);
- return true;
-}
-
-bool Worker::ProcessOrderedOp(const CowOperation* cow_op) {
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ProcessOrderedOp: Failed to get payload buffer";
- return false;
- }
-
- MERGE_GROUP_STATE state = snapuserd_->ProcessMergingBlock(cow_op->new_block, buffer);
-
- switch (state) {
- case MERGE_GROUP_STATE::GROUP_MERGE_COMPLETED: {
- // Merge is completed for this COW op; just read directly from
- // the base device
- SNAP_LOG(DEBUG) << "Merge-completed: Reading from base device sector: "
- << (cow_op->new_block >> SECTOR_SHIFT)
- << " Block-number: " << cow_op->new_block;
- if (!ReadDataFromBaseDevice(ChunkToSector(cow_op->new_block), BLOCK_SZ)) {
- SNAP_LOG(ERROR) << "ReadDataFromBaseDevice at sector: "
- << (cow_op->new_block >> SECTOR_SHIFT) << " after merge-complete.";
- return false;
- }
- return true;
- }
- case MERGE_GROUP_STATE::GROUP_MERGE_PENDING: {
- bool ret;
- if (cow_op->type == kCowCopyOp) {
- ret = ProcessCopyOp(cow_op);
- } else {
- ret = ProcessXorOp(cow_op);
- }
-
- // I/O is complete - decrement the refcount irrespective of the return
- // status
- snapuserd_->NotifyIOCompletion(cow_op->new_block);
- return ret;
- }
- // We already have the data in the buffer retrieved from RA thread.
- // Nothing to process further.
- case MERGE_GROUP_STATE::GROUP_MERGE_RA_READY: {
- [[fallthrough]];
- }
- case MERGE_GROUP_STATE::GROUP_MERGE_IN_PROGRESS: {
- return true;
- }
- default: {
- // All other states, fail the I/O viz (GROUP_MERGE_FAILED and GROUP_INVALID)
- return false;
- }
- }
-
- return false;
-}
-
-bool Worker::ProcessCowOp(const CowOperation* cow_op) {
- if (cow_op == nullptr) {
- SNAP_LOG(ERROR) << "ProcessCowOp: Invalid cow_op";
- return false;
- }
-
- switch (cow_op->type) {
- case kCowReplaceOp: {
- return ProcessReplaceOp(cow_op);
- }
-
- case kCowZeroOp: {
- return ProcessZeroOp();
- }
-
- case kCowCopyOp:
- [[fallthrough]];
- case kCowXorOp: {
- return ProcessOrderedOp(cow_op);
- }
-
- default: {
- SNAP_LOG(ERROR) << "Unknown operation-type found: " << cow_op->type;
- }
- }
- return false;
-}
-
-void Worker::InitializeBufsink() {
- // Allocate the buffer which is used to communicate between
- // daemon and dm-user. The buffer comprises of header and a fixed payload.
- // If the dm-user requests a big IO, the IO will be broken into chunks
- // of PAYLOAD_BUFFER_SZ.
- size_t buf_size = sizeof(struct dm_user_header) + PAYLOAD_BUFFER_SZ;
- bufsink_.Initialize(buf_size);
-}
-
-bool Worker::Init() {
- InitializeBufsink();
- xorsink_.Initialize(&bufsink_, BLOCK_SZ);
-
- if (!InitializeFds()) {
- return false;
- }
-
- if (!InitReader()) {
- return false;
- }
-
- return true;
-}
-
-bool Worker::RunThread() {
- SNAP_LOG(INFO) << "Processing snapshot I/O requests....";
- // Start serving IO
- while (true) {
- if (!ProcessIORequest()) {
- break;
- }
- }
-
- CloseFds();
- reader_->CloseCowFd();
-
- return true;
-}
-
-// Read Header from dm-user misc device. This gives
-// us the sector number for which IO is issued by dm-snapshot device
-bool Worker::ReadDmUserHeader() {
- if (!android::base::ReadFully(ctrl_fd_, bufsink_.GetBufPtr(), sizeof(struct dm_user_header))) {
- if (errno != ENOTBLK) {
- SNAP_PLOG(ERROR) << "Control-read failed";
- }
-
- SNAP_PLOG(DEBUG) << "ReadDmUserHeader failed....";
- return false;
- }
-
- return true;
-}
-
-// Send the payload/data back to dm-user misc device.
-bool Worker::WriteDmUserPayload(size_t size, bool header_response) {
- size_t payload_size = size;
- void* buf = bufsink_.GetPayloadBufPtr();
- if (header_response) {
- payload_size += sizeof(struct dm_user_header);
- buf = bufsink_.GetBufPtr();
- }
-
- if (!android::base::WriteFully(ctrl_fd_, buf, payload_size)) {
- SNAP_PLOG(ERROR) << "Write to dm-user failed size: " << payload_size;
- return false;
- }
-
- return true;
-}
-
-bool Worker::ReadDataFromBaseDevice(sector_t sector, size_t read_size) {
- CHECK(read_size <= BLOCK_SZ);
-
- void* buffer = bufsink_.GetPayloadBuffer(BLOCK_SZ);
- if (buffer == nullptr) {
- SNAP_LOG(ERROR) << "ReadFromBaseDevice: Failed to get payload buffer";
- return false;
- }
-
- loff_t offset = sector << SECTOR_SHIFT;
- if (!android::base::ReadFullyAtOffset(base_path_merge_fd_, buffer, read_size, offset)) {
- SNAP_PLOG(ERROR) << "ReadDataFromBaseDevice failed. fd: " << base_path_merge_fd_
- << "at sector :" << sector << " size: " << read_size;
- return false;
- }
-
- return true;
-}
-
-bool Worker::ReadAlignedSector(sector_t sector, size_t sz, bool header_response) {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
- size_t remaining_size = sz;
- std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
- bool io_error = false;
- int ret = 0;
-
- do {
- // Process 1MB payload at a time
- size_t read_size = std::min(PAYLOAD_BUFFER_SZ, remaining_size);
-
- header->type = DM_USER_RESP_SUCCESS;
- size_t total_bytes_read = 0;
- io_error = false;
- bufsink_.ResetBufferOffset();
-
- while (read_size) {
- // We need to check every 4k block to verify if it is
- // present in the mapping.
- size_t size = std::min(BLOCK_SZ, read_size);
-
- auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(),
- std::make_pair(sector, nullptr), SnapshotHandler::compare);
- bool not_found = (it == chunk_vec.end() || it->first != sector);
-
- if (not_found) {
- // Block not found in map - which means this block was not
- // changed as per the OTA. Just route the I/O to the base
- // device.
- if (!ReadDataFromBaseDevice(sector, size)) {
- SNAP_LOG(ERROR) << "ReadDataFromBaseDevice failed";
- header->type = DM_USER_RESP_ERROR;
- }
-
- ret = size;
- } else {
- // We found the sector in mapping. Check the type of COW OP and
- // process it.
- if (!ProcessCowOp(it->second)) {
- SNAP_LOG(ERROR) << "ProcessCowOp failed";
- header->type = DM_USER_RESP_ERROR;
- }
-
- ret = BLOCK_SZ;
- }
-
- // Just return the header if it is an error
- if (header->type == DM_USER_RESP_ERROR) {
- if (!RespondIOError(header_response)) {
- return false;
- }
-
- io_error = true;
- break;
- }
-
- read_size -= ret;
- total_bytes_read += ret;
- sector += (ret >> SECTOR_SHIFT);
- bufsink_.UpdateBufferOffset(ret);
- }
-
- if (!io_error) {
- if (!WriteDmUserPayload(total_bytes_read, header_response)) {
- return false;
- }
-
- SNAP_LOG(DEBUG) << "WriteDmUserPayload success total_bytes_read: " << total_bytes_read
- << " header-response: " << header_response
- << " remaining_size: " << remaining_size;
- header_response = false;
- remaining_size -= total_bytes_read;
- }
- } while (remaining_size > 0 && !io_error);
-
- return true;
-}
-
-int Worker::ReadUnalignedSector(
- sector_t sector, size_t size,
- std::vector<std::pair<sector_t, const CowOperation*>>::iterator& it) {
- size_t skip_sector_size = 0;
-
- SNAP_LOG(DEBUG) << "ReadUnalignedSector: sector " << sector << " size: " << size
- << " Aligned sector: " << it->first;
-
- if (!ProcessCowOp(it->second)) {
- SNAP_LOG(ERROR) << "ReadUnalignedSector: " << sector << " failed of size: " << size
- << " Aligned sector: " << it->first;
- return -1;
- }
-
- int num_sectors_skip = sector - it->first;
-
- if (num_sectors_skip > 0) {
- skip_sector_size = num_sectors_skip << SECTOR_SHIFT;
- char* buffer = reinterpret_cast<char*>(bufsink_.GetBufPtr());
- struct dm_user_message* msg = (struct dm_user_message*)(&(buffer[0]));
-
- if (skip_sector_size == BLOCK_SZ) {
- SNAP_LOG(ERROR) << "Invalid un-aligned IO request at sector: " << sector
- << " Base-sector: " << it->first;
- return -1;
- }
-
- memmove(msg->payload.buf, (char*)msg->payload.buf + skip_sector_size,
- (BLOCK_SZ - skip_sector_size));
- }
-
- bufsink_.ResetBufferOffset();
- return std::min(size, (BLOCK_SZ - skip_sector_size));
-}
-
-bool Worker::ReadUnalignedSector(sector_t sector, size_t size) {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
- header->type = DM_USER_RESP_SUCCESS;
- bufsink_.ResetBufferOffset();
- std::vector<std::pair<sector_t, const CowOperation*>>& chunk_vec = snapuserd_->GetChunkVec();
-
- auto it = std::lower_bound(chunk_vec.begin(), chunk_vec.end(), std::make_pair(sector, nullptr),
- SnapshotHandler::compare);
-
- // |-------|-------|-------|
- // 0 1 2 3
- //
- // Block 0 - op 1
- // Block 1 - op 2
- // Block 2 - op 3
- //
- // chunk_vec will have block 0, 1, 2 which maps to relavant COW ops.
- //
- // Each block is 4k bytes. Thus, the last block will span 8 sectors
- // ranging till block 3 (However, block 3 won't be in chunk_vec as
- // it doesn't have any mapping to COW ops. Now, if we get an I/O request for a sector
- // spanning between block 2 and block 3, we need to step back
- // and get hold of the last element.
- //
- // Additionally, we need to make sure that the requested sector is
- // indeed within the range of the final sector. It is perfectly valid
- // to get an I/O request for block 3 and beyond which are not mapped
- // to any COW ops. In that case, we just need to read from the base
- // device.
- bool merge_complete = false;
- bool header_response = true;
- if (it == chunk_vec.end()) {
- if (chunk_vec.size() > 0) {
- // I/O request beyond the last mapped sector
- it = std::prev(chunk_vec.end());
- } else {
- // This can happen when a partition merge is complete but snapshot
- // state in /metadata is not yet deleted; during this window if the
- // device is rebooted, subsequent attempt will mount the snapshot.
- // However, since the merge was completed we wouldn't have any
- // mapping to COW ops thus chunk_vec will be empty. In that case,
- // mark this as merge_complete and route the I/O to the base device.
- merge_complete = true;
- }
- } else if (it->first != sector) {
- if (it != chunk_vec.begin()) {
- --it;
- }
- } else {
- return ReadAlignedSector(sector, size, header_response);
- }
-
- loff_t requested_offset = sector << SECTOR_SHIFT;
-
- loff_t final_offset = 0;
- if (!merge_complete) {
- final_offset = it->first << SECTOR_SHIFT;
- }
-
- // Since a COW op span 4k block size, we need to make sure that the requested
- // offset is within the 4k region. Consider the following case:
- //
- // |-------|-------|-------|
- // 0 1 2 3
- //
- // Block 0 - op 1
- // Block 1 - op 2
- //
- // We have an I/O request for a sector between block 2 and block 3. However,
- // we have mapping to COW ops only for block 0 and block 1. Thus, the
- // requested offset in this case is beyond the last mapped COW op size (which
- // is block 1 in this case).
-
- size_t total_bytes_read = 0;
- size_t remaining_size = size;
- int ret = 0;
- if (!merge_complete && (requested_offset >= final_offset) &&
- (requested_offset - final_offset) < BLOCK_SZ) {
- // Read the partial un-aligned data
- ret = ReadUnalignedSector(sector, remaining_size, it);
- if (ret < 0) {
- SNAP_LOG(ERROR) << "ReadUnalignedSector failed for sector: " << sector
- << " size: " << size << " it->sector: " << it->first;
- return RespondIOError(header_response);
- }
-
- remaining_size -= ret;
- total_bytes_read += ret;
- sector += (ret >> SECTOR_SHIFT);
-
- // Send the data back
- if (!WriteDmUserPayload(total_bytes_read, header_response)) {
- return false;
- }
-
- header_response = false;
- // If we still have pending data to be processed, this will be aligned I/O
- if (remaining_size) {
- return ReadAlignedSector(sector, remaining_size, header_response);
- }
- } else {
- // This is all about handling I/O request to be routed to base device
- // as the I/O is not mapped to any of the COW ops.
- loff_t aligned_offset = requested_offset;
- // Align to nearest 4k
- aligned_offset += BLOCK_SZ - 1;
- aligned_offset &= ~(BLOCK_SZ - 1);
- // Find the diff of the aligned offset
- size_t diff_size = aligned_offset - requested_offset;
- CHECK(diff_size <= BLOCK_SZ);
- if (remaining_size < diff_size) {
- if (!ReadDataFromBaseDevice(sector, remaining_size)) {
- return RespondIOError(header_response);
- }
- total_bytes_read += remaining_size;
-
- if (!WriteDmUserPayload(total_bytes_read, header_response)) {
- return false;
- }
- } else {
- if (!ReadDataFromBaseDevice(sector, diff_size)) {
- return RespondIOError(header_response);
- }
-
- total_bytes_read += diff_size;
-
- if (!WriteDmUserPayload(total_bytes_read, header_response)) {
- return false;
- }
-
- remaining_size -= diff_size;
- size_t num_sectors_read = (diff_size >> SECTOR_SHIFT);
- sector += num_sectors_read;
- CHECK(IsBlockAligned(sector << SECTOR_SHIFT));
- header_response = false;
-
- // If we still have pending data to be processed, this will be aligned I/O
- return ReadAlignedSector(sector, remaining_size, header_response);
- }
- }
-
- return true;
-}
-
-bool Worker::RespondIOError(bool header_response) {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
- header->type = DM_USER_RESP_ERROR;
- // This is an issue with the dm-user interface. There
- // is no way to propagate the I/O error back to dm-user
- // if we have already communicated the header back. Header
- // is responded once at the beginning; however I/O can
- // be processed in chunks. If we encounter an I/O error
- // somewhere in the middle of the processing, we can't communicate
- // this back to dm-user.
- //
- // TODO: Fix the interface
- CHECK(header_response);
-
- if (!WriteDmUserPayload(0, header_response)) {
- return false;
- }
-
- // There is no need to process further as we have already seen
- // an I/O error
- return true;
-}
-
-bool Worker::DmuserReadRequest() {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
- // Unaligned I/O request
- if (!IsBlockAligned(header->sector << SECTOR_SHIFT)) {
- return ReadUnalignedSector(header->sector, header->len);
- }
-
- return ReadAlignedSector(header->sector, header->len, true);
-}
-
-bool Worker::ProcessIORequest() {
- struct dm_user_header* header = bufsink_.GetHeaderPtr();
-
- if (!ReadDmUserHeader()) {
- return false;
- }
-
- SNAP_LOG(DEBUG) << "Daemon: msg->seq: " << std::dec << header->seq;
- SNAP_LOG(DEBUG) << "Daemon: msg->len: " << std::dec << header->len;
- SNAP_LOG(DEBUG) << "Daemon: msg->sector: " << std::dec << header->sector;
- SNAP_LOG(DEBUG) << "Daemon: msg->type: " << std::dec << header->type;
- SNAP_LOG(DEBUG) << "Daemon: msg->flags: " << std::dec << header->flags;
-
- switch (header->type) {
- case DM_USER_REQ_MAP_READ: {
- if (!DmuserReadRequest()) {
- return false;
- }
- break;
- }
-
- case DM_USER_REQ_MAP_WRITE: {
- // TODO: We should not get any write request
- // to dm-user as we mount all partitions
- // as read-only. Need to verify how are TRIM commands
- // handled during mount.
- return false;
- }
- }
-
- return true;
-}
-
-} // namespace snapshot
-} // namespace android