diff --git a/fs_mgr/libdm/Android.bp b/fs_mgr/libdm/Android.bp index 5cc0346fa..c3ca758ae 100644 --- a/fs_mgr/libdm/Android.bp +++ b/fs_mgr/libdm/Android.bp @@ -71,6 +71,9 @@ cc_defaults { "libbase", "liblog", ], + header_libs: [ + "libstorage_literals_headers", + ], srcs: [":libdm_test_srcs"], auto_gen_config: true, require_root: true, diff --git a/fs_mgr/libdm/dm.cpp b/fs_mgr/libdm/dm.cpp index fee67fdfa..a9633226f 100644 --- a/fs_mgr/libdm/dm.cpp +++ b/fs_mgr/libdm/dm.cpp @@ -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(&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(&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 diff --git a/fs_mgr/libdm/dm_target.cpp b/fs_mgr/libdm/dm_target.cpp index 1f6bd1a21..b5cc9aa87 100644 --- a/fs_mgr/libdm/dm_target.cpp +++ b/fs_mgr/libdm/dm_target.cpp @@ -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 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 args{ + pool_dev_, + std::to_string(dev_id_), + }; + return android::base::Join(args, " "); +} + } // namespace dm } // namespace android diff --git a/fs_mgr/libdm/dm_test.cpp b/fs_mgr/libdm/dm_test.cpp index d043be63b..b890f4715 100644 --- a/fs_mgr/libdm/dm_test.cpp +++ b/fs_mgr/libdm/dm_test.cpp @@ -37,12 +37,14 @@ #include #include #include +#include #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(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(0, ThinSize / kSectorSize, pool.path(), thin_volume_id); + TempDevice thin("thin", thinTable); + ASSERT_TRUE(thin.valid()); +} diff --git a/fs_mgr/libdm/include/libdm/dm.h b/fs_mgr/libdm/include/libdm/dm.h index fa976535b..43d84f968 100644 --- a/fs_mgr/libdm/include/libdm/dm.h +++ b/fs_mgr/libdm/include/libdm/dm.h @@ -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 diff --git a/fs_mgr/libdm/include/libdm/dm_target.h b/fs_mgr/libdm/include/libdm/dm_target.h index 97f3c134e..c49fc5ec8 100644 --- a/fs_mgr/libdm/include/libdm/dm_target.h +++ b/fs_mgr/libdm/include/libdm/dm_target.h @@ -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 diff --git a/fs_mgr/tools/dmctl.cpp b/fs_mgr/tools/dmctl.cpp index f84382118..00f8038e1 100644 --- a/fs_mgr/tools/dmctl.cpp +++ b/fs_mgr/tools/dmctl.cpp @@ -50,6 +50,7 @@ static int Usage(void) { std::cerr << " create [-ro] " << std::endl; std::cerr << " delete " << std::endl; std::cerr << " list [-v]" << std::endl; + std::cerr << " message " << std::endl; std::cerr << " getpath " << std::endl; std::cerr << " getuuid " << std::endl; std::cerr << " ima " << std::endl; @@ -203,6 +204,46 @@ class TargetParser final { return std::make_unique(start_sector, num_sectors, control_device); } else if (target_type == "error") { return std::make_unique(start_sector, num_sectors); + } else if (target_type == "thin-pool") { + if (!HasArgs(4)) { + std::cerr << "Expected \"thin-pool\" " + " " + << 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(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\" " << 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(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 " << 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> cmdmap = { {"delete", DmDeleteCmdHandler}, {"replace", DmReplaceCmdHandler}, {"list", DmListCmdHandler}, + {"message", DmMessageCmdHandler}, {"help", HelpCmdHandler}, {"getpath", GetPathCmdHandler}, {"getuuid", GetUuidCmdHandler},