Linux通用块层提供给上层的接口函数是submit_bio。上层在构造好bio之后,调用submit_bio提交给通用块层处理。
submit_bio函数如下:
void submit_bio(int rw, struct bio *bio) { bio->bi_rw |= rw; //记录读写方式 /* * 执行有数据传输的读写或屏障请求统计,暂不关心 */ if (bio_has_data(bio)) { unsigned int count; if (unlikely(rw & REQ_WRITE_SAME)) count = bdev_logical_block_size(bio->bi_bdev) >> 9; else count = bio_sectors(bio); if (rw & WRITE) { count_vm_events(PGPGOUT, count); } else { task_io_account_read(bio->bi_size); count_vm_events(PGPGIN, count); } if (unlikely(block_dump)) { char b[BDEVNAME_SIZE]; printk(KERN_DEBUG "%s(%d): %s block %Lu on %s (%u sectors)\n", current->comm, task_pid_nr(current), (rw & WRITE) ? "WRITE" : "READ", (unsigned long long)bio->bi_sector, bdevname(bio->bi_bdev, b), count); } } //执行真实的IO处理 generic_make_request(bio); }
void generic_make_request(struct bio *bio) { struct bio_list bio_list_on_stack; if (!generic_make_request_checks(bio)) return; if (current->bio_list) { bio_list_add(current->bio_list, bio); return; } BUG_ON(bio->bi_next); bio_list_init(&bio_list_on_stack); current->bio_list = &bio_list_on_stack; do { struct request_queue *q = bdev_get_queue(bio->bi_bdev); //获取bio对应的请求队列 q->make_request_fn(q, bio); //调用请求队列的回调函数来处理IO bio = bio_list_pop(current->bio_list); } while (bio); current->bio_list = NULL; /* deactivate */ }
在调用make_request_fn处理bio的时候,可能会产生新的bio,即make_request_fn会递归调用generic_make_request 最直观的例子就是“栈式块设备”。后续学习device mapper之后补上这段分析。