天天看点

提交I/O传输请求

1.4.3 提交I/O传输请求

好了,bio这个数据我们建立好了,随后调用generic_make_request 函数。这个函数是通用块层的入口点,该层只有这一个函数处理请求:

3020void generic_make_request(struct bio *bio)

3021{

3022        request_queue_t *q;

3023        sector_t maxsector;

3024        int ret, nr_sectors = bio_sectors(bio);

3025        dev_t old_dev;

3026

3027        might_sleep();

3028       

3029        maxsector = bio->bi_bdev->bd_inode->i_size >> 9;

3030        if (maxsector) {

3031                sector_t sector = bio->bi_sector;

3032

3033                if (maxsector < nr_sectors || maxsector - nr_sectors < sector) {

3034                       

3039                        handle_bad_sector(bio);

3040                        goto end_io;

3041                }

3042        }

3043

3044       

3052        maxsector = -1;

3053        old_dev = 0;

3054        do {

3055                char b[BDEVNAME_SIZE];

3056

3057                q = bdev_get_queue(bio->bi_bdev);

3058                if (!q) {

3059                        printk(KERN_ERR

3060                               "generic_make_request: Trying to access "

3061                                "nonexistent block-device %s (%Lu)/n",

3062                                bdevname(bio->bi_bdev, b),

3063                                (long long) bio->bi_sector);

3064end_io:

3065                        bio_endio(bio, bio->bi_size, -EIO);

3066                        break;

3067                }

3068

3069                if (unlikely(bio_sectors(bio) > q->max_hw_sectors)) {

3070                        printk("bio too big device %s (%u > %u)/n",

3071                                bdevname(bio->bi_bdev, b),

3072                                bio_sectors(bio),

3073                                q->max_hw_sectors);

3074                        goto end_io;

3075                }

3076

3077                if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)))

3078                        goto end_io;

3079

3080               

3084                blk_partition_remap(bio);

3085

3086                if (maxsector != -1)

3087                        blk_add_trace_remap(q, bio, old_dev, bio->bi_sector,

3088                                            maxsector);

3089

3090                blk_add_trace_bio(q, bio, BLK_TA_QUEUE);

3091

3092                maxsector = bio->bi_sector;

3093                old_dev = bio->bi_bdev->bd_dev;

3094

3095                ret = q->make_request_fn(q, bio);

3096        } while (ret);

3097}

首先3033行,检查bio->bi_sector没有超过块设备的扇区数。如果超过,则调用handle_bad_sector(bio)函数将bio->bi_flags设置为BIO_EOF标志,然后打印一条内核错误信息。随后跳到end_io标号处执行3065行的bio_endio()函数,并终止。bio_endio()更新bio描述符中的bi_size和bi_sector值,然后调用bio的bi_end_io方法。

如果没有超过,那么进入一个循环,通过3057行的bdev_get_queue函数获取与块设备相关的请求队列q;其中地址存放在bio的块设备描述符bi_bdev的bd_disk字段所指向的gendisk结构中,而该块设备描述符则由bio->bi_bdev指向:

static inline request_queue_t *bdev_get_queue(struct block_device *bdev)

{

       return bdev->bd_disk->queue;

}

其中地址存放在块设备描述符的bd_disk字段所指向的gendisk结构中,而该块设备描述符则由bio->bi_bdev指向。

接下来3084行调用blk_partition_remap()函数检查块设备是否指的是一个磁盘分区(bio->bi_bdev不等于bio->bi_dev->bd_contains)。如果是,则从bio->bi_bdev获取分区的hd_struct描述符,从而执行下面的子操作:

a) 根据数据传送的方向,更新hd_struct描述符中的read_sectors和reads值或write_sectors和writes值。

b) 调整bio->bi_sector值使得把相对于分区的起始扇区号转变为相对于整有盘的扇区号。

c) 将bio->bi_bdev设置为整个磁盘的块设备描述符(bio->bd_contains)。

从现在开始,通用块层、I/O调度程序以及设备驱动程序将忘记磁盘分区的存在,直接作用于整个磁盘。

最终,3095行调用q->make_request_fn方法进入块设备的I/O调度层,将bio请求插入请求队列q中。