Linux通用块层提供给上层的接口函数是submit_bio。上层在构造好bio之后,调用submit_bio提交给通用块层处理。
 
submit_bio函数如下:
 
  1. void submit_bio(int rw, struct bio *bio)
  2. {
  3. bio->bi_rw |= rw; //记录读写方式
  4. /*
  5. * 执行有数据传输的读写或屏障请求统计,暂不关心
  6. */
  7. if (bio_has_data(bio)) {
  8. unsigned int count;
  9. if (unlikely(rw & REQ_WRITE_SAME))
  10. count = bdev_logical_block_size(bio->bi_bdev) >> ;
  11. else
  12. count = bio_sectors(bio);
  13. if (rw & WRITE) {
  14. count_vm_events(PGPGOUT, count);
  15. } else {
  16. task_io_account_read(bio->bi_size);
  17. count_vm_events(PGPGIN, count);
  18. }
  19. if (unlikely(block_dump)) {
  20. char b[BDEVNAME_SIZE];
  21. printk(KERN_DEBUG "%s(%d): %s block %Lu on %s (%u sectors)\n",
  22. current->comm, task_pid_nr(current),
  23. (rw & WRITE) ? "WRITE" : "READ",
  24. (unsigned long long)bio->bi_sector,
  25. bdevname(bio->bi_bdev, b),
  26. count);
  27. }
  28. }
  29. //执行真实的IO处理
  30. generic_make_request(bio);
  31. }
  1. void generic_make_request(struct bio *bio)
  2. {
  3. struct bio_list bio_list_on_stack;
  4. if (!generic_make_request_checks(bio))
  5. return;
  6.  
  7. if (current->bio_list) {
  8. bio_list_add(current->bio_list, bio);
  9. return;
  10. }
  11.  
  12. BUG_ON(bio->bi_next);
  13. bio_list_init(&bio_list_on_stack);
  14. current->bio_list = &bio_list_on_stack;
  15. do {
  16. struct request_queue *q = bdev_get_queue(bio->bi_bdev); //获取bio对应的请求队列
  17. q->make_request_fn(q, bio); //调用请求队列的回调函数来处理IO
  18. bio = bio_list_pop(current->bio_list);
  19. } while (bio);
  20. current->bio_list = NULL; /* deactivate */
  21. }
在调用make_request_fn处理bio的时候,可能会产生新的bio,即make_request_fn会递归调用generic_make_request 最直观的例子就是“栈式块设备”。为了防止栈式块设备执行请求可能出现问题,在一个时刻只允许进程有一个generic_make_request被调用。为此,在进程结构中定义了一个bio等待处理链表:bio_list。同时区分“活动”和“非活动”状态。活动状态表示进程已经在调用generic_make_request。这时,所有后续产生的bio都链入bio_list链表,在当前bio完成的情况下,逐个处理。
 
generic_make_request的执行过程:
  1. generic_make_request_checks
  2. 判断make_request是否处于活动状态。如果current->bio_list不为NULL,则表明当前进程已经有generic_make_request在执行,这时候传进来的bio都将链接到当前进程等待处理的bio链表尾部
  3. 设置current->bio_list表明当前的generic_make_request为活动状态,让后来的bio有机会插入等待链表
  4. 处理bio。这里的bio可能是传入的bio,也可能是当前进程待处理bio链表中的bio。如果是前者,上层保证了其bi_next必然为NULL;如果是后者,则在将bio从链表中脱离的时候,已经设置了bi_next为NULL
  5. 调用make_request_fn回调处理bio
  6. 检查当前进程的等待链表中是否还有bio,如果有,跳到第三步
  7. 至此,generic_make_request的“本轮执行周期”已经完毕,清零current->bio_list,使得generic_make_request处于“非活动”状态
这里再看下generic_make_request_checks

  1. static noinline_for_stack bool
  2. generic_make_request_checks(struct bio *bio)
  3. {
  4. struct request_queue *q;
  5. int nr_sectors = bio_sectors(bio);
  6. int err = -EIO;
  7. char b[BDEVNAME_SIZE];
  8. struct hd_struct *part;
  9.  
  10. might_sleep();
  11.  
  12. // 检查bio的扇区有没有超过块设备的扇区数
  13. if (bio_check_eod(bio, nr_sectors))
  14. goto end_io;
  15.  
  16. // 检测块设备的请求队列是否为空
  17. q = bdev_get_queue(bio->bi_bdev);
  18. if (unlikely(!q)) {
  19. printk(KERN_ERR
  20. "generic_make_request: Trying to access "
  21. "nonexistent block-device %s (%Lu)\n",
  22. bdevname(bio->bi_bdev, b),
  23. (long long) bio->bi_sector);
  24. goto end_io;
  25. }
  26.  
  27. // 检测请求的扇区长度是否超过物理限制
  28. if (likely(bio_is_rw(bio) &&
  29. nr_sectors > queue_max_hw_sectors(q))) {
  30. printk(KERN_ERR "bio too big device %s (%u > %u)\n",
  31. bdevname(bio->bi_bdev, b),
  32. bio_sectors(bio),
  33. queue_max_hw_sectors(q));
  34. goto end_io;
  35. }
  36.  
  37. part = bio->bi_bdev->bd_part;
  38. if (should_fail_request(part, bio->bi_size) ||
  39. should_fail_request(&part_to_disk(part)->part0,
  40. bio->bi_size))
  41. goto end_io;
  42.  
  43. /*
  44. * If this device has partitions, remap block n of partition p to block n+start(p) of the disk.
  45. * 如果请求的块设备可能代表一个分区,这里重新映射到所在的磁盘设备
  46. */
  47. blk_partition_remap(bio);
  48.  
  49. if (bio_check_eod(bio, nr_sectors))
  50. goto end_io;
  51.  
  52. /*
  53. * Filter flush bio's early so that make_request based
  54. * drivers without flush support don't have to worry
  55. * about them.
  56. */
  57. if ((bio->bi_rw & (REQ_FLUSH | REQ_FUA)) && !q->flush_flags) {
  58. bio->bi_rw &= ~(REQ_FLUSH | REQ_FUA);
  59. if (!nr_sectors) {
  60. err = ;
  61. goto end_io;
  62. }
  63. }
  64.  
  65. // 检查设备对DISCARD命令的支持
  66. if ((bio->bi_rw & REQ_DISCARD) &&
  67. (!blk_queue_discard(q) ||
  68. ((bio->bi_rw & REQ_SECURE) && !blk_queue_secdiscard(q)))) {
  69. err = -EOPNOTSUPP;
  70. goto end_io;
  71. }
  72.  
  73. if (bio->bi_rw & REQ_WRITE_SAME && !bdev_write_same(bio->bi_bdev)) {
  74. err = -EOPNOTSUPP;
  75. goto end_io;
  76. }
  77.  
  78. /*
  79. * Various block parts want %current->io_context and lazy ioc
  80. * allocation ends up trading a lot of pain for a small amount of
  81. * memory. Just allocate it upfront. This may fail and block
  82. * layer knows how to live with it.
  83. */
  84. create_io_context(GFP_ATOMIC, q->node);
  85.  
  86. if (blk_throtl_bio(q, bio))
  87. return false; /* throttled, will be resubmitted later */
  88.  
  89. trace_block_bio_queue(q, bio);
  90. return true;
  91.  
  92. end_io:
  93. bio_endio(bio, err);
  94. return false;
  95. }

generic_make_request_checks

Linux3.10.0块IO子系统流程(1)-- 上层提交请求的更多相关文章

  1. Linux3.10.0块IO子系统流程(0)-- 块IO子系统概述

    前言:这个系列主要是记录自己学习Linux块IO子系统的过程,其中代码分析皆基于Linux3.10.0版本,如有描述错误或不妥之处,敬请指出! 参考书籍:存储技术原理分析--基于Linux 2.6内核 ...

  2. Linux3.10.0块IO子系统流程(7)-- 请求处理完成

    和提交请求相反,完成请求的过程是从低层驱动开始的.请求处理完成分为两个部分:上半部和下半部.开始时,请求处理完成总是处在中断上下文,在这里的主要任务是将已完成的请求放到某个队列中,然后引发软终端让中断 ...

  3. Linux3.10.0块IO子系统流程(4)-- 为请求构造SCSI命令

    首先来看scsi_prep_fn int scsi_prep_fn(struct request_queue *q, struct request *req) { struct scsi_device ...

  4. Linux3.10.0块IO子系统流程(3)-- SCSI策略例程

    很长时间以来,Linux块设备使用了一种称为“蓄流/泄流”(plugging/unplugging)的技术来改进吞吐率.简单而言,这种工作方式类似浴盆排水系统的塞子.当IO被提交时,它被储存在一个队列 ...

  5. Linux3.10.0块IO子系统流程(2)-- 构造、排序、合并请求

    Linux块设备可以分为三类.分别针对顺序访问物理设备.随机访问物理设备和逻辑设备(即“栈式设备”)   类型 make_request_fn request_fn 备注 SCSI 设备等 从bio构 ...

  6. Linux3.10.0块IO子系统流程(6)-- 派发SCSI命令到低层驱动

    在SCSI策略例程中最后调用scsi_dispatch_cmd将SCSI命令描述符派发给低层驱动进行处理 /** * scsi_dispatch_command - Dispatch a comman ...

  7. Linux3.10.0块IO子系统流程(5)-- 为SCSI命令准备聚散列表

    SCSI数据缓冲区组织成聚散列表的形式.Linux内核中表示聚散列表的基本数据结构是scatterlist,虽然名字中有list,但它只对应一个内存缓冲区,聚散列表就是多个scatterlist的组合 ...

  8. DPA 9.1.85 升级到DPA 10.0.352流程

    SolarWinds DPA的升级其实是一件非常简单的事情,这里介绍一下从DPA 9.1.95升级到 DPA 10.0.352版本的流程.为什么要升级呢? DPA给用户发的邮件已经写的非常清楚了(如下 ...

  9. 【转】linux IO子系统和文件系统读写流程

    原文地址:linux IO子系统和文件系统读写流程 我们含有分析的,是基于2.6.32及其后的内核. 我们在linux上总是要保存数据,数据要么保存在文件系统里(如ext3),要么就保存在裸设备里.我 ...

随机推荐

  1. windows10 64bit 下的tensorflow 安装及demo

    目前流行的深度学习库有Caffe,Keras,Theano,本文采用谷歌开源的曾用来制作AlphaGo的深度学习系统Tensorflow. 1:安装Tensorflow 最早TensorFlow只支持 ...

  2. JavaScript 第九章总结

    Handing events 前言 这一章节主要讲了关于 events 的内容,讲了 event 的定义,以及如何用 code 来 react to events.同时,也说明了 JavaScript ...

  3. 雷林鹏分享:jQuery EasyUI 树形菜单 - 创建复杂树形网格

    jQuery EasyUI 树形菜单 - 创建复杂树形网格 树形网格(TreeGrid)可以展示有限空间上带有多列和复杂数据电子表格.本教程将演示如何将表格数据排列在分割的网格和多行表头中,以便组织共 ...

  4. yarn hadoop-2.3.0 installation cluster Centos 64bits

    Apache Hadoop -2.2.0 - How to Install a Three Nodes Cluster http://tonylixu.blogspot.ca/2014/02/apac ...

  5. CentOS6启动流程

    CentOS6启动流程 1.加载BIOS的硬件信息,获取第一个启动设备 在通电之后,CentOS6会进行加电自检(Power On Self Test),这个过程主要是由BIOS完成的.在自检完毕,会 ...

  6. Remove Duplicates From Sorted Array leetcode java

    算法描述: Given a sorted array, remove the duplicates in place such that each element appear only once a ...

  7. 3月26 document的练习

    1.Window.document对象 一.找到元素: docunment.getElementById("id"):根据id找,最多找一个:     var a =docunme ...

  8. node模块之path——path.join和path.resolve的区别

    1.path.join([...paths]) path.join() 方法使用平台特定的分隔符把全部给定的 path 片段连接到一起,并规范化生成的路径. 长度为零的 path 片段会被忽略. 如果 ...

  9. HDU-2767-tarjan/Kosaraju求scc

    http://acm.hdu.edu.cn/showproblem.php?pid=2767 问最少添加几条边使得图为强连通. tarjan跑一下,然后对强连通分量缩点,找下此时出度为零和入度为零的点 ...

  10. PAT 1005 Spell It Right

    1005 Spell It Right (20 分)   Given a non-negative integer N, your task is to compute the sum of all ...