Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 15e0f5a9 authored by Nikita Ioffe's avatar Nikita Ioffe
Browse files

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
parent 5e2363e1
Loading
Loading
Loading
Loading
+25 −7
Original line number Diff line number Diff line
@@ -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);
+15 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <thread>

#include <android-base/file.h>
#include <android-base/scopeguard.h>
#include <android-base/strings.h>
#include <android-base/unique_fd.h>
#include <gtest/gtest.h>
@@ -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));
}
+13 −0
Original line number Diff line number Diff line
@@ -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