系统构建SCSI指令后,将调用scsi_host的queucommand操作,将指令下移到LLD设备层进行处理。

scsi_host在iscsi协议中的角色

scsi_host在系统中启动承上启下的作用。对上接收上层驱动设备转发的命令,对下连接下层的软件硬件,进行SCSI命令的进一步处理。scsi_host逻辑上是scsi指令从scsi协议层到scsi传输层之间的接口。在物理上,scsi_host代表着scsi设备的适配器。SAS协议的适配器是sas控制器,FC协议的适配器是FC HBA卡,可以得出,通常意义上scsi_host接收的到的scsi命令的处理应该转到板卡上处理。

iscsi的可以分成两个实现机制,一是使用通用的网卡,一是使用专用的iscsi HBA卡(类似于FC)。使用专用HBA卡时,iscsi协议的解析处理由HBA卡进行,而scsi_host设置与HBA一对一。使用通用网卡时,物理的HBA卡不存在,换句话说,流程上我们不需要scsi_host这一层进行转换和传递,但考虑到系统概念的统一性、操作的一致性,依然需要在逻辑上存在这个概念。因而,当使用通用网卡实现iscsi协议时,这scsi_host的作用就成为接收上层的SCSI指令并进行处理。

另外一个问题,为什么我们不把网卡看成HBA卡,scsi_host与网卡一一对应呢。此处的设计与iscsi协议有一定关系。在iscsi协议里面,发起端与目标端间在传输数据之前先建立会话(session),协议又规定,在一个会话里面可以有多个连接存在,而为了实现连接之间的独立性,两个不同的链接不应该通过同一个网卡(逻辑上没有对此限制),因而,我们不应该限制会话跨网卡。因此,也不应该使scsi_host与链接一一对应。
scsi_host接收scsi指仅之后IO操作流程便转入了iscsi处理层次。在iscsi层,系统不关心scsi指令的具体含义,只关心scsi指令传输到目标端所面临的问题,最直接的问题就如传输的目的地,分几次传输,用哪个连接进行传输,传输出错如何处理。 这些问题在iscsi协议里均进行了很好的回答。

iscsi中数据传输

在iscsi层面,每个scsi命令将作为一个task进行传输,一个task在传输时,可能被分成多个数据段进行传递,在iscsi层次提出task的概念可以较好的进行错误处理。当发生传输故障的那一时刻,整个SCSI指令请求可能传递了一部分,如果是临时性的连接问题,处理方式继续传递即可。如果传输层的错误时间过长导致上层应用超时,这时上层应用要求整条命令进行重试,那么在重视之前必然需要将当前还未传输的数据取消掉。这时就用到了scsi协议定的任务管理相关的功能。

两个实体间交互的最小单位是PDU。scsi的CBD和数据都被封装在PDU里面进行传输。为了传输不同的交互内容,iscsi协议定义了17种PDU格式。具体可以参考rfc协议中每种PDU格式的定义。

回到iscsi IO操作层面,其参考流程在iscsi rfc 参考文档中已经给出。
iscsi读操作流程上如图所示。

READ操作请求方不包括数据,因此,发起端只需要发送READ指给目标端即可。目标端接收到命令后,获取对应的数据,并将其返回。从发送请求到收到响应整个过程完成后才表示READ指令执行完成。基于上述流程,我们可以总结以下几点:

  • 一个任务中在传输层可以需要经过1个或者多个PDU才能够完成。
  • 每个任务起始的第一个PDU类型必然是scsi command命令。
  • 在请求读之前,发起端要准备好数据接收环境,比如申请足够的内存等。

与之对应,写操作如下图所示:

写操作除了发送SCSI指令外,还要将数据传递给目标端。发起端向目标端传递的PDU的数目至少有1个(SCSI Command可以在PDU中携带数据),PDU的最大数取决于写数据的长度。

写操作除scsi command所携带的数据,每个传输数据之前需要等目标端准备好之后再进行传输。这样避免了目标端因不定传输数据规模而没有准备好传输空间,致命传输数据失效。

queuecommand

iscsi子系统的处理起源于scsi中间层调用LLD层的queuecommand,queuecommandscsi_host_template结构体的一个回调。前面,我们讲到scsi子系统通过scsi_host将scsi命令传递给scsi的传输层进行处理,而iscsi的实现没有真实的scsi_host存在。因此必须生成一个虚拟的。在实现中,虚拟的scis_host创建于会话建立时,当会话释放时,它也根着不存在了。创建过程逻辑相对比较简单,与IO相关的工作主要有两个:
一是:将包括各种回调的scsi_host模板iscsi_sw_tcp_sht注册到scsi子系统,以其通够通过设备描述符找到相应的回调并在相应的调用时机进行调用;
另外:创建协议处理需要的单线程工作队列。

在这个过程中,将scsi_host_templatequeuecommand回调指向了iscsi_queuecommand函数,可以这个函数为起点研究iscsi层次处理的主要逻辑。

iscsi_queuecommand原型如下:

int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc)

有两个参数。host指向当向需要处理的scsi指令的指针。sc指向当前需要处理的指令。当此函数能够正确的指scsi指令进行处理时,返回0,如果在处理过程中发现错误,则尝试结束这个任务,如果不能正常结束任务,则返回SCSI_MLQUEUE_TARGET_BUSY

在分析协议相关的处理时,我们需要严格区别两种类型的异常。一种是程序逻辑的异常,比如由于传递函数指针为空等。这种调用性的错误将通过错误返回进行处理。另外一种异常是协议级的异常。这类异常因素比较复杂,有网络问题,兼容性问题等等,而对这种异常的程序逻辑必须是没有错的。

iscsi_queuecommand执行流程主要有以下几步:
- 检查会话、链接等上下文状态是否可以处理scsi指令。
检查会话通过iscsi_session_chkready进行。当会话状态不是ISCSI_SESSION_LOGGED_IN时,不适合处理scsi指令。
链接检查通过链接是否存在、链接状态、链接可接收的命令窗口是否达到最大值。这几个方面判断。

    conn = session->leadconn;if (!conn) {reason = FAILURE_SESSION_FREED;sc->result = DID_NO_CONNECT << 16;goto fault;}if (test_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx)) {reason = FAILURE_SESSION_IN_RECOVERY;sc->result = DID_REQUEUE << 16;goto fault;}if (iscsi_check_cmdsn_window_closed(conn)) {reason = FAILURE_WINDOW_CLOSED;goto reject;}
  • iscsi_alloc_task分配并构建task对象。
  • 将链接插入到链接的命令队列中,待处理。

数据发送

iscsi数据收发由单独的线程进行。iscsi子系统为每个scsi_host初始化了一个工作队列用于收发数据。当某个链接有需要传输的命令时,
数据发送函数调用iscsi_xmitworker进行。这个函数遍历conn中各个task队列,并进行处理。每个conn有三个队列,分别称作mgmtqueuecmdqueuerequeue。数据路径中的task都放在cmdqueue
针对特定的task,系统先在conn的task域中记录这个任务,然后再执行发送。一个conntask被串行发送。当一个task未被发送完成前,下一次task不会开始。如果当前的task需要等待客户端的PDU RESPONSE后才能继续时,工作线程将会持续重试,直到达到要求时才会进一步处理。以数据写操作为例,在写操作这个task中,发起端首先发送写SCSI指令,接下来等待,直到目标端返回R2T响应后,发送下一次PDU。

针对某一个具体的task,iscsi子系统将调用iscsi_transportxmit_task接口进行处理。iscsi_transport定义了会话处理、链接处理、数据发送等一系列操作具体实现。我们之前讲到每种iscsi子系统的实现将会有不同的操作方式,比如在某厂商的HBA卡的实现中,此类操作真正的逻辑由HBA具体实现和管理,而驱动层通过寄存器或者总线相互传递状态和指令即可,而纯软iscsi的实现确要实现会话、链接、数据传输的协议规范细节。纯软iscsi实现中,定了iscsi_sw_tcp_transport结构。

iscsi_sw_tcp_transport结构的xmit_task定义为iscsi_tcp_task_xmit。该函数就是一个将task拆分成PDU,然后将PDU发送到目标端的过程。对于IO操作的命令。这个函数在调用之前,已经通过iscsi_prep_scsi_cmd_pdu初始化含有scsi指令的PDU。我们上面说过,这个是每个任务需要处理的第一个PDU。接下来,如果有数据需要发送的话,进一步调用iscsi_prep_data_out_pdu构建data type类型的PDU,然后按同样的试进行发送。当然,上面我们也提到过,在发送数据PDU之前,需要先收到目标端发过来的R2T响应,以表示目标端已经准备好了接收命令。

针对每个PDU的处理操作就是通用的socket发送逻辑了。我们不详细分析。

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

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

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

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

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

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

    上次我们讨论了iscsi initiator IO操作需要经过的各个层次,以及每层所涉及的IO数据结构的变化.今天主要讨论IO如何形成SCSI指令并下发的. 我们知道在通用块层,IO最终放在reque ...

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

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

  5. IO流 (四) ----- 转换流和标准字节输出流

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

  6. java字符流解析_Java IO(四):字符流详解

    一.字符流 字节流提供了处理任何类型输入/输出操作的功能(因为对于计算机而言,一切都是0和1,只需把数据以字节形式表示就够了),但它们不可以直接操作Unicode字符,因为一个Unicode字符占用2 ...

  7. Java IO实战操作(四)

    /** * 文件压缩 ZipOutputStream类 * @throws IOException */ public void ZipOutputStreamFile() throws IOExce ...

  8. IO:同步,异步,阻塞,非阻塞

    IO - 同步,异步,阻塞,非阻塞 都是老生常谈的东西,多通读几遍,理解透彻! 实际上同步与异步是针对应用程序与内核的交互而言的.同步过程中进程触发IO操作并等待(也就是我们说的阻塞)或者轮询的去查看 ...

  9. 【死磕NIO】— 阻塞IO,非阻塞IO,IO复用,信号驱动IO,异步IO,这你真的分的清楚吗?

    通过上篇文章([死磕NIO]- 阻塞.非阻塞.同步.异步,傻傻分不清楚),我想你应该能够区分了什么是阻塞.非阻塞.异步.非异步了,这篇文章我们来彻底弄清楚什么是阻塞IO,非阻塞IO,IO复用,信号驱动 ...

最新文章

  1. 多视图立体几何PatchMatchStereo:DSI与Cost Volume
  2. 使用 Kanban精益创新
  3. Linux下gcc编译生成动态链接库*.so文件并调用它
  4. 本程序主要实现了一个方阵的求逆与实现了逆矩阵和原矩阵的乘积为单位矩阵
  5. 一个口罩引发的老黄牛
  6. libgo 支持mysql,loadrunner通过使用libmysql.dll完成mysql的测试-Go语言中文社区
  7. 揭秘“21世纪最性感的职业”:数学、编程、沟通和商业技能一个都不能少!...
  8. Solaris 网络 配置
  9. iframe高度自适应 1
  10. 用idea创建vue项目
  11. YALMIP介绍及怎么在Matlab中加YALMIP、SDPT3
  12. HHL论文第一弹(总结算法基本思想、QRAM制备量子态)
  13. PyTorch学习(二):Transform
  14. 常微分方程求解器ODE solver
  15. 使用MobaXterm发布前端代码到服务器
  16. BZOJ 3653 谈笑风生
  17. 机器学习之深度学习简介
  18. LVGL-输入设备LV_INDEV_TYPE_POINTER类型
  19. Building Worlds In Unreal 学习笔记——07-11 岩石树落木灌木绘制/溪水着色器/潮湿与焦散贴花/后处理
  20. Elasticsearch常见搜索方法的实现

热门文章

  1. poj 1986 Distance Queries LCA
  2. 1701. Ostap and Partners(并查集-关系)
  3. IIS6中应用程序池和Web园,解决Session丢失问题
  4. 中国移动全球通寻宝第四期攻略
  5. 简单工厂(Simple Factory)模式
  6. Python+tkinter根据窗体大小自动缩放并显示图像
  7. php列表调多图,列表中调用多图显示的文章
  8. php判断平年和闰年,平年和闰年的三种判断方法
  9. jekins创建ssh_linux – Jenkins SSH slave无法创建/ home // jenkins
  10. accessors 作用_@Accessors介绍配置getter和setter