创建一个重要的设备映射器目标

Posted

技术标签:

【中文标题】创建一个重要的设备映射器目标【英文标题】:create a non-trivial device mapper target 【发布时间】:2018-06-11 18:56:33 【问题描述】:

我正在尝试编写一个重新映射目标以供 DM 使用。

我遵循了几个地方(包括this Answer)的说明,基本上都给出了相同的代码。

这还可以,但对我来说还不够。

我需要修改正在重新映射的struct bio 的“在途”数据。

这意味着我需要深度克隆bio,包括数据;显然提供的功能(例如:bio_clone_bioset())根本不复制数据,而是将iovec指向原始页面/偏移量。

我尝试了以下方案的一些变体:

void
mt_copy(struct bio *dst, struct bio *src) 
    struct bvec_iter src_iter, dst_iter;
    struct bio_vec src_bv, dst_bv;
    void *src_p, *dst_p;
    unsigned bytes;
        unsigned salt;

    src_iter = src->bi_iter;
    dst_iter = dst->bi_iter;
        salt = src_iter.bi_sector;

    while (1) 
        if (!src_iter.bi_size) 
            break;
        

        if (!dst_iter.bi_size) 
            break;
        

        src_bv = bio_iter_iovec(src, src_iter);
        dst_bv = bio_iter_iovec(dst, dst_iter);

        bytes = min(src_bv.bv_len, dst_bv.bv_len);

        src_p = kmap_atomic(src_bv.bv_page);
        dst_p = kmap_atomic(dst_bv.bv_page);

        memcpy(dst_p + dst_bv.bv_offset, src_p + src_bv.bv_offset, bytes);

        kunmap_atomic(dst_p);
        kunmap_atomic(src_p);

        bio_advance_iter(src, &src_iter, bytes);
        bio_advance_iter(dst, &dst_iter, bytes);
    


static struct bio *
mt_clone(struct bio *bio) 
        struct bio    *clone;

        clone = bio_clone_bioset(bio, GFP_KERNEL, NULL);
        if (!clone) 
                return NULL;
        
        if (bio_alloc_pages(clone, GFP_KERNEL)) 
                bio_put(clone);
                return NULL;
        

        clone->bi_private = bio;

        if (bio_data_dir(bio) == WRITE) 
                mt_copy(clone, bio);
        

        return clone;


static int
mt_map(struct dm_target *ti, struct bio *bio) 
        struct mt_private *mdt = (struct mt_private *) ti->private;

        bio->bi_bdev = mdt->dev->bdev;

        bio = mt_clone(bio);
        submit_bio(bio->bi_rw, bio);

        return DM_MAPIO_SUBMITTED;

但是,这 不起作用

当我 submit_bio() 使用克隆的 bio 时,我没有收到 .end_io 调用,调用任务被阻塞(“信息:任务挂载:488 阻塞超过 120 秒。”)。这与由单个 iovec (1024 字节)组成的 READ 请求。在这种情况下,当然 in 缓冲区不需要复制,因为它们应该被覆盖;我需要在请求完成后将传入的数据复制回原始缓冲区...但我没有到达那里。

我很明显遗漏了一些片段,但我无法理解是什么。

注意:我没有做任何优化(例如:使用更智能的分配策略),特别是因为我需要先了解基础知识。

注意:我更正了一个错误(感谢@RuslanRLaishev),不幸的是ininfluent;看看我自己的答案。

【问题讨论】:

您确定您的内核使用了正确版本的struct bio。您提供的链接中的那个似乎是几个旧版本。 (这是唯一立即脱颖而出的东西) @DavidC.Rankin:我希望我没有搞错。我正在使用内核 v4.4.9,它肯定不是最前沿的,但它是我的目标(一个旧的 at91samg25)。 我评论的唯一原因是您提供的链接仍然使用当前版本中不存在的旧硬编码 512b 扇区大小(我怀疑处理默认的 512/4096 扇区大小)。更改发生在 4.4.9 之前,所以如果您使用的是您提供的链接中的版本,我会仔细检查 struct bio 的版本。 @DavidC.Rankin:代码,在我尝试克隆 struct bio 之前,正如宣传的那样......不幸的是,它也没有做任何有用的事情。我实际上是从this code 开始的,这非常相似。如前所述,我第一次尝试重新提交原始生物的代码是成功的。我似乎连一个普通的克隆都做不了(当然没用,但这是第一步)。感谢您的耐心等待...您为什么说我们使用的是硬编码的 512b?这只是用于环回设置,我根本没有使用它,我的目标设备是 SD。 【参考方案1】:

对吗?

if (bio_alloc_pages(**bio**, GFP_KERNEL)) 
                bio_put(clone);
                return NULL;
        

if (bio_alloc_pages(**clone**, GFP_KERNEL)) 
                bio_put(bio);
                return NULL;
        

【讨论】:

很好,你说对了一半。应该是 if (bio_alloc_pages(clone, GFP_KERNEL)) bio_put(clone);返回空值;不幸的是,这与根本问题无关。看看我自己的答案。 看起来我有一个像你这样的任务,我处理要在 make_request_fn() 中写入的数据,并在 bi_end_io() 例程中读取数据。为了保留原始数据(写请求),我只克隆 bvecs。常bi_io_vec要复制吗?并在 bio_end_io() 中恢复它。希望这会对你有所帮助。 当心多个正在进行的请求......以及相关的内存消耗。 不,我无法解决内存消耗问题(我也尝试将邮件发送到正确的邮件列表,但没有得到答复;它似乎是“仅限大师”的 ML)。我“解决了”我的问题要低得多(直接在 MMC 设备驱动程序上),其中请求已经序列化(只有一个在飞行中),但这是非常不理想的,因为它非常特定于我当前的硬件,不幸的是。欢迎任何提示。 不,我没有内存泄漏。发生的是上层(ext4 文件系统),在某些情况下(例如:当解压缩一个非常大的 tar 文件时)发送无数个(>10000)个并行请求,每个请求都需要它自己的分配,显然在完成之前我无法释放它。这会耗尽内存池并且 tar 操作失败,之后释放内存并且一切都很好。您可以使用 gmail dot com 上的 myname 直接通过邮件与我联系(我希望我不会违反政策;如果找到,我会发布解决方案)。【参考方案2】:

原来bio_clone_bioset()和朋友在请求结束时没有复制回调地址来调用。

简单的解决方案是在mt_clone() 的末尾添加clone->bi_end_io = bio->bi_end_io;

不幸的是,这不足以使代码正常运行,因为事实证明上层可以产生 数千 个正在进行的请求(即:在前一个请求完成之前排队和预处理的请求)导致内存不足。试图通过返回 DM_MAPIO_REQUEUE 来减慢上层似乎不起作用(请参阅:https://unix.stackexchange.com/q/410525/130498)。然而,这与当前的问题无关。

【讨论】:

以上是关于创建一个重要的设备映射器目标的主要内容,如果未能解决你的问题,请参考以下文章

设备映射器,使用虚拟设备启动

由于设备映射器错误,无法运行 Docker 容器

docker-storage-setup 软件包在我的 rhel 7 盒子上不可用?我怎么得到它?我需要使用设备映射器创建一个精简池。

MyBatis配置文件--mappers映射器

MyBatis 生成器 - 生成映射器实现

在可视化映射器中将命名空间添加到根节点