linux的CFQ调度器解析(3)

static struct elevator_type iosched_cfq = {
.ops = {
.elevator_merge_fn = cfq_merge,
.elevator_merged_fn = cfq_merged_request,
.elevator_merge_req_fn = cfq_merged_requests,
.elevator_allow_merge_fn = cfq_allow_merge,
.elevator_bio_merged_fn = cfq_bio_merged,
.elevator_dispatch_fn = cfq_dispatch_requests,
.elevator_add_req_fn = cfq_insert_request,
.elevator_activate_req_fn = cfq_activate_request,
.elevator_deactivate_req_fn = cfq_deactivate_request,
.elevator_completed_req_fn = cfq_completed_request,
.elevator_former_req_fn = elv_rb_former_request,
.elevator_latter_req_fn = elv_rb_latter_request,
.elevator_set_req_fn = cfq_set_request,
.elevator_put_req_fn = cfq_put_request,
.elevator_may_queue_fn = cfq_may_queue,
.elevator_init_fn = cfq_init_queue,
.elevator_exit_fn = cfq_exit_queue,
.trim = cfq_free_io_context,
},
.elevator_attrs = cfq_attrs,
.elevator_name = "cfq",
.elevator_owner = THIS_MODULE,

};

以上是CFQ调度器在电梯算法接口中的实现函数,本文对重要的函数进行解析:

cfq_dispatch_requests

该函数是电梯算法用到的最重要的一个函数,底层的设备驱动会调用电梯算法的接口elevator_dispatch_fn来获取下一个执行的请求,也就是调用cfq_dispatch_requests

#2582 可以看到cfq_data指针存在request_queue->elevator->elevator_data中

#2585 如果cfq_data->busy_queues为0,说明没有请求队列,直接返回

#2588 cfq_forced_dispatch后面再讲

#2591 cfq_select_queue前面已经提过,最终是给出一个结果:保留当前cfq_queue还是重新选一个cfq_queue。这里面涉及了诸如time slice是否耗尽,是否需要slice_idle和group_idle,当前cfq_queue里是否还有请求等一系列判断。如果选择了新的一个cfq_queue,那么把这个新的cfq_queue放到cfq_data->active_queue里,把原来的cfq_queue也加入到cfq_data,cfq_group的一系列service tree, prio tree这些红黑树中

#2598 找到了cfq_queue之后,开始调用cfq_dispatch_request,该函数的作用和代码中的注释一样

/*

 * Dispatch a request from cfqq, moving them to the request queue

 * dispatch list.

 */

#2551 调用cfq_may_dispatch来判断当前是否可以分发给下层驱动请求,该函数的解析请看前一篇

#2557 调用cfq_check_fifo,查看并返回cfq_queue->fifo队列里已经超时的请求。我们前面提到过,cfq_queue内部是按照部分类似deadline的调度算法来进行的。如果没有超时的请求,则返回正常的红黑树的下一个请求next_rq

这里再提下cfq_queue的请求队列,cfq_queue->sort_list是一个红黑树结构,里面是这个队列里排过序的请求。请求排序的key是基于请求的起始sector的,也就是blk_rq_pos(request),具体可以参考elv_rb_add的实现

#2564 是真正把请求下发到底层驱动的实现

/*

 * Move request from internal lists to the request queue dispatch list.

 */

static void cfq_dispatch_insert(struct request_queue *q, struct request *rq)

{

struct cfq_data *cfqd = q->elevator->elevator_data;

struct cfq_queue *cfqq = RQ_CFQQ(rq);


cfq_log_cfqq(cfqd, cfqq, "dispatch_insert");


cfqq->next_rq = cfq_find_next_rq(cfqd, cfqq, rq);

cfq_remove_request(rq);

cfqq->dispatched++;

(RQ_CFQG(rq))->dispatched++;

elv_dispatch_sort(q, rq);


cfqd->rq_in_flight[cfq_cfqq_sync(cfqq)]++;

cfqq->nr_sectors += blk_rq_sectors(rq);

cfq_blkiocg_update_dispatch_stats(&cfqq->cfqg->blkg, blk_rq_bytes(rq),

rq_data_dir(rq), rq_is_sync(rq));

}

此时rq要被下发到底层驱动,所以需要找一个和rq磁盘位置最近的请求作为cfq_queue->next_rq,之后调用cfq_remove_request把请求从cfq_queue队列里删除。之后调用elv_dispatch_sort把请求加入到分发队列中并排序


#2608 如果cfq_data->busy_queues大于1,同时该cfq_queue是一个异步队列,同时cfq_queue使用的时间已经超出了cfq_prio_to_maxrq计算出来的时间或者该队列是idle class,都会调用cfq_slice_expired立刻让队列过期


static void cfq_insert_request(struct request_queue *q, struct request *rq)

#define RQ_CIC(rq) ((struct cfq_io_context *) (rq)->elevator_private[0])
#define RQ_CFQQ(rq) (struct cfq_queue *) ((rq)->elevator_private[1])
#define RQ_CFQG(rq) (struct cfq_group *) ((rq)->elevator_private[2])

从上面3个宏定义可以看出,request相关联的cfq_io_context, cfq_queue, cfq_group都是存在request->elevator_private数组中的,因此

#3466 - #3467 通过request_queue->elevator->elevator_data得到cfq_data,通过request得到相关联的cfq_queue,通过request得到相关联cfq_io_context进而得到io_context

#3470 调用cfq_init_prio_data

#2852 如果cfq_cfqq_prio_changed(cfqq)为false,直接返回,因为如果优先级没变化,没必要改变cfq_queue的优先级变量

#2855 通过io_context得到进程的io优先级ioprio_class

#2859 - #2879 ioprio_class有IOPRIO_CLASS_NONE, IOPRIO_CLASS_RT, IOPRIO_CLASS_BE, IOPRIO_CLASS_IDLE,相应的ioprio也有不同的计算方法

#2885 - #2887 按照ioprio_class, ioprio初始化好cfq_queue

#3473 cfqq->fifo加到request->queuelist末尾

#3474 调用cfq_add_rq_rb把request添加到cfq_queue的红黑树中

#3478 调用cfq_rq_enqueue


cfq_completed_request(struct request_queue *q, struct request *rq)

开始顺序调用cfqd->rq_in_driver--,cfqd->rq_in_flight--, cfqq->dispatched--, cfq_group->dispatched--

如果cfqq是当前cfqd->active_queue

/*
* If this is the active queue, check if it needs to be expired,
* or if we want to idle in case it has no pending requests.
*/

/*
* Idling is not enabled on:
* - expired queues
* - idle-priority queues
* - async queues
* - queues with still some requests queued
* - when there is a close cooperator
*/


cfq_may_queue(struct request_queue *q, int rw):判断请求是否必须要被加入队列


你可能感兴趣的:(算法,linux,struct,Class,insert,merge)