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参数解析
- 逗号用于分隔一个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>
- <target_type> ::= "verity" | "linear" | ... (see table below)
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_PARAMS、IOCTL_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
| 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 | 优化非对齐条带化设备访问 | 兼容旧有条带化设备 | 提升非对齐访问性能 | 使用场景有限 |
总结
-
性能优化:优先考虑
cache、writecache、striped。 -
数据安全:选择
mirror、raid、verity、integrity。 -
存储效率:使用
thin+thin-pool或vdo。 -
测试调试:
delay、flakey、error、dust是常用工具。
5.4 dm-verity
见《device-mapper(2):dm-verity》。
5.5 dm-linear
5.6 dm-stripe
参考文档:《dm-verity原理剖析》、《dm-verity使能》


浙公网安备 33010602011771号