From db70cbf78a874cbb679f92cdf8f395cbc7483c25 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Thu, 3 Aug 2023 16:57:12 -0700 Subject: [PATCH 1/5] snapuserd: Remove test dependence on LoopDevice. LoopDevice requires root, which is an obstacle to running this test in automation. The test also requires memfd which is not available in our included glibc. Create an IBackingDevice layer so we can use temporary files instead on host tests, while keeping the block-device code for on-device tests, which more closely matches how snapuserd runs. Bug: 288273605 Test: snapuserd_test Change-Id: I89b154921b6bbcf8fe213ef7f5c4da4d48322909 --- fs_mgr/libsnapshot/snapuserd/Android.bp | 1 + .../libsnapshot/snapuserd/testing/harness.cpp | 116 ++++++++++++++++++ .../libsnapshot/snapuserd/testing/harness.h | 9 ++ .../user-space-merge/snapuserd_test.cpp | 47 ++----- 4 files changed, 138 insertions(+), 35 deletions(-) create mode 100644 fs_mgr/libsnapshot/snapuserd/testing/harness.cpp diff --git a/fs_mgr/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp index 6251e5aea..fc260b692 100644 --- a/fs_mgr/libsnapshot/snapuserd/Android.bp +++ b/fs_mgr/libsnapshot/snapuserd/Android.bp @@ -221,6 +221,7 @@ cc_test { ], srcs: [ "testing/dm_user_harness.cpp", + "testing/harness.cpp", "user-space-merge/snapuserd_test.cpp", ], shared_libs: [ diff --git a/fs_mgr/libsnapshot/snapuserd/testing/harness.cpp b/fs_mgr/libsnapshot/snapuserd/testing/harness.cpp new file mode 100644 index 000000000..02ae549d1 --- /dev/null +++ b/fs_mgr/libsnapshot/snapuserd/testing/harness.cpp @@ -0,0 +1,116 @@ +// Copyright (C) 2023 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 "harness.h" + +#ifdef __ANDROID__ +#include +#endif +#include +#include +#include + +#include +#include +#include +#include "snapuserd_logging.h" + +namespace android { +namespace snapshot { + +using namespace std::chrono_literals; +using android::base::unique_fd; +using android::dm::LoopDevice; + +#ifdef __ANDROID__ +// Prefer this on device since it is a real block device, which is more similar +// to how we use snapuserd. +class MemoryBackedDevice final : public IBackingDevice { + public: + bool Init(uint64_t size) { + memfd_.reset(memfd_create("snapuserd_test", MFD_ALLOW_SEALING)); + if (memfd_ < 0) { + PLOG(ERROR) << "memfd_create failed"; + return false; + } + if (ftruncate(memfd_.get(), size) < 0) { + PLOG(ERROR) << "ftruncate failed"; + return false; + } + if (fcntl(memfd_.get(), F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) { + PLOG(ERROR) << "fcntl seal failed"; + return false; + } + dev_ = std::make_unique(memfd_, 10s); + return dev_->valid(); + } + const std::string& GetPath() override { return dev_->device(); } + uint64_t GetSize() override { + unique_fd fd(open(GetPath().c_str(), O_RDONLY | O_CLOEXEC)); + if (fd < 0) { + PLOG(ERROR) << "open failed: " << GetPath(); + return 0; + } + return get_block_device_size(fd.get()); + } + + private: + unique_fd memfd_; + std::unique_ptr dev_; +}; +#endif + +class FileBackedDevice final : public IBackingDevice { + public: + bool Init(uint64_t size) { + if (temp_.fd < 0) { + return false; + } + if (ftruncate(temp_.fd, size) < 0) { + PLOG(ERROR) << "ftruncate failed: " << temp_.path; + return false; + } + path_ = temp_.path; + return true; + } + + const std::string& GetPath() override { return path_; } + uint64_t GetSize() override { + off_t off = lseek(temp_.fd, 0, SEEK_END); + if (off < 0) { + PLOG(ERROR) << "lseek failed: " << temp_.path; + return 0; + } + return off; + } + + private: + TemporaryFile temp_; + std::string path_; +}; + +std::unique_ptr ITestHarness::CreateBackingDevice(uint64_t size) { +#ifdef __ANDROID__ + auto dev = std::make_unique(); +#else + auto dev = std::make_unique(); +#endif + if (!dev->Init(size)) { + return nullptr; + } + return dev; +} + +} // namespace snapshot +} // namespace android diff --git a/fs_mgr/libsnapshot/snapuserd/testing/harness.h b/fs_mgr/libsnapshot/snapuserd/testing/harness.h index c0d528f8b..ffe9f7bc7 100644 --- a/fs_mgr/libsnapshot/snapuserd/testing/harness.h +++ b/fs_mgr/libsnapshot/snapuserd/testing/harness.h @@ -33,6 +33,14 @@ class IUserDevice { virtual bool Destroy() = 0; }; +// Interface for an fd/temp file that is a block device when possible. +class IBackingDevice { + public: + virtual ~IBackingDevice() {} + virtual const std::string& GetPath() = 0; + virtual uint64_t GetSize() = 0; +}; + class ITestHarness { public: virtual ~ITestHarness() {} @@ -41,6 +49,7 @@ class ITestHarness { uint64_t num_sectors) = 0; virtual IBlockServerFactory* GetBlockServerFactory() = 0; virtual bool HasUserDevice() = 0; + virtual std::unique_ptr CreateBackingDevice(uint64_t size); }; } // namespace snapshot diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp index c40783499..7e1b2c40f 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp @@ -17,7 +17,6 @@ #include #include -#include #include #include #include @@ -97,7 +96,7 @@ class SnapuserdTest : public ::testing::Test { void InitDaemon(); void CreateUserDevice(); - unique_ptr base_loop_; + unique_ptr base_dev_; unique_ptr dmuser_dev_; std::string system_device_ctrl_name_; @@ -116,24 +115,6 @@ class SnapuserdTest : public ::testing::Test { std::unique_ptr harness_; }; -static unique_fd CreateTempFile(const std::string& name, size_t size) { - unique_fd fd(syscall(__NR_memfd_create, name.c_str(), MFD_ALLOW_SEALING)); - if (fd < 0) { - return {}; - } - if (size) { - if (ftruncate(fd, size) < 0) { - perror("ftruncate"); - return {}; - } - if (fcntl(fd, F_ADD_SEALS, F_SEAL_GROW | F_SEAL_SHRINK) < 0) { - perror("fcntl"); - return {}; - } - } - return fd; -} - void SnapuserdTest::SetUp() { harness_ = std::make_unique(); } @@ -189,14 +170,16 @@ bool SnapuserdTest::SetupDaemon() { } void SnapuserdTest::CreateBaseDevice() { - unique_fd rnd_fd; - total_base_size_ = (size_ * 5); - base_fd_ = CreateTempFile("base_device", total_base_size_); + + base_dev_ = harness_->CreateBackingDevice(total_base_size_); + ASSERT_NE(base_dev_, nullptr); + + base_fd_.reset(open(base_dev_->GetPath().c_str(), O_RDWR | O_CLOEXEC)); ASSERT_GE(base_fd_, 0); - rnd_fd.reset(open("/dev/random", O_RDONLY)); - ASSERT_TRUE(rnd_fd > 0); + unique_fd rnd_fd(open("/dev/random", O_RDONLY)); + ASSERT_GE(rnd_fd, 0); std::unique_ptr random_buffer = std::make_unique(1_MiB); @@ -206,9 +189,6 @@ void SnapuserdTest::CreateBaseDevice() { } ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0); - - base_loop_ = std::make_unique(base_fd_, 10s); - ASSERT_TRUE(base_loop_->valid()); } void SnapuserdTest::ReadSnapshotDeviceAndValidate() { @@ -544,8 +524,8 @@ void SnapuserdTest::InitCowDevice() { auto factory = harness_->GetBlockServerFactory(); auto opener = factory->CreateOpener(system_device_ctrl_name_); auto handler = - handlers_.AddHandler(system_device_ctrl_name_, cow_system_->path, base_loop_->device(), - base_loop_->device(), opener, 1, use_iouring, false); + handlers_.AddHandler(system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(), + base_dev_->GetPath(), opener, 1, use_iouring, false); ASSERT_NE(handler, nullptr); ASSERT_NE(handler->snapuserd(), nullptr); #ifdef __ANDROID__ @@ -566,11 +546,8 @@ void SnapuserdTest::SetDeviceControlName() { } void SnapuserdTest::CreateUserDevice() { - unique_fd fd(TEMP_FAILURE_RETRY(open(base_loop_->device().c_str(), O_RDONLY | O_CLOEXEC))); - ASSERT_TRUE(fd > 0); - - uint64_t dev_sz = get_block_device_size(fd.get()); - ASSERT_TRUE(dev_sz > 0); + auto dev_sz = base_dev_->GetSize(); + ASSERT_NE(dev_sz, 0); cow_num_sectors_ = dev_sz >> 9; From 02191dbfac2c7a555d9d154ec9b2aa23a8313ebc Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 5 Jul 2023 16:54:42 -0700 Subject: [PATCH 2/5] snapuserd: Fix race condition in HandleManager shutdown. When HandlerManager shuts down, the monitor thread is left detached. The monitor thread does not hold a shared_ptr reference to the HandlerManager, so the pointer can be left dangling. Fix this by not detaching the monitor merge thread. This patch also changes the test harness to destroy SnapshotHandlerManager on "shutdown", to avoid state leaking into the next instance of snapuserd. Bug: 288273605 Test: snapuserd_test Change-Id: Iaaf96a37657c85cff4d2a8b15ccfde4aa03d3220 --- .../user-space-merge/handler_manager.cpp | 13 ++++++++----- .../user-space-merge/handler_manager.h | 2 +- .../user-space-merge/snapuserd_test.cpp | 19 +++++++++++-------- 3 files changed, 20 insertions(+), 14 deletions(-) diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp index fd41cd419..50b9d4331 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp @@ -201,9 +201,8 @@ bool SnapshotHandlerManager::StartMerge(std::lock_guard* proof_of_lo handler->snapuserd()->MonitorMerge(); - if (!is_merge_monitor_started_) { - std::thread(&SnapshotHandlerManager::MonitorMerge, this).detach(); - is_merge_monitor_started_ = true; + if (!merge_monitor_.joinable()) { + merge_monitor_ = std::thread(&SnapshotHandlerManager::MonitorMerge, this); } merge_handlers_.push(handler); @@ -357,8 +356,12 @@ void SnapshotHandlerManager::JoinAllThreads() { if (th.joinable()) th.join(); } - stop_monitor_merge_thread_ = true; - WakeupMonitorMergeThread(); + if (merge_monitor_.joinable()) { + stop_monitor_merge_thread_ = true; + WakeupMonitorMergeThread(); + + merge_monitor_.join(); + } } auto SnapshotHandlerManager::FindHandler(std::lock_guard* proof_of_lock, diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h index 2a6da964e..b1605f098 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.h @@ -122,9 +122,9 @@ class SnapshotHandlerManager final : public ISnapshotHandlerManager { std::mutex lock_; HandlerList dm_users_; - bool is_merge_monitor_started_ = false; bool stop_monitor_merge_thread_ = false; int active_merge_threads_ = 0; + std::thread merge_monitor_; int num_partitions_merge_complete_ = 0; std::queue> merge_handlers_; android::base::unique_fd monitor_merge_event_fd_; diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp index 7e1b2c40f..0e02f0b85 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp @@ -106,7 +106,7 @@ class SnapuserdTest : public ::testing::Test { std::unique_ptr cow_system_; std::unique_ptr orig_buffer_; std::unique_ptr merged_buffer_; - SnapshotHandlerManager handlers_; + std::unique_ptr handlers_; bool setup_ok_ = false; bool merge_ok_ = false; size_t size_ = 100_MiB; @@ -117,15 +117,18 @@ class SnapuserdTest : public ::testing::Test { void SnapuserdTest::SetUp() { harness_ = std::make_unique(); + handlers_ = std::make_unique(); } void SnapuserdTest::Shutdown() { ASSERT_TRUE(dmuser_dev_->Destroy()); auto misc_device = "/dev/dm-user/" + system_device_ctrl_name_; - ASSERT_TRUE(handlers_.DeleteHandler(system_device_ctrl_name_)); + ASSERT_TRUE(handlers_->DeleteHandler(system_device_ctrl_name_)); ASSERT_TRUE(android::fs_mgr::WaitForFileDeleted(misc_device, 10s)); - handlers_.TerminateMergeThreads(); + handlers_->TerminateMergeThreads(); + handlers_->JoinAllThreads(); + handlers_ = std::make_unique(); } bool SnapuserdTest::SetupDefault() { @@ -524,8 +527,8 @@ void SnapuserdTest::InitCowDevice() { auto factory = harness_->GetBlockServerFactory(); auto opener = factory->CreateOpener(system_device_ctrl_name_); auto handler = - handlers_.AddHandler(system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(), - base_dev_->GetPath(), opener, 1, use_iouring, false); + handlers_->AddHandler(system_device_ctrl_name_, cow_system_->path, base_dev_->GetPath(), + base_dev_->GetPath(), opener, 1, use_iouring, false); ASSERT_NE(handler, nullptr); ASSERT_NE(handler->snapuserd(), nullptr); #ifdef __ANDROID__ @@ -557,12 +560,12 @@ void SnapuserdTest::CreateUserDevice() { } void SnapuserdTest::InitDaemon() { - ASSERT_TRUE(handlers_.StartHandler(system_device_ctrl_name_)); + ASSERT_TRUE(handlers_->StartHandler(system_device_ctrl_name_)); } void SnapuserdTest::CheckMergeCompletion() { while (true) { - double percentage = handlers_.GetMergePercentage(); + double percentage = handlers_->GetMergePercentage(); if ((int)percentage == 100) { break; } @@ -592,7 +595,7 @@ bool SnapuserdTest::Merge() { } void SnapuserdTest::StartMerge() { - ASSERT_TRUE(handlers_.InitiateMerge(system_device_ctrl_name_)); + ASSERT_TRUE(handlers_->InitiateMerge(system_device_ctrl_name_)); } void SnapuserdTest::ValidateMerge() { From 9aa421824686f102276a5578db2e9366bc4d2060 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Wed, 5 Jul 2023 16:57:28 -0700 Subject: [PATCH 3/5] snapuserd: Add diagnostics for debugging races. Adds calls to pthread_setname_np for each thread. Clarify error messages from io_uring calls that return -errno. Add log messages for some failure paths that didn't have any. Add an ostream overload for MERGE_IO_TRANSITION, and add an INVALID state for initialization. Bug: 288273605 Test: builds Change-Id: Ic0681cbf0017af67bcf52b98db184a9b48752faf --- .../user-space-merge/handler_manager.cpp | 4 ++++ .../user-space-merge/merge_worker.cpp | 8 ++++++- .../user-space-merge/read_worker.cpp | 4 ++++ .../user-space-merge/snapuserd_core.cpp | 2 -- .../user-space-merge/snapuserd_core.h | 8 +++++-- .../user-space-merge/snapuserd_readahead.cpp | 12 ++++++++-- .../snapuserd_transitions.cpp | 24 +++++++++++++++++++ 7 files changed, 55 insertions(+), 7 deletions(-) diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp index 50b9d4331..d979e20c0 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/handler_manager.cpp @@ -14,6 +14,7 @@ #include "handler_manager.h" +#include #include #include @@ -132,6 +133,8 @@ bool SnapshotHandlerManager::DeleteHandler(const std::string& misc_name) { void SnapshotHandlerManager::RunThread(std::shared_ptr handler) { LOG(INFO) << "Entering thread for handler: " << handler->misc_name(); + pthread_setname_np(pthread_self(), "Handler"); + if (!handler->snapuserd()->Start()) { LOG(ERROR) << " Failed to launch all worker threads"; } @@ -219,6 +222,7 @@ void SnapshotHandlerManager::WakeupMonitorMergeThread() { } void SnapshotHandlerManager::MonitorMerge() { + pthread_setname_np(pthread_self(), "Merge Monitor"); while (!stop_monitor_merge_thread_) { uint64_t testVal; ssize_t ret = diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp index 2d6972171..11b8d7c92 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/merge_worker.cpp @@ -15,6 +15,8 @@ */ #include "merge_worker.h" +#include + #include "snapuserd_core.h" #include "utility.h" @@ -198,6 +200,7 @@ bool MergeWorker::MergeOrderedOpsAsync() { // Wait for RA thread to notify that the merge window // is ready for merging. if (!snapuserd_->WaitForMergeBegin()) { + SNAP_LOG(ERROR) << "Failed waiting for merge to begin"; return false; } @@ -303,7 +306,7 @@ bool MergeWorker::MergeOrderedOpsAsync() { // will fallback to synchronous I/O. ret = io_uring_wait_cqe(ring_.get(), &cqe); if (ret) { - SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed: " << ret; + SNAP_LOG(ERROR) << "Merge: io_uring_wait_cqe failed: " << strerror(-ret); status = false; break; } @@ -546,6 +549,9 @@ void MergeWorker::FinalizeIouring() { bool MergeWorker::Run() { SNAP_LOG(DEBUG) << "Waiting for merge begin..."; + + pthread_setname_np(pthread_self(), "MergeWorker"); + if (!snapuserd_->WaitForMergeBegin()) { SNAP_LOG(ERROR) << "Merge terminated early..."; return true; diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp index e2c292bd8..b9ecfa5b3 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/read_worker.cpp @@ -16,6 +16,8 @@ #include "read_worker.h" +#include + #include "snapuserd_core.h" #include "utility.h" @@ -209,6 +211,8 @@ bool ReadWorker::Init() { bool ReadWorker::Run() { SNAP_LOG(INFO) << "Processing snapshot I/O requests...."; + pthread_setname_np(pthread_self(), "ReadWorker"); + if (!SetThreadPriority(kNiceValueForMergeThreads)) { SNAP_PLOG(ERROR) << "Failed to set thread priority"; } diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp index 5a82ca1f4..c2958512d 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp @@ -295,8 +295,6 @@ bool SnapshotHandler::Start() { if (ra_thread_) { ra_thread_status = std::async(std::launch::async, &ReadAhead::RunThread, read_ahead_thread_.get()); - - SNAP_LOG(INFO) << "Read-ahead thread started"; } // Launch worker threads diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h index a45e0bcc7..622fc5028 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -68,12 +69,13 @@ static constexpr int kNiceValueForMergeThreads = -5; #define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": " enum class MERGE_IO_TRANSITION { + INVALID, MERGE_READY, MERGE_BEGIN, MERGE_FAILED, MERGE_COMPLETE, IO_TERMINATED, - READ_AHEAD_FAILURE, + READ_AHEAD_FAILURE }; class MergeWorker; @@ -220,7 +222,7 @@ class SnapshotHandler : public std::enable_shared_from_this { bool populate_data_from_cow_ = false; bool ra_thread_ = false; int total_ra_blocks_merged_ = 0; - MERGE_IO_TRANSITION io_state_; + MERGE_IO_TRANSITION io_state_ = MERGE_IO_TRANSITION::INVALID; std::unique_ptr read_ahead_thread_; std::unordered_map read_ahead_buffer_map_; @@ -246,5 +248,7 @@ class SnapshotHandler : public std::enable_shared_from_this { std::shared_ptr block_server_opener_; }; +std::ostream& operator<<(std::ostream& os, MERGE_IO_TRANSITION value); + } // namespace snapshot } // namespace android 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 8b799ea93..d2128c5d0 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp @@ -16,6 +16,8 @@ #include "snapuserd_readahead.h" +#include + #include "snapuserd_core.h" #include "utility.h" @@ -428,7 +430,7 @@ bool ReadAhead::ReapIoCompletions(int pending_ios_to_complete) { // will fallback to synchronous I/O. int ret = io_uring_wait_cqe(ring_.get(), &cqe); if (ret) { - SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << ret; + SNAP_LOG(ERROR) << "Read-ahead - io_uring_wait_cqe failed: " << strerror(-ret); status = false; break; } @@ -691,6 +693,7 @@ bool ReadAhead::ReadAheadIOStart() { // window. If there is a crash during this time frame, merge should resume // based on the contents of the scratch space. if (!snapuserd_->WaitForMergeReady()) { + SNAP_LOG(ERROR) << "ReadAhead failed to wait for merge ready"; return false; } @@ -752,6 +755,10 @@ void ReadAhead::FinalizeIouring() { } bool ReadAhead::RunThread() { + SNAP_LOG(INFO) << "ReadAhead thread started."; + + pthread_setname_np(pthread_self(), "ReadAhead"); + if (!InitializeFds()) { return false; } @@ -770,6 +777,7 @@ bool ReadAhead::RunThread() { SNAP_PLOG(ERROR) << "Failed to set thread priority"; } + SNAP_LOG(INFO) << "ReadAhead processing."; while (!RAIterDone()) { if (!ReadAheadIOStart()) { break; @@ -780,7 +788,7 @@ bool ReadAhead::RunThread() { CloseFds(); reader_->CloseCowFd(); - SNAP_LOG(INFO) << " ReadAhead thread terminating...."; + SNAP_LOG(INFO) << " ReadAhead thread terminating."; return true; } diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp index 28c9f688a..52e4f89cd 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp @@ -199,6 +199,7 @@ bool SnapshotHandler::WaitForMergeBegin() { if (io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE || io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) { + SNAP_LOG(ERROR) << "WaitForMergeBegin failed with state: " << io_state_; return false; } } @@ -211,6 +212,7 @@ bool SnapshotHandler::WaitForMergeBegin() { if (io_state_ == MERGE_IO_TRANSITION::READ_AHEAD_FAILURE || io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) { + SNAP_LOG(ERROR) << "WaitForMergeBegin failed with state: " << io_state_; return false; } @@ -277,6 +279,7 @@ bool SnapshotHandler::WaitForMergeReady() { if (io_state_ == MERGE_IO_TRANSITION::MERGE_FAILED || io_state_ == MERGE_IO_TRANSITION::MERGE_COMPLETE || io_state_ == MERGE_IO_TRANSITION::IO_TERMINATED) { + SNAP_LOG(ERROR) << "Wait for merge ready failed: " << io_state_; return false; } return true; @@ -668,5 +671,26 @@ MERGE_GROUP_STATE SnapshotHandler::ProcessMergingBlock(uint64_t new_block, void* } } +std::ostream& operator<<(std::ostream& os, MERGE_IO_TRANSITION value) { + switch (value) { + case MERGE_IO_TRANSITION::INVALID: + return os << "INVALID"; + case MERGE_IO_TRANSITION::MERGE_READY: + return os << "MERGE_READY"; + case MERGE_IO_TRANSITION::MERGE_BEGIN: + return os << "MERGE_BEGIN"; + case MERGE_IO_TRANSITION::MERGE_FAILED: + return os << "MERGE_FAILED"; + case MERGE_IO_TRANSITION::MERGE_COMPLETE: + return os << "MERGE_COMPLETE"; + case MERGE_IO_TRANSITION::IO_TERMINATED: + return os << "IO_TERMINATED"; + case MERGE_IO_TRANSITION::READ_AHEAD_FAILURE: + return os << "READ_AHEAD_FAILURE"; + default: + return os << "unknown"; + } +} + } // namespace snapshot } // namespace android From 9fb4fc307c5e950a8eeb8c03d9b884eff137b8f0 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 7 Aug 2023 14:18:46 -0700 Subject: [PATCH 4/5] snapuserd: Add error propagation to tests. snapuserd_test in general doesn't propagate errors, which means the first failure will cascade into many failures. This can make it harder to figure out what's going on. Unfortunately error propagation is difficult with gtest, since bool returns don't work with ASSERT_EQ and Android doesn't use exceptions. Use ASSERT_NO_FATAL_FAILURE for now, since that is the least invasive change. Bug: 288273605 Test: snapuserd_test Change-Id: I6550682fd63602bc15649f705def2915f6329357 --- .../user-space-merge/snapuserd_test.cpp | 187 ++++++++---------- 1 file changed, 84 insertions(+), 103 deletions(-) diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp index 0e02f0b85..ae921fd35 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp @@ -58,11 +58,11 @@ using namespace std; class SnapuserdTest : public ::testing::Test { public: - bool SetupDefault(); - bool SetupOrderedOps(); - bool SetupOrderedOpsInverted(); - bool SetupCopyOverlap_1(); - bool SetupCopyOverlap_2(); + void SetupDefault(); + void SetupOrderedOps(); + void SetupOrderedOpsInverted(); + void SetupCopyOverlap_1(); + void SetupCopyOverlap_2(); bool Merge(); void ValidateMerge(); void ReadSnapshotDeviceAndValidate(); @@ -70,7 +70,7 @@ class SnapuserdTest : public ::testing::Test { void MergeInterrupt(); void MergeInterruptFixed(int duration); void MergeInterruptRandomly(int max_duration); - void StartMerge(); + bool StartMerge(); void CheckMergeCompletion(); static const uint64_t kSectorSize = 512; @@ -89,7 +89,7 @@ class SnapuserdTest : public ::testing::Test { void CreateCowDeviceOrderedOpsInverted(); void CreateCowDeviceWithCopyOverlap_1(); void CreateCowDeviceWithCopyOverlap_2(); - bool SetupDaemon(); + void SetupDaemon(); void CreateBaseDevice(); void InitCowDevice(); void SetDeviceControlName(); @@ -107,8 +107,6 @@ class SnapuserdTest : public ::testing::Test { std::unique_ptr orig_buffer_; std::unique_ptr merged_buffer_; std::unique_ptr handlers_; - bool setup_ok_ = false; - bool merge_ok_ = false; size_t size_ = 100_MiB; int cow_num_sectors_; int total_base_size_; @@ -131,45 +129,40 @@ void SnapuserdTest::Shutdown() { handlers_ = std::make_unique(); } -bool SnapuserdTest::SetupDefault() { - SetupImpl(); - return setup_ok_; +void SnapuserdTest::SetupDefault() { + ASSERT_NO_FATAL_FAILURE(SetupImpl()); } -bool SnapuserdTest::SetupOrderedOps() { - CreateBaseDevice(); - CreateCowDeviceOrderedOps(); - return SetupDaemon(); +void SnapuserdTest::SetupOrderedOps() { + ASSERT_NO_FATAL_FAILURE(CreateBaseDevice()); + ASSERT_NO_FATAL_FAILURE(CreateCowDeviceOrderedOps()); + ASSERT_NO_FATAL_FAILURE(SetupDaemon()); } -bool SnapuserdTest::SetupOrderedOpsInverted() { - CreateBaseDevice(); - CreateCowDeviceOrderedOpsInverted(); - return SetupDaemon(); +void SnapuserdTest::SetupOrderedOpsInverted() { + ASSERT_NO_FATAL_FAILURE(CreateBaseDevice()); + ASSERT_NO_FATAL_FAILURE(CreateCowDeviceOrderedOpsInverted()); + ASSERT_NO_FATAL_FAILURE(SetupDaemon()); } -bool SnapuserdTest::SetupCopyOverlap_1() { - CreateBaseDevice(); - CreateCowDeviceWithCopyOverlap_1(); - return SetupDaemon(); +void SnapuserdTest::SetupCopyOverlap_1() { + ASSERT_NO_FATAL_FAILURE(CreateBaseDevice()); + ASSERT_NO_FATAL_FAILURE(CreateCowDeviceWithCopyOverlap_1()); + ASSERT_NO_FATAL_FAILURE(SetupDaemon()); } -bool SnapuserdTest::SetupCopyOverlap_2() { - CreateBaseDevice(); - CreateCowDeviceWithCopyOverlap_2(); - return SetupDaemon(); +void SnapuserdTest::SetupCopyOverlap_2() { + ASSERT_NO_FATAL_FAILURE(CreateBaseDevice()); + ASSERT_NO_FATAL_FAILURE(CreateCowDeviceWithCopyOverlap_2()); + ASSERT_NO_FATAL_FAILURE(SetupDaemon()); } -bool SnapuserdTest::SetupDaemon() { +void SnapuserdTest::SetupDaemon() { SetDeviceControlName(); - CreateUserDevice(); - InitCowDevice(); - InitDaemon(); - - setup_ok_ = true; - - return setup_ok_; + ASSERT_NO_FATAL_FAILURE(CreateUserDevice()); + ASSERT_NO_FATAL_FAILURE(InitCowDevice()); + ASSERT_NO_FATAL_FAILURE(InitDaemon()); } void SnapuserdTest::CreateBaseDevice() { @@ -575,27 +568,26 @@ void SnapuserdTest::CheckMergeCompletion() { } void SnapuserdTest::SetupImpl() { - CreateBaseDevice(); - CreateCowDevice(); + ASSERT_NO_FATAL_FAILURE(CreateBaseDevice()); + ASSERT_NO_FATAL_FAILURE(CreateCowDevice()); SetDeviceControlName(); - CreateUserDevice(); - InitCowDevice(); - InitDaemon(); - - setup_ok_ = true; + ASSERT_NO_FATAL_FAILURE(CreateUserDevice()); + ASSERT_NO_FATAL_FAILURE(InitCowDevice()); + ASSERT_NO_FATAL_FAILURE(InitDaemon()); } bool SnapuserdTest::Merge() { - StartMerge(); + if (!StartMerge()) { + return false; + } CheckMergeCompletion(); - merge_ok_ = true; - return merge_ok_; + return true; } -void SnapuserdTest::StartMerge() { - ASSERT_TRUE(handlers_->InitiateMerge(system_device_ctrl_name_)); +bool SnapuserdTest::StartMerge() { + return handlers_->InitiateMerge(system_device_ctrl_name_); } void SnapuserdTest::ValidateMerge() { @@ -606,67 +598,67 @@ void SnapuserdTest::ValidateMerge() { } void SnapuserdTest::SimulateDaemonRestart() { - Shutdown(); + ASSERT_NO_FATAL_FAILURE(Shutdown()); std::this_thread::sleep_for(500ms); SetDeviceControlName(); - CreateUserDevice(); - InitCowDevice(); - InitDaemon(); + ASSERT_NO_FATAL_FAILURE(CreateUserDevice()); + ASSERT_NO_FATAL_FAILURE(InitCowDevice()); + ASSERT_NO_FATAL_FAILURE(InitDaemon()); } void SnapuserdTest::MergeInterruptRandomly(int max_duration) { std::srand(std::time(nullptr)); - StartMerge(); + ASSERT_TRUE(StartMerge()); for (int i = 0; i < 20; i++) { int duration = std::rand() % max_duration; std::this_thread::sleep_for(std::chrono::milliseconds(duration)); - SimulateDaemonRestart(); - StartMerge(); + ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart()); + ASSERT_TRUE(StartMerge()); } - SimulateDaemonRestart(); + ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart()); ASSERT_TRUE(Merge()); } void SnapuserdTest::MergeInterruptFixed(int duration) { - StartMerge(); + ASSERT_TRUE(StartMerge()); for (int i = 0; i < 25; i++) { std::this_thread::sleep_for(std::chrono::milliseconds(duration)); - SimulateDaemonRestart(); - StartMerge(); + ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart()); + ASSERT_TRUE(StartMerge()); } - SimulateDaemonRestart(); + ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart()); ASSERT_TRUE(Merge()); } void SnapuserdTest::MergeInterrupt() { // Interrupt merge at various intervals - StartMerge(); + ASSERT_TRUE(StartMerge()); std::this_thread::sleep_for(250ms); - SimulateDaemonRestart(); + ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart()); - StartMerge(); + ASSERT_TRUE(StartMerge()); std::this_thread::sleep_for(250ms); - SimulateDaemonRestart(); + ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart()); - StartMerge(); + ASSERT_TRUE(StartMerge()); std::this_thread::sleep_for(150ms); - SimulateDaemonRestart(); + ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart()); - StartMerge(); + ASSERT_TRUE(StartMerge()); std::this_thread::sleep_for(100ms); - SimulateDaemonRestart(); + ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart()); - StartMerge(); + ASSERT_TRUE(StartMerge()); std::this_thread::sleep_for(800ms); - SimulateDaemonRestart(); + ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart()); - StartMerge(); + ASSERT_TRUE(StartMerge()); std::this_thread::sleep_for(600ms); - SimulateDaemonRestart(); + ASSERT_NO_FATAL_FAILURE(SimulateDaemonRestart()); ASSERT_TRUE(Merge()); } @@ -675,98 +667,87 @@ TEST_F(SnapuserdTest, Snapshot_IO_TEST) { if (!harness_->HasUserDevice()) { GTEST_SKIP() << "Skipping snapshot read; not supported"; } - ASSERT_TRUE(SetupDefault()); + ASSERT_NO_FATAL_FAILURE(SetupDefault()); // I/O before merge - ReadSnapshotDeviceAndValidate(); + ASSERT_NO_FATAL_FAILURE(ReadSnapshotDeviceAndValidate()); ASSERT_TRUE(Merge()); ValidateMerge(); // I/O after merge - daemon should read directly // from base device - ReadSnapshotDeviceAndValidate(); - Shutdown(); + ASSERT_NO_FATAL_FAILURE(ReadSnapshotDeviceAndValidate()); } TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST) { if (!harness_->HasUserDevice()) { GTEST_SKIP() << "Skipping snapshot read; not supported"; } - ASSERT_TRUE(SetupDefault()); + ASSERT_NO_FATAL_FAILURE(SetupDefault()); // Issue I/O before merge begins std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this); // Start the merge ASSERT_TRUE(Merge()); ValidateMerge(); - Shutdown(); } TEST_F(SnapuserdTest, Snapshot_MERGE_IO_TEST_1) { if (!harness_->HasUserDevice()) { GTEST_SKIP() << "Skipping snapshot read; not supported"; } - ASSERT_TRUE(SetupDefault()); + ASSERT_NO_FATAL_FAILURE(SetupDefault()); // Start the merge - StartMerge(); + ASSERT_TRUE(StartMerge()); // Issue I/O in parallel when merge is in-progress std::async(std::launch::async, &SnapuserdTest::ReadSnapshotDeviceAndValidate, this); CheckMergeCompletion(); ValidateMerge(); - Shutdown(); } TEST_F(SnapuserdTest, Snapshot_Merge_Resume) { - ASSERT_TRUE(SetupDefault()); - MergeInterrupt(); + ASSERT_NO_FATAL_FAILURE(SetupDefault()); + ASSERT_NO_FATAL_FAILURE(MergeInterrupt()); ValidateMerge(); - Shutdown(); } TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_TEST_1) { - ASSERT_TRUE(SetupCopyOverlap_1()); + ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_1()); ASSERT_TRUE(Merge()); ValidateMerge(); - Shutdown(); } TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_TEST_2) { - ASSERT_TRUE(SetupCopyOverlap_2()); + ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_2()); ASSERT_TRUE(Merge()); ValidateMerge(); - Shutdown(); } TEST_F(SnapuserdTest, Snapshot_COPY_Overlap_Merge_Resume_TEST) { - ASSERT_TRUE(SetupCopyOverlap_1()); - MergeInterrupt(); + ASSERT_NO_FATAL_FAILURE(SetupCopyOverlap_1()); + ASSERT_NO_FATAL_FAILURE(MergeInterrupt()); ValidateMerge(); - Shutdown(); } TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Ordered) { - ASSERT_TRUE(SetupOrderedOps()); - MergeInterruptFixed(300); + ASSERT_NO_FATAL_FAILURE(SetupOrderedOps()); + ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(300)); ValidateMerge(); - Shutdown(); } TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Random_Ordered) { - ASSERT_TRUE(SetupOrderedOps()); - MergeInterruptRandomly(500); + ASSERT_NO_FATAL_FAILURE(SetupOrderedOps()); + ASSERT_NO_FATAL_FAILURE(MergeInterruptRandomly(500)); ValidateMerge(); - Shutdown(); } TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Fixed_Inverted) { - ASSERT_TRUE(SetupOrderedOpsInverted()); - MergeInterruptFixed(50); + ASSERT_NO_FATAL_FAILURE(SetupOrderedOpsInverted()); + ASSERT_NO_FATAL_FAILURE(MergeInterruptFixed(50)); ValidateMerge(); - Shutdown(); } TEST_F(SnapuserdTest, Snapshot_Merge_Crash_Random_Inverted) { - ASSERT_TRUE(SetupOrderedOpsInverted()); - MergeInterruptRandomly(50); + ASSERT_NO_FATAL_FAILURE(SetupOrderedOpsInverted()); + ASSERT_NO_FATAL_FAILURE(MergeInterruptRandomly(50)); ValidateMerge(); - Shutdown(); } } // namespace snapshot From dbda1300aee31850ea89c4f76573dda225fed220 Mon Sep 17 00:00:00 2001 From: David Anderson Date: Mon, 7 Aug 2023 14:54:38 -0700 Subject: [PATCH 5/5] snapuserd: Factor a base class out of tests. This factors a lighter weight class out of SnapuserdTest, so we can construct tests without needing a HandlerManager/daemon. Bug: 288273605 Test: snapuserd_test Change-Id: Ib33dc593a5b3d3df86853e73f245918ef36fd9fa --- .../user-space-merge/snapuserd_test.cpp | 316 ++++++++++-------- 1 file changed, 168 insertions(+), 148 deletions(-) diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp index ae921fd35..9f7a91dae 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_test.cpp @@ -56,7 +56,167 @@ using namespace std::chrono_literals; using namespace android::dm; using namespace std; -class SnapuserdTest : public ::testing::Test { +class SnapuserdTestBase : public ::testing::Test { + protected: + void SetUp() override; + void TearDown() override; + void CreateBaseDevice(); + void CreateCowDevice(); + void SetDeviceControlName(); + std::unique_ptr CreateCowDeviceInternal(); + + std::unique_ptr harness_; + size_t size_ = 100_MiB; + int total_base_size_ = 0; + std::string system_device_ctrl_name_; + std::string system_device_name_; + + unique_ptr base_dev_; + unique_fd base_fd_; + + std::unique_ptr cow_system_; + + std::unique_ptr orig_buffer_; +}; + +void SnapuserdTestBase::SetUp() { + harness_ = std::make_unique(); +} + +void SnapuserdTestBase::TearDown() {} + +void SnapuserdTestBase::CreateBaseDevice() { + total_base_size_ = (size_ * 5); + + base_dev_ = harness_->CreateBackingDevice(total_base_size_); + ASSERT_NE(base_dev_, nullptr); + + base_fd_.reset(open(base_dev_->GetPath().c_str(), O_RDWR | O_CLOEXEC)); + ASSERT_GE(base_fd_, 0); + + unique_fd rnd_fd(open("/dev/random", O_RDONLY)); + ASSERT_GE(rnd_fd, 0); + + std::unique_ptr random_buffer = std::make_unique(1_MiB); + + for (size_t j = 0; j < ((total_base_size_) / 1_MiB); j++) { + ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true); + ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), 1_MiB), true); + } + + ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0); +} + +std::unique_ptr SnapuserdTestBase::CreateCowDeviceInternal() { + std::string path = android::base::GetExecutableDirectory(); + cow_system_ = std::make_unique(path); + + CowOptions options; + options.compression = "gz"; + + unique_fd fd(cow_system_->fd); + cow_system_->fd = -1; + + return CreateCowWriter(kDefaultCowVersion, options, std::move(fd)); +} + +void SnapuserdTestBase::CreateCowDevice() { + unique_fd rnd_fd; + loff_t offset = 0; + + auto writer = CreateCowDeviceInternal(); + ASSERT_NE(writer, nullptr); + + rnd_fd.reset(open("/dev/random", O_RDONLY)); + ASSERT_TRUE(rnd_fd > 0); + + std::unique_ptr random_buffer_1_ = std::make_unique(size_); + + // Fill random data + for (size_t j = 0; j < (size_ / 1_MiB); j++) { + ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0), + true); + + offset += 1_MiB; + } + + size_t num_blocks = size_ / writer->GetBlockSize(); + size_t blk_end_copy = num_blocks * 2; + size_t source_blk = num_blocks - 1; + size_t blk_src_copy = blk_end_copy - 1; + + uint32_t sequence[num_blocks * 2]; + // Sequence for Copy ops + for (int i = 0; i < num_blocks; i++) { + sequence[i] = num_blocks - 1 - i; + } + // Sequence for Xor ops + for (int i = 0; i < num_blocks; i++) { + sequence[num_blocks + i] = 5 * num_blocks - 1 - i; + } + ASSERT_TRUE(writer->AddSequenceData(2 * num_blocks, sequence)); + + size_t x = num_blocks; + while (1) { + ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy)); + x -= 1; + if (x == 0) { + break; + } + source_blk -= 1; + blk_src_copy -= 1; + } + + source_blk = num_blocks; + blk_src_copy = blk_end_copy; + + ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_)); + + size_t blk_zero_copy_start = source_blk + num_blocks; + size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks; + + ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks)); + + size_t blk_random2_replace_start = blk_zero_copy_end; + + ASSERT_TRUE(writer->AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_)); + + size_t blk_xor_start = blk_random2_replace_start + num_blocks; + size_t xor_offset = BLOCK_SZ / 2; + ASSERT_TRUE(writer->AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks, + xor_offset)); + + // Flush operations + ASSERT_TRUE(writer->Finalize()); + // Construct the buffer required for validation + orig_buffer_ = std::make_unique(total_base_size_); + std::string zero_buffer(size_, 0); + ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true); + memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_); + memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_); + memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_); + ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, &orig_buffer_.get()[size_ * 4], size_, + size_ + xor_offset), + true); + for (int i = 0; i < size_; i++) { + orig_buffer_.get()[(size_ * 4) + i] = + (uint8_t)(orig_buffer_.get()[(size_ * 4) + i] ^ random_buffer_1_.get()[i]); + } +} + +void SnapuserdTestBase::SetDeviceControlName() { + system_device_name_.clear(); + system_device_ctrl_name_.clear(); + + std::string str(cow_system_->path); + std::size_t found = str.find_last_of("/\\"); + ASSERT_NE(found, std::string::npos); + system_device_name_ = str.substr(found + 1); + + system_device_ctrl_name_ = system_device_name_ + "-ctrl"; +} + +class SnapuserdTest : public SnapuserdTestBase { public: void SetupDefault(); void SetupOrderedOps(); @@ -77,47 +237,38 @@ class SnapuserdTest : public ::testing::Test { protected: void SetUp() override; - void TearDown() override { Shutdown(); } + void TearDown() override; void SetupImpl(); void SimulateDaemonRestart(); - std::unique_ptr CreateCowDeviceInternal(); - void CreateCowDevice(); void CreateCowDeviceOrderedOps(); void CreateCowDeviceOrderedOpsInverted(); void CreateCowDeviceWithCopyOverlap_1(); void CreateCowDeviceWithCopyOverlap_2(); void SetupDaemon(); - void CreateBaseDevice(); void InitCowDevice(); - void SetDeviceControlName(); void InitDaemon(); void CreateUserDevice(); - unique_ptr base_dev_; unique_ptr dmuser_dev_; - std::string system_device_ctrl_name_; - std::string system_device_name_; - - unique_fd base_fd_; - std::unique_ptr cow_system_; - std::unique_ptr orig_buffer_; std::unique_ptr merged_buffer_; std::unique_ptr handlers_; - size_t size_ = 100_MiB; int cow_num_sectors_; - int total_base_size_; - std::unique_ptr harness_; }; void SnapuserdTest::SetUp() { - harness_ = std::make_unique(); + ASSERT_NO_FATAL_FAILURE(SnapuserdTestBase::SetUp()); handlers_ = std::make_unique(); } +void SnapuserdTest::TearDown() { + SnapuserdTestBase::TearDown(); + Shutdown(); +} + void SnapuserdTest::Shutdown() { ASSERT_TRUE(dmuser_dev_->Destroy()); @@ -165,28 +316,6 @@ void SnapuserdTest::SetupDaemon() { ASSERT_NO_FATAL_FAILURE(InitDaemon()); } -void SnapuserdTest::CreateBaseDevice() { - total_base_size_ = (size_ * 5); - - base_dev_ = harness_->CreateBackingDevice(total_base_size_); - ASSERT_NE(base_dev_, nullptr); - - base_fd_.reset(open(base_dev_->GetPath().c_str(), O_RDWR | O_CLOEXEC)); - ASSERT_GE(base_fd_, 0); - - unique_fd rnd_fd(open("/dev/random", O_RDONLY)); - ASSERT_GE(rnd_fd, 0); - - std::unique_ptr random_buffer = std::make_unique(1_MiB); - - for (size_t j = 0; j < ((total_base_size_) / 1_MiB); j++) { - ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer.get(), 1_MiB, 0), true); - ASSERT_EQ(android::base::WriteFully(base_fd_, random_buffer.get(), 1_MiB), true); - } - - ASSERT_EQ(lseek(base_fd_, 0, SEEK_SET), 0); -} - void SnapuserdTest::ReadSnapshotDeviceAndValidate() { unique_fd fd(open(dmuser_dev_->GetPath().c_str(), O_RDONLY)); ASSERT_GE(fd, 0); @@ -218,19 +347,6 @@ void SnapuserdTest::ReadSnapshotDeviceAndValidate() { ASSERT_EQ(memcmp(snapuserd_buffer.get(), (char*)orig_buffer_.get() + (size_ * 4), size_), 0); } -std::unique_ptr SnapuserdTest::CreateCowDeviceInternal() { - std::string path = android::base::GetExecutableDirectory(); - cow_system_ = std::make_unique(path); - - CowOptions options; - options.compression = "gz"; - - unique_fd fd(cow_system_->fd); - cow_system_->fd = -1; - - return CreateCowWriter(kDefaultCowVersion, options, std::move(fd)); -} - void SnapuserdTest::CreateCowDeviceWithCopyOverlap_2() { auto writer = CreateCowDeviceInternal(); ASSERT_NE(writer, nullptr); @@ -427,90 +543,6 @@ void SnapuserdTest::CreateCowDeviceOrderedOps() { } } -void SnapuserdTest::CreateCowDevice() { - unique_fd rnd_fd; - loff_t offset = 0; - - auto writer = CreateCowDeviceInternal(); - ASSERT_NE(writer, nullptr); - - rnd_fd.reset(open("/dev/random", O_RDONLY)); - ASSERT_TRUE(rnd_fd > 0); - - std::unique_ptr random_buffer_1_ = std::make_unique(size_); - - // Fill random data - for (size_t j = 0; j < (size_ / 1_MiB); j++) { - ASSERT_EQ(ReadFullyAtOffset(rnd_fd, (char*)random_buffer_1_.get() + offset, 1_MiB, 0), - true); - - offset += 1_MiB; - } - - size_t num_blocks = size_ / writer->GetBlockSize(); - size_t blk_end_copy = num_blocks * 2; - size_t source_blk = num_blocks - 1; - size_t blk_src_copy = blk_end_copy - 1; - - uint32_t sequence[num_blocks * 2]; - // Sequence for Copy ops - for (int i = 0; i < num_blocks; i++) { - sequence[i] = num_blocks - 1 - i; - } - // Sequence for Xor ops - for (int i = 0; i < num_blocks; i++) { - sequence[num_blocks + i] = 5 * num_blocks - 1 - i; - } - ASSERT_TRUE(writer->AddSequenceData(2 * num_blocks, sequence)); - - size_t x = num_blocks; - while (1) { - ASSERT_TRUE(writer->AddCopy(source_blk, blk_src_copy)); - x -= 1; - if (x == 0) { - break; - } - source_blk -= 1; - blk_src_copy -= 1; - } - - source_blk = num_blocks; - blk_src_copy = blk_end_copy; - - ASSERT_TRUE(writer->AddRawBlocks(source_blk, random_buffer_1_.get(), size_)); - - size_t blk_zero_copy_start = source_blk + num_blocks; - size_t blk_zero_copy_end = blk_zero_copy_start + num_blocks; - - ASSERT_TRUE(writer->AddZeroBlocks(blk_zero_copy_start, num_blocks)); - - size_t blk_random2_replace_start = blk_zero_copy_end; - - ASSERT_TRUE(writer->AddRawBlocks(blk_random2_replace_start, random_buffer_1_.get(), size_)); - - size_t blk_xor_start = blk_random2_replace_start + num_blocks; - size_t xor_offset = BLOCK_SZ / 2; - ASSERT_TRUE(writer->AddXorBlocks(blk_xor_start, random_buffer_1_.get(), size_, num_blocks, - xor_offset)); - - // Flush operations - ASSERT_TRUE(writer->Finalize()); - // Construct the buffer required for validation - orig_buffer_ = std::make_unique(total_base_size_); - std::string zero_buffer(size_, 0); - ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, orig_buffer_.get(), size_, size_), true); - memcpy((char*)orig_buffer_.get() + size_, random_buffer_1_.get(), size_); - memcpy((char*)orig_buffer_.get() + (size_ * 2), (void*)zero_buffer.c_str(), size_); - memcpy((char*)orig_buffer_.get() + (size_ * 3), random_buffer_1_.get(), size_); - ASSERT_EQ(android::base::ReadFullyAtOffset(base_fd_, &orig_buffer_.get()[size_ * 4], size_, - size_ + xor_offset), - true); - for (int i = 0; i < size_; i++) { - orig_buffer_.get()[(size_ * 4) + i] = - (uint8_t)(orig_buffer_.get()[(size_ * 4) + i] ^ random_buffer_1_.get()[i]); - } -} - void SnapuserdTest::InitCowDevice() { bool use_iouring = true; if (FLAGS_force_config == "iouring_disabled") { @@ -529,18 +561,6 @@ void SnapuserdTest::InitCowDevice() { #endif } -void SnapuserdTest::SetDeviceControlName() { - system_device_name_.clear(); - system_device_ctrl_name_.clear(); - - std::string str(cow_system_->path); - std::size_t found = str.find_last_of("/\\"); - ASSERT_NE(found, std::string::npos); - system_device_name_ = str.substr(found + 1); - - system_device_ctrl_name_ = system_device_name_ + "-ctrl"; -} - void SnapuserdTest::CreateUserDevice() { auto dev_sz = base_dev_->GetSize(); ASSERT_NE(dev_sz, 0);