ceph kernel rbd (二): rbd map , rbd unmap

当我们使用krbd 的时候,第一件事就是rbd map,这个命令的目的是将一个rbd image 挂载到linux 成为一个block 设备。

比如:

[root@atest-guest build]# rbd ls
test
[root@atest-guest build]# rbd info test
rbd image 'test':
        size 1 GiB in 256 objects
        order 22 (4 MiB objects)
        snapshot_count: 0
        id: 1726c754beda9
        block_name_prefix: rbd_data.1726c754beda9
        format: 2
        features: layering, exclusive-lock, object-map, fast-diff, deep-flatten, journaling
        op_features: 
        flags: 
        create_timestamp: Thu Jul 25 10:15:15 2019
        access_timestamp: Thu Jul 25 10:15:15 2019
        modify_timestamp: Thu Jul 25 10:15:15 2019
        journal: 1726c754beda9
        mirroring state: disabled
[root@atest-guest build]# rbd map test
/dev/rbd0
[root@atest-guest build]# lsblk /dev/rbd0
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
rbd0 250:0    0   1G  0 disk 
[root@atest-guest build]# mkfs.xfs -f /dev/rbd0
meta-data=/dev/rbd0              isize=512    agcount=8, agsize=32768 blks
         =                       sectsz=512   attr=2, projid32bit=1
         =                       crc=1        finobt=0, sparse=0
data     =                       bsize=4096   blocks=262144, imaxpct=25
         =                       sunit=16     swidth=16 blks
naming   =version 2              bsize=4096   ascii-ci=0 ftype=1
log      =internal log           bsize=4096   blocks=2560, version=2
         =                       sectsz=512   sunit=16 blks, lazy-count=1
realtime =none                   extsz=4096   blocks=0, rtextents=0

如上,rbd map test 之后,linux 模拟出一个block 设备:/dev/rbd0 我们可以吧这个设备当做一块普通盘进行操作,比如mkfs.xfs。

下面我们来详细分析一下rbd map和unmap的过程:

(1) userspace rbd 命令:

首先看一下rbd map 命令的入口:src/tools/rbd/action/Kernel.cc

ceph kernel rbd (二): rbd map , rbd unmap_第1张图片

以上逻辑比较简单:

1, 用户执行rbd map test 命令,rbd 进程先注册一个udev monior,用来监听udev event。

2. 然后通过sysfs 告诉krbd driver 去添加一个rbd block device。这个过程在下面一部分详细介绍。添加结束之后,会发送一个kerne event 给udevd 服务。

3. udevd 服务执行rbd-rule,这个rule 的任务其实主要是建立一个link,/dev/rbd/rbd/test -> /dev/rbd0。另外回去读取block设备的superblock,如果有必要回去执行其他的rule。结束之后,会抛出一个udev 的event 给接受者。

4. rbd map 会接收到udev的event,解析之后发现设备添加结束,然后返回给user。退出进程。

其中有一个地方,rbd map 进程在等待udev event的时候,可能会出现一直等不到的情况,具体原因现在还不清楚。但是现在有timeout 在wait 里面,如果超时,就失败退出。

(2)do_rbd_add

在(1)里面可以看到,整个过程中,最重要的一部分就是krbd driver去添加block 设备的过程。可以看到代码如下:

 540 static BUS_ATTR_WO(add);
 541 static BUS_ATTR_WO(remove);
 542 static BUS_ATTR_WO(add_single_major);
 543 static BUS_ATTR_WO(remove_single_major);
 544 static BUS_ATTR_RO(supported_features);
 545 
 546 static struct attribute *rbd_bus_attrs[] = {
 547         &bus_attr_add.attr,
 548         &bus_attr_remove.attr,
 549         &bus_attr_add_single_major.attr,
 550         &bus_attr_remove_single_major.attr,
 551         &bus_attr_supported_features.attr,
 552         NULL,
 553 };

所以当我们去写入/sys/bus/rbd/add_single_major 的时候,会去调用

7138 static ssize_t add_single_major_store(struct bus_type *bus, const char *buf,
     /* [previous][next][first][last][top][bottom][index][help] [+7138 drivers/block/rbd.c] */
7139                                       size_t count)
7140 {
7141         return do_rbd_add(bus, buf, count);
7142 }

所以重点来了,主要的实现在do_rbd_add,下面来看看do_rbd_add 到底做了什么?

ceph kernel rbd (二): rbd map , rbd unmap_第2张图片

如上图所示:do_rbd_add 主要三个步骤:

(2.1)解析参数,创建设备。(rbd_add_parse_args -> rbd_dev_create)

(2.2)读取设备信息,初始化设备metadata。并且读取和初始化parent信息,知道没有parent为止。(#define RBD_MAX_PARENT_CHAIN_LEN        16) 目前krbd 支持最多16 层父子链接。(rbd_dev_image_probe)

(2.3)创建disk 并且加入到linux device 的hierarchy。(rbd_dev_device_setup -> blk_put_queue)

(3)do_rbd_remove

do_rbd_remove 其实就很简单了,主要是将do_rbd_add 里面的事情逆向再做一遍:

7220         if (force) {
7221                 /*
7222                  * Prevent new IO from being queued and wait for existing
7223                  * IO to complete/fail.
7224                  */
7225                 blk_mq_freeze_queue(rbd_dev->disk->queue);
7226                 blk_set_queue_dying(rbd_dev->disk->queue);
7227         }
7228 
7229         del_gendisk(rbd_dev->disk);
7230         spin_lock(&rbd_dev_list_lock);
7231         list_del_init(&rbd_dev->node);
7232         spin_unlock(&rbd_dev_list_lock);
7233         device_del(&rbd_dev->dev);
7234 
7235         rbd_dev_image_unlock(rbd_dev);
7236         rbd_dev_device_release(rbd_dev);
7237         rbd_dev_image_release(rbd_dev);
7238         rbd_dev_destroy(rbd_dev);

唯一值得注意的是force 这个option,他会忽略其他打开这个rbd device 的进程,继续去做remove。但是如果有IO hang住了,即使有force 参数,也不能成功unmap。这里我们会实现一个full_force option,这个option会abort 掉inflight的所有request,然后进行remove。

 

综上,本文分三个部分介绍了一个rbd 设备的创建和删除过程。从userspace 到kernelspace,详细浏览了代码执行过程。其中rbd map 是一个复杂的过程,当然也还有可以优化空间。比如V1 已经从librbd 里面mark 成deprecated,甚至已经移除了v1 的支持。所以krbd 也可以开始考虑将v1的支持去掉了。

你可能感兴趣的:(ceph,rbd,kernel,rbd,map,代码解析,ceph,rbd,kernel)