linux/block/blk-core.c定义
void submit_bio(int rw, struct bio *bio)
该函数为通用块层的接口函数。
submit_bio调用generic_make_request(bio);加入到对应的请求队列。
generic_make_request循环针对一个进行的所有bio调用__generic_make_request
__generic_make_request,主要完成相关检查,分区的remap,然后调用对应请求队列的q->make_request_fn(q, bio);
请求队列的make_request_fn(q, bio),通过blk_queue_make_request进行初始化
blk_queue_make_request
void blk_queue_make_request(struct request_queue *q, make_request_fn *mfn)
floppy_queue = blk_init_queue(do_fd_request, &amiflop_lock);
blk_init_queue-》blk_init_queue_node-》blk_init_allocated_queue_node-》blk_queue_make_request(q, blk_queue_bio);
所有普通的块设备驱动,通过blk_init_queue初始化请求队列的设备。
q->make_request_fn(q, bio); 对应的是blk_queue_bio
blk_queue_bio的处理流程:
spin_lock_irq(q->queue_lock); //加锁,由于该锁在中断上下文也是用,因此是关中断的
如果请求队列是空的,则直接插入请求。
if (elv_queue_empty(q))
goto get_rq;
init_request_from_bio(req, bio);
spin_lock_irq(q->queue_lock);
if (test_bit(QUEUE_FLAG_SAME_COMP, &q->queue_flags) ||
bio_flagged(bio, BIO_CPU_AFFINE))
req->cpu = raw_smp_processor_id();
if (queue_should_plug(q) && elv_queue_empty(q))
blk_plug_device(q);
/* insert the request into the elevator */
drive_stat_acct(req, 1);
__elv_add_request(q, req, where, 0);
out:
if (unplug || !queue_should_plug(q))
__generic_unplug_device(q);
out_unlock:
spin_unlock_irq(q->queue_lock);
make_request_fn(q, bio);只是把请求加入到请求队列,并没有实际下发到控制器。
实际下发到控制器是由__generic_unplug_device触发。
__generic_unplug_device
/*
* remove the plug and let it rip..
*/
void __generic_unplug_device(struct request_queue *q)
{
if (unlikely(blk_queue_stopped(q)))
return;
if (!blk_remove_plug(q) && !blk_queue_nonrot(q))
return;
q->request_fn(q);
}
调用请求队列的 q->request_fn(q);函数。
这个函数对应的是驱动程序在初始化的时候传入。
floppy_queue = blk_init_queue(do_fd_request, &floppy_lock);
request_fn的一般处理流程是循环下发所有的请求到控制器。
需要注意的是, 请求函数的启动(常常地)与任何用户空间进程之间是完全异步的. 你不能假设内核运行在发起当前请求的进程上下文. 你不知道由这个请求提供的 I/O 缓冲是否在内核或者用户空间. 因此任何类型的明确存取用户空间的操作都是错误的并且将肯定引起麻烦. 如你将见到的, 你的驱动需要知道的关于请求的所有事情, 都包含在通过请求队列传递给你的结构中.
请求处理完成的流程。
__blk_end_request_all
bio = bio_alloc(GFP_NOFS, 1);
bio->bi_bdev = inode->i_sb->s_bdev;
bio->bi_sector = pblock << (inode->i_blkbits - 9);
bio->bi_end_io = metapage_write_end_io;
bio->bi_private = page;
bio完成的时候,会回调bi_end_io
void bio_endio(struct bio *bio, int error)
{
if (error)
clear_bit(BIO_UPTODATE, &bio->bi_flags);
else if (!test_bit(BIO_UPTODATE, &bio->bi_flags))
error = -EIO;
if (bio->bi_end_io)
bio->bi_end_io(bio, error);
}
EXPORT_SYMBOL(bio_endio);