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)
This commit is contained in:
Nikita Ioffe 2021-05-25 16:51:24 +01:00
parent d04966a2fd
commit 8c427b9f67
3 changed files with 91 additions and 0 deletions

View File

@ -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);

View File

@ -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 @@ 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<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);
}

View File

@ -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<Info> GetDetailedInfo(const std::string& name) const;