首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

<linux> Device Mapper 跟 Multiple Devices

2012-06-29 
linuxDevice Mapper 和Multiple Devices???DM 和 MD 。。。 一个用于逻辑卷 一个用于软RAID 。都是虚拟的。。。

<linux> Device Mapper 和 Multiple Devices

?

?

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

?

?

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

?

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

?

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

?

然后经过 ?block_io.c 的

?

static intblockio_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 结构里面有一个

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->limits);/*请求队列里面能处理的最多量*/blk_queue_max_hw_sectors(q, BLK_SAFE_MAX_SECTORS);/* * If the caller didn't supply a lock, fall back to our embedded * per-queue locks */if (!q->queue_lock)q->queue_lock = &q->__queue_lock;/*对于处在ZONE_HIGH的内存需要分配的mpool也设置限制 */blk_queue_bounce_limit(q, BLK_BOUNCE_HIGH);}

?

?

因为没有提供proc 接口 这里看到如果你想修改一些限制,主要就是

?

static void __map_bio(struct dm_target *ti, struct bio *clone, struct dm_target_io *tio){int r;sector_t sector;struct mapped_device *md;clone->bi_end_io = clone_endio;clone->bi_private = tio;atomic_inc(&tio->io->io_count);sector = clone->bi_sector; /*如果是liner策略就是:linear_map*/r = ti->type->map(ti, clone, &tio->info); if (r == DM_MAPIO_REMAPPED) { /* the bio has been remapped so dispatch it 这里又去调用 * */ generic_make_request(clone);} else if (r < 0 || r == DM_MAPIO_REQUEUE) {/* error the io and bail out, or requeue it if needed */md = tio->io->md;dec_pending(tio->io, r);clone->bi_private = md->bs;bio_put(clone);free_tio(md, tio);} else if (r) {//...}}?

?

?

?

回来看一下?

?

我们希望一次执行只调用 一个 q->make_request_fn ?,

但是对于基于栈的设备 就用 current->bio_list来维护 反复make_request_fn 提交的请求

?

同样 current->bio_list 也作为一个标志来表明是否?generic_make_request ?当前激活。

?

如果 bio_list == null 说明没有激活?generic_make_request , 所以新的请求需要加入到bio_list队尾

?

?

下面的函数是一个明显的递归 ,一起来看看

?

?

void generic_make_request(struct bio *bio)/*szx:__make_request*/{struct bio_list bio_list_on_stack;       /*第一次先不会进入:如果进入了这个时候gen_m_r 已经激活了,          *不断的把bio_list 里面放入要处理的bio 直到不需要remap 这次递归结束不会进gen_m_r了 */if (current->bio_list) {/* make_request is active */bio_list_add(current->bio_list, bio);return;}        /*调用者要保证 bio->bi_next 为空*/BUG_ON(bio->bi_next);bio_list_init(&bio_list_on_stack);current->bio_list = &bio_list_on_stack;static int raid_map(struct dm_target *ti, struct bio *bio, union map_info *map_context)
{struct raid_set *rs = ti->private;mddev_t *mddev = &rs->md;mddev->pers->make_request(mddev, bio);return DM_MAPIO_SUBMITTED;}
?do { /*第一次会从这里进入开始dm_requst这样的fn*/__generic_make_request(bio); /*如果是从这个调用返回了,说明current->bio_list 里面有料了, *就开始用真正需要的下层block device 处理*/bio = bio_list_pop(current->bio_list);} while (bio);current->bio_list = NULL; /* deactivate 去激活gn_m_r*/}

??我不知道到这里 你明白没有。。

?

?

?

对于raid 策略的 target_device 。dm_request --->....->

?

?到了dm_raid.c对应的 target_driver 之后就会调用事先注册的_map_io?

?

?

?

?

?

?

static int raid_map(struct dm_target *ti, struct bio *bio, union map_info *map_context){struct raid_set *rs = ti->private;mddev_t *mddev = &rs->md;/*这里就会去调用对应的  raid 级别(策略)*/mddev->pers->make_request(mddev, bio);return DM_MAPIO_SUBMITTED;}
?

?

然后就会调用自己管理的所有 raid_type 的make_request 方法。

?

static struct mdk_personality raid1_personality ={.name= "raid1", .level= 1,.owner= THIS_MODULE,.make_request= make_request,.run= run,.stop= stop,.status= status,.error_handler= error,.hot_add_disk= raid1_add_disk,.hot_remove_disk= raid1_remove_disk,.spare_active= raid1_spare_active,.sync_request= sync_request,.resize= raid1_resize,.size= raid1_size,.check_reshape= raid1_reshape,.quiesce= raid1_quiesce,.takeover= raid1_takeover,};

?

?

?

然后就会回到 上面的递归流程。

?

热点排行