本文的目的

由于Device Mapper框架的相关文章真的太少了,就想从项目来进行学习,深入了解DM框架的底层原理。收集了GitHub上前20页,有关Device-Mapper target的repos。从简单到复杂,依次进行深入地学习与分析。

1,device-mapper

整体比较简单,基本就是最简单的device mapper壳子,dm_template遇到非零bio就原地映射,学会了一个函数和一个宏:

  1. static inline void bio_set_dev(struct bio *bio, struct block_device *bdev)
    {
        bio_clear_flag(bio, BIO_REMAPPED);
        if (bio->bi_bdev != bdev)
            bio_clear_flag(bio, BIO_THROTTLED);
        bio->bi_bdev = bdev;
        bio_associate_blkg(bio);
    }
    <!--0-->
    
    这个宏通常用在"\_map"函数中,mapped之后,对bi\_sector赋值时。
  2. ”_map“函数的几种返回状态:

    1
    2
    3
    4
    DM_MAPIO_SUBMITTED: map函数将bio赋值后又分发出去,告诉DM不要再处理了。
    DM_MAPIO_REMAPPED: map函数修改了bio的内容,希望DM将bio按照新内容再分发。
    DM_MAPIO_REQUEUE: map函数将bio加入队列中等待后续处理
    DM_MAPIO_KILL: 结束一个bio? 暂时没找到说法。。

    若返回DM_MAPIO_SUBMITTED,后续DM框架就不会处理了,这样就可以更加灵活地添加/删除bio。

2,dm_invert

整体也不难,但是学到了很多。主要是通过.message方法与target进行交互,并将block进行管理,以红黑树的结构进行维护。

  1. DMINFO可以用来发出判断函数进出口,DMERR发出错误报告,例如:

    1
    2
    3
    4
    DMINFO("Entry: %s", __func__);
    char *emsg = "Block size is too large";
    DMERR("%s\n", emsg);
    DMINFO("Exit: %s", __func__);
  2. splinlock_t,是一种自旋锁,会忙等待,常用于短时间的轻量级加锁机制。

    详见:(4条消息) linux的自旋锁struct spinlock_t的使用_wang-bob的博客-CSDN博客

  3. 在”_map”函数中,不仅可以通过cdir来获得请求的方向,还可以用宏(blk_types.h - include/linux/blk_types.h - Linux source code (v4.9) - Bootlin)来得到请求的类型。这些宏包括:

    1
    2
    3
    4
    5
    6
    7
    8
    enum req_op {
    REQ_OP_READ,
    REQ_OP_WRITE,
    REQ_OP_DISCARD, /* request to discard sectors */
    REQ_OP_SECURE_ERASE, /* request to securely erase sectors */
    REQ_OP_WRITE_SAME, /* write same block many times */
    REQ_OP_FLUSH, /* request for cache flush */
    };

    这里竟然看到了安全删除!太好了!说明有种方式能够把安全删除的请求传递到底层设备了。

  4. 一个新的map状态:DM_MAPIO_KILL。在搜索相关资料时发现一个山东大学的大佬发了10多篇有关device mapper相关工作,可以去学习学习。((4条消息) qq_51946537的博客_CSDN博客-软件工程应用与实践,项目实训,笔记领域博主

  5. 一个方法:void **bio_endio** (struct bio * bio) will end I/O on the whole bio.

  6. 遍历bio的所有segment,并将这些segment赋值为1。提供了一种bio_for_each_segment()函数的用法,之前看着都一直不太会用。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    static void one_fill_bio(struct bio *bio)
    {
    struct bio_vec bvec;
    struct bvec_iter i;
    unsigned long flags;

    DMINFO("Entry: %s", __func__);

    bio_for_each_segment(bvec, bio, i) {
    char *buf;
    buf = bvec_kmap_irq(&bvec, &flags);
    memset(buf, 0xff, bvec.bv_len);
    flush_dcache_page(bvec.bv_page);
    bvec_kunmap_irq(buf, &flags);
    }
    }
  7. 还有 linux/bio.c 中定义的zero_fill_bio()函数(https://elixir.bootlin.com/linux/latest/source/block/bio.c#L531),以下是其内核中的实现。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    void zero_fill_bio(struct bio *bio)
    {
    struct bio_vec bv;
    struct bvec_iter iter;

    bio_for_each_segment(bv, bio, iter)
    memzero_bvec(&bv);
    }
    EXPORT_SYMBOL(zero_fill_bio);
  8. target_type的.message是什么意思?

  9. 红黑树插入元素,以及相关红黑树结构体。

    1
    2
    3
    参考的一些博客:
    1,https://blog.csdn.net/u013590407/article/details/52229330
    2,https://tanglinux.blog.csdn.net/article/details/7420689

先想清楚一个问题,再继续看这个代码。

安全删除可以依靠什么进行具体的实现。

A:其实不论ATA command还是TRIM command都只是建立一个通信协议,而底层需要做的实现方面的工作其实与发来的命令无关。