Merge "snapuserd: Read partition blocks to memory"

This commit is contained in:
Akilesh Kailash 2021-08-05 03:02:07 +00:00 committed by Gerrit Code Review
commit e19f7be126
8 changed files with 189 additions and 2 deletions

View file

@ -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<std::string, std::string> 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<std::string, std::string> 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

View file

@ -17,6 +17,7 @@
#ifndef _LIBDM_DM_H_
#define _LIBDM_DM_H_
#include <dirent.h>
#include <fcntl.h>
#include <linux/dm-ioctl.h>
#include <linux/kdev_t.h>
@ -26,6 +27,7 @@
#include <unistd.h>
#include <chrono>
#include <map>
#include <memory>
#include <optional>
#include <string>
@ -255,6 +257,12 @@ class DeviceMapper final {
// * A failure occurred.
std::optional<std::string> 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 <partition-name, /dev/block/dm-x>
std::map<std::string, std::string> 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

View file

@ -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",

View file

@ -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 {

View file

@ -16,10 +16,22 @@
#include "snapuserd.h"
#include <dirent.h>
#include <fcntl.h>
#include <linux/fs.h>
#include <unistd.h>
#include <algorithm>
#include <csignal>
#include <optional>
#include <set>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <snapuserd/snapuserd_client.h>
namespace android {
@ -678,6 +690,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<uint8_t> 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
*/
@ -706,6 +786,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;

View file

@ -38,6 +38,7 @@
#include <android-base/logging.h>
#include <android-base/stringprintf.h>
#include <android-base/unique_fd.h>
#include <ext4_utils/ext4_utils.h>
#include <libdm/dm.h>
#include <libsnapshot/cow_reader.h>
#include <libsnapshot/cow_writer.h>
@ -302,6 +303,7 @@ class Snapuserd : public std::enable_shared_from_this<Snapuserd> {
// 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);
@ -314,6 +316,10 @@ class Snapuserd : public std::enable_shared_from_this<Snapuserd> {
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_;
@ -353,6 +359,7 @@ class Snapuserd : public std::enable_shared_from_this<Snapuserd> {
bool merge_initiated_ = false;
bool attached_ = false;
bool is_socket_present_;
};
} // namespace snapshot

View file

@ -227,6 +227,7 @@ bool SnapuserdServer::Receivemsg(android::base::borrowed_fd fd, const std::strin
void SnapuserdServer::RunThread(std::shared_ptr<DmUserHandler> 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) {

View file

@ -96,6 +96,7 @@ class SnapuserdServer : public Stoppable {
bool terminating_;
volatile bool received_socket_signal_ = false;
std::vector<struct pollfd> watched_fds_;
bool is_socket_present_ = false;
std::mutex lock_;