创建一个重要的设备映射器目标
create a non-trivial device mapper target
我正在尝试编写一个用于 DM 的重映射目标。
我遵循了几个地方(包括 this Answer)的说明,所有这些基本上都给出了相同的代码。
还可以,但对我来说还不够。
我需要修改正在重新映射的 struct bio
的 "in transit" 数据。
这意味着我需要对 bio
进行深度克隆,包括数据;显然提供的函数(例如:bio_clone_bioset()
)根本不复制数据,而是将 iovec
指向原始的 pages/offsets.
我尝试了以下方案的一些变体:
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()
使用克隆的生物时,我没有收到 .end_io
调用并且调用任务被阻止 ("INFO: task mount:488 blocked for more than 120 seconds.")。这是一个 READ
请求,由一个 iovec
(1024 字节)组成。在这种情况下,输入缓冲区当然不需要复制,因为它们应该被覆盖;我需要在 请求完成后将传入数据复制回原始缓冲区 ...但我没有到达那里。
我显然遗漏了一些内容,但我无法理解是什么。
注意:我没有做任何优化(例如:使用更智能的分配策略),因为我需要先了解基础知识。
注:我更正了一个错误(感谢@RuslanRLaishev),不幸影响;看我自己的回答。
正确吗?
if (bio_alloc_pages(**bio**, GFP_KERNEL)) {
bio_put(clone);
return NULL;
}
或
if (bio_alloc_pages(**clone**, GFP_KERNEL)) {
bio_put(bio);
return NULL;
}
原来bio_clone_bioset()
朋友们在request结束时没有复制回调地址调用
简单的解决方案是在 mt_clone()
结束之前添加 clone->bi_end_io = bio->bi_end_io;
。
不幸的是,这不足以让代码发挥作用,因为事实证明上层可以产生 千 的飞行请求(即:请求在前一个完成之前排队和预处理)导致内存不足。试图通过返回 DM_MAPIO_REQUEUE 来减慢上层的速度似乎不起作用(参见:https://unix.stackexchange.com/q/410525/130498)。但是,这与当前问题无关。
我正在尝试编写一个用于 DM 的重映射目标。
我遵循了几个地方(包括 this Answer)的说明,所有这些基本上都给出了相同的代码。
还可以,但对我来说还不够。
我需要修改正在重新映射的 struct bio
的 "in transit" 数据。
这意味着我需要对 bio
进行深度克隆,包括数据;显然提供的函数(例如:bio_clone_bioset()
)根本不复制数据,而是将 iovec
指向原始的 pages/offsets.
我尝试了以下方案的一些变体:
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()
使用克隆的生物时,我没有收到 .end_io
调用并且调用任务被阻止 ("INFO: task mount:488 blocked for more than 120 seconds.")。这是一个 READ
请求,由一个 iovec
(1024 字节)组成。在这种情况下,输入缓冲区当然不需要复制,因为它们应该被覆盖;我需要在 请求完成后将传入数据复制回原始缓冲区 ...但我没有到达那里。
我显然遗漏了一些内容,但我无法理解是什么。
注意:我没有做任何优化(例如:使用更智能的分配策略),因为我需要先了解基础知识。
注:我更正了一个错误(感谢@RuslanRLaishev),不幸影响;看我自己的回答。
正确吗?
if (bio_alloc_pages(**bio**, GFP_KERNEL)) {
bio_put(clone);
return NULL;
}
或
if (bio_alloc_pages(**clone**, GFP_KERNEL)) {
bio_put(bio);
return NULL;
}
原来bio_clone_bioset()
朋友们在request结束时没有复制回调地址调用
简单的解决方案是在 mt_clone()
结束之前添加 clone->bi_end_io = bio->bi_end_io;
。
不幸的是,这不足以让代码发挥作用,因为事实证明上层可以产生 千 的飞行请求(即:请求在前一个完成之前排队和预处理)导致内存不足。试图通过返回 DM_MAPIO_REQUEUE 来减慢上层的速度似乎不起作用(参见:https://unix.stackexchange.com/q/410525/130498)。但是,这与当前问题无关。