Add dm-thin support
thin-pool and thin targets are supported via DmTargetThinPool and DmTargetThin. DM_TARGET_MSG is also added via a new method SendMessage() because it's used to create a thin volumn. dmctl is extended to support thin-pool and thin targets. TODO: thin-pool target constructor can accept feature arguments. Bug: 327081431 Test: atest libdm_test (CF with dm-thin enabled kernel) Change-Id: I4c51c668bfe1489b959f6d03c205a5e2e63d9a1d
This commit is contained in:
parent
aedc0d0b41
commit
2f814176ea
7 changed files with 194 additions and 0 deletions
|
@ -71,6 +71,9 @@ cc_defaults {
|
|||
"libbase",
|
||||
"liblog",
|
||||
],
|
||||
header_libs: [
|
||||
"libstorage_literals_headers",
|
||||
],
|
||||
srcs: [":libdm_test_srcs"],
|
||||
auto_gen_config: true,
|
||||
require_root: true,
|
||||
|
|
|
@ -769,5 +769,25 @@ bool DeviceMapper::CreatePlaceholderDevice(const std::string& name) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool DeviceMapper::SendMessage(const std::string& name, uint64_t sector,
|
||||
const std::string& message) {
|
||||
std::string ioctl_buffer(sizeof(struct dm_ioctl) + sizeof(struct dm_target_msg), 0);
|
||||
ioctl_buffer += message;
|
||||
ioctl_buffer.push_back('\0');
|
||||
|
||||
struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(&ioctl_buffer[0]);
|
||||
InitIo(io, name);
|
||||
io->data_size = ioctl_buffer.size();
|
||||
io->data_start = sizeof(struct dm_ioctl);
|
||||
struct dm_target_msg* msg =
|
||||
reinterpret_cast<struct dm_target_msg*>(&ioctl_buffer[sizeof(struct dm_ioctl)]);
|
||||
msg->sector = sector;
|
||||
if (ioctl(fd_, DM_TARGET_MSG, io)) {
|
||||
PLOG(ERROR) << "DM_TARGET_MSG failed";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace dm
|
||||
} // namespace android
|
||||
|
|
|
@ -298,5 +298,43 @@ std::string DmTargetUser::GetParameterString() const {
|
|||
return android::base::Join(argv, " ");
|
||||
}
|
||||
|
||||
DmTargetThinPool::DmTargetThinPool(uint64_t start, uint64_t length, const std::string& metadata_dev,
|
||||
const std::string& data_dev, uint64_t data_block_size,
|
||||
uint64_t low_water_mark)
|
||||
: DmTarget(start, length),
|
||||
metadata_dev_(metadata_dev),
|
||||
data_dev_(data_dev),
|
||||
data_block_size_(data_block_size),
|
||||
low_water_mark_(low_water_mark) {}
|
||||
|
||||
std::string DmTargetThinPool::GetParameterString() const {
|
||||
std::vector<std::string> args{
|
||||
metadata_dev_,
|
||||
data_dev_,
|
||||
std::to_string(data_block_size_),
|
||||
std::to_string(low_water_mark_),
|
||||
};
|
||||
return android::base::Join(args, " ");
|
||||
}
|
||||
|
||||
bool DmTargetThinPool::Valid() const {
|
||||
// data_block_size: must be between 128 (64KB) and 2097152 (1GB) and a multiple of 128 (64KB)
|
||||
if (data_block_size_ < 128 || data_block_size_ > 2097152) return false;
|
||||
if (data_block_size_ % 128) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
DmTargetThin::DmTargetThin(uint64_t start, uint64_t length, const std::string& pool_dev,
|
||||
uint64_t dev_id)
|
||||
: DmTarget(start, length), pool_dev_(pool_dev), dev_id_(dev_id) {}
|
||||
|
||||
std::string DmTargetThin::GetParameterString() const {
|
||||
std::vector<std::string> args{
|
||||
pool_dev_,
|
||||
std::to_string(dev_id_),
|
||||
};
|
||||
return android::base::Join(args, " ");
|
||||
}
|
||||
|
||||
} // namespace dm
|
||||
} // namespace android
|
||||
|
|
|
@ -37,12 +37,14 @@
|
|||
#include <gtest/gtest.h>
|
||||
#include <libdm/dm.h>
|
||||
#include <libdm/loop_control.h>
|
||||
#include <storage_literals/storage_literals.h>
|
||||
#include "test_util.h"
|
||||
#include "utility.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace std::chrono_literals;
|
||||
using namespace android::dm;
|
||||
using namespace android::storage_literals;
|
||||
using android::base::make_scope_guard;
|
||||
using android::base::unique_fd;
|
||||
|
||||
|
@ -773,3 +775,42 @@ TEST_F(DmTest, GetNameAndUuid) {
|
|||
ASSERT_EQ(name, test_name_);
|
||||
ASSERT_FALSE(uuid.empty());
|
||||
}
|
||||
|
||||
TEST_F(DmTest, ThinProvisioning) {
|
||||
if (!DeviceMapper::Instance().GetTargetByName("thin-pool", nullptr)) GTEST_SKIP();
|
||||
|
||||
constexpr uint64_t MetaSize = 2_MiB;
|
||||
constexpr uint64_t DataSize = 64_MiB;
|
||||
constexpr uint64_t ThinSize = 1_TiB;
|
||||
|
||||
// Prepare two loop devices for meta and data devices.
|
||||
TemporaryFile meta;
|
||||
ASSERT_GE(meta.fd, 0);
|
||||
ASSERT_EQ(0, ftruncate64(meta.fd, MetaSize));
|
||||
TemporaryFile data;
|
||||
ASSERT_GE(data.fd, 0);
|
||||
ASSERT_EQ(0, ftruncate64(data.fd, DataSize));
|
||||
|
||||
LoopDevice loop_meta(meta.fd, 10s);
|
||||
ASSERT_TRUE(loop_meta.valid());
|
||||
LoopDevice loop_data(data.fd, 10s);
|
||||
ASSERT_TRUE(loop_data.valid());
|
||||
|
||||
// Create a thin-pool
|
||||
DmTable poolTable;
|
||||
poolTable.Emplace<DmTargetThinPool>(0, DataSize / kSectorSize, loop_meta.device(),
|
||||
loop_data.device(), 128, 0);
|
||||
TempDevice pool("pool", poolTable);
|
||||
ASSERT_TRUE(pool.valid());
|
||||
|
||||
// Create a thin volume
|
||||
uint64_t thin_volume_id = 0;
|
||||
ASSERT_TRUE(DeviceMapper::Instance().SendMessage(
|
||||
"pool", 0, "create_thin " + std::to_string(thin_volume_id)));
|
||||
|
||||
// Use a thin volume to create a 1T device
|
||||
DmTable thinTable;
|
||||
thinTable.Emplace<DmTargetThin>(0, ThinSize / kSectorSize, pool.path(), thin_volume_id);
|
||||
TempDevice thin("thin", thinTable);
|
||||
ASSERT_TRUE(thin.valid());
|
||||
}
|
||||
|
|
|
@ -307,6 +307,9 @@ class DeviceMapper final : public IDeviceMapper {
|
|||
|
||||
bool GetDeviceNameAndUuid(dev_t dev, std::string* name, std::string* uuid);
|
||||
|
||||
// Send |message| to target, pointed by |name| and |sector|. Use 0 if |sector| is not needed.
|
||||
bool SendMessage(const std::string& name, uint64_t sector, const std::string& message);
|
||||
|
||||
private:
|
||||
// Maximum possible device mapper targets registered in the kernel.
|
||||
// This is only used to read the list of targets from kernel so we allocate
|
||||
|
|
|
@ -349,6 +349,35 @@ class DmTargetError final : public DmTarget {
|
|||
std::string GetParameterString() const override { return ""; }
|
||||
};
|
||||
|
||||
class DmTargetThinPool final : public DmTarget {
|
||||
public:
|
||||
DmTargetThinPool(uint64_t start, uint64_t length, const std::string& metadata_dev,
|
||||
const std::string& data_dev, uint64_t data_block_size,
|
||||
uint64_t low_water_mark);
|
||||
|
||||
std::string name() const override { return "thin-pool"; }
|
||||
std::string GetParameterString() const override;
|
||||
bool Valid() const override;
|
||||
|
||||
private:
|
||||
std::string metadata_dev_;
|
||||
std::string data_dev_;
|
||||
uint64_t data_block_size_;
|
||||
uint64_t low_water_mark_;
|
||||
};
|
||||
|
||||
class DmTargetThin final : public DmTarget {
|
||||
public:
|
||||
DmTargetThin(uint64_t start, uint64_t length, const std::string& pool_dev, uint64_t dev_id);
|
||||
|
||||
std::string name() const override { return "thin"; }
|
||||
std::string GetParameterString() const override;
|
||||
|
||||
private:
|
||||
std::string pool_dev_;
|
||||
uint64_t dev_id_;
|
||||
};
|
||||
|
||||
} // namespace dm
|
||||
} // namespace android
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@ static int Usage(void) {
|
|||
std::cerr << " create <dm-name> [-ro] <targets...>" << std::endl;
|
||||
std::cerr << " delete <dm-name>" << std::endl;
|
||||
std::cerr << " list <devices | targets> [-v]" << std::endl;
|
||||
std::cerr << " message <dm-name> <sector> <message>" << std::endl;
|
||||
std::cerr << " getpath <dm-name>" << std::endl;
|
||||
std::cerr << " getuuid <dm-name>" << std::endl;
|
||||
std::cerr << " ima <dm-name>" << std::endl;
|
||||
|
@ -203,6 +204,46 @@ class TargetParser final {
|
|||
return std::make_unique<DmTargetUser>(start_sector, num_sectors, control_device);
|
||||
} else if (target_type == "error") {
|
||||
return std::make_unique<DmTargetError>(start_sector, num_sectors);
|
||||
} else if (target_type == "thin-pool") {
|
||||
if (!HasArgs(4)) {
|
||||
std::cerr << "Expected \"thin-pool\" <metadata dev> <data dev> <data block size> "
|
||||
"<low water mark> <feature args>"
|
||||
<< std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string metadata_dev = NextArg();
|
||||
std::string data_dev = NextArg();
|
||||
std::string data_block_size_str = NextArg();
|
||||
std::string low_water_mark_str = NextArg();
|
||||
|
||||
uint64_t data_block_size;
|
||||
if (!android::base::ParseUint(data_block_size_str, &data_block_size)) {
|
||||
std::cerr << "Data block size must be an unsigned integer.\n";
|
||||
return nullptr;
|
||||
}
|
||||
uint64_t low_water_mark;
|
||||
if (!android::base::ParseUint(low_water_mark_str, &low_water_mark)) {
|
||||
std::cerr << "Low water mark must be an unsigned integer.\n";
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_unique<DmTargetThinPool>(start_sector, num_sectors, metadata_dev,
|
||||
data_dev, data_block_size, low_water_mark);
|
||||
} else if (target_type == "thin") {
|
||||
if (!HasArgs(2)) {
|
||||
std::cerr << "Expected \"thin\" <pool dev> <dev id>" << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string pool_dev = NextArg();
|
||||
std::string dev_id_str = NextArg();
|
||||
|
||||
uint64_t dev_id;
|
||||
if (!android::base::ParseUint(dev_id_str, &dev_id)) {
|
||||
std::cerr << "Dev id must be an unsigned integer.\n";
|
||||
return nullptr;
|
||||
}
|
||||
return std::make_unique<DmTargetThin>(start_sector, num_sectors, pool_dev, dev_id);
|
||||
} else {
|
||||
std::cerr << "Unrecognized target type: " << target_type << std::endl;
|
||||
return nullptr;
|
||||
|
@ -417,6 +458,24 @@ static int DmListCmdHandler(int argc, char** argv) {
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int DmMessageCmdHandler(int argc, char** argv) {
|
||||
if (argc != 3) {
|
||||
std::cerr << "Usage: dmctl message <name> <sector> <message>" << std::endl;
|
||||
return -EINVAL;
|
||||
}
|
||||
uint64_t sector;
|
||||
if (!android::base::ParseUint(argv[1], §or)) {
|
||||
std::cerr << "Invalid argument for sector: " << argv[1] << std::endl;
|
||||
return -EINVAL;
|
||||
}
|
||||
DeviceMapper& dm = DeviceMapper::Instance();
|
||||
if (!dm.SendMessage(argv[0], sector, argv[2])) {
|
||||
std::cerr << "Could not send message to " << argv[0] << std::endl;
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int HelpCmdHandler(int /* argc */, char** /* argv */) {
|
||||
Usage();
|
||||
return 0;
|
||||
|
@ -576,6 +635,7 @@ static std::map<std::string, std::function<int(int, char**)>> cmdmap = {
|
|||
{"delete", DmDeleteCmdHandler},
|
||||
{"replace", DmReplaceCmdHandler},
|
||||
{"list", DmListCmdHandler},
|
||||
{"message", DmMessageCmdHandler},
|
||||
{"help", HelpCmdHandler},
|
||||
{"getpath", GetPathCmdHandler},
|
||||
{"getuuid", GetUuidCmdHandler},
|
||||
|
|
Loading…
Reference in a new issue