summaryrefslogtreecommitdiff
path: root/fs_mgr
diff options
context:
space:
mode:
authoralk3pInjection <webmaster@raspii.tech>2023-04-20 00:08:54 +0800
committeralk3pInjection <webmaster@raspii.tech>2023-04-20 00:08:54 +0800
commita7dd355e8fe8ad0c579a4f0acd06b2e3b52dfc3a (patch)
tree2b552b59793a33466247fc6fb8cf89ecbfdc4a05 /fs_mgr
parentf0103ea35d56ccebbae16a43cac19ac38b11a9a2 (diff)
parent43816573a268998f892081eebf3ffe91d65b7e18 (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')
-rw-r--r--fs_mgr/fs_mgr_overlayfs.cpp8
-rw-r--r--fs_mgr/libdm/dm.cpp38
-rw-r--r--fs_mgr/libdm/dm_test.cpp20
-rw-r--r--fs_mgr/libdm/include/libdm/dm.h15
-rw-r--r--fs_mgr/libdm/include/libdm/dm_table.h3
-rw-r--r--fs_mgr/libdm/include/libdm/dm_target.h8
-rw-r--r--fs_mgr/libsnapshot/cow_reader.cpp70
-rw-r--r--fs_mgr/libsnapshot/include/libsnapshot/cow_reader.h3
-rw-r--r--fs_mgr/libsnapshot/include_test/libsnapshot/test_helpers.h3
-rw-r--r--fs_mgr/libsnapshot/snapshot.cpp11
-rw-r--r--fs_mgr/libsnapshot/snapshot_test.cpp4
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.cpp5
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_core.h11
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_merge.cpp20
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_readahead.cpp4
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.cpp73
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_server.h18
-rw-r--r--fs_mgr/libsnapshot/snapuserd/user-space-merge/snapuserd_transitions.cpp19
-rw-r--r--fs_mgr/libsnapshot/vts_ota_config_test.cpp7
-rw-r--r--fs_mgr/tests/vts_fs_test.cpp4
-rw-r--r--fs_mgr/tools/dmctl.cpp62
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(), &notify, 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;
}