linux io schedule: CFQ

   CFQ,即Completely Fair Queueing绝对公平调度器,力图为竞争块设备使用权的所有进程分配一个等同的时间片,在调度器分配给进程的时间片内,进程可以将其读写请求发送给底层块设备,当进程的时间片消耗完,进程的请求队列将被挂起,等待调度。相对于Noop和Deadline调度器,CFQ要复杂得多,因此可能要分几次才能将其分析完。

 

优先级

      每个进程都会有一个IO优先级,CFQ调度器将会将其作为考虑的因素之一,来确定该进程的请求队列何时可以获取块设备的使用权。IO优先级从高到低可以分为三大类:RT(real time),BE(best try),IDLE(idle),其中RT和BE又可以再划分为8个子优先级。实际,我们已经知道CFQ调度器的公平是针对于进程而言的,而只有同步请求(read或syn write)才是针对进程而存在的,他们会放入进程自身的请求队列,而所有同优先级的异步请求,无论来自于哪个进程,都会被放入公共的队列,异步请求的队列总共有8(RT)+8(BE)+1(IDLE)=17个。

调度器的结构

      CFQ调度器在整个工作过程中所涉及到的结构比较多,我们可以把这些结构分为两类,一类是用来描述调度器本身相关的结构,由于CFQ将进程作为考虑对象,因此另一类结构就是特定于进程的结构,对于这些结构,我们只选择其内部的重要元素进行分析。和调度器相关的数据结构主要有两个,一个是描述调度器的struct cfq_data,一个是描述队列的struct cfq_queue

 

struct cfq_data {
	struct request_queue *queue;

	/*
	 * rr list of queues with requests and the count of them
	 */
	struct cfq_rb_root service_tree;

	/*
	 * Each priority tree is sorted by next_request position.  These
	 * trees are used when determining if two or more queues are
	 * interleaving requests (see cfq_close_cooperator).
	 */
	struct rb_root prio_trees[CFQ_PRIO_LISTS];

	unsigned int busy_queues;

	int rq_in_driver[2];
	int sync_flight;

	/*
	 * queue-depth detection
	 */
	int rq_queued;
	int hw_tag;
	int hw_tag_samples;
	int rq_in_driver_peak;

	/*
	 * idle window management
	 */
	struct timer_list idle_slice_timer;
	struct work_struct unplug_work;

	struct cfq_queue *active_queue;
	struct cfq_<strong style="color: black; background-color: rgb(160, 255, 255);">io</strong>_context *active_cic;

	/*
	 * async queue for each priority case
	 */
	struct cfq_queue *async_cfqq[2][IOPRIO_BE_NR];  
	struct cfq_queue *async_idle_cfqq;
	sector_t last_position;

	/*
	 * tunables, see top of file
	 */
	unsigned int cfq_quantum;
	unsigned int cfq_fifo_expire[2];
	unsigned int cfq_back_penalty;
	unsigned int cfq_back_max;
	unsigned int cfq_slice[2];
	unsigned int cfq_slice_async_rq;
	unsigned int cfq_slice_idle;
	unsigned int cfq_latency;

	struct list_head cic_list;

	/*
	 * Fallback dummy cfqq for extreme OOM conditions
	 */
	struct cfq_queue oom_cfqq;

	unsigned long last_end_sync_rq;
}; 

queue:指向块设备对应的request_queue

service_tree:所有待调度的队列都被添加进该红黑树,等待调度获取时间片

prio_trees[CFQ_PRIO_LISTS]:对应8个优先级的红黑树,所有优先级类别为RT或BE的进程的同步请求队列,都会根据优先级添加至相应的红黑树

 busy_queues:用于计算service_tree中有多少个队列在等待调度

active_queue:指向当前占有块设备的队列

async_cfqq[2][IOPRIO_BE_NR]:对应RT和BE优先级类的16个异步请求队列

async_idle_cfqq:对应优先级类别为IDLE的异步请求队列

cfq_quantum:用于计算在一个队列的时间片内,最多发放多少个请求到底层的块设备

cfq_fifo_expire[2]:同步、异步请求的响应期限时间

cfq_slice[2]:同步、异步请求队列的时间片长度

 

struct cfq_queue {
	/* reference count */
	atomic_t ref;
	/* various state flags, see below */
	unsigned int flags;
	/* parent cfq_data */
	struct cfq_data *cfqd;
	/* service_tree member */
	struct rb_node rb_node;
	/* service_tree key */
	unsigned long rb_key;
	/* prio tree member */
	struct rb_node p_node;	
	/* prio tree root we belong to, if any */
	struct rb_root *p_root;
	/* sorted list of pending requests */
	struct rb_root sort_list; 
	/* if fifo isn't expired, next request to serve */
	struct request *next_rq;
	/* requests queued in sort_list */
	int queued[2];
	/* currently allocated requests */
	int allocated[2];
	/* fifo list of requests in sort_list */
	struct list_head fifo;	   

	unsigned long slice_end;
	long slice_resid;
	unsigned int slice_dispatch;

	/* pending metadata requests */
	int meta_pending;
	/* number of requests that are on the dispatch list or inside driver */
	int dispatched;

	/* <strong style="color: black; background-color: rgb(160, 255, 255);">io</strong> prio of this group */
	unsigned short ioprio, org_ioprio;
	unsigned short ioprio_class, org_ioprio_class;

	unsigned int seek_samples;
	u64 seek_total;
	sector_t seek_mean;
	sector_t last_request_pos;
	unsigned long seeky_start;

	pid_t pid;

	struct cfq_queue *new_cfqq;
};

cfqd:指向队列所属的cfq_data

rb_node:用于将队列插入service_tree

rb_key:红黑树节点关键值,用于确定队列在service_tree中的位置,该值要综合jiffies,进程的IO优先级等因素进行计算

p_node:用于将队列插入对应优先级的prio_tree

p_root:对应的prio_tree树根

sort_list:组织队列内的请求用的红黑树,按请求的起始扇区进行排序

fifo:组织队列内的请求用的链表头,按请求的响应期限排序

slice_end:指明时间片何时消耗完

slice_dispatch:在时间片内发送的请求数

ioprio:进程的当前IO优先级

 

相对于进程的结构有struct io_context和struct cfq_io_context。io_context的核心结构是一个基数树,里面组织了进程所访问的所有块设备所对应的cfq_io_context。cfq_io_context中的核心结构是两个队列,也就是进程在一个CFQ调度器所关系到的队列,一个是同步的,一个是异步的,下面是我根据自己的理解画的一张关系图:

linux io schedule: CFQ_第1张图片

 

这一节暂时就分析道这里了,后面再结合代码来分析CFQ调度器的具体实现

 

前文介绍了CFQ调度器的一些概念和结构之间的关系,这里再结合实际的代码,来分析CFQ的工作流程。CFQ调度器的定义如下:

[cpp] view plain copy print ?
  1. static struct elevator_type iosched_cfq = {
  2. .ops = {
  3. .elevator_merge_fn = cfq_merge,
  4. .elevator_merged_fn = cfq_merged_request,
  5. .elevator_merge_req_fn = cfq_merged_requests,
  6. .elevator_allow_merge_fn = cfq_allow_merge,
  7. .elevator_dispatch_fn = cfq_dispatch_requests,
  8. .elevator_add_req_fn = cfq_insert_request,
  9. .elevator_activate_req_fn = cfq_activate_request,
  10. .elevator_deactivate_req_fn = cfq_deactivate_request,
  11. .elevator_queue_empty_fn = cfq_queue_empty,
  12. .elevator_completed_req_fn = cfq_completed_request,
  13. .elevator_former_req_fn = elv_rb_former_request,
  14. .elevator_latter_req_fn = elv_rb_latter_request,
  15. .elevator_set_req_fn = cfq_set_request,
  16. .elevator_put_req_fn = cfq_put_request,
  17. .elevator_may_queue_fn = cfq_may_queue,
  18. .elevator_init_fn = cfq_init_queue,
  19. .elevator_exit_fn = cfq_exit_queue,
  20. .trim = cfq_free_io_context,
  21. },
  22. .elevator_attrs = cfq_attrs,
  23. .elevator_name = "cfq",
  24. .elevator_owner = THIS_MODULE,
  25. };
[cpp] view plain copy print ?
  1. static struct elevator_type iosched_cfq = {  
  2.     .ops = {  
  3.         .elevator_merge_fn =        cfq_merge,  
  4.         .elevator_merged_fn =       cfq_merged_request,  
  5.         .elevator_merge_req_fn =    cfq_merged_requests,  
  6.         .elevator_allow_merge_fn =  cfq_allow_merge,  
  7.         .elevator_dispatch_fn =     cfq_dispatch_requests,  
  8.         .elevator_add_req_fn =      cfq_insert_request,  
  9.         .elevator_activate_req_fn = cfq_activate_request,  
  10.         .elevator_deactivate_req_fn =   cfq_deactivate_request,  
  11.         .elevator_queue_empty_fn =  cfq_queue_empty,  
  12.         .elevator_completed_req_fn =    cfq_completed_request,  
  13.         .elevator_former_req_fn =   elv_rb_former_request,  
  14.         .elevator_latter_req_fn =   elv_rb_latter_request,  
  15.         .elevator_set_req_fn =      cfq_set_request,  
  16.         .elevator_put_req_fn =      cfq_put_request,  
  17.         .elevator_may_queue_fn =    cfq_may_queue,  
  18.         .elevator_init_fn =     cfq_init_queue,  
  19.         .elevator_exit_fn =     cfq_exit_queue,  
  20.         .trim =             cfq_free_io_context,  
  21.     },  
  22.     .elevator_attrs =   cfq_attrs,  
  23.     .elevator_name =    "cfq",  
  24.     .elevator_owner =   THIS_MODULE,  
  25. };  

 

可以看到CFQ调度器涉及到的操作函数还是比较多的,这里我只打算选一些和提交bio以及request相关的函数进行分析。在提交bio的时候,如果在通用层寻找可以合并bio的途径失败,要通过cfq_merge()来判断是否能够将bio插入到某个request的bio链表首部

[cpp] view plain copy print ?
  1. static struct request *
  2. cfq_find_rq_fmerge(struct cfq_data *cfqd, struct bio *bio)
  3. {
  4. struct task_struct *tsk = current;
  5. struct cfq_io_context *cic;
  6. struct cfq_queue *cfqq;
  7. //在进程的io_context中,找到进程特定于块设备的cfq_io_context
  8. cic = cfq_cic_lookup(cfqd, tsk->io_context);
  9. if (!cic)
  10. return NULL;
  11. //根据同步还是异步,确定cfq_queue
  12. cfqq = cic_to_cfqq(cic, cfq_bio_sync(bio));
  13. if (cfqq) {
  14. sector_t sector = bio->bi_sector + bio_sectors(bio);//得到末尾扇区号
  15. //从cfq_queue的红黑树中查找对应的节点
  16. return elv_rb_find(&cfqq->sort_list, sector);
  17. }
  18. return NULL;
  19. }
[cpp] view plain copy print ?
  1. static struct request *  
  2. cfq_find_rq_fmerge(struct cfq_data *cfqd, struct bio *bio)  
  3. {  
  4.     struct task_struct *tsk = current;  
  5.     struct cfq_io_context *cic;  
  6.     struct cfq_queue *cfqq;  
  7.   
  8.     //在进程的io_context中,找到进程特定于块设备的cfq_io_context  
  9.     cic = cfq_cic_lookup(cfqd, tsk->io_context);  
  10.     if (!cic)  
  11.         return NULL;  
  12.   
  13.     //根据同步还是异步,确定cfq_queue  
  14.     cfqq = cic_to_cfqq(cic, cfq_bio_sync(bio));  
  15.     if (cfqq) {  
  16.         sector_t sector = bio->bi_sector + bio_sectors(bio);//得到末尾扇区号  
  17.   
  18.         //从cfq_queue的红黑树中查找对应的节点  
  19.         return elv_rb_find(&cfqq->sort_list, sector);  
  20.     }  
  21.   
  22.     return NULL;  
  23. }  

 

cfq_find_rq_fmerge()进行实际的搜索工作,要确定bio的归属request,必须先确定进程的通信对象是谁(因为一个进程有可能和多个块设备通信),也就是要找到进程对应的cfq_io_context结构,其中包含了进程的同步请求队列和异步请求队列的地址,只要找到了相应的cfq_io_context,就可以通过bio的同异步性确定对应的cfq_queue了,最后再判断对应的cfq_queue中是否存在可以容纳bio的request。推导cfq_io_context的关键在于以块设备CFQ调度器的描述结构cfq_data的地址为关键值,在进程的io_context的基数树中进行搜索

[html] view plain copy print ?
  1. <SPAN style="FONT-SIZE: 12px">static struct request *
  2. cfq_find_rq_fmerge(struct cfq_data *cfqd, struct bio *bio)
  3. {
  4. struct task_struct *tsk = current;
  5. struct cfq_io_context *cic;
  6. struct cfq_queue *cfqq;
  7. //在进程的io_context中,找到进程特定于块设备的cfq_io_context
  8. cic = cfq_cic_lookup(cfqd, tsk->io_context);
  9. if (!cic)
  10. return NULL;
  11. //根据同步还是异步,确定cfq_queue
  12. cfqq = cic_to_cfqq(cic, cfq_bio_sync(bio));
  13. if (cfqq) {
  14. sector_t sector = bio->bi_sector + bio_sectors(bio);//得到末尾扇区号
  15. //从cfq_queue的红黑树中查找对应的节点
  16. return elv_rb_find(&cfqq->sort_list, sector);
  17. }
  18. return NULL;
  19. }
  20. </SPAN>
[html] view plain copy print ?
  1. <span style="font-size: 12px;">static struct request *  
  2. cfq_find_rq_fmerge(struct cfq_data *cfqd, struct bio *bio)  
  3. {  
  4.     struct task_struct *tsk = current;  
  5.     struct cfq_io_context *cic;  
  6.     struct cfq_queue *cfqq;  
  7.   
  8.     //在进程的io_context中,找到进程特定于块设备的cfq_io_context  
  9.     cic = cfq_cic_lookup(cfqd, tsk->io_context);  
  10.     if (!cic)  
  11.         return NULL;  
  12.   
  13.     //根据同步还是异步,确定cfq_queue  
  14.     cfqq = cic_to_cfqq(cic, cfq_bio_sync(bio));  
  15.     if (cfqq) {  
  16.         sector_t sector = bio->bi_sector + bio_sectors(bio);//得到末尾扇区号  
  17.   
  18.         //从cfq_queue的红黑树中查找对应的节点  
  19.         return elv_rb_find(&cfqq->sort_list, sector);  
  20.     }  
  21.   
  22.     return NULL;  
  23. }  
  24. </span>  

 


通过基数树寻找对应设备的cfq_io_context:

[cpp] view plain copy print ?
  1. <SPAN style="FONT-SIZE: 12px">static struct cfq_io_context *
  2. cfq_cic_lookup(struct cfq_data *cfqd, struct io_context *ioc)
  3. {
  4. struct cfq_io_context *cic;
  5. unsigned long flags;
  6. void *k;
  7. if (unlikely(!ioc))
  8. return NULL;
  9. rcu_read_lock();
  10. /*
  11. * we maintain a last-hit cache, to avoid browsing over the tree
  12. */
  13. //由于进程很有可能连续访问同一块设备,因此先将cic中的关键值直接与cfqd比较
  14. cic = rcu_dereference(ioc->ioc_data);
  15. if (cic && cic->key == cfqd) {
  16. rcu_read_unlock();
  17. return cic;
  18. }
  19. do {//在进程io_context的基数树中寻找对应访问的块设备的cfq_data结构
  20. cic = radix_tree_lookup(&ioc->radix_root, (unsigned long) cfqd);
  21. rcu_read_unlock();
  22. if (!cic)
  23. break;
  24. /* ->key must be copied to avoid race with cfq_exit_queue() */
  25. k = cic->key;
  26. if (unlikely(!k)) {
  27. cfq_drop_dead_cic(cfqd, ioc, cic);
  28. rcu_read_lock();
  29. continue;
  30. }
  31. //保存cic到ioc->ioc_data
  32. spin_lock_irqsave(&ioc->lock, flags);
  33. rcu_assign_pointer(ioc->ioc_data, cic);
  34. spin_unlock_irqrestore(&ioc->lock, flags);
  35. break;
  36. } while (1);
  37. return cic;
  38. }</SPAN>
[cpp] view plain copy print ?
  1. <span style="font-size: 12px;">static struct cfq_io_context *  
  2. cfq_cic_lookup(struct cfq_data *cfqd, struct io_context *ioc)  
  3. {  
  4.     struct cfq_io_context *cic;  
  5.     unsigned long flags;  
  6.     void *k;  
  7.   
  8.     if (unlikely(!ioc))  
  9.         return NULL;  
  10.   
  11.     rcu_read_lock();  
  12.   
  13.     /* 
  14.      * we maintain a last-hit cache, to avoid browsing over the tree 
  15.      */  
  16.   
  17.     //由于进程很有可能连续访问同一块设备,因此先将cic中的关键值直接与cfqd比较  
  18.     cic = rcu_dereference(ioc->ioc_data);  
  19.     if (cic && cic->key == cfqd) {  
  20.         rcu_read_unlock();  
  21.         return cic;  
  22.     }  
  23.   
  24.     do {//在进程io_context的基数树中寻找对应访问的块设备的cfq_data结构  
  25.         cic = radix_tree_lookup(&ioc->radix_root, (unsigned long) cfqd);  
  26.         rcu_read_unlock();  
  27.         if (!cic)  
  28.             break;  
  29.         /* ->key must be copied to avoid race with cfq_exit_queue() */  
  30.         k = cic->key;  
  31.         if (unlikely(!k)) {  
  32.             cfq_drop_dead_cic(cfqd, ioc, cic);  
  33.             rcu_read_lock();  
  34.             continue;  
  35.         }  
  36.   
  37.         //保存cic到ioc->ioc_data  
  38.         spin_lock_irqsave(&ioc->lock, flags);  
  39.         rcu_assign_pointer(ioc->ioc_data, cic);  
  40.         spin_unlock_irqrestore(&ioc->lock, flags);  
  41.         break;  
  42.     } while (1);  
  43.   
  44.     return cic;  
  45. }</span>  

 

在通用层__make_request()函数中,如果bio找不到接收对象,那么就要重新创建一个request来接收它。分配一个request必须进行初始化,需要调用cfq_set_request()函数。和前面的过程比较类似的是,request要先找到接收它的cfq_queue,然后这里出现了一个问题,如果这个request是进程的第一个同步请求或者异步请求,那么就要分配新的cfq_queue来接收request。同步请求和异步请求的处理情况有点区别,因为进程独自拥有自己的同步请求队列,而进程的异步请求都是共享cfq_data中的异步请求队列的,所以只有当request是同步请求时才需要进行cfq_queue的分配

[cpp] view plain copy print ?
  1. static int
  2. cfq_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask)
  3. {
  4. struct cfq_data *cfqd = q->elevator->elevator_data;
  5. struct cfq_io_context *cic;
  6. const int rw = rq_data_dir(rq);
  7. const bool is_sync = rq_is_sync(rq);
  8. struct cfq_queue *cfqq;
  9. unsigned long flags;
  10. might_sleep_if(gfp_mask & __GFP_WAIT);
  11. //获取进程特定于块设备的cfq_io_context结构
  12. cic = cfq_get_io_context(cfqd, gfp_mask);
  13. spin_lock_irqsave(q->queue_lock, flags);
  14. if (!cic)
  15. goto queue_fail;
  16. new_queue:
  17. cfqq = cic_to_cfqq(cic, is_sync);//根据同异步情况,获取进程中对应的cfq_queue
  18. if (!cfqq || cfqq == &cfqd->oom_cfqq) {//如果还没有相应的cfqq则进行分配
  19. cfqq = cfq_get_queue(cfqd, is_sync, cic->ioc, gfp_mask);//分配cfq_queue
  20. cic_set_cfqq(cic, cfqq, is_sync);//设置cic->cfqq[is_sync] = cfqq
  21. } else {
  22. /*
  23. * If the queue was seeky for too long, break it apart.
  24. */
  25. if (cfq_cfqq_coop(cfqq) && should_split_cfqq(cfqq)) {
  26. cfq_log_cfqq(cfqd, cfqq, "breaking apart cfqq");
  27. cfqq = split_cfqq(cic, cfqq);
  28. if (!cfqq)
  29. goto new_queue;
  30. }
  31. /*
  32. * Check to see if this queue is scheduled to merge with
  33. * another, closely cooperating queue. The merging of
  34. * queues happens here as it must be done in process context.
  35. * The reference on new_cfqq was taken in merge_cfqqs.
  36. */
  37. if (cfqq->new_cfqq)
  38. cfqq = cfq_merge_cfqqs(cfqd, cic, cfqq);
  39. }
  40. cfqq->allocated[rw]++;
  41. atomic_inc(&cfqq->ref);
  42. spin_unlock_irqrestore(q->queue_lock, flags);
  43. rq->elevator_private = cic;//保存cfq_io_context到request
  44. rq->elevator_private2 = cfqq;//保存cfq_queue到request
  45. return 0;
  46. queue_fail:
  47. if (cic)
  48. put_io_context(cic->ioc);
  49. cfq_schedule_dispatch(cfqd);
  50. spin_unlock_irqrestore(q->queue_lock, flags);
  51. cfq_log(cfqd, "set_request fail");
  52. return 1;
  53. }
[cpp] view plain copy print ?
  1. static int  
  2. cfq_set_request(struct request_queue *q, struct request *rq, gfp_t gfp_mask)  
  3. {  
  4.     struct cfq_data *cfqd = q->elevator->elevator_data;  
  5.     struct cfq_io_context *cic;  
  6.     const int rw = rq_data_dir(rq);  
  7.     const bool is_sync = rq_is_sync(rq);  
  8.     struct cfq_queue *cfqq;  
  9.     unsigned long flags;  
  10.   
  11.     might_sleep_if(gfp_mask & __GFP_WAIT);  
  12.   
  13.     //获取进程特定于块设备的cfq_io_context结构  
  14.     cic = cfq_get_io_context(cfqd, gfp_mask);  
  15.   
  16.     spin_lock_irqsave(q->queue_lock, flags);  
  17.   
  18.     if (!cic)  
  19.         goto queue_fail;  
  20.   
  21. new_queue:  
  22.     cfqq = cic_to_cfqq(cic, is_sync);//根据同异步情况,获取进程中对应的cfq_queue  
  23.     if (!cfqq || cfqq == &cfqd->oom_cfqq) {//如果还没有相应的cfqq则进行分配  
  24.         cfqq = cfq_get_queue(cfqd, is_sync, cic->ioc, gfp_mask);//分配cfq_queue  
  25.         cic_set_cfqq(cic, cfqq, is_sync);//设置cic->cfqq[is_sync] = cfqq  
  26.     } else {  
  27.         /* 
  28.          * If the queue was seeky for too long, break it apart. 
  29.          */  
  30.         if (cfq_cfqq_coop(cfqq) && should_split_cfqq(cfqq)) {  
  31.             cfq_log_cfqq(cfqd, cfqq, "breaking apart cfqq");  
  32.             cfqq = split_cfqq(cic, cfqq);  
  33.             if (!cfqq)  
  34.                 goto new_queue;  
  35.         }  
  36.   
  37.         /* 
  38.          * Check to see if this queue is scheduled to merge with 
  39.          * another, closely cooperating queue.  The merging of 
  40.          * queues happens here as it must be done in process context. 
  41.          * The reference on new_cfqq was taken in merge_cfqqs. 
  42.          */  
  43.         if (cfqq->new_cfqq)  
  44.             cfqq = cfq_merge_cfqqs(cfqd, cic, cfqq);  
  45.     }  
  46.   
  47.     cfqq->allocated[rw]++;  
  48.     atomic_inc(&cfqq->ref);  
  49.   
  50.     spin_unlock_irqrestore(q->queue_lock, flags);  
  51.   
  52.     rq->elevator_private = cic;//保存cfq_io_context到request  
  53.     rq->elevator_private2 = cfqq;//保存cfq_queue到request  
  54.     return 0;  
  55.   
  56. queue_fail:  
  57.     if (cic)  
  58.         put_io_context(cic->ioc);  
  59.   
  60.     cfq_schedule_dispatch(cfqd);  
  61.     spin_unlock_irqrestore(q->queue_lock, flags);  
  62.     cfq_log(cfqd, "set_request fail");  
  63.     return 1;  
  64. }  

 


当一个request创建初始化,就要将其插入到相应的队列中,cfq_insert_request()函数完成这个功能,和deadline调度器相似,request会被放入两个队列里,一个是按照起始扇区号排列的红黑树(sort_list),一个是按响应期限排列的链表(fifo)

[cpp] view plain copy print ?
  1. static void cfq_insert_request(struct request_queue *q, struct request *rq)
  2. {
  3. struct cfq_data *cfqd = q->elevator->elevator_data;
  4. struct cfq_queue *cfqq = RQ_CFQQ(rq);
  5. cfq_log_cfqq(cfqd, cfqq, "insert_request");
  6. cfq_init_prio_data(cfqq, RQ_CIC(rq)->ioc);//根据ioc设定cfq_queue的优先级类和优先级
  7. cfq_add_rq_rb(rq);//将rq添加到cfq_queue的红黑树
  8. //设置期限值
  9. rq_set_fifo_time(rq, jiffies + cfqd->cfq_fifo_expire[rq_is_sync(rq)]);
  10. list_add_tail(&rq->queuelist, &cfqq->fifo);//添加至fifo
  11. cfq_rq_enqueued(cfqd, cfqq, rq);
  12. }
[cpp] view plain copy print ?
  1. static void cfq_insert_request(struct request_queue *q, struct request *rq)  
  2. {  
  3.     struct cfq_data *cfqd = q->elevator->elevator_data;  
  4.     struct cfq_queue *cfqq = RQ_CFQQ(rq);  
  5.   
  6.     cfq_log_cfqq(cfqd, cfqq, "insert_request");  
  7.     cfq_init_prio_data(cfqq, RQ_CIC(rq)->ioc);//根据ioc设定cfq_queue的优先级类和优先级  
  8.   
  9.     cfq_add_rq_rb(rq);//将rq添加到cfq_queue的红黑树  
  10.   
  11.     //设置期限值  
  12.     rq_set_fifo_time(rq, jiffies + cfqd->cfq_fifo_expire[rq_is_sync(rq)]);  
  13.     list_add_tail(&rq->queuelist, &cfqq->fifo);//添加至fifo  
  14.   
  15.     cfq_rq_enqueued(cfqd, cfqq, rq);  
  16. }  

 


当一个request纳入一个新的bio后,要考虑能否和其他request进行合并,假如是将bio插入到链表的末尾,那么就要在sort_list中获取request后面的一个request(通过函数elv_rb_latter_request()来获取),然后判断前者的结束扇区和前者的起始扇区是否一样,这些工作都是再通用层代码中完成的,当两个request通过了通用层的审核和,并完成合并操作后,将调用cfq_merged_requests进行一些针对队列的额外工作

[cpp] view plain copy print ?
  1. tatic void
  2. cfq_merged_requests(struct request_queue *q, struct request *rq,
  3. struct request *next)
  4. {
  5. /*
  6. * reposition in fifo if next is older than rq
  7. */
  8. if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist) &&
  9. time_before(rq_fifo_time(next), rq_fifo_time(rq))) {
  10. list_move(&rq->queuelist, &next->queuelist);
  11. rq_set_fifo_time(rq, rq_fifo_time(next));
  12. }
  13. cfq_remove_request(next);
  14. }
[cpp] view plain copy print ?
  1. tatic void  
  2. cfq_merged_requests(struct request_queue *q, struct request *rq,  
  3.             struct request *next)  
  4. {  
  5.     /* 
  6.      * reposition in fifo if next is older than rq 
  7.      */  
  8.     if (!list_empty(&rq->queuelist) && !list_empty(&next->queuelist) &&  
  9.         time_before(rq_fifo_time(next), rq_fifo_time(rq))) {  
  10.         list_move(&rq->queuelist, &next->queuelist);  
  11.         rq_set_fifo_time(rq, rq_fifo_time(next));  
  12.     }  
  13.   
  14.     cfq_remove_request(next);  
  15. }  

 

这里主要是考察两个request的期限,因为合并时都是将后者合并入前者,因此当后者的期限时间小于前者时,要进行相应的调整,这一点和deadline调度器是一样的。

最后分析的依然是每个调度器最重要的函数--elevator_dispatch_fn

CFQ调度器在发送request到底层块设备时的流程大致如下:

1.选择一个cfq_queue

2.从cfq_queue中选择一个request进行发送

选择cfq_queue的思路如下:

1.如果当前的cfq_queue的时间片还没用完,则继续当前的cfq_queue

2.如果当前的cfq_queue的时间片消耗完了,则优先在对应的prio_tree中选择一个cfq_queue,该cfq_queue的第一个访问扇区与整个调度器最后处理的扇区之间的差值必须小于一个阈值,如果OK的话就选择这个cfq_queue

3.如果找不到这样的cfq_queue,再从service_tree中调度其他的cfq_queue

[cpp] view plain copy print ?
  1. <SPAN style="FONT-SIZE: 12px">static int cfq_dispatch_requests(struct request_queue *q, int force)
  2. {
  3. struct cfq_data *cfqd = q->elevator->elevator_data;
  4. struct cfq_queue *cfqq;
  5. if (!cfqd->busy_queues)
  6. return 0;
  7. if (unlikely(force))
  8. return cfq_forced_dispatch(cfqd);
  9. //先要选择一个队列
  10. cfqq = cfq_select_queue(cfqd);
  11. if (!cfqq)
  12. return 0;
  13. /*
  14. * Dispatch a request from this cfqq, if it is allowed
  15. */
  16. //从选择的cfq_queue中选择request进行分派
  17. if (!cfq_dispatch_request(cfqd, cfqq))
  18. return 0;
  19. cfqq->slice_dispatch++;
  20. cfq_clear_cfqq_must_dispatch(cfqq);
  21. /*
  22. * expire an async queue immediately if it has used up its slice. idle
  23. * queue always expire after 1 dispatch round.
  24. */
  25. /*如果service_tree中有其他队列等待调度,而刚刚发送请求的
  26. 队列是异步队列并且发送数已经超过了时间片的内最大请求数,
  27. 则将异步队列挂起,如果是idle队列则在发送一个请求后直接将队列挂起
  28. */
  29. if (cfqd->busy_queues > 1 && ((!cfq_cfqq_sync(cfqq) &&
  30. cfqq->slice_dispatch >= cfq_prio_to_maxrq(cfqd, cfqq)) ||
  31. cfq_class_idle(cfqq))) {
  32. cfqq->slice_end = jiffies + 1;
  33. cfq_slice_expired(cfqd, 0);
  34. }
  35. cfq_log_cfqq(cfqd, cfqq, "dispatched a request");
  36. return 1;
  37. }
  38. </SPAN>
[cpp] view plain copy print ?
  1. <span style="font-size: 12px;">static int cfq_dispatch_requests(struct request_queue *q, int force)  
  2. {  
  3.     struct cfq_data *cfqd = q->elevator->elevator_data;  
  4.     struct cfq_queue *cfqq;  
  5.   
  6.     if (!cfqd->busy_queues)  
  7.         return 0;  
  8.   
  9.     if (unlikely(force))  
  10.         return cfq_forced_dispatch(cfqd);  
  11.   
  12.     //先要选择一个队列  
  13.     cfqq = cfq_select_queue(cfqd);  
  14.     if (!cfqq)  
  15.         return 0;  
  16.   
  17.     /* 
  18.      * Dispatch a request from this cfqq, if it is allowed 
  19.      */  
  20.      //从选择的cfq_queue中选择request进行分派  
  21.     if (!cfq_dispatch_request(cfqd, cfqq))  
  22.         return 0;  
  23.   
  24.     cfqq->slice_dispatch++;  
  25.     cfq_clear_cfqq_must_dispatch(cfqq);  
  26.   
  27.     /* 
  28.      * expire an async queue immediately if it has used up its slice. idle 
  29.      * queue always expire after 1 dispatch round. 
  30.      */  
  31.      /*如果service_tree中有其他队列等待调度,而刚刚发送请求的 
  32.        队列是异步队列并且发送数已经超过了时间片的内最大请求数, 
  33.        则将异步队列挂起,如果是idle队列则在发送一个请求后直接将队列挂起 
  34.      */  
  35.     if (cfqd->busy_queues > 1 && ((!cfq_cfqq_sync(cfqq) &&  
  36.         cfqq->slice_dispatch >= cfq_prio_to_maxrq(cfqd, cfqq)) ||  
  37.         cfq_class_idle(cfqq))) {  
  38.         cfqq->slice_end = jiffies + 1;  
  39.         cfq_slice_expired(cfqd, 0);  
  40.     }  
  41.   
  42.     cfq_log_cfqq(cfqd, cfqq, "dispatched a request");  
  43.     return 1;  
  44. }  
  45. </span>  

 


cfq_select_queue()用来选择一个队列,active_queue为当前运行的队列

[cpp] view plain copy print ?
  1. static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd)
  2. {
  3. struct cfq_queue *cfqq, *new_cfqq = NULL;
  4. /*先检查active_queue*/
  5. cfqq = cfqd->active_queue;
  6. if (!cfqq)//没有指定active_queue则跳转到new_queue去选择新的队列
  7. goto new_queue;
  8. /*
  9. * The active queue has run out of time, expire it and select new.
  10. */
  11. //指定了active_queue,这里检查该队列的时间片是否已经过去
  12. if (cfq_slice_used(cfqq) && !cfq_cfqq_must_dispatch(cfqq))
  13. goto expire;
  14. /*
  15. * The active queue has requests and isn't expired, allow it to
  16. * dispatch.
  17. */
  18. /*走到这里表示时间片尚在,这里检查cfq_queue的sort_list是否为空*/
  19. if (!RB_EMPTY_ROOT(&cfqq->sort_list))
  20. goto keep_queue;
  21. /*
  22. * If another queue has a request waiting within our mean seek
  23. * distance, let it run. The expire code will check for close
  24. * cooperators and put the close queue at the front of the service
  25. * tree. If possible, merge the expiring queue with the new cfqq.
  26. */
  27. //走到这里表示active_queue内已经没有请求了,因此要找一个最适合的cfq_queue
  28. new_cfqq = cfq_close_cooperator(cfqd, cfqq);
  29. if (new_cfqq) {
  30. if (!cfqq->new_cfqq)
  31. cfq_setup_merge(cfqq, new_cfqq);
  32. goto expire;
  33. }
  34. /*
  35. * No requests pending. If the active queue still has requests in
  36. * flight or is idling for a new request, allow either of these
  37. * conditions to happen (or time out) before selecting a new queue.
  38. */
  39. if (timer_pending(&cfqd->idle_slice_timer) ||
  40. (cfqq->dispatched && cfq_cfqq_idle_window(cfqq))) {
  41. cfqq = NULL;
  42. goto keep_queue;
  43. }
  44. expire:
  45. cfq_slice_expired(cfqd, 0);//将时间片消耗完的active_queue重新插入service_tree
  46. new_queue:
  47. cfqq = cfq_set_active_queue(cfqd, new_cfqq);//设定new_cfqq为新的active_tree接收时间片
  48. keep_queue:
  49. return cfqq;
  50. }
[cpp] view plain copy print ?
  1. static struct cfq_queue *cfq_select_queue(struct cfq_data *cfqd)  
  2. {  
  3.     struct cfq_queue *cfqq, *new_cfqq = NULL;  
  4.   
  5.     /*先检查active_queue*/  
  6.     cfqq = cfqd->active_queue;  
  7.     if (!cfqq)//没有指定active_queue则跳转到new_queue去选择新的队列  
  8.         goto new_queue;  
  9.   
  10.     /* 
  11.      * The active queue has run out of time, expire it and select new. 
  12.      */  
  13.      //指定了active_queue,这里检查该队列的时间片是否已经过去  
  14.     if (cfq_slice_used(cfqq) && !cfq_cfqq_must_dispatch(cfqq))  
  15.         goto expire;  
  16.   
  17.     /* 
  18.      * The active queue has requests and isn't expired, allow it to 
  19.      * dispatch. 
  20.      */  
  21.      /*走到这里表示时间片尚在,这里检查cfq_queue的sort_list是否为空*/  
  22.     if (!RB_EMPTY_ROOT(&cfqq->sort_list))  
  23.         goto keep_queue;  
  24.   
  25.     /* 
  26.      * If another queue has a request waiting within our mean seek 
  27.      * distance, let it run.  The expire code will check for close 
  28.      * cooperators and put the close queue at the front of the service 
  29.      * tree.  If possible, merge the expiring queue with the new cfqq. 
  30.      */  
  31.      //走到这里表示active_queue内已经没有请求了,因此要找一个最适合的cfq_queue  
  32.     new_cfqq = cfq_close_cooperator(cfqd, cfqq);  
  33.     if (new_cfqq) {  
  34.         if (!cfqq->new_cfqq)  
  35.             cfq_setup_merge(cfqq, new_cfqq);  
  36.         goto expire;  
  37.     }  
  38.   
  39.     /* 
  40.      * No requests pending. If the active queue still has requests in 
  41.      * flight or is idling for a new request, allow either of these 
  42.      * conditions to happen (or time out) before selecting a new queue. 
  43.      */  
  44.     if (timer_pending(&cfqd->idle_slice_timer) ||  
  45.         (cfqq->dispatched && cfq_cfqq_idle_window(cfqq))) {  
  46.         cfqq = NULL;  
  47.         goto keep_queue;  
  48.     }  
  49.   
  50. expire:  
  51.     cfq_slice_expired(cfqd, 0);//将时间片消耗完的active_queue重新插入service_tree  
  52. new_queue:  
  53.     cfqq = cfq_set_active_queue(cfqd, new_cfqq);//设定new_cfqq为新的active_tree接收时间片  
  54. keep_queue:  
  55.     return cfqq;  
  56. }  

 

cfq_close_cooperator查找一个在扇区地址上满足要求的队列,这个操作只对同步请求队列有效

[cpp] view plain copy print ?
  1. static struct cfq_queue *cfq_close_cooperator(struct cfq_data *cfqd,
  2. struct cfq_queue *cur_cfqq)
  3. {
  4. struct cfq_queue *cfqq;
  5. if (!cfq_cfqq_sync(cur_cfqq))
  6. return NULL;
  7. if (CFQQ_SEEKY(cur_cfqq))
  8. return NULL;
  9. /*
  10. * We should notice if some of the queues are cooperating, eg
  11. * working closely on the same area of the disk. In that case,
  12. * we can group them together and don't waste time idling.
  13. */
  14. //根据扇区的差值,寻找一个最接近的节点
  15. cfqq = cfqq_close(cfqd, cur_cfqq);
  16. if (!cfqq)
  17. return NULL;
  18. /*
  19. * It only makes sense to merge sync queues.
  20. */
  21. if (!cfq_cfqq_sync(cfqq))
  22. return NULL;
  23. if (CFQQ_SEEKY(cfqq))
  24. return NULL;
  25. return cfqq;
  26. }
[cpp] view plain copy print ?
  1. static struct cfq_queue *cfq_close_cooperator(struct cfq_data *cfqd,  
  2.                           struct cfq_queue *cur_cfqq)  
  3. {  
  4.     struct cfq_queue *cfqq;  
  5.   
  6.     if (!cfq_cfqq_sync(cur_cfqq))  
  7.         return NULL;  
  8.     if (CFQQ_SEEKY(cur_cfqq))  
  9.         return NULL;  
  10.   
  11.     /* 
  12.      * We should notice if some of the queues are cooperating, eg 
  13.      * working closely on the same area of the disk. In that case, 
  14.      * we can group them together and don't waste time idling. 
  15.      */  
  16.      //根据扇区的差值,寻找一个最接近的节点  
  17.     cfqq = cfqq_close(cfqd, cur_cfqq);  
  18.     if (!cfqq)  
  19.         return NULL;  
  20.   
  21.     /* 
  22.      * It only makes sense to merge sync queues. 
  23.      */  
  24.     if (!cfq_cfqq_sync(cfqq))  
  25.         return NULL;  
  26.     if (CFQQ_SEEKY(cfqq))  
  27.         return NULL;  
  28.   
  29.     return cfqq;  
  30. }  

 

cfqq_close执行实际的工作

[cpp] view plain copy print ?
  1. static struct cfq_queue *cfqq_close(struct cfq_data *cfqd,
  2. struct cfq_queue *cur_cfqq)
  3. {
  4. struct rb_root *root = &cfqd->prio_trees[cur_cfqq->org_ioprio];
  5. struct rb_node *parent, *node;
  6. struct cfq_queue *__cfqq;
  7. sector_t sector = cfqd->last_position;//这里区最后访问的扇区
  8. if (RB_EMPTY_ROOT(root))
  9. return NULL;
  10. /*
  11. * First, if we find a request starting at the end of the last
  12. * request, choose it.
  13. */
  14. //首先试图在同优先级的红黑树中寻找与最近访问的扇区相接的另一个cfq_queue
  15. __cfqq = cfq_prio_tree_lookup(cfqd, root, sector, &parent, NULL);
  16. if (__cfqq)//如果找到了的话则选定该cfq_queue进行调度
  17. return __cfqq;
  18. /*
  19. * If the exact sector wasn't found, the parent of the NULL leaf
  20. * will contain the closest sector.
  21. */
  22. //parent保存了最接近的节点的父节点,如果没找到相邻的cfq_queue,获取父节点对应的cfq_queue
  23. __cfqq = rb_entry(parent, struct cfq_queue, p_node);
  24. /*如果__cfqq的下一个派送请求(next_rq指定)的扇区地址和调度器最后派送的请求的结束扇区之间的间隙满足要求,
  25. 则选定该cfq_queue*/
  26. if (cfq_rq_close(cfqd, cur_cfqq, __cfqq->next_rq))
  27. return __cfqq;
  28. /* 前面都不成功,则根据next_rq和sector的大小关系,
  29. 来选择最接近的比__cfqq的起始扇区大的节点或者小的节点*/
  30. if (blk_rq_pos(__cfqq->next_rq) < sector)
  31. node = rb_next(&__cfqq->p_node);
  32. else
  33. node = rb_prev(&__cfqq->p_node);
  34. if (!node)
  35. return NULL;
  36. //这里再把选择的节点进行一次间隙的判断,如果间隙小于阈值,则选择该节点,否则返回NULL
  37. __cfqq = rb_entry(node, struct cfq_queue, p_node);
  38. if (cfq_rq_close(cfqd, cur_cfqq, __cfqq->next_rq))
  39. return __cfqq;
  40. return NULL;
  41. }
[cpp] view plain copy print ?
  1. static struct cfq_queue *cfqq_close(struct cfq_data *cfqd,  
  2.                     struct cfq_queue *cur_cfqq)  
  3. {  
  4.     struct rb_root *root = &cfqd->prio_trees[cur_cfqq->org_ioprio];  
  5.     struct rb_node *parent, *node;  
  6.     struct cfq_queue *__cfqq;  
  7.     sector_t sector = cfqd->last_position;//这里区最后访问的扇区  
  8.   
  9.     if (RB_EMPTY_ROOT(root))  
  10.         return NULL;  
  11.   
  12.     /* 
  13.      * First, if we find a request starting at the end of the last 
  14.      * request, choose it. 
  15.      */  
  16.      //首先试图在同优先级的红黑树中寻找与最近访问的扇区相接的另一个cfq_queue  
  17.     __cfqq = cfq_prio_tree_lookup(cfqd, root, sector, &parent, NULL);  
  18.     if (__cfqq)//如果找到了的话则选定该cfq_queue进行调度  
  19.         return __cfqq;  
  20.   
  21.     /* 
  22.      * If the exact sector wasn't found, the parent of the NULL leaf 
  23.      * will contain the closest sector. 
  24.      */  
  25.      //parent保存了最接近的节点的父节点,如果没找到相邻的cfq_queue,获取父节点对应的cfq_queue  
  26.     __cfqq = rb_entry(parent, struct cfq_queue, p_node);  
  27.       
  28.     /*如果__cfqq的下一个派送请求(next_rq指定)的扇区地址和调度器最后派送的请求的结束扇区之间的间隙满足要求, 
  29.       则选定该cfq_queue*/  
  30.     if (cfq_rq_close(cfqd, cur_cfqq, __cfqq->next_rq))  
  31.         return __cfqq;  
  32.   
  33.     /* 前面都不成功,则根据next_rq和sector的大小关系, 
  34.        来选择最接近的比__cfqq的起始扇区大的节点或者小的节点*/  
  35.     if (blk_rq_pos(__cfqq->next_rq) < sector)  
  36.         node = rb_next(&__cfqq->p_node);  
  37.     else  
  38.         node = rb_prev(&__cfqq->p_node);  
  39.     if (!node)  
  40.         return NULL;  
  41.   
  42.     //这里再把选择的节点进行一次间隙的判断,如果间隙小于阈值,则选择该节点,否则返回NULL  
  43.     __cfqq = rb_entry(node, struct cfq_queue, p_node);  
  44.     if (cfq_rq_close(cfqd, cur_cfqq, __cfqq->next_rq))  
  45.         return __cfqq;  
  46.   
  47.     return NULL;  
  48. }  

 

如果找到了这样的队列,则在cfq_set_active_queue()函数中设定该队列为运行队列,否则就从service_tree中调度时间点最邻近的队列

[cpp] view plain copy print ?
  1. static struct cfq_queue *cfq_set_active_queue(struct cfq_data *cfqd,
  2. struct cfq_queue *cfqq)
  3. {
  4. if (!cfqq)//假如没指定cfqq,则从service_tree中最前的节点
  5. cfqq = cfq_get_next_queue(cfqd);
  6. __cfq_set_active_queue(cfqd, cfqq);//设定active_queue为cfqq
  7. return cfqq;
  8. }
[cpp] view plain copy print ?
  1. static struct cfq_queue *cfq_set_active_queue(struct cfq_data *cfqd,  
  2.                           struct cfq_queue *cfqq)  
  3. {  
  4.     if (!cfqq)//假如没指定cfqq,则从service_tree中最前的节点  
  5.         cfqq = cfq_get_next_queue(cfqd);  
  6.   
  7.     __cfq_set_active_queue(cfqd, cfqq);//设定active_queue为cfqq  
  8.     return cfqq;  
  9. }  

 

队列选择完毕,下面要通过cfq_dispatch_request()函数在队列中选择合适的request进行发送

[cpp] view plain copy print ?
  1. static bool cfq_dispatch_request(struct cfq_data *cfqd, struct cfq_queue *cfqq)
  2. {
  3. struct request *rq;
  4. BUG_ON(RB_EMPTY_ROOT(&cfqq->sort_list));
  5. //先判断是否确定发放cfqq里的请求
  6. if (!cfq_may_dispatch(cfqd, cfqq))
  7. return false;
  8. /*
  9. * follow expired path, else get first next available
  10. */
  11. /*这里先检查fifo中的请求,如果fifo为空或者fifo中第一个请求的期限还没到,则不会获取到request*/
  12. rq = cfq_check_fifo(cfqq);
  13. if (!rq)//如果fifo中获取rq失败,则考虑备选的next_rq,next_rq总是从扇区的连续性上考虑的
  14. rq = cfqq->next_rq;
  15. /*
  16. * insert request into driver dispatch list
  17. */
  18. cfq_dispatch_insert(cfqd->queue, rq);//将rq插入到设备的请求队列
  19. if (!cfqd->active_cic) {
  20. struct cfq_io_context *cic = RQ_CIC(rq);
  21. atomic_long_inc(&cic->ioc->refcount);
  22. cfqd->active_cic = cic;
  23. }
  24. return true;
  25. }
[cpp] view plain copy print ?
  1. static bool cfq_dispatch_request(struct cfq_data *cfqd, struct cfq_queue *cfqq)  
  2. {  
  3.     struct request *rq;  
  4.   
  5.     BUG_ON(RB_EMPTY_ROOT(&cfqq->sort_list));  
  6.   
  7.     //先判断是否确定发放cfqq里的请求  
  8.     if (!cfq_may_dispatch(cfqd, cfqq))  
  9.         return false;  
  10.   
  11.     /* 
  12.      * follow expired path, else get first next available 
  13.      */  
  14.      /*这里先检查fifo中的请求,如果fifo为空或者fifo中第一个请求的期限还没到,则不会获取到request*/  
  15.     rq = cfq_check_fifo(cfqq);  
  16.     if (!rq)//如果fifo中获取rq失败,则考虑备选的next_rq,next_rq总是从扇区的连续性上考虑的  
  17.         rq = cfqq->next_rq;  
  18.   
  19.     /* 
  20.      * insert request into driver dispatch list 
  21.      */  
  22.     cfq_dispatch_insert(cfqd->queue, rq);//将rq插入到设备的请求队列  
  23.   
  24.     if (!cfqd->active_cic) {  
  25.         struct cfq_io_context *cic = RQ_CIC(rq);  
  26.   
  27.         atomic_long_inc(&cic->ioc->refcount);  
  28.         cfqd->active_cic = cic;  
  29.     }  
  30.   
  31.     return true;  
  32. }  

 

在发送具体的request之前,先要确定是否适合为该队列发放请求,主要是判断队列在时间片内发送的请求数有没有超额

[cpp] view plain copy print ?
  1. static bool cfq_may_dispatch(struct cfq_data *cfqd, struct cfq_queue *cfqq)
  2. {
  3. unsigned int max_dispatch;
  4. /*
  5. * Drain async requests before we start sync IO
  6. */
  7. //如果cfqq可以被idle并且设备有异步请求处理,则不进行新的同步请求的发放
  8. if (cfq_cfqq_idle_window(cfqq) && cfqd->rq_in_driver[BLK_RW_ASYNC])
  9. return false;
  10. /*
  11. * If this is an async queue and we have sync IO in flight, let it wait
  12. */
  13. //如果发放的是异步请求并且request_queue中还有同步请求在等待提交,则不方法
  14. if (cfqd->sync_flight && !cfq_cfqq_sync(cfqq))
  15. return false;
  16. //默认max_dispatch为预定的cfq_quantum,也就是说cfqq在时间片内最多dispatch
  17. //cfq_quantum个请求
  18. max_dispatch = cfqd->cfq_quantum;
  19. //如果cfqq的优先级类为idle,则在时间片内只能dispatch一个请求
  20. if (cfq_class_idle(cfqq))
  21. max_dispatch = 1;
  22. /*
  23. * Does this cfqq already have too much IO in flight?
  24. */
  25. //如果队列发放下去的请求数超额
  26. if (cfqq->dispatched >= max_dispatch) {
  27. /*
  28. * idle queue must always only have a single IO in flight
  29. */
  30. if (cfq_class_idle(cfqq))//如果是idle优先级,则不能进行发放
  31. return false;
  32. /*
  33. * We have other queues, don't allow more IO from this one
  34. */
  35. if (cfqd->busy_queues > 1)//如果有其他的队列在等待发放请求,则不进行发放
  36. return false;
  37. /*
  38. * Sole queue user, allow bigger slice
  39. */
  40. //如果service_tree中只有该队列,且优先级高于idle,则扩宽max_dispatch的限制
  41. max_dispatch *= 4;
  42. }
  43. /*
  44. * Async queues must wait a bit before being allowed dispatch.
  45. * We also ramp up the dispatch depth gradually for async IO,
  46. * based on the last sync IO we serviced
  47. */
  48. /*下面根据最后一次发送的同步请求和现在的时间间隔以及同步请求时间片的值,计算出
  49. depth,根据depth重置max_dispatch,这里的意图应该是在从同步请求的发送转为
  50. 异步请求的发送时,延迟一下异步请求的发送*/
  51. if (!cfq_cfqq_sync(cfqq) && cfqd->cfq_latency) {
  52. unsigned long last_sync = jiffies - cfqd->last_end_sync_rq;
  53. unsigned int depth;
  54. depth = last_sync / cfqd->cfq_slice[1];
  55. if (!depth && !cfqq->dispatched)
  56. depth = 1;
  57. if (depth < max_dispatch)
  58. max_dispatch = depth;
  59. }
  60. /*
  61. * If we're below the current max, allow a dispatch
  62. */
  63. return cfqq->dispatched < max_dispatch;
  64. }
[cpp] view plain copy print ?
  1. static bool cfq_may_dispatch(struct cfq_data *cfqd, struct cfq_queue *cfqq)  
  2. {  
  3.     unsigned int max_dispatch;  
  4.   
  5.     /* 
  6.      * Drain async requests before we start sync IO 
  7.      */  
  8.      //如果cfqq可以被idle并且设备有异步请求处理,则不进行新的同步请求的发放  
  9.     if (cfq_cfqq_idle_window(cfqq) && cfqd->rq_in_driver[BLK_RW_ASYNC])  
  10.         return false;  
  11.   
  12.     /* 
  13.      * If this is an async queue and we have sync IO in flight, let it wait 
  14.      */  
  15.      //如果发放的是异步请求并且request_queue中还有同步请求在等待提交,则不方法  
  16.     if (cfqd->sync_flight && !cfq_cfqq_sync(cfqq))  
  17.         return false;  
  18.     //默认max_dispatch为预定的cfq_quantum,也就是说cfqq在时间片内最多dispatch  
  19.     //cfq_quantum个请求  
  20.     max_dispatch = cfqd->cfq_quantum;  
  21.   
  22.     //如果cfqq的优先级类为idle,则在时间片内只能dispatch一个请求  
  23.     if (cfq_class_idle(cfqq))  
  24.         max_dispatch = 1;  
  25.   
  26.     /* 
  27.      * Does this cfqq already have too much IO in flight? 
  28.      */  
  29.      //如果队列发放下去的请求数超额  
  30.     if (cfqq->dispatched >= max_dispatch) {  
  31.         /* 
  32.          * idle queue must always only have a single IO in flight 
  33.          */  
  34.         if (cfq_class_idle(cfqq))//如果是idle优先级,则不能进行发放  
  35.             return false;  
  36.   
  37.         /* 
  38.          * We have other queues, don't allow more IO from this one 
  39.          */  
  40.         if (cfqd->busy_queues > 1)//如果有其他的队列在等待发放请求,则不进行发放  
  41.             return false;  
  42.   
  43.         /* 
  44.          * Sole queue user, allow bigger slice 
  45.          */  
  46.          //如果service_tree中只有该队列,且优先级高于idle,则扩宽max_dispatch的限制  
  47.         max_dispatch *= 4;  
  48.     }  
  49.   
  50.     /* 
  51.      * Async queues must wait a bit before being allowed dispatch. 
  52.      * We also ramp up the dispatch depth gradually for async IO, 
  53.      * based on the last sync IO we serviced 
  54.      */  
  55.      /*下面根据最后一次发送的同步请求和现在的时间间隔以及同步请求时间片的值,计算出 
  56.        depth,根据depth重置max_dispatch,这里的意图应该是在从同步请求的发送转为 
  57.        异步请求的发送时,延迟一下异步请求的发送*/  
  58.     if (!cfq_cfqq_sync(cfqq) && cfqd->cfq_latency) {  
  59.         unsigned long last_sync = jiffies - cfqd->last_end_sync_rq;  
  60.         unsigned int depth;  
  61.   
  62.         depth = last_sync / cfqd->cfq_slice[1];  
  63.         if (!depth && !cfqq->dispatched)  
  64.             depth = 1;  
  65.         if (depth < max_dispatch)  
  66.             max_dispatch = depth;  
  67.     }  
  68.   
  69.     /* 
  70.      * If we're below the current max, allow a dispatch 
  71.      */  
  72.     return cfqq->dispatched < max_dispatch;  
  73. }  

 


通过检查后,则要正式开始选择request,选择的原则是:如果fifo中的第一个请求期限已到,则选择该请求,否则选择next_rq指针指定的请求,该指针指向的请求总是从物理扇区的连续性上考虑的。最后调用cfq_dispatch_insert()函数将request插入到request_queue中去,等待块设备的响应

[cpp] view plain copy print ?
  1. static void cfq_dispatch_insert(struct request_queue *q, struct request *rq)
  2. {
  3. struct cfq_data *cfqd = q->elevator->elevator_data;
  4. struct cfq_queue *cfqq = RQ_CFQQ(rq);
  5. cfq_log_cfqq(cfqd, cfqq, "dispatch_insert");
  6. cfqq->next_rq = cfq_find_next_rq(cfqd, cfqq, rq);//选择一个扇区间隙最近的请求赋给next_rq作为下一个请求的备选
  7. cfq_remove_request(rq);//将rq从sort_list和fifo中移除
  8. cfqq->dispatched++;
  9. elv_dispatch_sort(q, rq);//将rq分派到request queue的请求队列等候响应
  10. if (cfq_cfqq_sync(cfqq))
  11. cfqd->sync_flight++;
  12. }

 

你可能感兴趣的:(linux io schedule: CFQ)