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)
diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp
index d5b8a61..c4874b8 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 @@
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 41d3145..8006db2 100644
--- a/fs_mgr/libdm/dm_test.cpp
+++ b/fs_mgr/libdm/dm_test.cpp
@@ -35,6 +35,7 @@
#include <libdm/dm.h>
#include <libdm/loop_control.h>
#include "test_util.h"
+#include "utility.h"
using namespace std;
using namespace std::chrono_literals;
@@ -617,3 +618,64 @@
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<DmTargetLinear>(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<DmTargetLinear>(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 5d6db46..70b14fa 100644
--- a/fs_mgr/libdm/include/libdm/dm.h
+++ b/fs_mgr/libdm/include/libdm/dm.h
@@ -95,6 +95,12 @@
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<Info> GetDetailedInfo(const std::string& name) const;