通用块层

 

通用块层是一个内核组件,处理来自系统中的所有块设备发出的请求,接着上一层,处理提交过来的缓存区头。如果上一章address_space是关键的数据结构,这一章是bio和bio_vec,这两个数据结构
一、 两个重要的数据结构

struct bio {
01 sector_t  bi_sector; 
02 struct bio  *bi_next; /* request queue link */
03 struct block_device *bi_bdev;
04 unsigned long  bi_flags; /* status, command, etc */
05 unsigned long  bi_rw;  /* bottom bits READ/WRITE,
06 unsigned short  bi_vcnt; /* how many bio_vec's */
07 unsigned short  bi_idx;  /* current index into bvl_vec */
08 unsigned int  bi_phys_segments;
09 unsigned int  bi_size; /* residual I/O count */
10 unsigned int  bi_seg_front_size;
11 unsigned int  bi_seg_back_size;
12 unsigned int  bi_max_vecs; /* max bvl_vecs we can hold */
13 unsigned int  bi_comp_cpu; /* completion CPU */
14 atomic_t  bi_cnt;  /* pin count */
15 struct bio_vec  *bi_io_vec; /* the actual vec list */
16 bio_end_io_t  *bi_end_io;
17 void   *bi_private;
18 bio_destructor_t *bi_destructor; /* destructor */
19 struct bio_vec  bi_inline_vecs[0];
20};

第1行块I/O操作的第一个磁盘扇区
第2行链接到请求队列中的下一个bio
第3行指向块设备描述符的指针
第4行bio的状态标志
第5行I/O操作标志
第6行bio的bio_vec数组中段的数目
第7行bio的bio_vec数组中段的当前索引值
第8行合并之后bio中物理段的数目
第9行需要传送的字节数
第10行硬件段合并算法使用
第11行硬件段合并算法使用
第12行bio的bio_vec数组中允许的最大段数
第15行指向bio的bio_vec数组中的段的指针
第16行bio的I/O操作结束时调用的方法
第17行通用块层和块设备驱动程序的I/O完成方法使用的指针
第18行释放bio时调用的析构方法
bio_vec的数据结构

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

第1行指向段的页框中页描述符的指针
第2行段的字节长度
第3行页框中段数据的偏移量
二、 两者之间的关系
引用一张图

三、 提交请求
当通用块层提交一个I/O操作请求时,内核所执行的步骤如下。分配一个bio描述符,然后对它进行初始化。主要的入口函数是generic_make_request函数

void generic_make_request(struct bio *bio)
{
01 if (current->bio_tail) {
02  /* make_request is active */
03  *(current->bio_tail) = bio;
04  bio->bi_next = NULL;
05  current->bio_tail = &bio->bi_next;
06  return;
07 }
08 BUG_ON(bio->bi_next);
09 do {
10  current->bio_list = bio->bi_next;
11  if (bio->bi_next == NULL)
12   current->bio_tail = ¤t->bio_list;
13  else
14   bio->bi_next = NULL;
15  __generic_make_request(bio);
16  bio = current->bio_list;
17 } while (bio);
18 current->bio_tail = NULL; /* deactivate */
19}


第1行判断是否为bio的最后一个
第3行把就请求的bio放到队尾
第4行设置bio的下一个节点为空
第10行目前的bio链表指向请求bio的下一个结点
第11行如果bio的下个结点为空
第12行就把目前bio的队尾的指针指向目前bio的链表。
第13-15行否则的话,设置bio的下个结点为空,调用generic_make_request函数

static inline void __generic_make_request(struct bio *bio)
{
01 struct request_queue *q;
02 sector_t old_sector;
03 int ret, nr_sectors = bio_sectors(bio);
04 dev_t old_dev;
05 int err = -EIO;
06
07 might_sleep();
08
09 if (bio_check_eod(bio, nr_sectors))
10  goto end_io;
11 old_sector = -1;
12 old_dev = 0;
13 do {
14  char b[BDEVNAME_SIZE];
15
16  q = bdev_get_queue(bio->bi_bdev);
17  if (unlikely(!q)) {
18   printk(KERN_ERR
19          "generic_make_request: Trying to access "
20    "nonexistent block-device %s (%Lu)\n",
21    bdevname(bio->bi_bdev, b),
22    (long long) bio->bi_sector);
23   goto end_io;
24  }
25  if (unlikely(nr_sectors > q->max_hw_sectors)) {
26   printk(KERN_ERR "bio too big device %s (%u > %u)\n",
27    bdevname(bio->bi_bdev, b),
28    bio_sectors(bio),
29    q->max_hw_sectors);
30   goto end_io;
31  }
32  if (unlikely(test_bit(QUEUE_FLAG_DEAD, &q->queue_flags)))
33   goto end_io;
34
35  if (should_fail_request(bio))
36   goto end_io;
37  blk_partition_remap(bio);
38
39  if (bio_integrity_enabled(bio) && bio_integrity_prep(bio))
40   goto end_io;
41
42  if (old_sector != -1)
43   trace_block_remap(q, bio, old_dev, bio->bi_sector,
44         old_sector);
45
46  trace_block_bio_queue(q, bio);
47
48  old_sector = bio->bi_sector;
49  old_dev = bio->bi_bdev->bd_dev;
50
51  if (bio_check_eod(bio, nr_sectors))
52   goto end_io;
53
54  if (bio_discard(bio) && !q->prepare_discard_fn) {
55   err = -EOPNOTSUPP;
56   goto end_io;
57  }
58
59  ret = q->make_request_fn(q, bio);
60 } while (ret);
61
62 return;
63
64end_io:
65 bio_endio(bio, err);
66}
67

第3行获得扇区数
第9行检查bio->bi_sector没有超过块设备的扇区数。如果超过,将bio->bi_flags设置为BIO_EOF标志。调用bio_endio()函数,并终止。
第16行获取与块设备相关的请求队列q;其地址存放在块设备描述符的bd_disk字段中。
第37行调用blk_partition_remap函数检查块设备是否指的是一个磁盘分区。如果是,则从bio->bi_bdev获取分区的hd_struct描述符。从而执行下面的子操作
a、 根据数据传送的方向,更新hd_struct描述符中的read_sectors和reads值,或write_sectors和writes值
b、 调整bio->bi_sector值使得把相对于分区的起始扇区号转变为相对于整个磁盘的盲区号。
c、 将bio->bi_bdev设置为整个磁盘的块设备描述符
第59行调用q->make_request_fn访求将bio请求插入请求队列q中。
四、 总结
通用块层主要做的工作,就是初始化一个bio,然后把它插入到请求队列中。同时可以将缓冲区放在高端内存中,实现”零-复制”等一些功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值