上次我们讨论了iscsi initiator IO操作需要经过的各个层次,以及每层所涉及的IO数据结构的变化。今天主要讨论IO如何形成SCSI指令并下发的。
我们知道在通用块层,IO最终放在request_queue中暂时保存。为了减少寻道时间,通用块层采用“蓄流”的方式,将IO蓄到队列里一段时间,使接下来的IO有机会与已经存在的IO进行合并,即所谓的电梯算法。
当在一定合适的时机,“蓄流”打开时,IO继续进行向下层传递,进而调用块设备驱动为其请求队列提供的request_fn进行处理。SCSI磁盘驱动在扫描发现并为磁盘分配请求队列时将期实例化。具体指向为scsi_request_fn函数。
下图展现了scsi_request_fn的调用情况。

该函数调用blk_peek_request中逐个取出请求,并进行处理。每次调用它就从可处理的请求队列中获到一个请求。请求有可能是新的,有可能是由于SCSI子系统的底层相关设备不能处理请求再次加入到请求队列的。如果已经处理过意味着scsi cmd已经申请;如果是新的,调用prep_rq_fn进行一些请求处理前的准备工作。scsi磁盘驱动(以下简称sd)在这个时间进行SCSI命令的构建(SCSI CDB)。这个预期可能有三种返回值:

- BLKPREP_OK:表示命令初期准备成功,则继续往下执行
- BLKPREP_DEFER:暂时不能够进行处理,则将请求再次加入到队列
- BLKPREP_KILL:请求没办法继续进行下去。这时从队列中请下请求,向上层报告IO失败。

接下来做三件事:

首先,进行设备的检查工作。
当前的计算机体系结构中,设备的操作明显慢于CPU的计算能力,是现代计算机的关键瓶颈之一。因而,在真正需要设备进行处理之前,尽可能的完善检查IO处理的条件是否已经满足。总之,针对IO层面的错误处理,要么尽可能多的恢复现场使业务能够正常的运行下去,要么尽可能的早的失败,给予应用进行错误处理的机会。此次的检查主要针对后者,主要是硬件层面的检查,具体点是对设备状态、目标端、主机适配器等。
设备不能接收IO的情况分为三种分别为设备离线、设备正忙于处理其他事务、设备被阻塞住:
- 设备离线时,当设备再次上线时,需要重新初始化、重新建立请求队列,当前的状态和数据不需要处理,因而IO请求不需要进行处理了,直接放弃掉并向上层返回错误信息。
- 设备忙和设备阻塞住意味着设备临时不能处理IO请求,一旦状态移除,IO将会被继续处理,此时系统将IO请求放置到延迟队列中,择机进行处理。
- 如果设备的状态没有问题,而设备所属的目标端(target)、适配器(host)存在问题时,可以确保是临时不能处理IO,此时将IO再次加入到设备的请求队列中,下次再次进行重试。

其次,初始化错误处理。
scsi_init_cmd_errh用于命令执行出错处理的初始化,设置超时定时器(新内核中的出理只进行了一些初始化的工作,超时没有进行设置)。

最后,将scsi命令转交到底层设备(LLD Low Level Device)进行处理。
scsi_dispatch_cmd用于将命令传递到底层设备驱动进行处理,一旦这个操作完成,意味着IO请求已经穿过SCSI中间层,传递到SCSI transport layer层。如果该返回0表示分发成功,否则表示出现错误,不再继续处理请求队列中的其它请求。
这个函数的主要工作如下:
- 检查设备是否已经被删除。如果被删除,不需要下发了,直接返回错即可。
- 检查设备是否被阻塞住。
- 检查命令的长度是否已经超过设备可以接受的长度。
- 调用主要适配器的queuecommand命令将数据进行分发

如果SCSI指令没有被分发,最终可能因为:
- SCSI_MLQUEUE_DEVICE_BUSY:设备临时被阻塞。
- SCSI_MLQUEUE_TARGET_BUSY:目标被阻塞
- SCSI_MLQUEUE_HOST_BUSY:主机适配器被阻塞
在上述三种情况下,将IO请求再次放入队列,以便下次重试。

到此为止,SCSI层面的处理完成。
总结一下,IO请求将视系统当前的情况进行处理,最终都会执行结果分为三种情况:
当设备因故障移除时,直接向上层报告IO错误;当SCSI子系统的相关设备(适配器、目标节点、设备)暂时不能处理IO,加入到队列后期处理;当设备正常时,回调适配器的queuecommand进一步进行处理。

返回来,我们再讨论一下SCSI命令初始化的问题。
上面说明,系统根据请求信息准备scsi指令在prep_rq_fn中进行。sd初始化时,将其初始化为scsi_prep_fnscsi_prep_fn调用scsi_setup_cmnd进行scsi命令的初始化工作。scsi子系统的请求两个层次(1)上层用请求(内核中称做来源于文件系统的请求,这种说法不是特别的准确);(2)来源于scsi中间层的请求。前者需要构建SCSI命令,后者SCSI命令已经构建完成。

来自上层应用的请求需要构建成哪些指令,如何构建指令与设备类型有很大关系:来源于磁盘的IO请求需要构建的命令与块操作相关,遵循SCSI SPC标准,而来源于磁带的IO请求需要遵循相关的标准。下图表达了SCSI协议族各协议之间的关系。一般地,设备类型特定的命令集由具体设备驱动来实现。此时,将调用scsi上层驱动的init_cmd实现。对于磁盘设备,设备加载时,将其指向函数:sd_init_command
综上sd设备来源于上层应用请求的scsi命令构建调用关系为:
prep_rq_fn->scsi_prep_fn->scsi_setup_cmnd->scsi_setup_fs_cmnd->sd_init_command

个人感觉上层应用请求命令构建的时机找的有点别扭。道理上,由sd来构建磁盘读写相关的命令是符合常理设计的。sd作为SCSI服务的上层驱动,主要通过设备类型与其他相同层次的上层驱动在功能进行区分。必然的,磁盘IO相关的SCSI指令的构建由sd来完成;磁盘带IO相关的SCSI指令由st来完成…………但是时机选择回调机制实现,而不是在请求下发之前就准备好,不确定是什么原因。

sd_init_command函数实现如下,

static int sd_init_command(struct scsi_cmnd *cmd)
{struct request *rq = cmd->request;switch (req_op(rq)) {case REQ_OP_DISCARD:switch (scsi_disk(rq->rq_disk)->provisioning_mode) {case SD_LBP_UNMAP:return sd_setup_unmap_cmnd(cmd);case SD_LBP_WS16:return sd_setup_write_same16_cmnd(cmd, true);case SD_LBP_WS10:return sd_setup_write_same10_cmnd(cmd, true);case SD_LBP_ZERO:return sd_setup_write_same10_cmnd(cmd, false);default:return BLKPREP_INVALID;}case REQ_OP_WRITE_ZEROES:return sd_setup_write_zeroes_cmnd(cmd);case REQ_OP_WRITE_SAME:return sd_setup_write_same_cmnd(cmd);case REQ_OP_FLUSH:return sd_setup_flush_cmnd(cmd);case REQ_OP_READ:case REQ_OP_WRITE:return sd_setup_read_write_cmnd(cmd);case REQ_OP_ZONE_REPORT:return sd_zbc_setup_report_cmnd(cmd);case REQ_OP_ZONE_RESET:return sd_zbc_setup_reset_cmnd(cmd);default:BUG();}
}

它根据请求的操作的类型构建不同的scsi指令。
- REQ_OP_DISCARD:告诉块设备放弃使用某指定的块。这个请求是配合自动精简配置使用。这个功能对应SCSI指令的UNMAP命令。当此操作没调用时,target不再为相应 LBA映射存储空间;
- REQ_OP_WRITE_ZEROES:对指定的1个或者多个扇区写0
- REQ_OP_WRITE_SAME: 将1个或者多个扇区写成相同的数据。
- REQ_OP_FLUSH:通知target同步缓存数据
- REQ_OP_READ:读操作
- REQ_OP_WRITE:写操作
- REQ_OP_ZONE_REPORT REQ_OP_ZONE_RESET:应用于瓦式存储的特殊操作
请写请求将sd_setup_read_write_cmnd函数构建相应的scsi命令结构。由于协议不断的发展,SCSI标准中有4种不同规格的写读操作称作:read(10)、read(12)、read(16)、read(32)和write(10)、write(12)、write(16)、write(32)。括号中的数字与命令描述块的大小,如read(10)的命令描述符为10个字节。10、12、16这三种的命令的区别是表求LBA所占用的空间不同,分别为4、6、8个字节。read(32)与write(32)主要是用于端到端T10 type2数据验证时使用。
理解了上述区别就比较好理解操作的构建过程了。
- 如果需要支持T10 type2数据验证,使用read(32)、write(32)
- 否则根据硬盘的最大扇区数分析,尽可能使用最小的命令描述块(CDB)

iscsi:IO操作流程(二)相关推荐

  1. iscsi:IO操作流程(四)

    系统构建SCSI指令后,将调用scsi_host的queucommand操作,将指令下移到LLD设备层进行处理. scsi_host在iscsi协议中的角色 scsi_host在系统中启动承上启下的作 ...

  2. iscsi:IO操作流程(五)——IO完成处理

    概述 前面讲到,iscsi initiator实现过程采用了多级的异步模式,通过异步模式使IO操作阶段能够批量处理.这种异步机制的存在主要为了提升系统的吞吐量.从设计的角度,考虑采用异步操作机制的任务 ...

  3. iscsi:IO操作流程(一)

    从应用的视角,iscsi展现为一个块设备,即一块硬盘.在Linux操作系统中可以通过fdisk -l看到这块磁盘.iscsi协议所涉及的一系列的组件经过层层虚拟化,在多个层次上其操作与本地硬盘无异.这 ...

  4. iscsi:IO操作流程(三)

    概述 当我们讲到scsi命令这个概念时,需要根据上下文去理解.可能指代两个概念: - 一种含义指SCSI协议规划中定义的SCSI 命令描述块(Command descriptor block (CDB ...

  5. Linux中的基础IO(二)

    Linux中的基础IO(二) 文章目录 Linux中的基础IO(二) 一.基本接口 二.文件描述符 三.文件描述符的分配规则 四.重定向 五.dup2系统调用 六.minishell 一.基本接口 i ...

  6. IO流 (二) ----- 文件流

    相关文章: <IO流 (一) ----- 基本概念和File类> <IO流 (二) ----- 文件流> <IO流 (三) ----- 字符流和字符缓冲流> < ...

  7. python 异步io 写excel_python异步IO编程(二)

    python异步IO编程(二) 目录 开门见山 Async IO设计模式 事件循环 asyncio 中的其他顶层函数 开门见山 下面我们用两个简单的例子来让你对异步IO有所了解 importasync ...

  8. Java IO(二)——RandomAccessFile

    一.RandomAccessFile RandomAccessFile类可以说是Java语言中功能最为丰富的文件访问类,它提供了众多的文件访问方法.RandomAccessFile类支持"随 ...

  9. Linux非阻塞IO(二)网络编程中非阻塞IO与IO复用模型结合

    上文描述了最简易的非阻塞IO,采用的是轮询的方式,这节我们使用IO复用模型. 阻塞IO 过去我们使用IO复用与阻塞IO结合的时候,IO复用模型起到的作用是并发监听多个fd. 以简单的回射服务器为例,我 ...

最新文章

  1. 并行计算实战-双调排序
  2. android 虚拟设备的用法
  3. python判断字符串中包含某个字符串_Python中最常用的字符串方法!
  4. 进入全真互联网——音视频通信的技术变革
  5. 安徽计算机应用基础高考试题,安徽省对口高考试题(计算机应用基础部分)
  6. 强连通分量 Kosaraju PK Tarjan(转)
  7. 【Python】之split()方法
  8. 洛谷P2486 [SDOI2011]染色(树链剖分+线段树判断边界)
  9. DiQuick Web UI 框架 V1.3.2 版本更新
  10. .NET Remoting 入门实例
  11. SAP License:今天你‘牺牲’了吗?
  12. html颜色趋势,展望下一年的网页设计配色趋势
  13. 松本行弘为什么开发Ruby
  14. 计算机网络详细笔记【湖科大教书匠,内含B站链接】
  15. 今日头条推荐算法原理解析
  16. 1946计算机用途,计算机在我们的工作、生活中的作用越来越大, 你知道计算机的起源于发展吗?请就计算机的发明时间(1946年)、大小、用途等...
  17. 前端静态资源缓存最优解以及max-age的陷阱
  18. 简单实现一个虚拟形象系统
  19. 贝多芬没能写完的《第十交响曲》,即将被人工智能完成
  20. ffmpeg 提取 视频,音频,字幕 方法

热门文章

  1. HTML 5适合小公司,适合在大平台上做内容
  2. 提交数据库访问性能一些简单措施
  3. Nobot控件------拒绝机器人行为
  4. java编写两邮件传输,JAVA邮件发送(文字+图片+附件)【源码】
  5. Java面向对象之继承、super关键字、方法重写
  6. 计算机名代表电脑什么,电脑开机蓝屏的各种文件名是什么意思
  7. android 多数据图表,Android统计图表MPAndroidChart:为多条统计折线动态更新数据,以高温低温曲线为例【7】...
  8. Python中超类是如何知道自己被继承的
  9. 《Python程序设计》在亚马逊京东当当互动出版网淘宝全面上架
  10. python numpy 技巧