device-mapper(1):概述 【转】

转自:device-mapper(1):概述 - ArnoldLu - 博客园

device-mapper是一种块设备虚拟化技术,下面分别介绍device-mapper的背景、在Linux块设备框架结构中位置、内核配置、相关文件、核心数据结构、相关module初始化、支持的Target。

1 dm说明

Device Mapper(设备映射器) 是 Linux 内核中的一个块设备虚拟化框架,旨在提供灵活的块设备抽象和管理能力。

  • 将物理块设备(如磁盘、分区)映射为逻辑设备,支持对底层设备的透明扩展、分割、组合或功能增强。
  • 通过插件化的Target Driver机制,实现数据加密、完整性校验、快照、精简配置(Thin Provisioning)等高级功能。
  • 提供一致的设备管理接口(如dmsetup工具),简化复杂存储方案的配置和维护。

当用户通过device-mapper设备访问数据时,大致经过如下层次:

  • 文件系统层(SquashFS):解析文件路径,确定需要读取的逻辑块。向 Block 层提交 I/O 请求。
  • Block 层:接收来自 SquashFS 的 `bio` 请求。将请求转发到 `/dev/dm-0` 设备。
  • Device Mapper(dm-verity):截获请求,验证数据块的哈希值(若使用 dm-verity)。通过哈希树校验数据完整性,校验通过后将请求转发到 `/dev/ubiblock0_0`。

  • UBI Block层:将块设备的逻辑地址转换为 UBI 卷的 LEB(Logical Erase Block)地址。调用 UBI 接口读取数据。

  • UBI 层:管理 UBI 卷与物理 NAND 的映射。处理磨损均衡,将逻辑 LEB 转换为物理 PEB(Physical Erase Block)。通过 MTD 层读取物理块。
  • MTD层:为不同类型的闪存(如 NAND、NOR)提供统一的抽象接口,简化上层应用和文件系统对闪存硬件的访问。

  • MTD Block层是 MTD 子系统的一部分,用于将原始闪存(MTD设备)直接映射为块设备,使其能够被标准文件系统(如 EXT4、FAT)挂载。
  • NAND 驱动:操作 NAND 控制器,从指定 PEB 读取数据。执行 ECC 校验,修复可纠正的错误。

  • 如何使能device-mapper?
    • 配置device-mapper的两种方式:
    • 启动ramdisk,在ramdisk中配置device-mapper,然后通过pivot_chroot切换到device-mapper设备执行。
    • 在kernel启动的booargs中配置dm-mod.create创建device-mapper设备。

2 dm内核配置

通过如下可以配置可以打开dm,并且使能dm-verity:

复制代码
Device Drivers
  ->Multiple devices driver support (RAID and LVM)
    ->Device mapper support
      ->Device mapper debugging support
      ->DM "dm-mod.create=" parameter support
      ->Verity target support
        ->Verity data device root hash signature verification support
        ->Verity forward error correction support
复制代码

3 dm相关文件

dm涉及到的文件主要有:

复制代码
drivers/md/
├── dm-bufio.c--实现了设备映射器的缓冲区管理,用于高效地管理内存中的数据块,支持读取、写入和缓存操作。 ├── dm-builtin.c--提供了设备映射器的初始化代码,用于在内核早期引导阶段创建和配置设备映射器。 ├── dm.c--定义了设备映射器(Device-Mapper)的核心结构和功能,包括设备的创建、删除、映射、事件通知和内存管理。 ├── dm-init.c--实现了设备映射器的初始化功能,允许在系统启动早期创建映射设备。 ├── dm-io.c--实现了设备映射器的I/O操作,包括读写请求的提交、完成处理和错误管理。 ├── dm-ioctl.c--实现了设备映射器的ioctl接口,用于在用户空间和内核空间之间传递控制命令和设备信息。 ├── dm-io-rewind.c--定义了设备映射器的I/O重绕接口,用于在I/O操作过程中调整和重绕请求。 ├── dm-kcopyd.c--实现了设备映射器的Kcopyd模块,用于在不同块设备之间进行高效的数据复制。 ├── dm-linear.c--定义了设备映射器的线性映射目标,允许将一个块设备的区域线性映射到另一个块设备。 ├── dm-rq.c--定义了设备映射器的请求队列管理,用于处理基于请求的I/O操作。 ├── dm-stats.c--实现了设备映射器的统计功能,用于收集和报告设备的操作统计信息。 ├── dm-stripe.c--实现了设备映射器的条带化目标,支持将数据分布在多个块设备上以提高性能。 ├── dm-sysfs.c--实现了设备映射器的系统文件(sysfs)接口,用于在用户空间和内核空间之间传递设备信息。 ├── dm-table.c--定义了设备映射器的表管理功能,包括表的创建、销毁和操作。 ├── dm-target.c--实现了设备映射器的目标类型管理,包括目标类型的注册、注销和迭代。 ├── dm-verity-fec.c--实现了设备映射器的Verity目标的前向纠错编码(FEC)功能,用于在数据损坏时进行错误纠正。 ├── dm-verity-target.c--定义了设备映射器的Verity目标,用于验证块设备的数据完整性。
└── dm-verity-verify-sig.c--提供了设备映射器的Verity目标的签名验证功能,确保根哈希的可信性。
复制代码

4 dm-mod.create参数解析

dm-mod.create参数格式:

  • 逗号用于分隔一个device内的不同fileld;分号用于分隔不同device。
  • 一个device参数格式:<name>,<uuid>,<minor>,<flags>,<table>[,<table>+],table可以有多个。
  • 多个device参数格式:<name>,<uuid>,<minor>,<flags>,<table>[,<table>+][;<name>,<uuid>,<minor>,<flags>,<table>[,<table>+]+]。
  • device内field解释如下:
    • <name> ::= The device name.
    • <uuid> ::= xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx | ""
    • <minor> ::= The device minor number | ""
    • <flags> ::= "ro" | "rw"
    • <table> ::= <start_sector> <num_sectors> <target_type> <target_args>

5 dm代码走读

5.1 dm数据结构和API

struct mapped_device代表一个虚拟的块设备(如/dev/dm-0),用户通过该设备与底层物理设备或逻辑设备交互。它包含了指向通用磁盘描述符的disk指针,指向请求队列描述符的queue指针,以及指向dm_target的immutable_target指针。

struct dm_table存储一个设备映射表,描述虚拟设备到物理设备的映射规则(如分段、加密、多路径等)。定义了mapped_device与dm_target之间的联系。它包含了映射规则,每条规则都用dm_target来表示。映射表中可以包含一个或多个dm_target结构,并且通过链表链接起来。

struct dm_target定义设备映射的一个逻辑段(如线性映射、加密、快照等),每个目标负责处理特定范围的 IO 请求。表示映射表中的一条映射规则。它包含了指向映射目标类型的target_type结构的指针,以及指向私有配置结构的private指针。dm_target结构定义了映射目标的属性,如开始扇区、长度、最大I/O长度等。

struct target_type定义一种设备映射目标的类型(如linear, striped, crypt),提供目标的操作方法。每个目标类型以名字为标识,对应一个模块,实现了构造、解构、映射、endio、挂起、恢复和状态等函数,以实现映射目标的语义。

struct dm_dev封装对底层物理设备(如/dev/sda1)的引用,供 DM 目标使用。包含了指向块设备描述符的bdev指针,指向DAX设备的dax_dev指针,以及模式(mode)和名称(name)。

struct dm_io跟踪一个异步 IO 请求的状态,用于协调多个子请求的完成。

struct dm_kobject_holder管理 DM 设备在 sysfs 中的表示(如 /sys/block/dm-0/)。

struct dm_stats记录 DM 设备的 IO 统计信息(如延迟、错误计数)。

5.2 dm初始化

5.2.1 dm core初始化

dm_init是dm core的初始化,包括:

  • uevent、workqueue、块设备初始化。
  • 注册error、linear、striped 3个target。
  • io和kcopyd所需kmem cache分配。
  • 注册device-mapper misc设备。
  • 统计信息变量初始化。
复制代码
dm_init
  _inits--遍历数据,执行里面的函数指针。
    local_init
      dm_uevent_init
      alloc_ordered_workqueue
      register_blkdev--向内核注册一个device-mapper块设备。
    dm_target_init
      dm_register_target--error_target主要用作调试和测试。
    dm_linear_init
      dm_register_target--linear_target实现逻辑设备到物理设备的线性映射,将多个物理设备以线性连接的方式组成一个大的逻辑设备。
    dm_stripe_init
      alloc_workqueue
      dm_register_target--stripe_target实现数据条带化存储,将数据分散到多个物理设备上,以提高数据读写的速度和性能。
    dm_io_init
      KMEM_CACHE
    dm_kcopyd_init
      kmem_cache_create--创建kcopyd_jog高速缓存。
    dm_interface_init
      misc_register--根据_dm_misc创建misc设备,操作函数集为_ctl_fops。
    dm_statistics_init
复制代码

创建/dev/mapper/control misc设备:

复制代码
static struct miscdevice _dm_misc = {
        .minor          = MAPPER_CTRL_MINOR,
        .name           = DM_NAME,
        .nodename       = DM_DIR "/" DM_CONTROL_NODE,
        .fops           = &_ctl_fops
};
复制代码

_ctl_fops为misc设备的操作函数集:

  • open/release仅打开关闭。
  • poll负责和用于用户空间等待查询。
  • ioctl是核心。
复制代码
static const struct file_operations _ctl_fops = {
        .open    = dm_open,
        .release = dm_release,
        .poll    = dm_poll,
        .unlocked_ioctl  = dm_ctl_ioctl,
        .compat_ioctl = dm_compat_ctl_ioctl,
        .owner   = THIS_MODULE,
        .llseek  = noop_llseek,
};
复制代码

dm_ctl_ioctl负责处理/dev/mapper/control的ioctl命令:

复制代码
dm_ctl_ioctl
  ctl_ioctl
    check_version--检查输入DM版本号是否满足需求,否则返回错误;满足则返回当前kernel的DM版本号。
    loopup_ioctl--根据输入的cmd命令,返回cmd对应的处理函数。
    copy_params--从用户空间拷贝参数。
    validate_params--检查参数合法性。
    fn--执行命令对应函数。
    dm_issue_global_event--触发pool事件。
    copy_to_user--将结果拷贝到用户空间。
    free_params--释放内存。
复制代码

DM支持的ioctl命令如下:

命令宏定义处理函数作用说明特殊标志
DM_VERSION_CMD check_version 检查用户空间与内核的版本兼容性,并返回内核版本信息
DM_REMOVE_ALL_CMD remove_all 移除所有映射设备(若支持延迟删除则标记) IOCTL_FLAGS_NO_PARAMSIOCTL_FLAGS_ISSUE_GLOBAL_EVENT
DM_LIST_DEVICES_CMD list_devices 列出所有注册的映射设备名称和 UUID
DM_DEV_CREATE_CMD dev_create 创建一个新的映射设备 IOCTL_FLAGS_ISSUE_GLOBAL_EVENT
DM_DEV_REMOVE_CMD dev_remove 删除指定的映射设备(支持延迟删除) IOCTL_FLAGS_ISSUE_GLOBAL_EVENT
DM_DEV_RENAME_CMD dev_rename 重命名映射设备或修改其 UUID IOCTL_FLAGS_ISSUE_GLOBAL_EVENT
DM_DEV_SUSPEND_CMD dev_suspend 挂起或恢复映射设备的 I/O 操作 IOCTL_FLAGS_NO_PARAMS
DM_DEV_STATUS_CMD dev_status 获取设备的当前状态(如挂起状态、只读标志等) IOCTL_FLAGS_NO_PARAMS
DM_DEV_WAIT_CMD dev_wait 等待设备的事件(如数据变更或状态变更)
DM_TABLE_LOAD_CMD table_load 加载设备的映射表(定义目标设备和逻辑块的映射关系)
DM_TABLE_CLEAR_CMD table_clear 清除设备的当前映射表 IOCTL_FLAGS_NO_PARAMS
DM_TABLE_DEPS_CMD table_deps 获取设备映射表依赖的物理设备列表
DM_TABLE_STATUS_CMD table_status 获取设备映射表的状态信息(如目标类型和参数)
DM_LIST_VERSIONS_CMD list_versions 列出内核支持的所有目标驱动(Target Driver)的版本信息
DM_TARGET_MSG_CMD target_message 向目标驱动发送自定义消息(如调整缓存策略或触发操作)
DM_DEV_SET_GEOMETRY_CMD dev_set_geometry 设置设备的几何参数(如柱面、磁头、扇区等)
DM_DEV_ARM_POLL_CMD dev_arm_poll 启用设备事件轮询(记录当前全局事件号以便后续检查) IOCTL_FLAGS_NO_PARAMS
DM_GET_TARGET_VERSION_CMD get_target_version 获取指定目标驱动的版本信息

dev_create创建一个dm设备:

复制代码
dev_create
  check_name
  dm_create--分配并初始化 mapped_device 核心结构。
    alloc_dev--分配struct mapped_device。
    dm_ima_reset_data
  dm_hash_insert--将设备插入哈希表(按 name/uuid 管理设备实例)。
  __dev_status
  dm_put
复制代码

alloc_dev分配并初始化一个struct mapped_device:

复制代码
alloc_dev
  blk_alloc_disk-- 分配块设备的 gendisk 结构(管理磁盘操作)。
  INIT_WORK--创建md->work工作。
    dm_wq_work
  INIT_WORK--
    dm_wq_requeue_work
  dm_blk_dops--dm块设备的操作函数集。
  alloc_workqueue--创建kdmflush/xxx工作队列。
  dm_stats_init--初始化设备的 I/O 统计计数器(用于性能监控)。
  idr_replace
复制代码

dm_blk_dops作为默认dm块设备操作函数集:

复制代码
static const struct block_device_operations dm_blk_dops = {
        .submit_bio = dm_submit_bio,--用于直接向块设备提交一个 `bio` 结构,这允许驱动程序绕过标准的请求队列,直接处理 I/O 请求,通常用于高性能或特殊用途的块设备。
        .poll_bio = dm_poll_bio,--用于轮询 `bio` 结构的状态,以检查 I/O 请求是否完成。这在某些情况下可以提供比传统的请求完成回调更高效的 I/O 处理方式。
        .open = dm_blk_open,--当设备被打开时调用。通常用于初始化设备,分配资源等。
        .release = dm_blk_close,--当设备被释放时调用。用于释放资源,执行清理工作。
        .ioctl = dm_blk_ioctl,--实现设备特定的控制命令。
        .getgeo = dm_blk_getgeo,--用于获取设备的几何参数,如圆柱数、磁头数和扇区数。
        .report_zones = dm_blk_report_zones,--用于报告设备的区域信息,这在支持区域化的存储设备(如某些类型的 SSD)中很有用。区域化存储设备可以将数据组织成不同的区域,以优化性能和寿命。
        .pr_ops = &dm_pr_ops,-用于提供自定义的请求队列操作。这允许块设备驱动程序定义自己的请求处理逻辑,而不是使用标准的请求队列。
        .owner = THIS_MODULE
};
复制代码

dm_submit_bio作为dm设备向底层块设备发起IO操作接口:

复制代码
dm_submit_bio--提交一个bio请求到设备映射(Device Mapper)层进行处理。
  dm_get_live_table   dm_split_and_process_bio--将一个bio请求分割并处理,以适应设备映射的要求。
    __split_and_process_bio
      dm_table_find_target
      setup_split_accounting
      alloc_tio
      __map_bio
        ti->type->map--调用特定target的map函数,将bio请求映射到具体的扇区。比如verity_map
    bio_trim
    bio_inc_remainning
    submit_bio_noacct
  dm_put_live_table
复制代码

table_load加载设备映射表并激活新配置:

复制代码
table_load
  find_device
  dm_table_create
  populate_table--解析并填充映射表中的目标设备条目。
    dm_table_add_target
      dm_get_target_type
      dm_target_needs_singleton
      ti->type->ctr--调用target的ctr函数。比如verity_ctr
  dm_ima_measure_on_table_load
  dm_get_immutable_target_table
  dm_setup_md_queue--初始化映射设备的请求队列和并创建块设备。
    dm_calculate_queue_limits--计算并设置队列的 I/O 限制(如最大扇区数)。
    dm_table_set_restrictions--应用映射表的限制到设备队列(如只读标志)。
    add_disk--创建/sys/devices/virtual/block/dm-0目录及相关节点,同时也会自动创建/dev/dm-0。
    dm_sysfs_init--创建/sys/devices/virtual/block/dm-0/dm目录下的一系列sysfs节点。
  dm_get_mdptr
  __dev_status
复制代码

5.2.2 dm buffer io初始化

dm_bufio_init主要做如下工作:

  • 创建工作队列,并触发周期性清理缓存的工作。
  • 全局清理一次。
复制代码
dm_bufio_init
  alloc_workqueue--创建dm_bufio_wq。
  INIT_DELAYED_WORK--创建延时工作dm_bufio_cleanup_old_work,超时函数为work_fn。
    work_fn
      cleanup_old_buffers
      queue_delayed_work--开始周期性清理缓存。
  INIT_WORK--创建工作dm_bufio_replacement_work,工作函数为do_global_cleanup。
    do_global_cleanup--
      check_watermarks
      evict_old
  queue_delayed_work--开始周期性30秒触发一次dm_bufio_cleanup_old_work工作。
复制代码

5.2.3 dm early初始化

dm_init_init根据传入的dm-mod.create参数在early boot阶段创建dm设备:

复制代码
dm_init_init--dm初始化,属于late_initcall。
  kstrndup--将dm-mod.create参数内容复制到devices中。
  dm_parse_devices--解析内核启动参数devices中的DM设备配置列表。
    dm_parse_device_entry--按照name,uuid,minor,flags,table格式解析参数,多个table entry之间以,分隔;多个device entry参数以;进行分隔。
      dm_parse_table--解析设备的映射表(由多个表条目组成)。
        dm_parse_table_entry--按照<start_sector> <num_sectors> <target_type> <target_args>。
          支持的target_type包括:crypt, delay, linear, snapshot-origin, striped, verity。
  wait_for_devcie_probe--等待系统中所有probe_waitqueue上的设备探测完成。
  early_lookup_bdev
  dm_early_create
    check_name
    dm_create--分配并初始化mapped_device结构。
    dm_hash_insert
    dm_table_create--创建新的 DM 映射表(存储目标设备映射关系)。
    dm_table_add_target--向映射表中添加目标设备(如 linear/crypt 等)。
    dm_table_complete--完成映射表构建(校验并锁定表结构)。
    dm_setup_md_queue
    dm_suspend--挂起设备 I/O(准备切换映射表)。
    dm_swap_table
    set_disk_ro--设置磁盘的只读属性(根据 flags 标记)。
    dm_resume
    dm_put
复制代码

dm-mod这个module包含两个参数:

  • dm-mod.create:在early boot阶段创建dm设备。
  • dm-mod.waitfor:创建table之前需要等待的设备列表。

5.3 dm支持的Target

Device Mapper Target 功能对比表:

Target简要说明典型场景优点缺点
linear 线性拼接多个设备为一个逻辑设备 合并磁盘分区 简单、无性能损失 不支持冗余或性能优化
striped 条带化分布数据(类似 RAID 0) 高性能并行读写 提升吞吐量 设备需大小一致;无冗余
zero 返回全零数据的虚拟设备 测试、占位符 无需物理存储 无实际数据存储能力
mirror 数据镜像(类似 RAID 1) 磁盘冗余 高可用性 存储利用率低(50%)
snapshot 创建块设备快照 备份、冻结状态 快速创建 快照空间不足会导致失效
snapshot-origin 快照的原始设备 与快照配合使用 支持读写原始数据 依赖快照设备管理
snapshot-merge 将快照合并回原始设备 还原操作 快速还原 合并期间可能影响性能
raid 支持多种 RAID 级别(如 1/4/5/6/10) 数据冗余与性能平衡 替代 mdadm 部分功能 配置复杂;依赖内核版本
cache 用快速设备(如 SSD)缓存慢速设备数据 加速读操作 显著提升读性能 写性能提升有限;缓存策略需调优
writecache 将写入缓存到快速设备,异步写入慢速设备 加速写操作 提升写吞吐量 断电可能丢失缓存数据
thin 精简配置逻辑卷(按需分配空间) 虚拟化存储 节省存储空间 需配合 thin-pool 使用
thin-pool 管理精简配置的存储池 支持多个 thin 设备 灵活分配资源 元数据管理复杂
crypt 透明加密块设备(如 LUKS) 全盘加密 安全性高 加密/解密带来 CPU 开销
verity 基于哈希树验证数据完整性 防篡改(如 Android 系统) 强完整性保护 只读设备;哈希树占用额外空间
delay 为 I/O 添加固定延迟 模拟高延迟环境 测试容错能力 不模拟真实网络波动
flakey 随机返回错误或断开设备 测试故障恢复 模拟硬件故障 需手动配置错误模式
log-writes 记录所有写入操作到日志设备 调试文件系统一致性 支持回放验证 日志设备需额外存储
era 跟踪块修改时间,支持增量备份 增量备份 高效备份变更数据 元数据管理复杂
switch 动态切换不同设备(基于策略) 动态存储池 灵活管理多设备 策略配置复杂
integrity 数据完整性校验(如 HMAC) 防数据篡改 支持读写校验 性能开销较大
multipath 多路径访问同一存储设备 高可用存储网络 负载均衡与故障切换 配置复杂;依赖硬件支持
vdo 提供去重、压缩和精简配置 节省存储空间 显著减少存储需求 CPU 和内存开销高
dust 模拟块设备随机损坏(如扇区错误) 测试数据恢复能力 精确控制错误位置 需内核 5.2+ 支持
error 所有 I/O 返回错误 测试错误处理逻辑 简单直接 仅用于调试,无实际用途
unstriped 优化非对齐条带化设备访问 兼容旧有条带化设备 提升非对齐访问性能 使用场景有限

总结

  • 性能优化:优先考虑 cachewritecachestriped

  • 数据安全:选择 mirrorraidverityintegrity

  • 存储效率:使用 thin+thin-pool 或 vdo

  • 测试调试delayflakeyerrordust 是常用工具。

5.4 dm-verity

见《device-mapper(2):dm-verity》。

5.5 dm-linear

5.6 dm-stripe

参考文档:《dm-verity原理剖析》、《dm-verity使能

联系方式:arnoldlu@qq.com
posted @ 2025-07-24 14:59  Sky&Zhang  阅读(130)  评论(0)    收藏  举报