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,
};
然后就会回到 上面的递归流程。
分享到:
相关推荐
return mapper.readValue(jsonString, new TypeReference<List<Object>>(){}); } catch (Exception e) { e.printStackTrace(); } return null; } ``` 7. **注意事项**: - 对象中的字段需要有对应的getter...
tk.mabatis的jar包 4.1.5版本。可参考以下方式使用 <properties> <tk-mapper.version>4.1.5</... <artifactId>mapper</artifactId> <version>${tk-mapper.version}</version> </dependency> </dependencies>
在Java开发中,数据对象(通常称为Bean)之间的转换是一个常见的任务。...通过深入阅读给出的博文链接,开发者可以进一步掌握如何在实际项目中利用BeanMapper优化代码,提高代码的可维护性和可读性。
需要在项目pom文件的插件节点里面加上逆向生成插件 将配置文件... <configurationFile>GeneratorMapper.xml</configurationFile> <verbose>true</verbose> <overwrite>true</overwrite> </configuration> </plugin>
本文结合具体代码对Linux内核中的devicemapper映射机制进行了介绍。Devicemapper是Linux2.6内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机制下,用户可以很方便的根据自己的需要制定实现存储资源的...
<artifactId>jackson-mapper-asl</artifactId> <version>1.9.13</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> ...
<artifactId>mapper</artifactId> <version>4.0.3</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.12</...
<javaClientGenerator type="XMLMAPPER" targetPackage="com.example.mapper" targetProject="src/main/java"> <property name="enableSubPackages" value="true"/> </javaClientGenerator> <!-- 表配置 --> ...
</mapper> ``` namespace是Mapper接口的全限定名,id对应Mapper接口中的方法名。 四、mybatis-config.xml配置文件 mybatis-config.xml是Mybatis的全局配置文件,包含数据库连接、事务管理、插件等配置。基础配置...
PageInfo<User> pageInfo = new PageInfo<>(users); return pageInfo; } } ``` 在Controller中,你可以将分页信息传递给前端: ```java @RestController @RequestMapping("/users") public class ...
MyBatis Generator(MBG)是MyBatis的一个配套工具,用于自动生成MyBatis的DAO、Mapper和Domain(实体类)文件,极大地提高了开发效率,减少了手动编写这些基础代码的工作量。 MyBatis Generator通过XML配置文件...
<artifactId>mapper-spring-boot-starter</artifactId> <version>3.4.6</version> </dependency> </dependencies> ``` 接下来,我们需要配置数据库连接。在`application.properties`文件中设置MySQL的相关属性:...
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.example.mapper"/> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> </...
</mapper> ``` 然后,我们需要一个Service来处理业务逻辑,如`UserService.java`: ```java @Service public class UserService { @Autowired private UserMapper userMapper; public User getUserById(Long ...
例如,它定义了`<select>`, `<insert>`, `<update>`, `<delete>`等元素,这些都是在执行SQL查询和更新时常用的操作。这些元素通常包含SQL语句,并且可以有参数绑定、结果映射等高级特性。 在编写mapper.xml文件时,...
* MyBatis 映射文件中的常用元素包括:<mapper>、<cache>、<cache-ref>、<resultMap>、<sql>、<insert>、<delete>、<update> 和 <select> 等。 * <mapper> 元素是映射文件的根元素,具有 namespace 属性,用于区分...
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.yourcompany.petstore.mapper"/> <property name="sqlSessionFactoryBeanName" value=...
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile> <overwrite>true</overwrite> <verbose>true</verbose> </configuration> </plugin> </plugins> </build> ``` 6. **生成...
在命令行下使用,请看例子: <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...
</mapper> ``` 最后,在Spring Boot的启动类中,可以使用`@Autowired`注解注入Mapper接口,并进行数据库操作: ```java @SpringBootApplication public class Application { @Autowired private UserMapper ...