Linux I/O Block--递交I/O请求

本文详细解析了Linux内核中I/O请求从bio结构到request结构的转换过程,包括bio结构定义、请求队列的使用及请求合并策略等关键技术点。
   在通用块层中,bio用来描述单一的I/O请求,它记录了一次I/O操作所必需的相关信息,如用于I/O操作的数据缓存位置,I/O操作的块设备起始扇区,是读操作还是写操作等等。struct bio的定义如下
  1. struct bio {  
  2.     sector_t        bi_sector;  /* device address in 512 byte 
  3.                            sectors */  
  4.     struct bio      *bi_next;   /* request queue link */  
  5.     struct block_device *bi_bdev;  
  6.     unsigned long       bi_flags;   /* status, command, etc */  
  7.     unsigned long       bi_rw;      /* bottom bits READ/WRITE, 
  8.                          * top bits priority 
  9.                          */  
  10.   
  11.     unsigned short      bi_vcnt;    /* how many bio_vec's */  
  12.     unsigned short      bi_idx;     /* current index into bvl_vec */  
  13.   
  14.     /* Number of segments in this BIO after 
  15.      * physical address coalescing is performed. 
  16.      */  
  17.     unsigned int        bi_phys_segments;  
  18.   
  19.     unsigned int        bi_size;    /* residual I/O count */  
  20.   
  21.     /* 
  22.      * To keep track of the max segment size, we account for the 
  23.      * sizes of the first and last mergeable segments in this bio. 
  24.      */  
  25.     unsigned int        bi_seg_front_size;  
  26.     unsigned int        bi_seg_back_size;  
  27.   
  28.     unsigned int        bi_max_vecs;    /* max bvl_vecs we can hold */  
  29.   
  30.     unsigned int        bi_comp_cpu;    /* completion CPU */  
  31.   
  32.     atomic_t        bi_cnt;     /* pin count */  
  33.   
  34.     struct bio_vec      *bi_io_vec; /* the actual vec list */  
  35.   
  36.     bio_end_io_t        *bi_end_io;  
  37.   
  38.     void            *bi_private;  
  39. #if defined(CONFIG_BLK_DEV_INTEGRITY)   
  40.     struct bio_integrity_payload *bi_integrity;  /* data integrity */  
  41. #endif   
  42.   
  43.     bio_destructor_t    *bi_destructor; /* destructor */  
  44.   
  45.     /* 
  46.      * We can inline a number of vecs at the end of the bio, to avoid 
  47.      * double allocations for a small number of bio_vecs. This member 
  48.      * MUST obviously be kept at the very end of the bio. 
  49.      */  
  50.     struct bio_vec      bi_inline_vecs[0];  
  51. };  


bi_sector:该I/O操作的起始扇区号

bi_rw:指明了读写方向

bi_vcnt:该I/O操作中涉及到了多少个缓存向量,每个缓存向量由[page,offset,len]来描述

bi_idx:指示当前的缓存向量

bi_io_vec:缓存向量数组

缓存向量的定义:

  1. struct bio_vec {  
  2.     struct page *bv_page;  
  3.     unsigned int    bv_len;  
  4.     unsigned int    bv_offset;  
  5. };  


 

struct request用于描述提交给块设备的I/O请求,bio会动态地添加进request,因此一个request往往会包含若干相邻的bio。

  1. struct request {  
  2.     struct list_head queuelist;  
  3.     struct call_single_data csd;  
  4.     int cpu;  
  5.   
  6.     struct request_queue *q;  
  7.   
  8.     unsigned int cmd_flags;  
  9.     enum rq_cmd_type_bits cmd_type;  
  10.     unsigned long atomic_flags;  
  11.   
  12.     /* the following two fields are internal, NEVER access directly */  
  13.     sector_t __sector;      /* sector cursor */  
  14.     unsigned int __data_len;    /* total data len */  
  15.   
  16.     struct bio *bio;  
  17.     struct bio *biotail;  
  18.   
  19.     struct hlist_node hash; /* merge hash */  
  20.     /* 
  21.      * The rb_node is only used inside the io scheduler, requests 
  22.      * are pruned when moved to the dispatch queue. So let the 
  23.      * completion_data share space with the rb_node. 
  24.      */  
  25.     union {  
  26.         struct rb_node rb_node; /* sort/lookup */  
  27.         void *completion_data;  
  28.     };  
  29.   
  30.     /* 
  31.      * two pointers are available for the IO schedulers, if they need 
  32.      * more they have to dynamically allocate it. 
  33.      */  
  34.     void *elevator_private;  
  35.     void *elevator_private2;  
  36.   
  37.     struct gendisk *rq_disk;  
  38.     unsigned long start_time;  
  39.   
  40.     /* Number of scatter-gather DMA addr+len pairs after 
  41.      * physical address coalescing is performed. 
  42.      */  
  43.     unsigned short nr_phys_segments;  
  44.   
  45.     unsigned short ioprio;  
  46.   
  47.     void *special;      /* opaque pointer available for LLD use */  
  48.     char *buffer;       /* kaddr of the current segment if available */  
  49.   
  50.     int tag;  
  51.     int errors;  
  52.   
  53.     int ref_count;  
  54.   
  55.     /* 
  56.      * when request is used as a packet command carrier 
  57.      */  
  58.     unsigned short cmd_len;  
  59.     unsigned char __cmd[BLK_MAX_CDB];  
  60.     unsigned char *cmd;  
  61.   
  62.     unsigned int extra_len; /* length of alignment and padding */  
  63.     unsigned int sense_len;  
  64.     unsigned int resid_len; /* residual count */  
  65.     void *sense;  
  66.   
  67.     unsigned long deadline;  
  68.     struct list_head timeout_list;  
  69.     unsigned int timeout;  
  70.     int retries;  
  71.   
  72.     /* 
  73.      * completion callback. 
  74.      */  
  75.     rq_end_io_fn *end_io;  
  76.     void *end_io_data;  
  77.   
  78.     /* for bidi */  
  79.     struct request *next_rq;  
  80. };  

queuelist:用于将request链入请求队列的链表元素

q:指向所属的请求队列

__sector:下一个要传输的bio的起始扇区号

__data_len:request要传输的数据字节数

bio,biotail:用于维护request中的bio链表

 

在之前介绍的gendisk结构中,可以看到每个块设备(或分区)都对应了一个request_queue的结构,该结构用来容纳request,并且包含了相应的递交request以及I/O调度的方法

递交一个bio的主要工作是从generic_make_request()函数开始的,我们以此为入口来分析一个bio的递交过程。在每个进程的task_struct中,都包含有两个变量----struct bio *bio_list, **bio_tail,generic_make_request()的主要工作就是用这两个变量来维护当前待添加的bio链表,实际的提交操作会由generic_make_request()调用__generic_make_request()函数完成。而在__generic_make_request()中,会调用到queue_list中定义的make_request_fn函数,也就是特定于设备的提交请求函数来完成后续的工作。在这里便会有一些问题,大部分设备的make_request_fn都可以直接定义为内核实现的__make_request函数,而一些设备需要使用自己的make_request_fn,而自行实现的make_request_fn有可能会递归调用gerneric_make_request(),由于内核的堆栈十分有限,因此在generic_make_request()的实现中,玩了一些小把戏,使得递归的深度不会超过一层。我们注意到bio_tail是一个二级指针,这个值最初是NULL,当有bio添加进来,bio_tail将会指向bio->bi_next(如果bio全都递交上去了,则bio_tail将会指向bio_list),也就是说除了第一次调用外,其他每次递归调用generic_make_request()函数都会出现bio_tail不为NULL的情形,因此当bio_tail不为NULL时,则只将bio添加到由bio_list和bio_tail维护的链表中,然后直接返回,而不调用__generic_make_request(),这样便防止了多重递归的产生

  1. void generic_make_request(struct bio *bio)  
  2. {  
  3.     if (current->bio_tail) {//current->bio_tail不为空则表明有bio正在提交,也就是说是处于递归调用   
  4.         /* make_request is active */  
  5.         bio->bi_next = NULL;  
  6.         /*这里current->tail有两种情况,当current的bio链表为空时,bio_tail指向的是bio_list 
  7.         当current的bio链表不为空时,bio_tail指向的是最后一个bio的bi_next指针,因此 
  8.         这句的实际作用就是将bio添加到了current的bio链表的尾部*/  
  9.         *(current->bio_tail) = bio;  
  10.         current->bio_tail = &bio->bi_next;  
  11.         /*这里直接返回,遍历并且提交bio的工作永远都是交给最先调用的generic_make_request来处理的,避免了多重递归*/  
  12.         return;  
  13.     }  
  14.     /* following loop may be a bit non-obvious, and so deserves some 
  15.      * explanation. 
  16.      * Before entering the loop, bio->bi_next is NULL (as all callers 
  17.      * ensure that) so we have a list with a single bio. 
  18.      * We pretend that we have just taken it off a longer list, so 
  19.      * we assign bio_list to the next (which is NULL) and bio_tail 
  20.      * to &bio_list, thus initialising the bio_list of new bios to be 
  21.      * added.  __generic_make_request may indeed add some more bios 
  22.      * through a recursive call to generic_make_request.  If it 
  23.      * did, we find a non-NULL value in bio_list and re-enter the loop 
  24.      * from the top.  In this case we really did just take the bio 
  25.      * of the top of the list (no pretending) and so fixup bio_list and 
  26.      * bio_tail or bi_next, and call into __generic_make_request again. 
  27.      * 
  28.      * The loop was structured like this to make only one call to 
  29.      * __generic_make_request (which is important as it is large and 
  30.      * inlined) and to keep the structure simple. 
  31.      */  
  32.     BUG_ON(bio->bi_next);  
  33.     do {  
  34.         current->bio_list = bio->bi_next;//这里取current的待提交bio链表的下一个bio   
  35.         if (bio->bi_next == NULL)//bi_next为空,也就是说待提交链表已经空了,只剩下最后一个bio了   
  36.             current->bio_tail = ¤t->bio_list;//bio_tail指向bio_list   
  37.         else  
  38.             bio->bi_next = NULL;//否则将bio提取出来   
  39.         __generic_make_request(bio);//提交bio   
  40.         bio = current->bio_list;//取新的待提交bio   
  41.     } while (bio);  
  42.     current->bio_tail = NULL; /* deactivate */  
  43. }  



__generic_make_request()首先由bio对应的block_device获取等待队列q,然后要检查对应的设备是不是分区,如果是分区的话要将扇区地址进行重新计算,最后调用make_request_fn完成bio的递交

  1. static inline void __generic_make_request(struct bio *bio)  
  2. {  
  3.     struct request_queue *q;  
  4.     sector_t old_sector;  
  5.     int ret, nr_sectors = bio_sectors(bio);//提取bio的大小,以扇区为单位   
  6.     dev_t old_dev;  
  7.     int err = -EIO;  
  8.   
  9.     might_sleep();  
  10.   
  11.     //这里检查bio的传输起始扇区是否超过设备的最大扇区,并且两者之间的差不能小于nr_sector   
  12.     if (bio_check_eod(bio, nr_sectors))  
  13.         goto end_io;  
  14.   
  15.     /* 
  16.      * Resolve the mapping until finished. (drivers are 
  17.      * still free to implement/resolve their own stacking 
  18.      * by explicitly returning 0) 
  19.      * 
  20.      * NOTE: we don't repeat the blk_size check for each new device. 
  21.      * Stacking drivers are expected to know what they are doing. 
  22.      */  
  23.     old_sector = -1;  
  24.     old_dev = 0;  
  25.     do {  
  26.         char b[BDEVNAME_SIZE];  
  27.   
  28.         q = bdev_get_queue(bio->bi_bdev);//获取对应设备的请求队列   
  29.         if (unlikely(!q)) {  
  30.             printk(KERN_ERR  
  31.                    "generic_make_request: Trying to access "  
  32.                 "nonexistent block-device %s (%Lu)\n",  
  33.                 bdevname(bio->bi_bdev, b),  
  34.                 (long long) bio->bi_sector);  
  35.             goto end_io;  
  36.         }  
  37.           
  38.         /*下面做一些必要的检查*/  
  39.         if (unlikely(!bio_rw_flagged(bio, BIO_RW_DISCARD) &&  
  40.                  nr_sectors > queue_max_hw_sectors(q))) {  
  41.             printk(KERN_ERR "bio too big device %s (%u > %u)\n",  
  42.                    bdevname(bio->bi_bdev, b),  
  43.                    bio_sectors(bio),  
  44.                    queue_max_hw_sectors(q));  
  45.             goto end_io;  
  46.         }  
  47.   
  48.         if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)))  
  49.             goto end_io;  
  50.   
  51.         if (should_fail_request(bio))  
  52.             goto end_io;  
  53.   
  54.         /* 
  55.          * If this device has partitions, remap block n 
  56.          * of partition p to block n+start(p) of the disk. 
  57.          */  
  58.          //如果bio指定的是一个分区,则传输点要重新进行计算   
  59.         blk_partition_remap(bio);  
  60.   
  61.         if (bio_integrity_enabled(bio) && bio_integrity_prep(bio))  
  62.             goto end_io;  
  63.   
  64.         if (old_sector != -1)  
  65.             trace_block_remap(q, bio, old_dev, old_sector);  
  66.   
  67.         old_sector = bio->bi_sector;  
  68.         old_dev = bio->bi_bdev->bd_dev;  
  69.   
  70.         if (bio_check_eod(bio, nr_sectors))  
  71.             goto end_io;  
  72.   
  73.         if (bio_rw_flagged(bio, BIO_RW_DISCARD) &&  
  74.             !blk_queue_discard(q)) {  
  75.             err = -EOPNOTSUPP;  
  76.             goto end_io;  
  77.         }  
  78.   
  79.         trace_block_bio_queue(q, bio);  
  80.   
  81.         ret = q->make_request_fn(q, bio);//这里是关键,调用请求队列中的make_request_fn函数处理请求   
  82.     } while (ret);  
  83.   
  84.     return;  
  85.   
  86. end_io:  
  87.     bio_endio(bio, err);  
  88. }  

辅助函数blk_partition_remap():

  1. static inline void blk_partition_remap(struct bio *bio)  
  2. {  
  3.     struct block_device *bdev = bio->bi_bdev;  
  4.   
  5.     /*首先要保证传输的大小不能小于1个扇区并且bdev确实是分区*/  
  6.     if (bio_sectors(bio) && bdev != bdev->bd_contains) {  
  7.         struct hd_struct *p = bdev->bd_part;//获取分区信息   
  8.   
  9.         bio->bi_sector += p->start_sect;//在传输起点的原基础上加上分区的起始扇区号   
  10.         bio->bi_bdev = bdev->bd_contains;//将bio的bdev置为主设备   
  11.   
  12.         trace_block_remap(bdev_get_queue(bio->bi_bdev), bio,  
  13.                     bdev->bd_dev,  
  14.                     bio->bi_sector - p->start_sect);  
  15.     }  
  16. }  

可以看到这里将bio的参考对象设置为了主设备,而不是分区,因此对应的扇区起始号也要计算为扇区的绝对值。

大多数的make_request_fn函数都可以直接定义为__make_request(),我们通过这个函数来分析递交bio的关键操作

  1. static int __make_request(struct request_queue *q, struct bio *bio)  
  2. {  
  3.     struct request *req;  
  4.     int el_ret;  
  5.     unsigned int bytes = bio->bi_size;  
  6.     const unsigned short prio = bio_prio(bio);  
  7.     const bool sync = bio_rw_flagged(bio, BIO_RW_SYNCIO);  
  8.     const bool unplug = bio_rw_flagged(bio, BIO_RW_UNPLUG);  
  9.     const unsigned int ff = bio->bi_rw & REQ_FAILFAST_MASK;  
  10.     int rw_flags;  
  11.   
  12.     /*如果BIO_RW_BARRIER被置位(表示必须得让请求队列中的所有bio传递完毕才处理自己), 
  13.       但是不支持hardbarrier,不能进行bio的提交*/  
  14.     if (bio_rw_flagged(bio, BIO_RW_BARRIER) &&  
  15.         (q->next_ordered == QUEUE_ORDERED_NONE)) {  
  16.         bio_endio(bio, -EOPNOTSUPP);  
  17.         return 0;  
  18.     }  
  19.     /* 
  20.      * low level driver can indicate that it wants pages above a 
  21.      * certain limit bounced to low memory (ie for highmem, or even 
  22.      * ISA dma in theory) 
  23.      */  
  24.     blk_queue_bounce(q, &bio);  
  25.   
  26.     spin_lock_irq(q->queue_lock);  
  27.   
  28.     //如果BIO_RW_BARRIER被置位或者请求队列为空,则情况比较简单,不用进行bio的合并,跳转到get_rq处处理   
  29.     if (unlikely(bio_rw_flagged(bio, BIO_RW_BARRIER)) || elv_queue_empty(q))  
  30.         goto get_rq;  
  31.   
  32.     /**请求队列不为空**/  
  33.       
  34.     /*elv_merge()试图寻找一个已存在的request,将bio并入其中*/  
  35.     el_ret = elv_merge(q, &req, bio);  
  36.       
  37.     switch (el_ret) {  
  38.     case ELEVATOR_BACK_MERGE:  
  39.         BUG_ON(!rq_mergeable(req));  
  40.   
  41.         /*相关检查*/  
  42.         if (!ll_back_merge_fn(q, req, bio))  
  43.             break;  
  44.   
  45.         trace_block_bio_backmerge(q, bio);  
  46.   
  47.         if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff)  
  48.             blk_rq_set_mixed_merge(req);  
  49.   
  50.         /*这里将bio插入到request尾部*/  
  51.         req->biotail->bi_next = bio;  
  52.         req->biotail = bio;  
  53.         req->__data_len += bytes;  
  54.         req->ioprio = ioprio_best(req->ioprio, prio);  
  55.         if (!blk_rq_cpu_valid(req))  
  56.             req->cpu = bio->bi_comp_cpu;  
  57.         drive_stat_acct(req, 0);  
  58.         if (!attempt_back_merge(q, req))  
  59.             elv_merged_request(q, req, el_ret);  
  60.         goto out;  
  61.   
  62.     case ELEVATOR_FRONT_MERGE:  
  63.         BUG_ON(!rq_mergeable(req));  
  64.   
  65.         if (!ll_front_merge_fn(q, req, bio))  
  66.             break;  
  67.   
  68.         trace_block_bio_frontmerge(q, bio);  
  69.   
  70.         if ((req->cmd_flags & REQ_FAILFAST_MASK) != ff) {  
  71.             blk_rq_set_mixed_merge(req);  
  72.             req->cmd_flags &= ~REQ_FAILFAST_MASK;  
  73.             req->cmd_flags |= ff;  
  74.         }  
  75.   
  76.         /*这里将bio插入到request的头部*/  
  77.         bio->bi_next = req->bio;  
  78.         req->bio = bio;  
  79.   
  80.         /* 
  81.          * may not be valid. if the low level driver said 
  82.          * it didn't need a bounce buffer then it better 
  83.          * not touch req->buffer either... 
  84.          */  
  85.         req->buffer = bio_data(bio);  
  86.         req->__sector = bio->bi_sector;  
  87.         req->__data_len += bytes;  
  88.         req->ioprio = ioprio_best(req->ioprio, prio);  
  89.         if (!blk_rq_cpu_valid(req))  
  90.             req->cpu = bio->bi_comp_cpu;  
  91.         drive_stat_acct(req, 0);  
  92.         if (!attempt_front_merge(q, req))  
  93.             elv_merged_request(q, req, el_ret);  
  94.         goto out;  
  95.   
  96.     /* ELV_NO_MERGE: elevator says don't/can't merge. */  
  97.     default:  
  98.         ;  
  99.     }  
  100.   
  101. get_rq:/**下面的代码对应请求队列为空的情况,需要先分配一个request,再将bio插入***/  
  102.     /* 
  103.      * This sync check and mask will be re-done in init_request_from_bio(), 
  104.      * but we need to set it earlier to expose the sync flag to the 
  105.      * rq allocator and io schedulers. 
  106.      */  
  107.     rw_flags = bio_data_dir(bio);//确定读写标识   
  108.     if (sync)  
  109.         rw_flags |= REQ_RW_SYNC;  
  110.   
  111.     /* 
  112.      * Grab a free request. This is might sleep but can not fail. 
  113.      * Returns with the queue unlocked. 
  114.      */  
  115.     req = get_request_wait(q, rw_flags, bio);//分配一个新的request   
  116.   
  117.     /* 
  118.      * After dropping the lock and possibly sleeping here, our request 
  119.      * may now be mergeable after it had proven unmergeable (above). 
  120.      * We don't worry about that case for efficiency. It won't happen 
  121.      * often, and the elevators are able to handle it. 
  122.      */  
  123.      //根据bio初始化新分配的request,并将bio插入到request中   
  124.     init_request_from_bio(req, bio);  
  125.   
  126.     spin_lock_irq(q->queue_lock);  
  127.     if (test_bit(QUEUE_FLAG_SAME_COMP, &q->queue_flags) ||  
  128.         bio_flagged(bio, BIO_CPU_AFFINE))  
  129.         req->cpu = blk_cpu_to_group(smp_processor_id());  
  130.     if (queue_should_plug(q) && elv_queue_empty(q))  
  131.         blk_plug_device(q);  
  132.     add_request(q, req);//将request插入到请求队列   
  133. out:  
  134.     if (unplug || !queue_should_plug(q))  
  135.         __generic_unplug_device(q);  
  136.     spin_unlock_irq(q->queue_lock);  
  137.     return 0;  
  138. }  


elv_merge()是执行合并的关键所在,执行完后会有三种情况:

1.bio添加到了一个request的bio链表尾部

2.bio添加到了一个request的bio链表首部

3.未能找到一个request可以添加,将重新分配一个request

  1. int elv_merge(struct request_queue *q, struct request **req, struct bio *bio)  
  2. {  
  3.     struct elevator_queue *e = q->elevator;  
  4.     struct request *__rq;  
  5.     int ret;  
  6.   
  7.     /* 
  8.      * First try one-hit cache. 
  9.      */  
  10.     //last_merge指向了最近进行合并操作的request,最先试图将bio合并到该request中   
  11.     if (q->last_merge) {  
  12.         ret = elv_try_merge(q->last_merge, bio);  
  13.         if (ret != ELEVATOR_NO_MERGE) {  
  14.             *req = q->last_merge;  
  15.             return ret;  
  16.         }  
  17.     }  
  18.   
  19.     if (blk_queue_nomerges(q))//请求队列不允许合并请求,则返回NO_MERGE   
  20.         return ELEVATOR_NO_MERGE;  
  21.   
  22.     /* 
  23.      * See if our hash lookup can find a potential backmerge. 
  24.      */  
  25.      //根据bio的起始扇区号,通过rq的哈希表寻找一个request,可以将bio合并到request的尾部   
  26.     __rq = elv_rqhash_find(q, bio->bi_sector);  
  27.     if (__rq && elv_rq_merge_ok(__rq, bio)) {  
  28.         *req = __rq;  
  29.         return ELEVATOR_BACK_MERGE;  
  30.     }  
  31.   
  32.     /*如果以上的方法不成功,则调用特定于io调度器的elevator_merge_fn函数寻找一个合适的request*/  
  33.     if (e->ops->elevator_merge_fn)  
  34.         return e->ops->elevator_merge_fn(q, req, bio);  
  35.   
  36.     return ELEVATOR_NO_MERGE;  
  37. }  

elevator_merge_fn是特定于I/O调度器的方式,至此,递交I/O请求的通用层部分也就分析完了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值