Linux Block模块之IO合并代码解析( 三 )

2.3 Elevator合并bio生成新的request后,会插入到两个位置 。一是电梯调度算法的哈希表中 , 以request的结束位置为哈希值进行哈希,便于查找可以后向合并的请求 , 取值为 q->elevator->hash ;二是具体调度算法的调度队列,用于调度算法进行IO调度,以deadline调度算法为例,取值为 q->elevator->elevator_data->sort_list/fifo_expire。调度算法派发请求后,请求会进入 q 的派发队列并同时从哈希和调度队列中移除 。执行Elevator合并的函数为 elv_merge(),主要包含电梯调度算法的后向合并以及具体IO调度算法的前向合并 , 并且为了提高合并效率,函数在最开始先检查最近一次合并成功的请求能否与bio进行合并 。代码逻辑如下:
int elv_merge(struct request_queue *q, struct request **req, struct bio *bio){ struct elevator_queue *e = q->elevator; struct request *__rq; int ret; /** Levels of merges:*nomerges:No merges at all attempted*noxmerges: Only simple one-hit cache try*merges:All merge tries attempted** 检查队列是否设置禁止合并的标记*/ if (blk_queue_nomerges(q))return ELEVATOR_NO_MERGE; /** First try one-hit cache.* 首先检测上次进行了合并的请求能否再次合并*/ if (q->last_merge && elv_bio_merge_ok(q->last_merge, bio)) {ret = blk_try_merge(q->last_merge, bio);if (ret != ELEVATOR_NO_MERGE) {*req = q->last_merge;return ret;} } if (blk_queue_noxmerges(q))return ELEVATOR_NO_MERGE; /** See if our hash lookup can find a potential backmerge.* 在哈希中查找可能后向合并的请求*/ __rq = elv_rqhash_find(q, bio->bi_sector); if (__rq && elv_bio_merge_ok(__rq, bio)) {*req = __rq;return ELEVATOR_BACK_MERGE; } /* 调用具体调度算法的接口判断能否进行前向合并 */ if (e->uses_mq && e->aux->ops.mq.request_merge)return e->aux->ops.mq.request_merge(q, req, bio); else if (!e->uses_mq && e->aux->ops.sq.elevator_merge_fn)return e->aux->ops.sq.elevator_merge_fn(q, req, bio); return ELEVATOR_NO_MERGE;}2.4 前/后向合并在结构体 struct request 中有两个成员分别是 struct bio *biostruct bio *biotail,成员 bio 表示还没有完成传输的第一个bio,成员 biotail 指向最后一个需要处理的bio 。在结构体 struct bio 中有一个成员 struct bio *bi_next,该成员指向下一个需要处理的bio,多个需要连续处理的bio通过该成员链接 。
前向合并指的是将bio合并到req的前边 , 故基本逻辑为:
/* 将bio插入到req前边 */bio->bi_next = req->bio;req->bio = bio;后向合并指的是将bio合并到req的后边,故基本逻辑为:
/* 将bio插入到req尾部 */req->biotail->bi_next = bio;req->biotail = bio;【Linux Block模块之IO合并代码解析】

推荐阅读