summaryrefslogtreecommitdiff
path: root/fs_mgr/libsnapshot/snapuserd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'fs_mgr/libsnapshot/snapuserd.cpp')
-rw-r--r--fs_mgr/libsnapshot/snapuserd.cpp769
1 files changed, 0 insertions, 769 deletions
diff --git a/fs_mgr/libsnapshot/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd.cpp
deleted file mode 100644
index 57a61a791..000000000
--- a/fs_mgr/libsnapshot/snapuserd.cpp
+++ /dev/null
@@ -1,769 +0,0 @@
-/*
- * Copyright (C) 2020 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.h"
-
-#include <csignal>
-#include <optional>
-#include <set>
-
-#include <libsnapshot/snapuserd_client.h>
-
-namespace android {
-namespace snapshot {
-
-using namespace android;
-using namespace android::dm;
-using android::base::unique_fd;
-
-#define SNAP_LOG(level) LOG(level) << misc_name_ << ": "
-#define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": "
-
-Snapuserd::Snapuserd(const std::string& misc_name, const std::string& cow_device,
- const std::string& backing_device) {
- misc_name_ = misc_name;
- cow_device_ = cow_device;
- backing_store_device_ = backing_device;
- control_device_ = "/dev/dm-user/" + misc_name;
-}
-
-bool Snapuserd::InitializeWorkers() {
- for (int i = 0; i < NUM_THREADS_PER_PARTITION; i++) {
- std::unique_ptr<WorkerThread> wt = std::make_unique<WorkerThread>(
- cow_device_, backing_store_device_, control_device_, misc_name_, GetSharedPtr());
-
- worker_threads_.push_back(std::move(wt));
- }
-
- read_ahead_thread_ = std::make_unique<ReadAheadThread>(cow_device_, backing_store_device_,
- misc_name_, GetSharedPtr());
- return true;
-}
-
-bool Snapuserd::CommitMerge(int num_merge_ops) {
- struct CowHeader* ch = reinterpret_cast<struct CowHeader*>(mapped_addr_);
- ch->num_merge_ops += num_merge_ops;
-
- if (read_ahead_feature_ && read_ahead_ops_.size() > 0) {
- struct BufferState* ra_state = GetBufferState();
- ra_state->read_ahead_state = kCowReadAheadInProgress;
- }
-
- int ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);
- if (ret < 0) {
- PLOG(ERROR) << "msync header failed: " << ret;
- return false;
- }
-
- merge_initiated_ = true;
-
- return true;
-}
-
-void Snapuserd::PrepareReadAhead() {
- if (!read_ahead_feature_) {
- return;
- }
-
- struct BufferState* ra_state = GetBufferState();
- // Check if the data has to be re-constructed from COW device
- if (ra_state->read_ahead_state == kCowReadAheadDone) {
- populate_data_from_cow_ = true;
- } else {
- populate_data_from_cow_ = false;
- }
-
- StartReadAhead();
-}
-
-bool Snapuserd::GetRABuffer(std::unique_lock<std::mutex>* lock, uint64_t block, void* buffer) {
- if (!lock->owns_lock()) {
- SNAP_LOG(ERROR) << "GetRABuffer - Lock not held";
- return false;
- }
- std::unordered_map<uint64_t, void*>::iterator it = read_ahead_buffer_map_.find(block);
-
- // This will be true only for IO's generated as part of reading a root
- // filesystem. IO's related to merge should always be in read-ahead cache.
- if (it == read_ahead_buffer_map_.end()) {
- return false;
- }
-
- // Theoretically, we can send the data back from the read-ahead buffer
- // all the way to the kernel without memcpy. However, if the IO is
- // un-aligned, the wrapper function will need to touch the read-ahead
- // buffers and transitions will be bit more complicated.
- memcpy(buffer, it->second, BLOCK_SZ);
- return true;
-}
-
-// ========== State transition functions for read-ahead operations ===========
-
-bool Snapuserd::GetReadAheadPopulatedBuffer(uint64_t block, void* buffer) {
- if (!read_ahead_feature_) {
- return false;
- }
-
- {
- std::unique_lock<std::mutex> lock(lock_);
- if (io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE) {
- return false;
- }
-
- if (io_state_ == READ_AHEAD_IO_TRANSITION::IO_IN_PROGRESS) {
- return GetRABuffer(&lock, block, buffer);
- }
- }
-
- {
- // Read-ahead thread IO is in-progress. Wait for it to complete
- std::unique_lock<std::mutex> lock(lock_);
- while (!(io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE ||
- io_state_ == READ_AHEAD_IO_TRANSITION::IO_IN_PROGRESS)) {
- cv.wait(lock);
- }
-
- return GetRABuffer(&lock, block, buffer);
- }
-}
-
-// This is invoked by read-ahead thread waiting for merge IO's
-// to complete
-bool Snapuserd::WaitForMergeToComplete() {
- {
- std::unique_lock<std::mutex> lock(lock_);
- while (!(io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_BEGIN ||
- io_state_ == READ_AHEAD_IO_TRANSITION::IO_TERMINATED)) {
- cv.wait(lock);
- }
-
- if (io_state_ == READ_AHEAD_IO_TRANSITION::IO_TERMINATED) {
- return false;
- }
-
- io_state_ = READ_AHEAD_IO_TRANSITION::READ_AHEAD_IN_PROGRESS;
- return true;
- }
-}
-
-// This is invoked during the launch of worker threads. We wait
-// for read-ahead thread to by fully up before worker threads
-// are launched; else we will have a race between worker threads
-// and read-ahead thread specifically during re-construction.
-bool Snapuserd::WaitForReadAheadToStart() {
- {
- std::unique_lock<std::mutex> lock(lock_);
- while (!(io_state_ == READ_AHEAD_IO_TRANSITION::IO_IN_PROGRESS ||
- io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE)) {
- cv.wait(lock);
- }
-
- if (io_state_ == READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE) {
- return false;
- }
-
- return true;
- }
-}
-
-// Invoked by worker threads when a sequence of merge operation
-// is complete notifying read-ahead thread to make forward
-// progress.
-void Snapuserd::StartReadAhead() {
- {
- std::lock_guard<std::mutex> lock(lock_);
- io_state_ = READ_AHEAD_IO_TRANSITION::READ_AHEAD_BEGIN;
- }
-
- cv.notify_one();
-}
-
-void Snapuserd::MergeCompleted() {
- {
- std::lock_guard<std::mutex> lock(lock_);
- io_state_ = READ_AHEAD_IO_TRANSITION::IO_TERMINATED;
- }
-
- cv.notify_one();
-}
-
-bool Snapuserd::ReadAheadIOCompleted(bool sync) {
- if (sync) {
- // Flush the entire buffer region
- int ret = msync(mapped_addr_, total_mapped_addr_length_, MS_SYNC);
- if (ret < 0) {
- PLOG(ERROR) << "msync failed after ReadAheadIOCompleted: " << ret;
- return false;
- }
-
- // Metadata and data are synced. Now, update the state.
- // We need to update the state after flushing data; if there is a crash
- // when read-ahead IO is in progress, the state of data in the COW file
- // is unknown. kCowReadAheadDone acts as a checkpoint wherein the data
- // in the scratch space is good and during next reboot, read-ahead thread
- // can safely re-construct the data.
- struct BufferState* ra_state = GetBufferState();
- ra_state->read_ahead_state = kCowReadAheadDone;
-
- ret = msync(mapped_addr_, BLOCK_SZ, MS_SYNC);
- if (ret < 0) {
- PLOG(ERROR) << "msync failed to flush Readahead completion state...";
- return false;
- }
- }
-
- // Notify the worker threads
- {
- std::lock_guard<std::mutex> lock(lock_);
- io_state_ = READ_AHEAD_IO_TRANSITION::IO_IN_PROGRESS;
- }
-
- cv.notify_all();
- return true;
-}
-
-void Snapuserd::ReadAheadIOFailed() {
- {
- std::lock_guard<std::mutex> lock(lock_);
- io_state_ = READ_AHEAD_IO_TRANSITION::READ_AHEAD_FAILURE;
- }
-
- cv.notify_all();
-}
-
-//========== End of state transition functions ====================
-
-bool Snapuserd::IsChunkIdMetadata(chunk_t chunk) {
- uint32_t stride = exceptions_per_area_ + 1;
- lldiv_t divresult = lldiv(chunk, stride);
-
- return (divresult.rem == NUM_SNAPSHOT_HDR_CHUNKS);
-}
-
-// Find the next free chunk-id to be assigned. Check if the next free
-// chunk-id represents a metadata page. If so, skip it.
-chunk_t Snapuserd::GetNextAllocatableChunkId(chunk_t chunk) {
- chunk_t next_chunk = chunk + 1;
-
- if (IsChunkIdMetadata(next_chunk)) {
- next_chunk += 1;
- }
- return next_chunk;
-}
-
-void Snapuserd::CheckMergeCompletionStatus() {
- if (!merge_initiated_) {
- SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: " << reader_->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();
-}
-
-/*
- * Read the metadata from COW device and
- * construct the metadata as required by the kernel.
- *
- * Please see design on kernel COW format
- *
- * 1: Read the metadata from internal COW device
- * 2: There are 3 COW operations:
- * a: Replace op
- * b: Copy op
- * c: Zero op
- * 3: For each of the 3 operations, op->new_block
- * represents the block number in the base device
- * for which one of the 3 operations have to be applied.
- * This represents the old_chunk in the kernel COW format
- * 4: We need to assign new_chunk for a corresponding old_chunk
- * 5: The algorithm is similar to how kernel assigns chunk number
- * while creating exceptions. However, there are few cases
- * which needs to be addressed here:
- * a: During merge process, kernel scans the metadata page
- * from backwards when merge is initiated. Since, we need
- * to make sure that the merge ordering follows our COW format,
- * we read the COW operation from backwards and populate the
- * metadata so that when kernel starts the merging from backwards,
- * those ops correspond to the beginning of our COW format.
- * b: Kernel can merge successive operations if the two chunk IDs
- * are contiguous. This can be problematic when there is a crash
- * during merge; specifically when the merge operation has dependency.
- * These dependencies can only happen during copy operations.
- *
- * To avoid this problem, we make sure overlap copy operations
- * are not batch merged.
- * 6: Use a monotonically increasing chunk number to assign the
- * new_chunk
- * 7: Each chunk-id represents either
- * a: Metadata page or
- * b: Data page
- * 8: Chunk-id representing a data page is stored in a map.
- * 9: Chunk-id representing a metadata page is converted into a vector
- * index. We store this in vector as kernel requests metadata during
- * two stage:
- * a: When initial dm-snapshot device is created, kernel requests
- * all the metadata and stores it in its internal data-structures.
- * b: During merge, kernel once again requests the same metadata
- * once-again.
- * In both these cases, a quick lookup based on chunk-id is done.
- * 10: When chunk number is incremented, we need to make sure that
- * if the chunk is representing a metadata page and skip.
- * 11: Each 4k page will contain 256 disk exceptions. We call this
- * exceptions_per_area_
- * 12: Kernel will stop issuing metadata IO request when new-chunk ID is 0.
- */
-bool Snapuserd::ReadMetadata() {
- reader_ = std::make_unique<CowReader>();
- CowHeader header;
- CowOptions options;
- bool metadata_found = false;
- int replace_ops = 0, zero_ops = 0, copy_ops = 0;
-
- SNAP_LOG(DEBUG) << "ReadMetadata: Parsing cow file";
-
- if (!reader_->Parse(cow_fd_)) {
- SNAP_LOG(ERROR) << "Failed to parse";
- return false;
- }
-
- if (!reader_->GetHeader(&header)) {
- SNAP_LOG(ERROR) << "Failed to get header";
- return false;
- }
-
- if (!(header.block_size == BLOCK_SZ)) {
- SNAP_LOG(ERROR) << "Invalid header block size found: " << header.block_size;
- return false;
- }
-
- reader_->InitializeMerge();
- SNAP_LOG(DEBUG) << "Merge-ops: " << header.num_merge_ops;
-
- if (!MmapMetadata()) {
- SNAP_LOG(ERROR) << "mmap failed";
- return false;
- }
-
- // Initialize the iterator for reading metadata
- cowop_riter_ = reader_->GetRevOpIter();
-
- exceptions_per_area_ = (CHUNK_SIZE << SECTOR_SHIFT) / sizeof(struct disk_exception);
-
- // Start from chunk number 2. Chunk 0 represents header and chunk 1
- // represents first metadata page.
- chunk_t data_chunk_id = NUM_SNAPSHOT_HDR_CHUNKS + 1;
- size_t num_ops = 0;
-
- loff_t offset = 0;
- std::unique_ptr<uint8_t[]> de_ptr =
- std::make_unique<uint8_t[]>(exceptions_per_area_ * sizeof(struct disk_exception));
-
- // This memset is important. Kernel will stop issuing IO when new-chunk ID
- // is 0. When Area is not filled completely with all 256 exceptions,
- // 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();
- 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
- // handling of assigning chunk-id's. Furthermore, we make
- // sure that replace/zero and copy ops are not batch merged; hence,
- // the bump in the chunk_id before break of this loop
- if (cow_op->type == kCowCopyOp) {
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- break;
- }
-
- if (cow_op->type == kCowReplaceOp) {
- replace_ops++;
- } else if (cow_op->type == kCowZeroOp) {
- zero_ops++;
- }
-
- // Construct the disk-exception
- de->old_chunk = cow_op->new_block;
- de->new_chunk = data_chunk_id;
-
-
- // Store operation pointer.
- chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
- num_ops += 1;
- offset += sizeof(struct disk_exception);
- cowop_riter_->Next();
-
- SNAP_LOG(DEBUG) << num_ops << ":"
- << " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
-
- if (num_ops == exceptions_per_area_) {
- // Store it in vector at the right index. This maps the chunk-id to
- // vector index.
- vec_.push_back(std::move(de_ptr));
- offset = 0;
- num_ops = 0;
-
- // Create buffer for next area
- de_ptr = std::make_unique<uint8_t[]>(exceptions_per_area_ *
- sizeof(struct disk_exception));
- memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
-
- if (cowop_riter_->Done()) {
- vec_.push_back(std::move(de_ptr));
- }
- }
-
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- }
-
- int num_ra_ops_per_iter = ((GetBufferDataSize()) / BLOCK_SZ);
- std::optional<chunk_t> prev_id = {};
- std::vector<const CowOperation*> vec;
- std::set<uint64_t> dest_blocks;
- std::set<uint64_t> source_blocks;
- size_t pending_copy_ops = exceptions_per_area_ - num_ops;
- uint64_t total_copy_ops = reader_->total_copy_ops();
-
- 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()) {
- do {
- const CowOperation* cow_op = &cowop_riter_->Get();
- if (IsMetadataOp(*cow_op)) {
- cowop_riter_->Next();
- continue;
- }
-
- // We have two cases specific cases:
- //
- // =====================================================
- // Case 1: Overlapping copy regions
- //
- // Ex:
- //
- // Source -> Destination
- //
- // 1: 15 -> 18
- // 2: 16 -> 19
- // 3: 17 -> 20
- // 4: 18 -> 21
- // 5: 19 -> 22
- // 6: 20 -> 23
- //
- // We have 6 copy operations to be executed in OTA and there is a overlap. Update-engine
- // will write to COW file as follows:
- //
- // Op-1: 20 -> 23
- // Op-2: 19 -> 22
- // Op-3: 18 -> 21
- // Op-4: 17 -> 20
- // Op-5: 16 -> 19
- // Op-6: 15 -> 18
- //
- // Note that the blocks numbers are contiguous. Hence, all 6 copy
- // operations can be batch merged. However, that will be
- // problematic if we have a crash as block 20, 19, 18 would have
- // been overwritten and hence subsequent recovery may end up with
- // a silent data corruption when op-1, op-2 and op-3 are
- // re-executed.
- //
- // To address the above problem, read-ahead thread will
- // read all the 6 source blocks, cache them in the scratch
- // space of the COW file. During merge, read-ahead
- // thread will serve the blocks from the read-ahead cache.
- // If there is a crash during merge; on subsequent reboot,
- // read-ahead thread will recover the data from the
- // scratch space and re-construct it thereby there
- // is no loss of data.
- //
- // Note that we will follow the same order of COW operations
- // as present in the COW file. This will make sure that\
- // the merge of operations are done based on the ops present
- // in the file.
- //===========================================================
- if (prev_id.has_value()) {
- if (dest_blocks.count(cow_op->new_block) || source_blocks.count(cow_op->source)) {
- break;
- }
- }
- metadata_found = true;
- pending_copy_ops -= 1;
- vec.push_back(cow_op);
- dest_blocks.insert(cow_op->source);
- source_blocks.insert(cow_op->new_block);
- prev_id = cow_op->new_block;
- cowop_riter_->Next();
- } while (!cowop_riter_->Done() && pending_copy_ops);
-
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- SNAP_LOG(DEBUG) << "Batch Merge copy-ops of size: " << vec.size()
- << " Area: " << vec_.size() << " Area offset: " << offset
- << " Pending-copy-ops in this area: " << pending_copy_ops;
-
- for (size_t i = 0; i < vec.size(); i++) {
- struct disk_exception* de =
- reinterpret_cast<struct disk_exception*>((char*)de_ptr.get() + offset);
- const CowOperation* cow_op = vec[i];
-
- de->old_chunk = cow_op->new_block;
- de->new_chunk = data_chunk_id;
-
- // Store operation pointer.
- chunk_vec_.push_back(std::make_pair(ChunkToSector(data_chunk_id), cow_op));
- offset += sizeof(struct disk_exception);
- num_ops += 1;
- copy_ops++;
- if (read_ahead_feature_) {
- read_ahead_ops_.push_back(cow_op);
- }
-
- SNAP_LOG(DEBUG) << num_ops << ":"
- << " Copy-op: "
- << " Old-chunk: " << de->old_chunk << " New-chunk: " << de->new_chunk;
-
- if (num_ops == exceptions_per_area_) {
- // Store it in vector at the right index. This maps the chunk-id to
- // vector index.
- vec_.push_back(std::move(de_ptr));
- num_ops = 0;
- offset = 0;
-
- // Create buffer for next area
- de_ptr = std::make_unique<uint8_t[]>(exceptions_per_area_ *
- sizeof(struct disk_exception));
- memset(de_ptr.get(), 0, (exceptions_per_area_ * sizeof(struct disk_exception)));
-
- if (cowop_riter_->Done()) {
- vec_.push_back(std::move(de_ptr));
- SNAP_LOG(DEBUG) << "ReadMetadata() completed; Number of Areas: " << vec_.size();
- }
-
- if (!(pending_copy_ops == 0)) {
- SNAP_LOG(ERROR)
- << "Invalid pending_copy_ops: expected: 0 found: " << pending_copy_ops;
- return false;
- }
- pending_copy_ops = exceptions_per_area_;
- }
-
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- total_copy_ops -= 1;
- /*
- * Split the number of ops based on the size of read-ahead buffer
- * region. We need to ensure that kernel doesn't issue IO on blocks
- * which are not read by the read-ahead thread.
- */
- if (read_ahead_feature_ && (total_copy_ops % num_ra_ops_per_iter == 0)) {
- data_chunk_id = GetNextAllocatableChunkId(data_chunk_id);
- }
- }
- vec.clear();
- dest_blocks.clear();
- source_blocks.clear();
- prev_id.reset();
- }
-
- // Partially filled area or there is no metadata
- // If there is no metadata, fill with zero so that kernel
- // is aware that merge is completed.
- if (num_ops || !metadata_found) {
- vec_.push_back(std::move(de_ptr));
- SNAP_LOG(DEBUG) << "ReadMetadata() completed. Partially filled area num_ops: " << num_ops
- << "Areas : " << vec_.size();
- }
-
- chunk_vec_.shrink_to_fit();
- vec_.shrink_to_fit();
- read_ahead_ops_.shrink_to_fit();
-
- // Sort the vector based on sectors as we need this during un-aligned access
- std::sort(chunk_vec_.begin(), chunk_vec_.end(), compare);
-
- SNAP_LOG(INFO) << "ReadMetadata completed. Final-chunk-id: " << data_chunk_id
- << " Num Sector: " << ChunkToSector(data_chunk_id)
- << " 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 number of sectors required for creating dm-user device
- num_sectors_ = ChunkToSector(data_chunk_id);
- merge_initiated_ = false;
- PrepareReadAhead();
-
- return true;
-}
-
-bool Snapuserd::MmapMetadata() {
- CowHeader header;
- reader_->GetHeader(&header);
-
- if (header.major_version >= 2 && header.buffer_size > 0) {
- total_mapped_addr_length_ = header.header_size + BUFFER_REGION_DEFAULT_SIZE;
- read_ahead_feature_ = true;
- } else {
- // mmap the first 4k page - older COW format
- total_mapped_addr_length_ = BLOCK_SZ;
- read_ahead_feature_ = false;
- }
-
- mapped_addr_ = mmap(NULL, total_mapped_addr_length_, PROT_READ | PROT_WRITE, MAP_SHARED,
- cow_fd_.get(), 0);
- if (mapped_addr_ == MAP_FAILED) {
- SNAP_LOG(ERROR) << "mmap metadata failed";
- return false;
- }
-
- return true;
-}
-
-void Snapuserd::UnmapBufferRegion() {
- int ret = munmap(mapped_addr_, total_mapped_addr_length_);
- if (ret < 0) {
- SNAP_PLOG(ERROR) << "munmap failed";
- }
-}
-
-void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
- unsigned int, const char* message) {
- if (severity == android::base::ERROR) {
- fprintf(stderr, "%s\n", message);
- } else {
- fprintf(stdout, "%s\n", message);
- }
-}
-
-bool Snapuserd::InitCowDevice() {
- cow_fd_.reset(open(cow_device_.c_str(), O_RDWR));
- if (cow_fd_ < 0) {
- SNAP_PLOG(ERROR) << "Open Failed: " << cow_device_;
- return false;
- }
-
- return ReadMetadata();
-}
-
-/*
- * Entry point to launch threads
- */
-bool Snapuserd::Start() {
- std::vector<std::future<bool>> threads;
- std::future<bool> ra_thread;
- bool rathread = (read_ahead_feature_ && (read_ahead_ops_.size() > 0));
-
- // Start the read-ahead thread and wait
- // for it as the data has to be re-constructed
- // from COW device.
- if (rathread) {
- ra_thread = std::async(std::launch::async, &ReadAheadThread::RunThread,
- read_ahead_thread_.get());
- if (!WaitForReadAheadToStart()) {
- SNAP_LOG(ERROR) << "Failed to start Read-ahead thread...";
- return false;
- }
-
- SNAP_LOG(INFO) << "Read-ahead thread started...";
- }
-
- // Launch worker threads
- for (int i = 0; i < worker_threads_.size(); i++) {
- threads.emplace_back(
- std::async(std::launch::async, &WorkerThread::RunThread, worker_threads_[i].get()));
- }
-
- bool ret = true;
- for (auto& t : threads) {
- ret = t.get() && ret;
- }
-
- if (rathread) {
- // Notify the read-ahead thread that all worker threads
- // are done. We need this explicit notification when
- // there is an IO failure or there was a switch
- // of dm-user table; thus, forcing the read-ahead
- // thread to wake up.
- MergeCompleted();
- ret = ret && ra_thread.get();
- }
-
- return ret;
-}
-
-uint64_t Snapuserd::GetBufferMetadataOffset() {
- CowHeader header;
- reader_->GetHeader(&header);
-
- size_t size = header.header_size + sizeof(BufferState);
- return size;
-}
-
-/*
- * Metadata for read-ahead is 16 bytes. For a 2 MB region, we will
- * end up with 8k (2 PAGE) worth of metadata. Thus, a 2MB buffer
- * region is split into:
- *
- * 1: 8k metadata
- *
- */
-size_t Snapuserd::GetBufferMetadataSize() {
- CowHeader header;
- reader_->GetHeader(&header);
-
- size_t metadata_bytes = (header.buffer_size * sizeof(struct ScratchMetadata)) / BLOCK_SZ;
- return metadata_bytes;
-}
-
-size_t Snapuserd::GetBufferDataOffset() {
- CowHeader header;
- reader_->GetHeader(&header);
-
- return (header.header_size + GetBufferMetadataSize());
-}
-
-/*
- * (2MB - 8K = 2088960 bytes) will be the buffer region to hold the data.
- */
-size_t Snapuserd::GetBufferDataSize() {
- CowHeader header;
- reader_->GetHeader(&header);
-
- size_t size = header.buffer_size - GetBufferMetadataSize();
- return size;
-}
-
-struct BufferState* Snapuserd::GetBufferState() {
- CowHeader header;
- reader_->GetHeader(&header);
-
- struct BufferState* ra_state =
- reinterpret_cast<struct BufferState*>((char*)mapped_addr_ + header.header_size);
- return ra_state;
-}
-
-} // namespace snapshot
-} // namespace android