From 8c427b9f676d37068ef0a8376950b51f05d90233 Mon Sep 17 00:00:00 2001 From: Nikita Ioffe Date: Tue, 25 May 2021 16:51:24 +0100 Subject: [PATCH] libdm: Add DeleteDeviceDeferred API This can be useful in case when a device mapper device can't be deleted straight away, and instead delete needs to be enqueued until last reference to the device is closed. Bug: 187864524 Bug: 188713178 Test: atest libdm_test Change-Id: Ie8a130baa54e6e16d8d159389bd760bf873eca40 Merged-In: Ie8a130baa54e6e16d8d159389bd760bf873eca40 (cherry picked from commit d13cef743545c6de7b5122cb515bb82b508d019e) --- fs_mgr/libdm/dm.cpp | 23 ++++++++++++ fs_mgr/libdm/dm_test.cpp | 62 +++++++++++++++++++++++++++++++++ fs_mgr/libdm/include/libdm/dm.h | 6 ++++ 3 files changed, 91 insertions(+) diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp index d5b8a6113..c4874b8d1 100644 --- a/fs_mgr/libdm/dm.cpp +++ b/fs_mgr/libdm/dm.cpp @@ -35,6 +35,10 @@ #include "utility.h" +#ifndef DM_DEFERRED_REMOVE +#define DM_DEFERRED_REMOVE (1 << 17) +#endif + namespace android { namespace dm { @@ -133,6 +137,25 @@ bool DeviceMapper::DeleteDevice(const std::string& name) { return DeleteDevice(name, 0ms); } +bool DeviceMapper::DeleteDeviceDeferred(const std::string& name) { + struct dm_ioctl io; + InitIo(&io, name); + + io.flags |= DM_DEFERRED_REMOVE; + if (ioctl(fd_, DM_DEV_REMOVE, &io)) { + PLOG(ERROR) << "DM_DEV_REMOVE with DM_DEFERRED_REMOVE failed for [" << name << "]"; + return false; + } + return true; +} + +bool DeviceMapper::DeleteDeviceIfExistsDeferred(const std::string& name) { + if (GetState(name) == DmDeviceState::INVALID) { + return true; + } + return DeleteDeviceDeferred(name); +} + static std::string GenerateUuid() { uuid_t uuid_bytes; uuid_generate(uuid_bytes); diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp index 41d3145bb..8006db220 100644 --- a/fs_mgr/libdm/dm_test.cpp +++ b/fs_mgr/libdm/dm_test.cpp @@ -35,6 +35,7 @@ #include #include #include "test_util.h" +#include "utility.h" using namespace std; using namespace std::chrono_literals; @@ -617,3 +618,64 @@ TEST(libdm, GetParentBlockDeviceByPath) { auto sub_block_device = dm.GetParentBlockDeviceByPath(dev.path()); ASSERT_EQ(loop.device(), *sub_block_device); } + +TEST(libdm, DeleteDeviceDeferredNoReferences) { + unique_fd tmp(CreateTempFile("file_1", 4096)); + ASSERT_GE(tmp, 0); + LoopDevice loop(tmp, 10s); + ASSERT_TRUE(loop.valid()); + + DmTable table; + ASSERT_TRUE(table.Emplace(0, 1, loop.device(), 0)); + ASSERT_TRUE(table.valid()); + TempDevice dev("libdm-test-dm-linear", table); + ASSERT_TRUE(dev.valid()); + + DeviceMapper& dm = DeviceMapper::Instance(); + + std::string path; + ASSERT_TRUE(dm.GetDmDevicePathByName("libdm-test-dm-linear", &path)); + ASSERT_EQ(0, access(path.c_str(), F_OK)); + + ASSERT_TRUE(dm.DeleteDeviceDeferred("libdm-test-dm-linear")); + + ASSERT_TRUE(WaitForFileDeleted(path, 5s)); + ASSERT_EQ(DmDeviceState::INVALID, dm.GetState("libdm-test-dm-linear")); + ASSERT_NE(0, access(path.c_str(), F_OK)); + ASSERT_EQ(ENOENT, errno); +} + +TEST(libdm, DeleteDeviceDeferredWaitsForLastReference) { + unique_fd tmp(CreateTempFile("file_1", 4096)); + ASSERT_GE(tmp, 0); + LoopDevice loop(tmp, 10s); + ASSERT_TRUE(loop.valid()); + + DmTable table; + ASSERT_TRUE(table.Emplace(0, 1, loop.device(), 0)); + ASSERT_TRUE(table.valid()); + TempDevice dev("libdm-test-dm-linear", table); + ASSERT_TRUE(dev.valid()); + + DeviceMapper& dm = DeviceMapper::Instance(); + + std::string path; + ASSERT_TRUE(dm.GetDmDevicePathByName("libdm-test-dm-linear", &path)); + ASSERT_EQ(0, access(path.c_str(), F_OK)); + + { + // Open a reference to block device. + unique_fd fd(TEMP_FAILURE_RETRY(open(dev.path().c_str(), O_RDONLY | O_CLOEXEC))); + ASSERT_GE(fd.get(), 0); + + ASSERT_TRUE(dm.DeleteDeviceDeferred("libdm-test-dm-linear")); + + ASSERT_EQ(0, access(path.c_str(), F_OK)); + } + + // After release device will be removed. + ASSERT_TRUE(WaitForFileDeleted(path, 5s)); + ASSERT_EQ(DmDeviceState::INVALID, dm.GetState("libdm-test-dm-linear")); + ASSERT_NE(0, access(path.c_str(), F_OK)); + ASSERT_EQ(ENOENT, errno); +} diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h index 5d6db467b..70b14fa46 100644 --- a/fs_mgr/libdm/include/libdm/dm.h +++ b/fs_mgr/libdm/include/libdm/dm.h @@ -95,6 +95,12 @@ class DeviceMapper final { bool DeleteDevice(const std::string& name, const std::chrono::milliseconds& timeout_ms); bool DeleteDeviceIfExists(const std::string& name, const std::chrono::milliseconds& timeout_ms); + // Enqueues a deletion of device mapper device with the given name once last reference is + // closed. + // Returns 'true' on success, false otherwise. + bool DeleteDeviceDeferred(const std::string& name); + bool DeleteDeviceIfExistsDeferred(const std::string& name); + // Fetches and returns the complete state of the underlying device mapper // device with given name. std::optional GetDetailedInfo(const std::string& name) const;