首页 > 技术文章 > fs_mgr_libdm模块-dm设备的创建

pyjetson 2021-06-10 19:34 原文

概述

android/system/core/fs_mgr/libdm和dm设备的创建

源码解析

1. DmTarget模块

1.1 DmTargetLinear构造函数

DmTargetLinear(uint64_t start, uint64_t length, const std::string& block_device,
                   uint64_t physical_sector)
    // 开始,长度,/dev/block/mmcblk,物理扇区起始数
        : DmTarget(start, length), block_device_(block_device), physical_sector_(physical_sector) {}

1.2 DmTarget构造函数

DmTarget(uint64_t start, uint64_t length) : start_(start), length_(length) {}

1.3 Serialize

std::string DmTarget::Serialize() const {
    // Create a string containing a dm_target_spec, parameter data, and an
    // explicit null terminator.
    std::string data(sizeof(dm_target_spec), '\0');
    // dm_target_spec + /dev/block/mmcblk0p05 1234
    data += GetParameterString();
    data.push_back('\0');

    // The kernel expects each target to be 8-byte aligned.
    size_t padding = DM_ALIGN(data.size()) - data.size();
    for (size_t i = 0; i < padding; i++) {
        data.push_back('\0');
    }

    // Finally fill in the dm_target_spec.
    struct dm_target_spec* spec = reinterpret_cast<struct dm_target_spec*>(&data[0]);
    spec->sector_start = start();
    spec->length = size();
    snprintf(spec->target_type, sizeof(spec->target_type), "%s", name().c_str());
    spec->next = (uint32_t)data.size();
    return data;
}
struct dm_target_spec {
  __u64 sector_start;
  __u64 length;
  __s32 status;
  __u32 next;
  char target_type[DM_MAX_TYPE_NAME];
};

1.4 GetParameterString-/dev/block/mmcblk0p05-1234

std::string DmTargetLinear::GetParameterString() const {
    return block_device_ + " " + std::to_string(physical_sector_);
}

2. DeviceMapper模块

2.1 DeviceMapper构造函数-打开device-mapper设备

DeviceMapper::DeviceMapper() : fd_(-1) {
    // 打开/dev/device-mapper设备
    fd_ = TEMP_FAILURE_RETRY(open("/dev/device-mapper", O_RDWR | O_CLOEXEC));
    if (fd_ < 0) {
        PLOG(ERROR) << "Failed to open device-mapper";
    }
}
DeviceMapper& DeviceMapper::Instance() {
    static DeviceMapper instance;
    return instance;
}

2.2 CreateDevice-创建dm设备

// system_a,table,path是要传回去的,/sys/devices/virtual/block/dm-0/dm可以看到
bool DeviceMapper::CreateDevice(const std::string& name, const DmTable& table, std::string* path,
                                const std::chrono::milliseconds& timeout_ms) {
    // 1. 生成uuid
    std::string uuid = GenerateUuid();
    // 2. 通过ioctl函数创建DM_DEV_CREATE设备
    if (!CreateDevice(name, uuid)) {
        return false;
    }

    // We use the unique path for testing whether the device is ready. After
    // that, it's safe to use the dm-N path which is compatible with callers
    // that expect it to be formatted as such.
    std::string unique_path;
    // 1. 加载dm table
    // 2. 获取设备文件/dev/block/mapper/by-uuid/uuid
    // 3. 获取设备文件/dev/block/dm-0
    if (!LoadTableAndActivate(name, table) || !GetDeviceUniquePath(name, &unique_path) ||
        !GetDmDevicePathByName(name, path)) {
        DeleteDevice(name);
        return false;
    }

    if (timeout_ms <= std::chrono::milliseconds::zero()) {
        return true;
    }

    if (IsRecovery()) {
        bool non_ab_device = android::base::GetProperty("ro.build.ab_update", "").empty();
        int sdk = android::base::GetIntProperty("ro.build.version.sdk", 0);
        if (non_ab_device && sdk && sdk <= 29) {
            LOG(INFO) << "Detected ueventd incompatibility, reverting to legacy libdm behavior.";
            unique_path = *path;
        }
    }

    if (!WaitForFile(unique_path, timeout_ms)) {
        LOG(ERROR) << "Failed waiting for device path: " << unique_path;
        DeleteDevice(name);
        return false;
    }
    return true;
}
bool DeviceMapper::CreateDevice(const std::string& name, const std::string& uuid) {
    if (name.empty()) {
        LOG(ERROR) << "Unnamed device mapper device creation is not supported";
        return false;
    }
    if (name.size() >= DM_NAME_LEN) {
        LOG(ERROR) << "[" << name << "] is too long to be device mapper name";
        return false;
    }

    struct dm_ioctl io;
    InitIo(&io, name);
    if (!uuid.empty()) {
        snprintf(io.uuid, sizeof(io.uuid), "%s", uuid.c_str());
    }
// 创建dm设备
    if (ioctl(fd_, DM_DEV_CREATE, &io)) {
        PLOG(ERROR) << "DM_DEV_CREATE failed for [" << name << "]";
        return false;
    }

    // Check to make sure the newly created device doesn't already have targets
    // added or opened by someone
    CHECK(io.target_count == 0) << "Unexpected targets for newly created [" << name << "] device";
    CHECK(io.open_count == 0) << "Unexpected opens for newly created [" << name << "] device";

    // Creates a new device mapper device with the name passed in
    return true;
}

2.3 GenerateUuid-随机生成uuid数

static std::string GenerateUuid() {
    uuid_t uuid_bytes;
    // 通过/dev/urandom生成随机数
    uuid_generate(uuid_bytes);

    char uuid_chars[37] = {};
    // 将二进制格式转成36-byte字符串的类型
    uuid_unparse_lower(uuid_bytes, uuid_chars);

    return std::string{uuid_chars};
}

2.4 InitIo-初始化dm_ioctl数据

void DeviceMapper::InitIo(struct dm_ioctl* io, const std::string& name) const {
    CHECK(io != nullptr) << "nullptr passed to dm_ioctl initialization";
    memset(io, 0, sizeof(*io));

    io->version[0] = DM_VERSION0;
    io->version[1] = DM_VERSION1;
    io->version[2] = DM_VERSION2;
    io->data_size = sizeof(*io);
    io->data_start = 0;
    if (!name.empty()) {
        snprintf(io->name, sizeof(io->name), "%s", name.c_str());
    }
}

2.5 LoadTableAndActivate-传table数据下去

// system_a table
bool DeviceMapper::LoadTableAndActivate(const std::string& name, const DmTable& table) {
    std::string ioctl_buffer(sizeof(struct dm_ioctl), 0);
    // 建成serialize
    ioctl_buffer += table.Serialize();

    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);
    io->target_count = static_cast<uint32_t>(table.num_targets());
    if (table.readonly()) {
        io->flags |= DM_READONLY_FLAG;
    }// 设成DM_TABLE_LOAD
    if (ioctl(fd_, DM_TABLE_LOAD, io)) {
        PLOG(ERROR) << "DM_TABLE_LOAD failed";
        return false;
    }

    InitIo(io, name);
    // 设成DM_DEV_SUSPEND
    if (ioctl(fd_, DM_DEV_SUSPEND, io)) {
        PLOG(ERROR) << "DM_TABLE_SUSPEND resume failed";
        return false;
    }
    return true;
}

2.6 GetDeviceUniquePath-获取dm设备的uuid,看dm设备是否创建成功了

bool DeviceMapper::GetDeviceUniquePath(const std::string& name, std::string* path) {
    struct dm_ioctl io;
    InitIo(&io, name);
    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
        PLOG(ERROR) << "Failed to get device path: " << name;
        return false;
    }

    if (io.uuid[0] == '\0') {
        LOG(ERROR) << "Device does not have a unique path: " << name;
        return false;
    }
    *path = "/dev/block/mapper/by-uuid/"s + io.uuid;
    return true;
}

2.7 GetDmDevicePathByName-获取dm设备,看dm设备是否创建成功

bool DeviceMapper::GetDmDevicePathByName(const std::string& name, std::string* path) {
    struct dm_ioctl io;
    InitIo(&io, name);
    if (ioctl(fd_, DM_DEV_STATUS, &io) < 0) {
        PLOG(WARNING) << "DM_DEV_STATUS failed for " << name;
        return false;
    }

    uint32_t dev_num = minor(io.dev);
    *path = "/dev/block/dm-" + std::to_string(dev_num);
    return true;
}

2.8 GetAvailableDevices-DM_LIST_DEVICES获取已经创建的设备

bool DeviceMapper::GetAvailableDevices(std::vector<DmBlockDevice>* devices) {
    devices->clear();

    // calculate the space needed to read a maximum of 256 targets, each with
    // name with maximum length of 16 bytes
    // 分配buffer
    uint32_t payload_size = sizeof(struct dm_name_list);
    // 128-bytes for the name
    payload_size += DM_NAME_LEN;
    // dm wants every device spec to be aligned at 8-byte boundary
    payload_size = DM_ALIGN(payload_size);
    payload_size *= kMaxPossibleDmDevices;
    uint32_t data_size = sizeof(struct dm_ioctl) + payload_size;
    // 分配buffer
    auto buffer = std::unique_ptr<void, void (*)(void*)>(calloc(1, data_size), free);
    if (buffer == nullptr) {
        LOG(ERROR) << "failed to allocate memory";
        return false;
    }

    // Sets appropriate data size and data_start to make sure we tell kernel
    // about the total size of the buffer we are passing and where to start
    // writing the list of targets.
    struct dm_ioctl* io = reinterpret_cast<struct dm_ioctl*>(buffer.get());
    InitIo(io);
    io->data_size = data_size;
    io->data_start = sizeof(*io);
// 获取dm设备信息
    if (ioctl(fd_, DM_LIST_DEVICES, io)) {
        PLOG(ERROR) << "DM_LIST_DEVICES failed";
        return false;
    }

    // If the provided buffer wasn't enough to list all devices any data
    // beyond sizeof(*io) must not be read.
    if (io->flags & DM_BUFFER_FULL_FLAG) {
        LOG(INFO) << data_size << " is not enough memory to list all dm devices";
        return false;
    }

    // if there are no devices created yet, return success with empty vector
    if (io->data_size == sizeof(*io)) {
        return true;
    }

    // Parse each device and add a new DmBlockDevice to the vector
    // created from the kernel data.
    uint32_t next = sizeof(*io);
    data_size = io->data_size - next;
    struct dm_name_list* dm_dev =
            reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);

    while (next && data_size) {
        // dm设备信息
        devices->emplace_back((dm_dev));
        if (dm_dev->next == 0) {
            break;
        }
        next += dm_dev->next;
        data_size -= dm_dev->next;
        dm_dev = reinterpret_cast<struct dm_name_list*>(static_cast<char*>(buffer.get()) + next);
    }

    return true;
}

2.9 DeleteDevice-删除设备

bool DeviceMapper::DeleteDevice(const std::string& name,
                                const std::chrono::milliseconds& timeout_ms) {
    std::string unique_path;
    if (!GetDeviceUniquePath(name, &unique_path)) {
        LOG(ERROR) << "Failed to get unique path for device " << name;
    }
    struct dm_ioctl io;
    InitIo(&io, name);
// 删除设备
    if (ioctl(fd_, DM_DEV_REMOVE, &io)) {
        PLOG(ERROR) << "DM_DEV_REMOVE failed for [" << name << "]";
        return false;
    }

    // Check to make sure appropriate uevent is generated so ueventd will
    // do the right thing and remove the corresponding device node and symlinks.
    CHECK(io.flags & DM_UEVENT_GENERATED_FLAG)
            << "Didn't generate uevent for [" << name << "] removal";

    if (timeout_ms <= std::chrono::milliseconds::zero()) {
        return true;
    }
    if (unique_path.empty()) {
        return false;
    }
    if (!WaitForFileDeleted(unique_path, timeout_ms)) {
        LOG(ERROR) << "Failed waiting for " << unique_path << " to be deleted";
        return false;
    }
    return true;
}

bool DeviceMapper::DeleteDevice(const std::string& name) {
    return DeleteDevice(name, 0ms);
}

3. DmTable模块

3.1 Serialize

std::string DmTable::Serialize() const {
    if (!valid()) {
        return "";
    }

    std::string table;
    for (const auto& target : targets_) {
        table += target->Serialize();
    }
    return table;
}

问题

补充

参考

推荐阅读