日期:2014-05-16  浏览次数:20726 次

<linux> Device Mapper 和 Multiple Devices

?

?

?DM 和 MD 。。。 一个用于逻辑卷 一个用于软RAID 。都是虚拟的。。。

?

?

?开始我也很好奇,如果同时启用2个设备,bio 是如何分发的。 现在有了点眉目。

?

先说一下iscsi 的理解。 简单的看了一下iscsi mod。我的理解就是

?

网络过来的数据包组织成了 struct tio

?

然后经过 ?block_io.c 的

?

static int
blockio_make_request(struct iet_volume *volume, struct tio *tio, int rw)
?

?

处理生成bio 后 直接 submit_bio 到generic layer。

?

这里其实 iscsi mod 替代了VFS层注册了自己的方法直接去处理用户态数据

<这里可能丢失了page buffer 层,这里按照存储器山的设计是不是不合理 后面再研究>。(当然他也支持通过VFS 接口下去)

?

好了下面就来看看到了 G层 是如何处理的 :?

?

在 sched 的伟大的 task_struct 结构里面有一个这个

?

?

struct task_struct {
//...
  struct bio_list *bio_list;
//...
}
?

?

bio 结构里面有一个

bi_next :用于连接下一个bio ,把他们放到设备 request queue 中

这里把他们用 bio_list管理起来 ?。 首尾都快速访问。

?

?

在正常的情况下 (实际 设备)bio_alloc 被产生之后 ,就会去通过?

?

generic_make_request 进入 generic block 层 。通过一些检查 ,修改分区偏移 放入队列后 会去通过

request_queue ?内的 make_request_fn(q, bio) 调用__make_request 。这个大家都知道,就不那代码解释了

?

?

现在就是 ?在?Multiple Devices driver 里面我们可以看到:?

?

?

?

static int md_alloc(dev_t dev, char *name)
{
	static DEFINE_MUTEX(disks_mutex);
	mddev_t *mddev = mddev_find(dev);
	struct gendisk *disk;
	int partitioned;
	int shift;
	int unit;
	int error;
//...

	blk_queue_make_request(mddev->queue, md_make_request);/*注册函数*/
//...

}

?

所以 RAID 的bio 请求会到?md_make_request

?

?

而在 ?Device Mapper?driver?里面,我们同样可以在初始化的地方看到

?

?

?

static struct mapped_device *alloc_dev(int minor)
{
	int r;
	struct mapped_device *md = kzalloc(sizeof(*md), GFP_KERNEL);
	void *old_md;
//...

	dm_init_md_queue(md);
//...

}

紧接着:

?

?

static void dm_init_md_queue(struct mapped_device *md)
{
	queue_flag_clear_unlocked(QUEUE_FLAG_STACKABLE, md->queue);

	md->queue->queuedata = md;
	md->queue->backing_dev_info.congested_fn = dm_any_congested;
	md->queue->backing_dev_info.congested_data = md;
	blk_queue_make_request(md->queue, dm_request);
	blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY);
//...
}
?

所以LVM 的bio 请求会到?dm_request

?

?

对于一个 bio 普通的内核处理路线 就直接把它放入整合进一个request 然后传给对应设备的request queue,设备在软中断或者调度的时候处理这个队列。

?

但是对于我们上面说的虚拟设备 最好直接通过一个请求调用传递给虚拟设备 这样可以让他们立刻服务。

而让bio 知道自己要被谁服务的方法就是我们上面2个地方都看到的 ?

?

?blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)?

?

函数。

?

?

void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)
{
	/*请求队列的最多安置请求数 (128)*/
	q->nr_requests = BLKDEV_MAX_RQ;
       /*这里就是bio 处理函数啦,generic_make_request调用的*/
	q->make_request_fn = mfn;
	blk_queue_dma_alignment(q, 511);/*和普通的设备一样对于direct的IO 也通过DMA直接处理,这里设置了对齐掩码*/
       /*设置了 请求拥塞开关上下限 113-111*/
	blk_queue_congestion_threshold(q);
        /*队列已满 仍可以作为一次提交的请求数*/
	q->nr_batching = BLK_BATCH_REQ;
       /*都是经典的默认值  利用插拔来提高合并率(我叫他逼尿法)*/
	q->unplug_thresh = 4;		/* hmm */
	q->unplug_delay = msecs_to_jiffies(3);	/* 3 milliseconds */
	if (q->unplug_delay == 0)
		q->unplug_delay = 1;
       /*【kblockd】 线程处理*/
	q->unplug_timer.function = blk_unplug_timeout;
	q->unplug_timer.data = (unsigned long)q;
         /*设置虚拟设备队列的相关限制*/
	blk_set_default_limits(&q->li