From a11a338e2b46248b927d3dc092e7c5fd44eeb0e7 Mon Sep 17 00:00:00 2001 From: Akilesh Kailash Date: Thu, 22 Jul 2021 07:54:31 +0000 Subject: [PATCH] snapuserd: Read partition blocks to memory When the device is rebooted after OTA is done, daemon will read through all the dynamic partitions to bring the blocks to memory. When update-verifier runs, all the blocks would be in page-cache thereby cutting down the boot time. Boot time improvements on Pixel: Full OTA: =========== VABC (Without this patch): 37.308 seconds VABC (With this patch): 28.604 seconds Incremental OTA: ================= VABC (Without this patch): 39.072 seconds VABC (With this patch): 27.523 seconds We read the blocks only during second stage transition. Thus, it shouldn't impact when snapuserd is spin up during post-install phase or during first-stage init. Bug: 193863442 Test: Full and Incremental OTA on pixel Signed-off-by: Akilesh Kailash Change-Id: Id654449238e22125f7d6288c7100fde512cc2ced --- fs_mgr/libdm/dm.cpp | 56 +++++++++ fs_mgr/libdm/include/libdm/dm.h | 8 ++ fs_mgr/libsnapshot/snapuserd/Android.bp | 2 + .../include/snapuserd/snapuserd_kernel.h | 2 - fs_mgr/libsnapshot/snapuserd/snapuserd.cpp | 113 ++++++++++++++++++ fs_mgr/libsnapshot/snapuserd/snapuserd.h | 7 ++ .../snapuserd/snapuserd_server.cpp | 2 + .../libsnapshot/snapuserd/snapuserd_server.h | 1 + 8 files changed, 189 insertions(+), 2 deletions(-) diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp index a5eda2983..8e3d5a5ae 100644 --- a/fs_mgr/libdm/dm.cpp +++ b/fs_mgr/libdm/dm.cpp @@ -651,5 +651,61 @@ bool DeviceMapper::TargetInfo::IsOverflowSnapshot() const { return spec.target_type == "snapshot"s && data == "Overflow"s; } +// Find directories in format of "/sys/block/dm-X". +static int DmNameFilter(const dirent* de) { + if (android::base::StartsWith(de->d_name, "dm-")) { + return 1; + } + return 0; +} + +std::map DeviceMapper::FindDmPartitions() { + static constexpr auto DM_PATH_PREFIX = "/sys/block/"; + dirent** namelist; + int n = scandir(DM_PATH_PREFIX, &namelist, DmNameFilter, alphasort); + if (n == -1) { + PLOG(ERROR) << "Failed to scan dir " << DM_PATH_PREFIX; + return {}; + } + if (n == 0) { + LOG(ERROR) << "No dm block device found."; + free(namelist); + return {}; + } + + static constexpr auto DM_PATH_SUFFIX = "/dm/name"; + static constexpr auto DEV_PATH = "/dev/block/"; + std::map dm_block_devices; + while (n--) { + std::string path = DM_PATH_PREFIX + std::string(namelist[n]->d_name) + DM_PATH_SUFFIX; + std::string content; + if (!android::base::ReadFileToString(path, &content)) { + PLOG(WARNING) << "Failed to read " << path; + } else { + std::string dm_block_name = android::base::Trim(content); + // AVB is using 'vroot' for the root block device but we're expecting 'system'. + if (dm_block_name == "vroot") { + dm_block_name = "system"; + } else if (android::base::EndsWith(dm_block_name, "-verity")) { + auto npos = dm_block_name.rfind("-verity"); + dm_block_name = dm_block_name.substr(0, npos); + } else if (!android::base::GetProperty("ro.boot.avb_version", "").empty()) { + // Verified Boot 1.0 doesn't add a -verity suffix. On AVB 2 devices, + // if DAP is enabled, then a -verity suffix must be used to + // differentiate between dm-linear and dm-verity devices. If we get + // here, we're AVB 2 and looking at a non-verity partition. + free(namelist[n]); + continue; + } + + dm_block_devices.emplace(dm_block_name, DEV_PATH + std::string(namelist[n]->d_name)); + } + free(namelist[n]); + } + free(namelist); + + return dm_block_devices; +} + } // namespace dm } // namespace android diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h index 8fcdf7459..31c300326 100644 --- a/fs_mgr/libdm/include/libdm/dm.h +++ b/fs_mgr/libdm/include/libdm/dm.h @@ -17,6 +17,7 @@ #ifndef _LIBDM_DM_H_ #define _LIBDM_DM_H_ +#include #include #include #include @@ -26,6 +27,7 @@ #include #include +#include #include #include #include @@ -255,6 +257,12 @@ class DeviceMapper final { // * A failure occurred. std::optional GetParentBlockDeviceByPath(const std::string& path); + // Iterate the content over "/sys/block/dm-x/dm/name" and find + // all the dm-wrapped block devices. + // + // Returns mapping + std::map FindDmPartitions(); + 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/libsnapshot/snapuserd/Android.bp b/fs_mgr/libsnapshot/snapuserd/Android.bp index bc97afc32..47268d49d 100644 --- a/fs_mgr/libsnapshot/snapuserd/Android.bp +++ b/fs_mgr/libsnapshot/snapuserd/Android.bp @@ -78,6 +78,7 @@ cc_defaults { "liblog", "libsnapshot_cow", "libz", + "libext4_utils", ], } @@ -121,6 +122,7 @@ cc_test { "libz", "libfs_mgr", "libdm", + "libext4_utils", ], header_libs: [ "libstorage_literals_headers", diff --git a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h index 6bb7a394e..c592257ca 100644 --- a/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h +++ b/fs_mgr/libsnapshot/snapuserd/include/snapuserd/snapuserd_kernel.h @@ -47,8 +47,6 @@ typedef sector_t chunk_t; static constexpr uint32_t CHUNK_SIZE = 8; static constexpr uint32_t CHUNK_SHIFT = (__builtin_ffs(CHUNK_SIZE) - 1); -#define DIV_ROUND_UP(n, d) (((n) + (d)-1) / (d)) - // This structure represents the kernel COW header. // All the below fields should be in Little Endian format. struct disk_header { diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp index 31d0221ef..b32dd8a7e 100644 --- a/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp +++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.cpp @@ -16,10 +16,22 @@ #include "snapuserd.h" +#include +#include +#include +#include +#include + #include #include #include +#include +#include +#include +#include +#include +#include #include namespace android { @@ -659,6 +671,74 @@ bool Snapuserd::InitCowDevice() { return ReadMetadata(); } +void Snapuserd::ReadBlocksToCache(const std::string& dm_block_device, + const std::string partition_name, off_t offset, size_t size) { + android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY))); + if (fd.get() == -1) { + SNAP_PLOG(ERROR) << "Error reading " << dm_block_device + << " partition-name: " << partition_name; + return; + } + + size_t remain = size; + off_t file_offset = offset; + // We pick 4M I/O size based on the fact that the current + // update_verifier has a similar I/O size. + size_t read_sz = 1024 * BLOCK_SZ; + std::vector buf(read_sz); + + while (remain > 0) { + size_t to_read = std::min(remain, read_sz); + + if (!android::base::ReadFullyAtOffset(fd.get(), buf.data(), to_read, file_offset)) { + SNAP_PLOG(ERROR) << "Failed to read block from block device: " << dm_block_device + << " at offset: " << file_offset + << " partition-name: " << partition_name << " total-size: " << size + << " remain_size: " << remain; + return; + } + + file_offset += to_read; + remain -= to_read; + } + + SNAP_LOG(INFO) << "Finished reading block-device: " << dm_block_device + << " partition: " << partition_name << " size: " << size + << " offset: " << offset; +} + +void Snapuserd::ReadBlocks(const std::string partition_name, const std::string& dm_block_device) { + SNAP_LOG(DEBUG) << "Reading partition: " << partition_name + << " Block-Device: " << dm_block_device; + + uint64_t dev_sz = 0; + + unique_fd fd(TEMP_FAILURE_RETRY(open(dm_block_device.c_str(), O_RDONLY | O_CLOEXEC))); + if (fd < 0) { + SNAP_LOG(ERROR) << "Cannot open block device"; + return; + } + + dev_sz = get_block_device_size(fd.get()); + if (!dev_sz) { + SNAP_PLOG(ERROR) << "Could not determine block device size: " << dm_block_device; + return; + } + + int num_threads = 2; + size_t num_blocks = dev_sz >> BLOCK_SHIFT; + size_t num_blocks_per_thread = num_blocks / num_threads; + size_t read_sz_per_thread = num_blocks_per_thread << BLOCK_SHIFT; + off_t offset = 0; + + for (int i = 0; i < num_threads; i++) { + std::async(std::launch::async, &Snapuserd::ReadBlocksToCache, this, dm_block_device, + partition_name, offset, read_sz_per_thread); + + offset += read_sz_per_thread; + } +} + /* * Entry point to launch threads */ @@ -687,6 +767,39 @@ bool Snapuserd::Start() { std::async(std::launch::async, &WorkerThread::RunThread, worker_threads_[i].get())); } + bool second_stage_init = true; + + // We don't want to read the blocks during first stage init. + if (android::base::EndsWith(misc_name_, "-init") || is_socket_present_) { + second_stage_init = false; + } + + if (second_stage_init) { + SNAP_LOG(INFO) << "Reading blocks to cache...."; + auto& dm = DeviceMapper::Instance(); + auto dm_block_devices = dm.FindDmPartitions(); + if (dm_block_devices.empty()) { + SNAP_LOG(ERROR) << "No dm-enabled block device is found."; + } else { + auto parts = android::base::Split(misc_name_, "-"); + std::string partition_name = parts[0]; + + const char* suffix_b = "_b"; + const char* suffix_a = "_a"; + + partition_name.erase(partition_name.find_last_not_of(suffix_b) + 1); + partition_name.erase(partition_name.find_last_not_of(suffix_a) + 1); + + if (dm_block_devices.find(partition_name) == dm_block_devices.end()) { + SNAP_LOG(ERROR) << "Failed to find dm block device for " << partition_name; + } else { + ReadBlocks(partition_name, dm_block_devices.at(partition_name)); + } + } + } else { + SNAP_LOG(INFO) << "Not reading block device into cache"; + } + bool ret = true; for (auto& t : threads) { ret = t.get() && ret; diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd.h b/fs_mgr/libsnapshot/snapuserd/snapuserd.h index 95d2f7726..f4c296aee 100644 --- a/fs_mgr/libsnapshot/snapuserd/snapuserd.h +++ b/fs_mgr/libsnapshot/snapuserd/snapuserd.h @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -284,6 +285,7 @@ class Snapuserd : public std::enable_shared_from_this { // Total number of blocks to be merged in a given read-ahead buffer region void SetTotalRaBlocksMerged(int x) { total_ra_blocks_merged_ = x; } int GetTotalRaBlocksMerged() { return total_ra_blocks_merged_; } + void SetSocketPresent(bool socket) { is_socket_present_ = socket; } private: bool IsChunkIdMetadata(chunk_t chunk); @@ -296,6 +298,10 @@ class Snapuserd : public std::enable_shared_from_this { bool IsBlockAligned(int read_size) { return ((read_size & (BLOCK_SZ - 1)) == 0); } struct BufferState* GetBufferState(); + void ReadBlocks(const std::string partition_name, const std::string& dm_block_device); + void ReadBlocksToCache(const std::string& dm_block_device, const std::string partition_name, + off_t offset, size_t size); + std::string cow_device_; std::string backing_store_device_; std::string control_device_; @@ -335,6 +341,7 @@ class Snapuserd : public std::enable_shared_from_this { bool merge_initiated_ = false; bool attached_ = false; + bool is_socket_present_; }; } // namespace snapshot diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp index 672c13c5f..2f87557c6 100644 --- a/fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp +++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.cpp @@ -227,6 +227,7 @@ bool SnapuserdServer::Receivemsg(android::base::borrowed_fd fd, const std::strin void SnapuserdServer::RunThread(std::shared_ptr handler) { LOG(INFO) << "Entering thread for handler: " << handler->misc_name(); + handler->snapuserd()->SetSocketPresent(is_socket_present_); if (!handler->snapuserd()->Start()) { LOG(ERROR) << " Failed to launch all worker threads"; } @@ -290,6 +291,7 @@ bool SnapuserdServer::StartWithSocket(bool start_listening) { } AddWatchedFd(sockfd_, POLLIN); + is_socket_present_ = true; // If started in first-stage init, the property service won't be online. if (access("/dev/socket/property_service", F_OK) == 0) { diff --git a/fs_mgr/libsnapshot/snapuserd/snapuserd_server.h b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.h index 846f84881..3b6ff1583 100644 --- a/fs_mgr/libsnapshot/snapuserd/snapuserd_server.h +++ b/fs_mgr/libsnapshot/snapuserd/snapuserd_server.h @@ -96,6 +96,7 @@ class SnapuserdServer : public Stoppable { bool terminating_; volatile bool received_socket_signal_ = false; std::vector watched_fds_; + bool is_socket_present_ = false; std::mutex lock_;