Linux Device Mapper入门
2022-09-11 15:45:41 阿炯

Device Mapper的缩写为DM,是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的管理策略,当前比较流行的 Linux 下的逻辑卷管理器如 LVM2Linux Volume Manager 2 version)、dmraid(Device Mapper Raid Tool)等都是基于该机制实现的。也是软件界非常典型的加一层来完成更多功能在操作系统存储层的实现。


Wikipedia中摘取的片段

设备映射机制是Linux内核提供的一个框架,用于将物理块设备映射到更高级别的虚拟块设备。它构成了逻辑卷管理器LVM、软件RAID和dm crypt磁盘加密的基础,并提供了其他功能,如文件系统快照。通过将数据从虚拟块设备由设备映射机制自身提供传递到另一个块设备来工作。数据也可以在转换中修改,例如,在设备映射器提供磁盘加密或模拟不可靠硬件行为的情况下执行。


Device mapper是内核中支持逻辑卷管理的通用设备映射机制,它为实现用于存储资源管理的块设备驱动提供了一个高度模块化的内核架构,它包含三个重要的对象概念,Mapped Device、Mapping Table、Target device。


在内核中它通过一个一个模块化的 target driver 插件实现对 IO 请求的过滤或者重新定向等工作,当前已经实现的 target driver 插件包括软 raid、软加密、逻辑卷条带、多路径、镜像、快照等,图中 linear、mirror、snapshot、multipath 表示的就是这些 target driver。Device mapper 进一步体现了在 Linux 内核设计中策略和机制分离的原则,将所有与策略相关的工作放到用户空间完成,内核中主要提供完成这些策略所需要的机制。Device mapper 用户空间相关部分主要负责配置具体的策略和控制逻辑,比如逻辑设备和哪些物理设备建立映射,怎么建立这些映射关系等等,而具体过滤和重定向 IO 请求的工作由内核中相关代码完成。因此整个 device mapper 机制由两部分组成–内核空间的 device mapper 驱动、用户空间的device mapper 库以及它提供的 dmsetup 工具。在下文中将分内核和用户空间两部分进行介绍。

内核部分

Device mapper 的内核相关代码已经作为 Linux 2.6 内核发布版的一部分集成到内核源码中了,相关代码在内核源码的 driver/md/ 目录中,其代码文件可以划分为实现 device mapper 内核中基本架构的文件和实现具体映射工作的 target driver 插件文件两部分。文章下面的分析结果主要是基于上述源码文件得到的。

重要概念

Device mapper 在内核中作为一个块设备驱动被注册的,它包含三个重要的对象概念,mapped device、映射表、target device。Mapped device 是一个逻辑抽象,可以理解成为内核向外提供的逻辑设备,它通过映射表描述的映射关系和 target device 建立映射。从 Mapped device 到一个 target device 的映射表由一个多元组表示,该多元组由表示 mapped device 逻辑的起始地址、范围、和表示在 target device 所在物理设备的地址偏移量以及target 类型等变量组成这些地址和偏移量都是以磁盘的扇区为单位的,即 512 个字节大小。Target device 表示的是 mapped device 所映射的物理空间段,对 mapped device 所表示的逻辑设备来说,就是该逻辑设备映射到的一个物理设备。Device mapper 中这三个对象和 target driver 插件一起构成了一个可迭代的设备树。在该树型结构中的顶层根节点是最终作为逻辑设备向外提供的 mapped device,叶子节点是 target device 所表示的底层物理设备。最小的设备树由单个 mapped device 和 target device 组成。每个 target device 都是被mapped device 独占的,只能被一个 mapped device 使用。一个 mapped device 可以映射到一个或者多个 target device 上,而一个 mapped device 又可以作为它上层 mapped device的 target device 被使用,该层次在理论上可以在 device mapper 架构下无限迭代下去。


从上图中可以看到 mapped device1 通过映射表和 a、b、c 三个 target device 建立了映射关系,而 target device a 又是通过 mapped device 2 演化过来,mapped device 2 通过映射表和 target device d 建立映射关系。

进一步看一下上图中三个对象在代码中的具体实现,dm.c 文件定义的 mapped_device 结构用于表示 mapped device,它主要包括该 mapped device 相关的锁,注册的请求队列和一些内存池以及指向它所对应映射表的指针等域。

Mapped device 对应的映射表是由 dm_table.c 文件中定义的 dm_table 结构表示的,该结构中包含一个 dm_target结构数组,dm_target 结构具体描述了 mapped_device 到它某个 target device 的映射关系。

而在 dm_table 结构中将这些 dm_target 按照 B 树的方式组织起来方便 IO 请求映射时的查找操作。Dm_target 结构具体记录该结构对应 target device 所映射的 mapped device 逻辑区域的开始地址和范围,同时还包含指向具体 target device 相关操作的 target_type 结构的指针。

Target_type 结构主要包含了 target device 对应的 target driver 插件的名字、定义的构建和删除该类型target device的方法、该类target device对应的IO请求重映射和结束IO的方法等。而表示具体的target device的域是dm_target中的private域,该指针指向mapped device所映射的具体target device对应的结构。表示target device的具体结构由于不同的target 类型而不同,比如最简单的线性映射target类型对应target device的结构是dm-linear.c文件中定义的linear_c结构。其定义如下:
struct linear_c { struct dm_dev *dev; sector_t start; };

该target device的定义相当简单,就只包括了表示对应物理设备的dm_dev结构指针和在该物理设备中以扇区为单位的偏移地址start。上述几个数据结构关系如图所示:


内核中建立过程

下面结合具体的代码简要介绍下在内核中创建一个mapped device的过程:

1.根据内核向用户空间提供的ioctl 接口传来的参数,用dm-ioctl.c文件中的dev_create函数创建相应的mapped device结构。这个过程很简单,主要是向内核申请必要的内存资源,包括mapped device和为进行IO操作预申请的内存池,通过内核提供的blk_queue_make_request函数注册该mapped device对应的请求队列dm_request。并将该mapped device作为磁盘块设备注册到内核中。

2.调用dm_hash_insert将创建好的mapped device插入到device mapper中的一个全局hash表中,该表中保存了内核中当前创建的所有mapped device。

3.用户空间命令通过ioctl调用table_load函数,该函数根据用户空间传来的参数构建指定mapped device的映射表和所映射的target device。该函数先构建相应的dm_table、dm_target结构,再调用dm-table.c中的dm_table_add_target函数根据用户传入的参数初始化这些结构,并且根据参数所指定的target类型,调用相应的target类型的构建函数ctr在内存中构建target device对应的结构,然后再根据所建立的dm_target结构更新dm_table中维护的B树。上述过程完毕后,再将建立好的dm_table添加到mapped device的全局hash表对应的hash_cell结构中。

4.最后通过ioctl调用do_resume函数建立mapped device和映射表之间的绑定关系,事实上该过程就是通过dm_swap_table函数将当前dm_table结构指针值赋予mapped_device相应的map域中,然后再修改mapped_device表示当前状态的域。

通过上述的4个主要步骤,device mapper在内核中就建立一个可以提供给用户使用的mapped device逻辑块设备。

IO流

Device mapper本质功能就是根据映射关系和target driver描述的IO处理规则,将IO请求从逻辑设备mapped device转发相应的target device上。Device mapper处理所有从内核中块一级IO子系统的generic_make_request和submit_bio接口[两个接口具体的描述可以查看参考文献,其中对内核中的块IO层有比较详尽的讲解。] 中定向到mapped device的所有块读写IO请求。IO请求在device mapper的设备树中通过请求转发从上到下地进行处理。当一个bio请求在设备树中的mapped deivce向下层转发时,一个或者多个bio的克隆被创建并发送给下层target device。然后相同的过程在设备树的每一个层次上重复,只要设备树足够大理论上这种转发过程可以无限进行下去。在设备树上某个层次中,target driver结束某个bio请求后,将表示结束该bio请求的事件上报给它上层的mapped device,该过程在各个层次上进行直到该事件最终上传到根mapped device的为止,然后device mapper结束根mapped device上原始bio请求,结束整个IO请求过程。

Bio在device mapper的设备树进行逐层的转发时,最终转发到一个或多个叶子target节点终止。因为一个bio请求不可以跨多个target device(亦即物理空间段), 因此在每一个层次上,device mapper根据用户预先告知的mapped device 的target映射信息克隆一个或者多个bio,将bio进行拆分后转发到对应的target device上。这些克隆的bio先交给mapped device上对应的target driver上进行处理,根据target driver中定义的IO处理规则进行IO请求的过滤等处理,然后再提交给target device完成。上述过程在dm.c文件中的dm_request函数中完成。Target driver可以对这些bio做如下处理:
1.将这些bio在本驱动内部排队等待以后进行处理;
2.将bio重新定向到一个或多个target device上或者每个target device上的不同扇区;
3.向device mapper返回error 状态。

IO请求就按照上文中描述的过程在图2中所示的设备树中逐层进行处理,直到IO请求结束。


特性

设备映射机制的目标可在Linux内核存储堆栈的各个层中发挥其作用。其提供的功能包括线性、条带和坏道映射,以及加密和多路径目标multipath。例如,两个磁盘可以通过一对线性映射关联到一个逻辑卷中,每个磁盘一个映射。作为另一个示例,crypt target使用Linux内核的加密API对通过指定设备的数据进行加密。

截至2014年,以下映射目标可用:
cache – allows creation of hybrid volumes, by using solid-state drives (SSDs) as caches for hard disk drives (HDDs)
clone – will permit usage before a transfer is complete.
crypt – provides data encryption, by using the Linux kernel's Crypto API
delay – delays reads and/or writes to different devices (used for testing)
era – behaves in a way similar to the linear target, while it keeps track of blocks that were written to within a user-defined period of time[9]
error – simulates I/O errors for all mapped blocks (used for testing)
flakey – simulates periodic unreliable behaviour (used for testing)
linear – maps a continuous range of blocks onto another block device
mirror – maps a mirrored logical device, while providing data redundancy
multipath – supports the mapping of multipathed devices, through usage of their path groups
raid – offers an interface to the Linux kernel's software RAID driver (md)
snapshot and snapshot-origin – used for creation of LVM snapshots, as part of the underlying copy-on-write scheme
striped – stripes the data across physical devices, with the number of stripes and the striping chunk size as parameters
thin  – allows creation of devices larger than the underlying physical device, physical space is allocated only when written to
zero – an equivalent of /dev/zero, all reads return blocks of zeros, and writes are discarded

Device mapper 是 lvm 和 multipating 的底层技术。可以将多个物理设备合成为一个逻辑设备,可以做普通的合并,或者实现类似raid0的条带化,还可以用来屏蔽硬盘中的坏道扇区,还可做lvm快照来备份数据库,或者通过零设备文件来模拟非常大的设备,用于测试功能。相关的安装包:device-mapper device-mapper-multipath。

工作原理:通过mapping table来创建逻辑设备物理设备和逻辑设备每个扇区之间的对应关系。映射表内容包括:
逻辑设备的起始扇区:通常是0。
逻辑设备的扇区数量类型有linear线性,连续组合;striped条带化;error屏蔽坏道;snapshot快照;zero零设备。

磁盘扇区的计算:
1扇区=512字节b,1kb=1024b,扇区大小kb=扇区数512/1024,比如10G的磁盘,扇区数为:
10000000kb=扇区数512/1024=20000000个扇区

# blockdev --getsize /dev/sda6 查看设备扇区数量
# echo "0 'blockdev --getsize /dev/sda6' linear /dev/sda6 0" | dmsetup create mydevice

通过table创建逻辑设备
0表示这个逻辑设备从0扇区开始,有208782个扇区,linear表示连续的,/dev/sda6 0 表示从/dev/sda6的第0个扇区开始做逻辑设备。当一个设备用剩余空间做逻辑设备时候,扇区就不是从0开始了。

类型

linear线性存储:
把多个物理分区的扇区连续起来组合成一个逻辑设备。
0 20000 linear /dev/sda1 0 20000 60000 linear /dev/sdb1 0
注:逻辑设备从0到扇区取自sda1从0扇区开始取20000扇区,逻辑设备从20000扇区开始,从sdb1的0扇区开始取60000个扇区,该逻辑设备80000个扇区。实现命令:
# echo “0 20000 linear /dev/sda1 0\n20000 60000 linear /dev/sdb1 0” | dmsetup create mydevice

stripe条带化:
通过chunksize 轮流写入磁盘
0 1024 striped 2 256 /dev/sda1 0 /dev/sdb1 0
注:逻辑设备从0扇区开始到1024扇区,类型为striped ,2个设备, chunksize 256kb 从/dev/sda1和/dev/sdb1 的0 扇区开始 各取512扇区(注意扇区数必须是chunksize的倍数) 命令实现:
#echo “0 1024 striped 2 256 /dev/sda1 0 /dev/sdb1 0” | dmsetup create mydevice

error环道屏蔽:
通过合成逻辑设备去除错误扇区
0 80 linear /dev/sda1 0 80 100 error 181 200 linear /dev/sdb1 0
命令实现:
#echo “0 80 linear /dev/sda1 0\n80 100 error\n181 200 linear /dev/sdb1 0” | dmsetup create mydevice

snapshot逻辑卷快照:
创建快照后,出现3个设备原设备,快照设备,cow设备 如果数据没有改变,读取数据从原设备读取,写入变化的数据存储到cow区域中,快照设备保存原设备的数据。
#echo "0 1000 snapshot /dev/sda1 /dev/vg0/realdev P 16" | dmsetup create mydevice
从0扇区到1000扇区为/dev/sda1创建快照,名字为realdev,P表示下次启动仍然生效,16为chunksize。

zero零设备特性:
类似/dev/zero,但是他是个块设备,不能写东西,一般用来测试用,创建大的文件系统进行测试。比如测试创建10T大小的设备用ext3来格式化
#export HUGESIZE=$[100 * (2**40)/512] 100T的扇区数量 2**40为2的40次方
#echo "0 $HUGESIZE zero" | dmsetup create zerodev
生成的文件在/dev/mapper/zerodev ext3每个分区最大支持2TB

多路径功能特性:
多路径功能,用来提供线路冗余,监控每条链路,当链路失败时自动切换链路,而且自动恢复运行,防止单点故障。
生成的设备名 /dev/dm-X 类型:
当两路径优先级相等:
负载均衡 当两路径优先级不等: 冗余。

multipath列出多路径设备,后台需要开启multipathd服务,优先级大小为0-1024。实验步骤:
存储端配置双网卡,配置/dev/sda6为iscsi设备,服务器端安装device-mapper-multipath包,连接iscsi设备:
#vi /etc/multipath.conf
注释掉 blacklist { devnode "*" 不同厂商的配置是不一样的 },取消注释 default{ udev_dir .. .. path_grouping_policy failover(根据失效域来判断执行策略) }

#systemctl enable multipathd
#systemctl restart multipathd
之后生成的设备位置在/dev/mpath/下,可制作文件系统和挂载
#multipath –ll 查询设备状态

FC存储:
存储端建立raid设备,raid建立与HBA卡WWN号的映射关系连接哪个HBA卡则使用哪块磁盘设备;WWN为HBA卡的授权名称,用来区分一个或一组网络连接,表示网络上的一个连接。


应用

基于设备映射机制构建的Linux内核功能和应用有如下:
cryptsetup – utility used to conveniently setup disk encryption based on dm-crypt
dm-crypt/LUKS – mapping target that provides volume encryption
dm-cache – mapping target that allows creation of hybrid volumes
dm-integrity – mapping target that provides data integrity, either using checksumming or cryptographic verification, also used with LUKS
dm-log-writes – mapping target that uses two devices, passing through the first device and logging the write operations performed to it on the second device
dm-verity – validates the data blocks contained in a file system against a list of cryptographic hash values, developed as part of the Chromium OS project
dmraid(8) – provides access to "fake" RAID configurations via the device mapper
DM Multipath – provides I/O failover and load-balancing of block devices within the Linux kernel
Docker – uses device mapper to create copy-on-write storage for software containers
DRBD (Distributed Replicated Block Device)
EVMS (deprecated)
kpartx(8) – utility called from hotplug upon device maps creation and deletion
LVM2 – logical volume manager for the Linux kernel
VeraCrypt - Linux version of TrueCrypt
VDO - Virtual Data Optimizer


Device mapper在内核中向外提供了一个从逻辑设备到物理设备的映射架构,只要用户在用户空间制定好映射策略,按照自己的需要编写处理具体IO请求的target driver插件,就可以很方便的实现一个类似LVM的逻辑卷管理器。Device mapper以ioctl的方式向外提供接口,用户通过用户空间的device mapper库,向device mapper的字符设备发送ioctl命令,完成向内的通信。它还通过ioctl提供向往的事件通知机制,允许target driver将IO相关的某些事件传送到用户空间。


dm-0、dm-1、dm-N各自与那些磁盘映射呢,各自又代表的啥意义呢?首先来看看dm-N这些文件在设备目录中的指向:
# ls -l /dev/dm-0
brw-rw---- 1 root disk 253, 0 Jul  8 08:13 /dev/dm-0

使用'fdisk -l'命令也能看到,一般都位于 /dev/mapper目录下看到其指向。
# ls -l /dev/mapper/
total 0
lrwxrwxrwx 1 root root       7 Jul  8 08:13 centos-root -> ../dm-0
lrwxrwxrwx 1 root root       7 Jul  8 08:13 centos-swap -> ../dm-1
crw------- 1 root root 10, 236 Jul  8 08:13 control

可以使用命令'dmsetup ls'查看dm-N对应的设备,其中dm后面的数字253:N对应着块设备的MAJ:MIN编号(lsblk可输出该信息)。要查看块设备的具体信息就使用命令'dmsetup info'就能看到其详细信息。

有了上面信息就能知道dm-N分别对应下面的一些设备,这里列出一些可用的工具或方法:

lsblk command (e.g. list block devices)

lsblk --output NAME,KNAME,TYPE,SIZE,MAJ:MIN,MOUNTPOINT

lvdisplay
  --- Logical volume ---
  LV Path                /dev/centos/root
  LV Name                root
  VG Name                centos
...
  Block device           253:0

pvs显示支持LVM卷组的物理卷。可以将多个物理设备分组到一个卷组中。然后根据需要将该组重新拆分为逻辑卷,将物理磁盘视为单个资源,可以随意分割和重新调整大小。每次将卷组的一部分切片为逻辑卷时,它将显示为dm设备。

sar -bdp 1

CentOS7下在/dev/VGNAME下有相关的链接
# ls -l /dev/centos
total 0
lrwxrwxrwx 1 root root 7 Jul  8 08:13 root -> ../dm-0
lrwxrwxrwx 1 root root 7 Jul  8 08:13 swap -> ../dm-1


关于Device Mapper的理论知识,参考IBM网站上已经走失的文章:《Linux 内核中的 Device Mapper 机制》。


Linux Device Mapper

RHEL7配置和管理LVM逻辑卷-附录A.设备映射器