From 15e0f5a98acbec5f00f446ac61d6f79b9ee3bd80 Mon Sep 17 00:00:00 2001 From: Nikita Ioffe Date: Fri, 25 Jun 2021 19:48:10 +0100 Subject: [PATCH] Add CreateEmptyDevice and WaitForDevice APIs These APIs support a flow in which dm devices can be created before they are actually needed, hence minimizing the time a process will wait for ueventd to create user space paths. Bug: 190618831 Test: atest libdm_test Change-Id: I4dfa14e5271a6a13de6da73ec3c7efb1ebc0f8b8 --- fs_mgr/libdm/dm.cpp | 32 +++++++++++++++++++++++++------- fs_mgr/libdm/dm_test.cpp | 15 +++++++++++++++ fs_mgr/libdm/include/libdm/dm.h | 13 +++++++++++++ 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp index c4874b8d1..a5eda2983 100644 --- a/fs_mgr/libdm/dm.cpp +++ b/fs_mgr/libdm/dm.cpp @@ -170,19 +170,18 @@ static bool IsRecovery() { return access("/system/bin/recovery", F_OK) == 0; } -bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path, - const std::chrono::milliseconds& timeout_ms) { +bool DeviceMapper::CreateEmptyDevice(const std::string& name) { std::string uuid = GenerateUuid(); - if (!CreateDevice(name, uuid)) { - return false; - } + return CreateDevice(name, uuid); +} +bool DeviceMapper::WaitForDevice(const std::string& name, + const std::chrono::milliseconds& timeout_ms, std::string* path) { // We use the unique path for testing whether the device is ready. After // that, it's safe to use the dm-N path which is compatible with callers // that expect it to be formatted as such. std::string unique_path; - if (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) || - !GetDmDevicePathByName(name, path)) { + if (!GetDeviceUniquePath(name, &unique_path) || !GetDmDevicePathByName(name, path)) { DeleteDevice(name); return false; } @@ -208,6 +207,25 @@ bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, s return true; } +bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path, + const std::chrono::milliseconds& timeout_ms) { + if (!CreateEmptyDevice(name)) { + return false; + } + + if (!LoadTableAndActivate(name, table)) { + DeleteDevice(name); + return false; + } + + if (!WaitForDevice(name, timeout_ms, path)) { + DeleteDevice(name); + return false; + } + + return true; +} + bool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) { struct dm_ioctl io; InitIo(&io, name); diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp index 8006db220..8314ec596 100644 --- a/fs_mgr/libdm/dm_test.cpp +++ b/fs_mgr/libdm/dm_test.cpp @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -679,3 +680,17 @@ TEST(libdm, DeleteDeviceDeferredWaitsForLastReference) { ASSERT_NE(0, access(path.c_str(), F_OK)); ASSERT_EQ(ENOENT, errno); } + +TEST(libdm, CreateEmptyDevice) { + DeviceMapper& dm = DeviceMapper::Instance(); + ASSERT_TRUE(dm.CreateEmptyDevice("empty-device")); + auto guard = android::base::make_scope_guard([&]() { dm.DeleteDevice("empty-device", 5s); }); + + // Empty device should be in suspended state. + ASSERT_EQ(DmDeviceState::SUSPENDED, dm.GetState("empty-device")); + + std::string path; + ASSERT_TRUE(dm.WaitForDevice("empty-device", 5s, &path)); + // Path should exist. + ASSERT_EQ(0, access(path.c_str(), F_OK)); +} diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h index 70b14fa46..8fcdf7459 100644 --- a/fs_mgr/libdm/include/libdm/dm.h +++ b/fs_mgr/libdm/include/libdm/dm.h @@ -115,6 +115,19 @@ class DeviceMapper final { // - ACTIVE: resumes the device. bool ChangeState(const std::string& name, DmDeviceState state); + // Creates empty device. + // This supports a use case when a caller doesn't need a device straight away, but instead + // asks kernel to create it beforehand, thus avoiding blocking itself from waiting for ueventd + // to create user space paths. + // Callers are expected to then activate their device by calling LoadTableAndActivate function. + // To avoid race conditions, callers must still synchronize with ueventd by calling + // WaitForDevice function. + bool CreateEmptyDevice(const std::string& name); + + // Waits for device paths to be created in the user space. + bool WaitForDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms, + std::string* path); + // Creates a device, loads the given table, and activates it. If the device // is not able to be activated, it is destroyed, and false is returned. // After creation, |path| contains the result of calling