创建一个非平凡的设备映射器目标

时间:2018-01-02 01:47:32

标签: c linux-kernel device-mapper

我正在尝试使用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调用,并且调用任务被阻止(" INFO:任务安装:488被阻止超过120秒。& #34)。这有一个READ请求,由一个iovec(1024字节)组成。在这种情况下,当然in缓冲区不需要复制,因为它们应该被覆盖;我需要在请求完成后将传入的数据复制回原始缓冲区但是我不能到达那里。

我显然错过了一些内容,但我无法理解。

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

注意:我纠正了一个错误(感谢@RuslanRLaishev),遗憾的是,它不流利;看看我自己的答案。

2 个答案:

答案 0 :(得分: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;
        }

答案 1 :(得分:1)

事实证明bio_clone_bioset()并且朋友在请求结束时不会复制回拨地址。

简单的解决方案是在clone->bi_end_io = bio->bi_end_io;结束之前添加mt_clone()

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