diff options
author | alk3pInjection <webmaster@raspii.tech> | 2023-04-20 00:08:54 +0800 |
---|---|---|
committer | alk3pInjection <webmaster@raspii.tech> | 2023-04-20 00:08:54 +0800 |
commit | a7dd355e8fe8ad0c579a4f0acd06b2e3b52dfc3a (patch) | |
tree | 2b552b59793a33466247fc6fb8cf89ecbfdc4a05 /fs_mgr | |
parent | f0103ea35d56ccebbae16a43cac19ac38b11a9a2 (diff) | |
parent | 43816573a268998f892081eebf3ffe91d65b7e18 (diff) |
Merge tag 'LA.QSSI.13.0.r1-09800-qssi.0' into tachibanatachibana
"LA.QSSI.13.0.r1-09800-qssi.0"
Change-Id: I06ecf682f4d5595bce3383b6031506cc56bc0db2
Diffstat (limited to 'fs_mgr')
21 files changed, 319 insertions, 87 deletions
diff --git a/fs_mgr/fs_mgr_overlayfs.cpp b/fs_mgr/fs_mgr_overlayfs.cpp index 82b5275a3..6942d0ccd 100644 --- a/fs_mgr/fs_mgr_overlayfs.cpp +++ b/fs_mgr/fs_mgr_overlayfs.cpp @@ -1140,7 +1140,13 @@ static inline uint64_t GetIdealDataScratchSize() { return 0; } - return std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2); + auto ideal_size = std::min(super_info.size, (uint64_t(s.f_frsize) * s.f_bfree) / 2); + + // Align up to the filesystem block size. + if (auto remainder = ideal_size % s.f_bsize; remainder > 0) { + ideal_size += s.f_bsize - remainder; + } + return ideal_size; } static bool CreateScratchOnData(std::string* scratch_device, bool* partition_exists, bool* change) { diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp index 4034e30ab..deffae1ad 100644 --- a/fs_mgr/libdm/dm.cpp +++ b/fs_mgr/libdm/dm.cpp @@ -20,6 +20,7 @@ #include <sys/ioctl.h> #include <sys/sysmacros.h> #include <sys/types.h> +#include <sys/utsname.h> #include <chrono> #include <functional> @@ -289,7 +290,7 @@ bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table) { return true; } -bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) { +bool DeviceMapper::LoadTable(const std::string& name, const DmTable& table) { std::string ioctl_buffer(sizeof(struct dm_ioctl), 0); ioctl_buffer += table.Serialize(); @@ -305,9 +306,17 @@ bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& PLOG(ERROR) << "DM_TABLE_LOAD failed"; return false; } + return true; +} - InitIo(io, name); - if (ioctl(fd_, DM_DEV_SUSPEND, io)) { +bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) { + if (!LoadTable(name, table)) { + return false; + } + + struct dm_ioctl io; + InitIo(&io, name); + if (ioctl(fd_, DM_DEV_SUSPEND, &io)) { PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed"; return false; } @@ -703,5 +712,28 @@ std::map<std::string, std::string> DeviceMapper::FindDmPartitions() { return dm_block_devices; } +bool DeviceMapper::CreatePlaceholderDevice(const std::string& name) { + if (!CreateEmptyDevice(name)) { + return false; + } + + struct utsname uts; + unsigned int major, minor; + if (uname(&uts) != 0 || sscanf(uts.release, "%u.%u", &major, &minor) != 2) { + LOG(ERROR) << "Could not parse the kernel version from uname"; + return true; + } + + // On Linux 5.15+, there is no uevent until DM_TABLE_LOAD. + if (major > 5 || (major == 5 && minor >= 15)) { + DmTable table; + table.Emplace<DmTargetError>(0, 1); + if (!LoadTable(name, table)) { + return false; + } + } + return true; +} + } // namespace dm } // namespace android diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp index 541f254cb..4448d35c5 100644 --- a/fs_mgr/libdm/dm_test.cpp +++ b/fs_mgr/libdm/dm_test.cpp @@ -690,3 +690,23 @@ TEST(libdm, CreateEmptyDevice) { // Empty device should be in suspended state. ASSERT_EQ(DmDeviceState::SUSPENDED, dm.GetState("empty-device")); } + +TEST(libdm, UeventAfterLoadTable) { + static const char* kDeviceName = "libmd-test-uevent-load-table"; + + DeviceMapper& dm = DeviceMapper::Instance(); + ASSERT_TRUE(dm.CreateEmptyDevice(kDeviceName)); + + DmTable table; + table.Emplace<DmTargetError>(0, 1); + ASSERT_TRUE(dm.LoadTable(kDeviceName, table)); + + std::string ignore_path; + ASSERT_TRUE(dm.WaitForDevice(kDeviceName, 5s, &ignore_path)); + + auto info = dm.GetDetailedInfo(kDeviceName); + ASSERT_TRUE(info.has_value()); + ASSERT_TRUE(info->IsSuspended()); + + ASSERT_TRUE(dm.DeleteDevice(kDeviceName)); +} diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h index 1057d7f7a..dbef8f902 100644 --- a/fs_mgr/libdm/include/libdm/dm.h +++ b/fs_mgr/libdm/include/libdm/dm.h @@ -75,6 +75,7 @@ class IDeviceMapper { const std::chrono::milliseconds& timeout_ms) = 0; virtual DmDeviceState GetState(const std::string& name) const = 0; virtual bool LoadTableAndActivate(const std::string& name, const DmTable& table) = 0; + virtual bool LoadTable(const std::string& name, const DmTable& table) = 0; virtual bool GetTableInfo(const std::string& name, std::vector<TargetInfo>* table) = 0; virtual bool GetTableStatus(const std::string& name, std::vector<TargetInfo>* table) = 0; virtual bool GetDmDevicePathByName(const std::string& name, std::string* path) = 0; @@ -116,7 +117,7 @@ class DeviceMapper final : public IDeviceMapper { bool IsBufferFull() const { return flags_ & DM_BUFFER_FULL_FLAG; } bool IsInactiveTablePresent() const { return flags_ & DM_INACTIVE_PRESENT_FLAG; } bool IsReadOnly() const { return flags_ & DM_READONLY_FLAG; } - bool IsSuspended() const { return flags_ & DM_SUSPEND_FLAG; } + bool IsSuspended() const { return !IsActiveTablePresent() || (flags_ & DM_SUSPEND_FLAG); } }; // Removes a device mapper device with the given name. @@ -199,6 +200,12 @@ class DeviceMapper final : public IDeviceMapper { // Returns 'true' on success, false otherwise. bool LoadTableAndActivate(const std::string& name, const DmTable& table) override; + // Same as LoadTableAndActivate, but there is no resume step. This puts the + // new table in the inactive slot. + // + // Returns 'true' on success, false otherwise. + bool LoadTable(const std::string& name, const DmTable& table) override; + // Returns true if a list of available device mapper targets registered in the kernel was // successfully read and stored in 'targets'. Returns 'false' otherwise. bool GetAvailableTargets(std::vector<DmTargetTypeInfo>* targets); @@ -285,6 +292,12 @@ class DeviceMapper final : public IDeviceMapper { // Returns mapping <partition-name, /dev/block/dm-x> std::map<std::string, std::string> FindDmPartitions(); + // Create a placeholder device. This is useful for ensuring that a uevent is in the pipeline, + // to reduce the amount of time a future WaitForDevice will block. On kernels < 5.15, this + // simply calls CreateEmptyDevice. On 5.15 and higher, it also loads (but does not activate) + // a placeholder table containing dm-error. + bool CreatePlaceholderDevice(const std::string& name); + private: // Maximum possible device mapper targets registered in the kernel. // This is only used to read the list of targets from kernel so we allocate diff --git a/fs_mgr/libdm/include/libdm/dm_table.h b/fs_mgr/libdm/include/libdm/dm_table.h index ee66653a5..427f34dea 100644 --- a/fs_mgr/libdm/include/libdm/dm_table.h +++ b/fs_mgr/libdm/include/libdm/dm_table.h @@ -31,6 +31,7 @@ namespace dm { class DmTable { public: DmTable() : num_sectors_(0), readonly_(false) {} + DmTable(DmTable&& other) = default; // Adds a target to the device mapper table for a range specified in the target object. // The function will return 'true' if the target was successfully added and doesn't overlap with @@ -70,6 +71,8 @@ class DmTable { void set_readonly(bool readonly) { readonly_ = readonly; } bool readonly() const { return readonly_; } + DmTable& operator=(DmTable&& other) = default; + ~DmTable() = default; private: diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h index 954305816..09fe20012 100644 --- a/fs_mgr/libdm/include/libdm/dm_target.h +++ b/fs_mgr/libdm/include/libdm/dm_target.h @@ -323,6 +323,14 @@ class DmTargetUser final : public DmTarget { std::string control_device_; }; +class DmTargetError final : public DmTarget { + public: + DmTargetError(uint64_t start, uint64_t length) : DmTarget(start, length) {} + + std::string name() const override { return "error"; } + std::string GetParameterString() const override { return ""; } +}; + } // namespace dm } // namespace android diff --git a/fs_mgr/libsnapshot/cow_reader.cpp b/fs_mgr/libsnapshot/cow_reader.cpp index 746feeb8d..75a58a6fc 100644 --- a/fs_mgr/libsnapshot/cow_reader.cpp +++ b/fs_mgr/libsnapshot/cow_reader.cpp @@ -38,7 +38,7 @@ CowReader::CowReader(ReaderFlags reader_flag) : fd_(-1), header_(), fd_size_(0), - merge_op_blocks_(std::make_shared<std::vector<uint32_t>>()), + block_pos_index_(std::make_shared<std::vector<int>>()), reader_flag_(reader_flag) {} static void SHA256(const void*, size_t, uint8_t[]) { @@ -58,13 +58,12 @@ std::unique_ptr<CowReader> CowReader::CloneCowReader() { cow->fd_size_ = fd_size_; cow->last_label_ = last_label_; cow->ops_ = ops_; - cow->merge_op_blocks_ = merge_op_blocks_; cow->merge_op_start_ = merge_op_start_; - cow->block_map_ = block_map_; cow->num_total_data_ops_ = num_total_data_ops_; cow->num_ordered_ops_to_merge_ = num_ordered_ops_to_merge_; cow->has_seq_ops_ = has_seq_ops_; cow->data_loc_ = data_loc_; + cow->block_pos_index_ = block_pos_index_; return cow; } @@ -415,10 +414,10 @@ bool CowReader::ParseOps(std::optional<uint64_t> label) { // Replace-op-4, Zero-op-9, Replace-op-5 } //============================================================== bool CowReader::PrepMergeOps() { - auto merge_op_blocks = std::make_shared<std::vector<uint32_t>>(); + auto merge_op_blocks = std::make_unique<std::vector<uint32_t>>(); std::vector<int> other_ops; auto seq_ops_set = std::unordered_set<uint32_t>(); - auto block_map = std::make_shared<std::unordered_map<uint32_t, int>>(); + auto block_map = std::make_unique<std::unordered_map<uint32_t, int>>(); size_t num_seqs = 0; size_t read; @@ -477,13 +476,18 @@ bool CowReader::PrepMergeOps() { merge_op_blocks->insert(merge_op_blocks->end(), other_ops.begin(), other_ops.end()); + for (auto block : *merge_op_blocks) { + block_pos_index_->push_back(block_map->at(block)); + } + num_total_data_ops_ = merge_op_blocks->size(); if (header_.num_merge_ops > 0) { merge_op_start_ = header_.num_merge_ops; } - block_map_ = block_map; - merge_op_blocks_ = merge_op_blocks; + block_map->clear(); + merge_op_blocks->clear(); + return true; } @@ -589,9 +593,7 @@ const CowOperation& CowOpIter::Get() { class CowRevMergeOpIter final : public ICowOpIter { public: explicit CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops, - std::shared_ptr<std::vector<uint32_t>> merge_op_blocks, - std::shared_ptr<std::unordered_map<uint32_t, int>> map, - uint64_t start); + std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start); bool Done() override; const CowOperation& Get() override; @@ -602,17 +604,15 @@ class CowRevMergeOpIter final : public ICowOpIter { private: std::shared_ptr<std::vector<CowOperation>> ops_; - std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_; - std::shared_ptr<std::unordered_map<uint32_t, int>> map_; - std::vector<uint32_t>::reverse_iterator block_riter_; + std::vector<int>::reverse_iterator block_riter_; + std::shared_ptr<std::vector<int>> cow_op_index_vec_; uint64_t start_; }; class CowMergeOpIter final : public ICowOpIter { public: explicit CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops, - std::shared_ptr<std::vector<uint32_t>> merge_op_blocks, - std::shared_ptr<std::unordered_map<uint32_t, int>> map, uint64_t start); + std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start); bool Done() override; const CowOperation& Get() override; @@ -623,26 +623,21 @@ class CowMergeOpIter final : public ICowOpIter { private: std::shared_ptr<std::vector<CowOperation>> ops_; - std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_; - std::shared_ptr<std::unordered_map<uint32_t, int>> map_; - std::vector<uint32_t>::iterator block_iter_; + std::vector<int>::iterator block_iter_; + std::shared_ptr<std::vector<int>> cow_op_index_vec_; uint64_t start_; }; CowMergeOpIter::CowMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops, - std::shared_ptr<std::vector<uint32_t>> merge_op_blocks, - std::shared_ptr<std::unordered_map<uint32_t, int>> map, - uint64_t start) { + std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start) { ops_ = ops; - merge_op_blocks_ = merge_op_blocks; - map_ = map; start_ = start; - - block_iter_ = merge_op_blocks->begin() + start; + cow_op_index_vec_ = block_pos_index; + block_iter_ = cow_op_index_vec_->begin() + start; } bool CowMergeOpIter::RDone() { - return block_iter_ == merge_op_blocks_->begin(); + return block_iter_ == cow_op_index_vec_->begin(); } void CowMergeOpIter::Prev() { @@ -651,7 +646,7 @@ void CowMergeOpIter::Prev() { } bool CowMergeOpIter::Done() { - return block_iter_ == merge_op_blocks_->end(); + return block_iter_ == cow_op_index_vec_->end(); } void CowMergeOpIter::Next() { @@ -661,23 +656,20 @@ void CowMergeOpIter::Next() { const CowOperation& CowMergeOpIter::Get() { CHECK(!Done()); - return ops_->data()[map_->at(*block_iter_)]; + return ops_->data()[*block_iter_]; } CowRevMergeOpIter::CowRevMergeOpIter(std::shared_ptr<std::vector<CowOperation>> ops, - std::shared_ptr<std::vector<uint32_t>> merge_op_blocks, - std::shared_ptr<std::unordered_map<uint32_t, int>> map, + std::shared_ptr<std::vector<int>> block_pos_index, uint64_t start) { ops_ = ops; - merge_op_blocks_ = merge_op_blocks; - map_ = map; start_ = start; - - block_riter_ = merge_op_blocks->rbegin(); + cow_op_index_vec_ = block_pos_index; + block_riter_ = cow_op_index_vec_->rbegin(); } bool CowRevMergeOpIter::RDone() { - return block_riter_ == merge_op_blocks_->rbegin(); + return block_riter_ == cow_op_index_vec_->rbegin(); } void CowRevMergeOpIter::Prev() { @@ -686,7 +678,7 @@ void CowRevMergeOpIter::Prev() { } bool CowRevMergeOpIter::Done() { - return block_riter_ == merge_op_blocks_->rend() - start_; + return block_riter_ == cow_op_index_vec_->rend() - start_; } void CowRevMergeOpIter::Next() { @@ -696,7 +688,7 @@ void CowRevMergeOpIter::Next() { const CowOperation& CowRevMergeOpIter::Get() { CHECK(!Done()); - return ops_->data()[map_->at(*block_riter_)]; + return ops_->data()[*block_riter_]; } std::unique_ptr<ICowOpIter> CowReader::GetOpIter() { @@ -704,12 +696,12 @@ std::unique_ptr<ICowOpIter> CowReader::GetOpIter() { } std::unique_ptr<ICowOpIter> CowReader::GetRevMergeOpIter(bool ignore_progress) { - return std::make_unique<CowRevMergeOpIter>(ops_, merge_op_blocks_, block_map_, + return std::make_unique<CowRevMergeOpIter>(ops_, block_pos_index_, ignore_progress ? 0 : merge_op_start_); } std::unique_ptr<ICowOpIter> CowReader::GetMergeOpIter(bool ignore_progress) { - return std::make_unique<CowMergeOpIter>(ops_, merge_op_blocks_, block_map_, + return std::make_unique<CowMergeOpIter>(ops_, block_pos_index_, ignore_progress ? 0 : merge_op_start_); } diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h index f4d5c72f3..fbdd6b98b 100644 --- a/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h +++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h @@ -170,9 +170,8 @@ class CowReader final : public ICowReader { uint64_t fd_size_; std::optional<uint64_t> last_label_; std::shared_ptr<std::vector<CowOperation>> ops_; - std::shared_ptr<std::vector<uint32_t>> merge_op_blocks_; uint64_t merge_op_start_{}; - std::shared_ptr<std::unordered_map<uint32_t, int>> block_map_; + std::shared_ptr<std::vector<int>> block_pos_index_; uint64_t num_total_data_ops_{}; uint64_t num_ordered_ops_to_merge_{}; bool has_seq_ops_{}; diff --git a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h index c3b40dca4..1de6d9835 100644 --- a/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h +++ b/fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h @@ -143,6 +143,9 @@ class DeviceMapperWrapper : public android::dm::IDeviceMapper { virtual DmDeviceState GetState(const std::string& name) const override { return impl_.GetState(name); } + virtual bool LoadTable(const std::string& name, const DmTable& table) { + return impl_.LoadTable(name, table); + } virtual bool LoadTableAndActivate(const std::string& name, const DmTable& table) { return impl_.LoadTableAndActivate(name, table); } diff --git a/fs_mgr/libsnapshot/snapshot.cpp b/fs_mgr/libsnapshot/snapshot.cpp index 019b64a44..870801ef1 100644 --- a/fs_mgr/libsnapshot/snapshot.cpp +++ b/fs_mgr/libsnapshot/snapshot.cpp @@ -2151,8 +2151,17 @@ bool SnapshotManager::ListSnapshots(LockedFile* lock, std::vector<std::string>* if (!suffix.empty() && !android::base::EndsWith(name, suffix)) { continue; } - snapshots->emplace_back(std::move(name)); + + // Insert system and product partition at the beginning so that + // during snapshot-merge, these partitions are merged first. + if (name == "system_a" || name == "system_b" || name == "product_a" || + name == "product_b") { + snapshots->insert(snapshots->begin(), std::move(name)); + } else { + snapshots->emplace_back(std::move(name)); + } } + return true; } diff --git a/fs_mgr/libsnapshot/snapshot_test.cpp b/fs_mgr/libsnapshot/snapshot_test.cpp index 6a348b4e5..c145da718 100644 --- a/fs_mgr/libsnapshot/snapshot_test.cpp +++ b/fs_mgr/libsnapshot/snapshot_test.cpp @@ -2755,6 +2755,10 @@ bool IsDaemonRequired() { return false; } + if (!IsCompressionEnabled()) { + return false; + } + const std::string UNKNOWN = "unknown"; const std::string vendor_release = android::base::GetProperty("ro.vendor.build.version.release_or_codename", UNKNOWN); 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 692cb740c..718c13ce0 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp @@ -143,17 +143,18 @@ void SnapshotHandler::PrepareReadAhead() { NotifyRAForMergeReady(); } -void SnapshotHandler::CheckMergeCompletionStatus() { +bool SnapshotHandler::CheckMergeCompletionStatus() { if (!merge_initiated_) { SNAP_LOG(INFO) << "Merge was not initiated. Total-data-ops: " << reader_->get_num_total_data_ops(); - return; + return false; } 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_->get_num_total_data_ops(); + return true; } bool SnapshotHandler::ReadMetadata() { 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 83d40f635..c6805e5d9 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h @@ -18,6 +18,9 @@ #include <stdint.h> #include <stdlib.h> #include <sys/mman.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <unistd.h> #include <condition_variable> #include <cstring> @@ -54,6 +57,8 @@ static_assert(PAYLOAD_BUFFER_SZ >= BLOCK_SZ); static constexpr int kNumWorkerThreads = 4; +static constexpr int kNiceValueForMergeThreads = -5; + #define SNAP_LOG(level) LOG(level) << misc_name_ << ": " #define SNAP_PLOG(level) PLOG(level) << misc_name_ << ": " @@ -274,7 +279,7 @@ class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> { const bool& IsAttached() const { return attached_; } void AttachControlDevice() { attached_ = true; } - void CheckMergeCompletionStatus(); + bool CheckMergeCompletionStatus(); bool CommitMerge(int num_merge_ops); void CloseFds() { cow_fd_ = {}; } @@ -305,6 +310,8 @@ class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> { // State transitions for merge void InitiateMerge(); + void MonitorMerge(); + void WakeupMonitorMergeThread(); void WaitForMergeComplete(); bool WaitForMergeBegin(); void NotifyRAForMergeReady(); @@ -333,6 +340,7 @@ class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> { void SetSocketPresent(bool socket) { is_socket_present_ = socket; } void SetIouringEnabled(bool io_uring_enabled) { is_io_uring_enabled_ = io_uring_enabled; } bool MergeInitiated() { return merge_initiated_; } + bool MergeMonitored() { return merge_monitored_; } double GetMergePercentage() { return merge_completion_percentage_; } // Merge Block State Transitions @@ -407,6 +415,7 @@ class SnapshotHandler : public std::enable_shared_from_this<SnapshotHandler> { double merge_completion_percentage_; bool merge_initiated_ = false; + bool merge_monitored_ = false; bool attached_ = false; bool is_socket_present_; bool is_io_uring_enabled_ = false; diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp index c26a2cd5c..63f47d6f0 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp @@ -71,16 +71,16 @@ int Worker::PrepareMerge(uint64_t* source_offset, int* pending_ops, } bool Worker::MergeReplaceZeroOps() { - // Flush every 8192 ops. Since all ops are independent and there is no + // Flush after merging 2MB. Since all ops are independent and there is no // dependency between COW ops, we will flush the data and the number - // of ops merged in COW file for every 8192 ops. If there is a crash, - // we will end up replaying some of the COW ops which were already merged. - // That is ok. + // of ops merged in COW block device. If there is a crash, we will + // end up replaying some of the COW ops which were already merged. That is + // ok. // - // Why 8192 ops ? Increasing this may improve merge time 3-4 seconds but - // we need to make sure that we checkpoint; 8k ops seems optimal. In-case - // if there is a crash merge should always make forward progress. - int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 32; + // Although increasing this greater than 2MB may help in improving merge + // times; however, on devices with low memory, this can be problematic + // when there are multiple merge threads in parallel. + int total_ops_merged_per_commit = (PAYLOAD_BUFFER_SZ / BLOCK_SZ) * 2; int num_ops_merged = 0; SNAP_LOG(INFO) << "MergeReplaceZeroOps started...."; @@ -543,6 +543,10 @@ bool Worker::RunMergeThread() { return true; } + if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) { + SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid(); + } + SNAP_LOG(INFO) << "Merge starting.."; if (!Init()) { 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 fa2866f39..b9e4255b2 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp @@ -727,6 +727,10 @@ bool ReadAhead::RunThread() { InitializeIouring(); + if (setpriority(PRIO_PROCESS, gettid(), kNiceValueForMergeThreads)) { + SNAP_PLOG(ERROR) << "Failed to set priority for TID: " << gettid(); + } + while (!RAIterDone()) { if (!ReadAheadIOStart()) { break; diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp index 82b2b25dd..5d93f01a7 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp @@ -59,6 +59,14 @@ DaemonOps UserSnapshotServer::Resolveop(std::string& input) { return DaemonOps::INVALID; } +UserSnapshotServer::UserSnapshotServer() { + monitor_merge_event_fd_.reset(eventfd(0, EFD_CLOEXEC)); + if (monitor_merge_event_fd_ == -1) { + PLOG(FATAL) << "monitor_merge_event_fd_: failed to create eventfd"; + } + terminating_ = false; +} + UserSnapshotServer::~UserSnapshotServer() { // Close any client sockets that were added via AcceptClient(). for (size_t i = 1; i < watched_fds_.size(); i++) { @@ -249,7 +257,7 @@ bool UserSnapshotServer::Receivemsg(android::base::borrowed_fd fd, const std::st return Sendmsg(fd, "fail"); } - if (!StartMerge(*iter)) { + if (!StartMerge(&lock, *iter)) { return Sendmsg(fd, "fail"); } @@ -298,7 +306,7 @@ void UserSnapshotServer::RunThread(std::shared_ptr<UserSnapshotDmUserHandler> ha } handler->snapuserd()->CloseFds(); - handler->snapuserd()->CheckMergeCompletionStatus(); + bool merge_completed = handler->snapuserd()->CheckMergeCompletionStatus(); handler->snapuserd()->UnmapBufferRegion(); auto misc_name = handler->misc_name(); @@ -306,7 +314,11 @@ void UserSnapshotServer::RunThread(std::shared_ptr<UserSnapshotDmUserHandler> ha { std::lock_guard<std::mutex> lock(lock_); - num_partitions_merge_complete_ += 1; + if (merge_completed) { + num_partitions_merge_complete_ += 1; + active_merge_threads_ -= 1; + WakeupMonitorMergeThread(); + } handler->SetThreadTerminated(); auto iter = FindHandler(&lock, handler->misc_name()); if (iter == dm_users_.end()) { @@ -418,6 +430,9 @@ void UserSnapshotServer::JoinAllThreads() { if (th.joinable()) th.join(); } + + stop_monitor_merge_thread_ = true; + WakeupMonitorMergeThread(); } void UserSnapshotServer::AddWatchedFd(android::base::borrowed_fd fd, int events) { @@ -502,13 +517,24 @@ bool UserSnapshotServer::StartHandler(const std::shared_ptr<UserSnapshotDmUserHa return true; } -bool UserSnapshotServer::StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler) { +bool UserSnapshotServer::StartMerge(std::lock_guard<std::mutex>* proof_of_lock, + const std::shared_ptr<UserSnapshotDmUserHandler>& handler) { + CHECK(proof_of_lock); + if (!handler->snapuserd()->IsAttached()) { LOG(ERROR) << "Handler not attached to dm-user - Merge thread cannot be started"; return false; } - handler->snapuserd()->InitiateMerge(); + handler->snapuserd()->MonitorMerge(); + + if (!is_merge_monitor_started_.has_value()) { + std::thread(&UserSnapshotServer::MonitorMerge, this).detach(); + is_merge_monitor_started_ = true; + } + + merge_handlers_.push(handler); + WakeupMonitorMergeThread(); return true; } @@ -590,6 +616,42 @@ bool UserSnapshotServer::RemoveAndJoinHandler(const std::string& misc_name) { return true; } +void UserSnapshotServer::WakeupMonitorMergeThread() { + uint64_t notify = 1; + ssize_t rc = TEMP_FAILURE_RETRY(write(monitor_merge_event_fd_.get(), ¬ify, sizeof(notify))); + if (rc < 0) { + PLOG(FATAL) << "failed to notify monitor merge thread"; + } +} + +void UserSnapshotServer::MonitorMerge() { + while (!stop_monitor_merge_thread_) { + uint64_t testVal; + ssize_t ret = + TEMP_FAILURE_RETRY(read(monitor_merge_event_fd_.get(), &testVal, sizeof(testVal))); + if (ret == -1) { + PLOG(FATAL) << "Failed to read from eventfd"; + } else if (ret == 0) { + LOG(FATAL) << "Hit EOF on eventfd"; + } + + LOG(INFO) << "MonitorMerge: active-merge-threads: " << active_merge_threads_; + { + std::lock_guard<std::mutex> lock(lock_); + while (active_merge_threads_ < kMaxMergeThreads && merge_handlers_.size() > 0) { + auto handler = merge_handlers_.front(); + merge_handlers_.pop(); + LOG(INFO) << "Starting merge for partition: " + << handler->snapuserd()->GetMiscName(); + handler->snapuserd()->InitiateMerge(); + active_merge_threads_ += 1; + } + } + } + + LOG(INFO) << "Exiting MonitorMerge: size: " << merge_handlers_.size(); +} + bool UserSnapshotServer::WaitForSocket() { auto scope_guard = android::base::make_scope_guard([this]() -> void { JoinAllThreads(); }); @@ -646,6 +708,7 @@ bool UserSnapshotServer::WaitForSocket() { if (!StartWithSocket(true)) { return false; } + return Run(); } diff --git a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h index 34e7941bc..e0844aeb1 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h @@ -15,6 +15,7 @@ #pragma once #include <poll.h> +#include <sys/eventfd.h> #include <cstdio> #include <cstring> @@ -22,6 +23,8 @@ #include <future> #include <iostream> #include <mutex> +#include <optional> +#include <queue> #include <sstream> #include <string> #include <thread> @@ -34,6 +37,7 @@ namespace android { namespace snapshot { static constexpr uint32_t kMaxPacketSize = 512; +static constexpr uint8_t kMaxMergeThreads = 2; enum class DaemonOps { INIT, @@ -84,13 +88,19 @@ class UserSnapshotServer { std::vector<struct pollfd> watched_fds_; bool is_socket_present_ = false; int num_partitions_merge_complete_ = 0; + int active_merge_threads_ = 0; + bool stop_monitor_merge_thread_ = false; bool is_server_running_ = false; bool io_uring_enabled_ = false; + std::optional<bool> is_merge_monitor_started_; + + android::base::unique_fd monitor_merge_event_fd_; std::mutex lock_; using HandlerList = std::vector<std::shared_ptr<UserSnapshotDmUserHandler>>; HandlerList dm_users_; + std::queue<std::shared_ptr<UserSnapshotDmUserHandler>> merge_handlers_; void AddWatchedFd(android::base::borrowed_fd fd, int events); void AcceptClient(); @@ -108,6 +118,8 @@ class UserSnapshotServer { bool IsTerminating() { return terminating_; } void RunThread(std::shared_ptr<UserSnapshotDmUserHandler> handler); + void MonitorMerge(); + void JoinAllThreads(); bool StartWithSocket(bool start_listening); @@ -119,7 +131,7 @@ class UserSnapshotServer { void TerminateMergeThreads(std::lock_guard<std::mutex>* proof_of_lock); public: - UserSnapshotServer() { terminating_ = false; } + UserSnapshotServer(); ~UserSnapshotServer(); bool Start(const std::string& socketname); @@ -133,9 +145,11 @@ class UserSnapshotServer { const std::string& backing_device, const std::string& base_path_merge); bool StartHandler(const std::shared_ptr<UserSnapshotDmUserHandler>& handler); - bool StartMerge(const std::shared_ptr<UserSnapshotDmUserHandler>& handler); + bool StartMerge(std::lock_guard<std::mutex>* proof_of_lock, + const std::shared_ptr<UserSnapshotDmUserHandler>& handler); std::string GetMergeStatus(const std::shared_ptr<UserSnapshotDmUserHandler>& handler); + void WakeupMonitorMergeThread(); void SetTerminating() { terminating_ = true; } void ReceivedSocketSignal() { received_socket_signal_ = true; } void SetServerRunning() { is_server_running_ = 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 d4e1d7c7e..28c9f688a 100644 --- a/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp +++ b/fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp @@ -165,6 +165,13 @@ using namespace android; using namespace android::dm; using android::base::unique_fd; +void SnapshotHandler::MonitorMerge() { + { + std::lock_guard<std::mutex> lock(lock_); + merge_monitored_ = true; + } +} + // This is invoked once primarily by update-engine to initiate // the merge void SnapshotHandler::InitiateMerge() { @@ -361,10 +368,16 @@ void SnapshotHandler::WaitForMergeComplete() { std::string SnapshotHandler::GetMergeStatus() { bool merge_not_initiated = false; + bool merge_monitored = false; bool merge_failed = false; { std::lock_guard<std::mutex> lock(lock_); + + if (MergeMonitored()) { + merge_monitored = true; + } + if (!MergeInitiated()) { merge_not_initiated = true; } @@ -387,6 +400,12 @@ std::string SnapshotHandler::GetMergeStatus() { return "snapshot-merge-complete"; } + // Merge monitor thread is tracking the merge but the merge thread + // is not started yet. + if (merge_monitored) { + return "snapshot-merge"; + } + // Return the state as "snapshot". If the device was rebooted during // merge, we will return the status as "snapshot". This is ok, as // libsnapshot will explicitly resume the merge. This is slightly diff --git a/fs_mgr/libsnapshot/vts_ota_config_test.cpp b/fs_mgr/libsnapshot/vts_ota_config_test.cpp index afc2d81a4..02bcc3438 100644 --- a/fs_mgr/libsnapshot/vts_ota_config_test.cpp +++ b/fs_mgr/libsnapshot/vts_ota_config_test.cpp @@ -17,7 +17,14 @@ #include <android-base/properties.h> #include <gtest/gtest.h> +static int GetVsrLevel() { + return android::base::GetIntProperty("ro.vendor.api_level", -1); +} + TEST(VAB, Enabled) { ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.enabled", false)); + if (GetVsrLevel() < __ANDROID_API_T__) { + GTEST_SKIP(); + } ASSERT_TRUE(android::base::GetBoolProperty("ro.virtual_ab.userspace.snapshots.enabled", false)); } diff --git a/fs_mgr/tests/vts_fs_test.cpp b/fs_mgr/tests/vts_fs_test.cpp index aac2cfd9b..ae8e45992 100644 --- a/fs_mgr/tests/vts_fs_test.cpp +++ b/fs_mgr/tests/vts_fs_test.cpp @@ -28,8 +28,8 @@ static int GetVsrLevel() { } TEST(fs, ErofsSupported) { - // S and higher for this test. - if (GetVsrLevel() < __ANDROID_API_S__) { + // T-launch GKI kernels and higher must support EROFS. + if (GetVsrLevel() < __ANDROID_API_T__) { GTEST_SKIP(); } diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp index 62ca1620b..10efd0cab 100644 --- a/fs_mgr/tools/dmctl.cpp +++ b/fs_mgr/tools/dmctl.cpp @@ -33,6 +33,7 @@ #include <ios> #include <iostream> #include <map> +#include <optional> #include <sstream> #include <string> #include <vector> @@ -183,6 +184,8 @@ class TargetParser final { } std::string control_device = NextArg(); return std::make_unique<DmTargetUser>(start_sector, num_sectors, control_device); + } else if (target_type == "error") { + return std::make_unique<DmTargetError>(start_sector, num_sectors); } else { std::cerr << "Unrecognized target type: " << target_type << std::endl; return nullptr; @@ -206,16 +209,26 @@ class TargetParser final { char** argv_; }; -static bool parse_table_args(DmTable* table, int argc, char** argv) { +struct TableArgs { + DmTable table; + bool suspended = false; +}; + +static std::optional<TableArgs> parse_table_args(int argc, char** argv) { + TableArgs out; + // Parse extended options first. int arg_index = 1; while (arg_index < argc && argv[arg_index][0] == '-') { if (strcmp(argv[arg_index], "-ro") == 0) { - table->set_readonly(true); + out.table.set_readonly(true); + arg_index++; + } else if (strcmp(argv[arg_index], "-suspended") == 0) { + out.suspended = true; arg_index++; } else { std::cerr << "Unrecognized option: " << argv[arg_index] << std::endl; - return -EINVAL; + return {}; } } @@ -223,37 +236,44 @@ static bool parse_table_args(DmTable* table, int argc, char** argv) { TargetParser parser(argc - arg_index, argv + arg_index); while (parser.More()) { std::unique_ptr<DmTarget> target = parser.Next(); - if (!target || !table->AddTarget(std::move(target))) { - return -EINVAL; + if (!target || !out.table.AddTarget(std::move(target))) { + return {}; } } - if (table->num_targets() == 0) { + if (out.table.num_targets() == 0) { std::cerr << "Must define at least one target." << std::endl; - return -EINVAL; + return {}; } - return 0; + return {std::move(out)}; } static int DmCreateCmdHandler(int argc, char** argv) { if (argc < 1) { - std::cerr << "Usage: dmctl create <dm-name> [-ro] <targets...>" << std::endl; + std::cerr << "Usage: dmctl create <dm-name> [--suspended] [-ro] <targets...>" << std::endl; return -EINVAL; } std::string name = argv[0]; - DmTable table; - int ret = parse_table_args(&table, argc, argv); - if (ret) { - return ret; + auto table_args = parse_table_args(argc, argv); + if (!table_args) { + return -EINVAL; } std::string ignore_path; DeviceMapper& dm = DeviceMapper::Instance(); - if (!dm.CreateDevice(name, table, &ignore_path, 5s)) { + if (!dm.CreateEmptyDevice(name)) { std::cerr << "Failed to create device-mapper device with name: " << name << std::endl; return -EIO; } + if (!dm.LoadTable(name, table_args->table)) { + std::cerr << "Failed to load table for dm device: " << name << std::endl; + return -EIO; + } + if (!table_args->suspended && !dm.ChangeState(name, DmDeviceState::ACTIVE)) { + std::cerr << "Failed to activate table for " << name << std::endl; + return -EIO; + } return 0; } @@ -269,7 +289,6 @@ static int DmDeleteCmdHandler(int argc, char** argv) { std::cerr << "Failed to delete [" << name << "]" << std::endl; return -EIO; } - return 0; } @@ -280,17 +299,20 @@ static int DmReplaceCmdHandler(int argc, char** argv) { } std::string name = argv[0]; - DmTable table; - int ret = parse_table_args(&table, argc, argv); - if (ret) { - return ret; + auto table_args = parse_table_args(argc, argv); + if (!table_args) { + return -EINVAL; } DeviceMapper& dm = DeviceMapper::Instance(); - if (!dm.LoadTableAndActivate(name, table)) { + if (!dm.LoadTable(name, table_args->table)) { std::cerr << "Failed to replace device-mapper table to: " << name << std::endl; return -EIO; } + if (!table_args->suspended && !dm.ChangeState(name, DmDeviceState::ACTIVE)) { + std::cerr << "Failed to activate table for " << name << std::endl; + return -EIO; + } return 0; } |