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->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 接口 这里看到如果你想修改一些限制,主要就是
写道
enum blk_default_limits{}
blk_set_default_limits()
都知道 dm 有对应的设备树 ,层层转发
我们知道
dm_request(struct request_queue *q, struct bio *bio) -> _split_and_process_bio(md, bio)-> __clone_and_map(&ci);
->__map_bio(ti, clone, tio);
交给对于策略的 dm_type 处理map(ti, clone, &tio->info);
后就会根据 DM_MAPIO_REMAPPED 标志 ,继续 generic_make_request(clone);
代码如下:
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,
};
然后就会回到 上面的递归流程。
分享到:
相关推荐
tk.mabatis的jar包 4.1.5版本。可参考以下方式使用 <properties> <tk-mapper.version>4.1.5</... <artifactId>mapper</artifactId> <version>${tk-mapper.version}</version> </dependency> </dependencies>
NULL 博文链接:https://vernonchen163.iteye.com/blog/1879387
需要在项目pom文件的插件节点里面加上逆向生成插件 将配置文件... <configurationFile>GeneratorMapper.xml</configurationFile> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> </plugin>
<artifactId>jackson-mapper-asl</artifactId> <version>1.9.13</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> ...
<sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources"> <property name="enableSubPackages" value="true"/> </sqlMapGenerator> <!-- 生成DAO的包名和位置--> ...
Fport can be<br>used to quickly identify unknown open ports and their associated applications.<br><br>Usage: <br>C:\>fport<br>FPort v2.0 - TCP/IP Process to Port Mapper<br>Copyright 2000 by ...
一、 概述 1.1 简介 1.2 传统JDBC存在的问题 1.3 框架的好处 1.4 历史 二、 配置文件 2.1 核心配置文件 ...9.1 <mapper>根标签 9.2 <resultMap> 9.3 <insert> 9.4 <sql> 十、动态SQL 十一、逆向工程 十二、关联查询
| mapper:数据库SQL语句mapper文件<br>   | static:静态文件<br>     | css:css文件<br>     | js:JavaScript文件<br>     | images:...
| mapper:数据库SQL语句mapper文件<br>   | static:静态文件<br>     | css:css文件<br>     | js:JavaScript文件<br>     | images:...
在命令行下使用,请看例子: <br><br>C:\>fport<br>FPort v2.0 - TCP/IP Process to Port Mapper<br>Copyright 2000 by Foundstone, Inc.<br>http://www.foundstone.com<br><br>Pid Process Port Proto Path<br>1296...
<dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper</artifactId> <version>4.1.5</version> </dependency>
<artifactId>mapper</artifactId> <version>3.4.0</version> </dependency> 用了这个通用Mapper后,很多基础的方法,如:selectByPrimaryKey()等等,都不用写了,也不用自动生成了,等于自带了这些方法; 还用...
介绍了动态SQL常用元素:<if>、<choose>、<where>、<trim>、<set>、<foreach>等。 通过<if>元素示例演示了根据输入参数动态构建查询条件。 通过<choose>元素示例演示了多条件选择动态查询。 通过<where>、<trim>元素...
<br>在 Web 地图服务客户端中包含的众多新特性之中,集成了 Vertical Mapper 这一新的 3D 图像制图引<br>擎,并对 Universal Translator 作出了重要改进。第8 页“MapInfo Professional 7.5 新增特性” 提供<br>了...
这是个完整的例子 呵呵 看看适合你不 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-/... <mapper resource="com/sdzn/mybatis/mapper/UserMapper.xml" /> </mappers> </configuration>
b.<decorator:title/>这个标签会找到被装饰页面的title(<title></title>标签内)内容填入, <decorator:head/>找到被装饰页面的head(<head></head>标签内)内容填入,<decorator:body/>找到被装饰页面的body(<body></...
FieldMapper和ResultMap 一个将映射List<Map>>转换为对象列表的库。 它可以与数据库访问库一起使用... <id>FieldMapper Repo</id> <url>https://raw.github.com/rizvn/FieldMapper/mvn-repo/</url> </repository
<value>classpath:mapper/*.xml</value> </array> </property> --> <property name="typeAliasesPackage" value="org.jd.domain" /> <property name="plugins"> <array> <bean class=...
Linux device-mapper-udev-CRS-ASM
在XML文件中,<if>标签通常被嵌套在其他标签内,如<select>、<insert>和<update>等标签内,用于控制生成的SQL语句的结构和内容。 <if>标签通常包含一个test属性,该属性被用于指定条件表达式。如果表达式的结果为...