分析Linux 内核 SCSI IO 子系统

news2025/1/15 6:24:57

【推荐阅读】

浅析linux内核网络协议栈--linux bridge

virtio-net 实现机制【一】(图文并茂)

怎么在Windows下使用Makefile文件

概述

LINUX 内核中 SCSI 子系统由 SCSI 上层,中间层和底层驱动模块 [1] 三部分组成,主要负责管理 SCSI 资源和处理其他子系统,如文件系统,提交到 SCSI 子系统中的 IO 请求。因此,理解 SCSI 子系统的 IO 处理机制对理解整个 SCSI 子系统就显的十分重要,同时也有助于理解整个 LINUX 内核的 IO 处理机制。本文从 SCSI 设备访问请求的提交,SCSI 子系统对访问请求的处理和 SCSI 子系统错误处理三个方面,阐述了 SCSI 子系统的 IO 处理机制。

SCSI 设备访问请求的提交

SCSI 设备访问请求的提交分为两个步骤:用户空间提交访问请求到通用块层以及通用块层提交块访问请求到 SCSI 子系统。

用户空间提交访问请求到通用块层

在 LINUX 用户空间,有三种方式提交对 SCSI 设备的访问请求到通用块层:

  • 通过文件系统提供的文件访问接口进行访问。对建立在 SCSI 设备上的 LINUX 文件系统中的文件读写操作,就属于这种访问方式;
  • RAW 设备访问方式。这种访问方式比较常见的应用就是dd命令。 RAW 设备访问方式和通过文件系统提供的文件访问接口进行访问的最大区别在于前者对 SCSI 设备直接进行线性地址访问,不需要由文件系统进行地址映射;
  • SCSI PASSTHROUGH 方式。通过 LINUX 提供的 SG 进行访问,就属于这种方式,用户可以直接发 CDB[2] 命令给 SCSI 设备。所以,通过该接口,用户可以做一些 SCSI 管理操作,如 SES 管理等。

图 1 显示了 LINUX 内核对于三种请求提交方式的处理过程。​

经由文件系统或 RAW 设备方式提交的请求,会通过底层块设备访问层(ll_rw_block()),由其生成块 IO 请求(BIO),并提交给通用块层 [3] ;而通过 SG 接口提交的访问请求,会调用 SCSI 中间层提供的接口,将请求直接交由通用块层进行处理。

通用块层提交块访问请求到 SCSI 子系统

为什么要通过通用块层呢?这是因为首先通用块层会根据磁盘访问的特性对请求进行优化操作;其次,通用块层提供了调度功能,能够对请求进行调度;再次,通用块层可扩展的结构,使各种设备的块驱动都能比较容易的和其集成。

当请求提交到通用块层后,通用块层需要完成准备,调度并交付块访问请求给 SCSI 中间层的操作。块访问请求可以理解为描述了块访问区域,访问方式和关联的 BIO 的请求,在内核中用 'struct request'结构表示。块设备会有对应的块访问请求设备队列,用于记录需要该设备处理的访问请求,新生成的块访问请求会被加入到对应设备的块访问请求队列中。 SCSI 子系统对 IO 的处理,实际上是处理块访问请求队列上的块访问请求。

通用块层提供了两种方式调度处理块访问请求队列:直接调度和通过 LINUX 内核工作队列机制调度执行。两种方式,最后都会调用块访问请求队列处理函数进行处理,而 SCSI 设备在初始化时会向通用块层注册 SCSI 子系统定义的块访问请求队列处理函数。清单 1[4] 显示了这个过程。这样当通用块层处理 SCSI 设备的块访问请求队列时,调用的就是 SCSI 中间层定义的这些处理函数。通过这种方式,通用块层就将块访问请求的处理交给了 SCSI 子系统。

清单 1. 处理函数

struct request_queue *scsi_alloc_queue(struct scsi_device *sdev) 
 {   ……
    q = blk_init_queue(scsi_request_fn, NULL);
     //request generate block layer allocate a request queue 
    ……
    blk_queue_prep_rq(q, scsi_prep_fn); //Prepare a scsi request 
    blk_queue_max_hw_segments(q, shost->sg_tablesize); 
    //define sg table size 
    ……
    blk_queue_softirq_done(q, scsi_softirq_done); 
 }

SCSI 子系统处理块访问请求

当 SCSI 子系统的请求队列处理函数被通用块层调用后,SCSI 中间层会根据块访问请求的内容,生成、初始并提交 SCSI 命令 (struct scsi_cmd) 到 SCSI TARGET 端。

SCSI 命令初始化和提交

SCSI 命令记录了命令描述块 (CDB),感测数据缓存 (SENSE BUFFER),IO 超时时间等 SCSI 相关的信息和 SCSI 子系统处理命令需要的一些其他信息,如回调函数等。清单 2 显示了这个命令的主要结构。

清单 2. 主要结构

struct scsi_cmnd {
  ……
    void (*done) (struct scsi_cmnd *);   /* Mid-level done function */ 
    ……
    int retries;                /*retried time*/ 
    int timeout_per_command;   /*timeout define*/ 
    ……
    enum dma_data_direction sc_data_direction;  /*data transfer direction*/ 
    ……
    unsigned char cmnd[MAX_COMMAND_SIZE];   /*cdb*/ 
    void *request_buffer;        /* Actual requested buffer */ 
    struct request *request;     /* The command we are working on */ 
    ……
    unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE]; 
                               /* obtained by REQUEST SENSE when 
                               * CHECK CONDITION is received on original 
                               * command (auto-sense) */ 
    /* Low-level done function - can be used by */
    /*low-level driver to point  to completion function. */ 
    void (*scsi_done) (struct scsi_cmnd *); 
    ……
 };

初始化的过程首先按照电梯调度算法,从块设备的请求队列上取出一个块访问请求,根据块访问请求的信息,定义 SCSI 命令中数据传输的方向,长度和地址。其次,定义 CDB,SCSI 中间层的回调函数等。

在完成初始化后,SCSI 中间层通过调用scsi_host_template[5]结构中定义的queuecommand函数将 SCSI 命令提交给 SCSI 底层驱动部分。queuecommand函数,是一个 SCSI 命令队列处理函数,在 SCSI 底层驱动中,定义了queuecommand函数的具体实现。因此,SCSI 中间层,调用queuecommand函数实际上就是调用了底层驱动定义的queuecommand函数的处理实体,将 SCSI 命令提交给了各个厂家定义的 SCSI 底层驱动进行处理。这个过程和通用块设备层调用 SCSI 中间层的处理函数进行块请求处理的机制很相似,这也体现了 LINUX 内核代码具有很好的扩展性。底层驱动接受到请求后,就要开始处理 SCSI 命令了,这一层和硬件关系紧密,所以这块代码一般都是由各个厂家自己实现。基本流程可概括为:从底层驱动维护的队列中,取出一个 SCSI 命令,封装成厂家自定义的请求格式,然后采用 DMA 或者其他方式,将请求提交给 SCSI TARGET 端,由 SCSI TARGET 端对请求处理,并返回执行结果给 SCSI 底层驱动层。

SCSI 命令执行结果的处理

当 SCSI 底层驱动接受到 SCSI TARGET 端返回的命令执行结果后,SCSI 子系统主要通过两次回调过程完成对命令执行结果的处理。 SCSI 底层驱动在接受到 SCSI TARGET 端返回的命令执行结果后,会调用 SCSI 中间层定义的回调函数,将处理结果交付给 SCSI 中间层进行处理,这是第一次回调过程。 SCSI 中间层处理完成后,将调用 SCSI 上层定义的回调函数,结束 IO 在整个 SCSI 子系统中的处理,这为第二次回调过程。

第一次回调:

SCSI 中间层在调用queuecommand函数将 SCSI 命令提交给 SCSI 底层驱动的同时,也将回调函数指针传给了 SCSI 底层驱动。底层驱动接受到 SCSI TARGET 端返回的命令执行结果后,会调用该回调函数,产生一个中断号为 BLOCK_SOFTIRQ 的软中断进行第一次回调处理。在这次回调处理过程中,SCSI 中间层首先会根据 SCSI 底层驱动处理的结果判断请求处理是否成功。处理成功,并不意味着处理没有错误,而是返回的信息,能够让 SCSI 中间层很明确的知道,对于这个命令,中间层已经没有必要继续进行处理了。所以,对于处理成功的 SCSI 命令,SCSI 中间层会调用第二次回调函数进入到第二次回调过程。清单 3 显示了 SCSI 中间层定义的该软中断的处理函数。

清单 3. 该软中断的处理函数

static void scsi_softirq_done(struct request *rq) 
 { 
    ……
    disposition = scsi_decide_disposition(cmd); 
    ……
    switch (disposition) { 
      case SUCCESS: 
        scsi_finish_command(cmd);   
        //enter to second callback process 
        break; 
      case NEEDS_RETRY: 
        scsi_retry_command(cmd); 
        break; 
      case ADD_TO_MLQUEUE: 
        scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY); 
         break; 
       default: 
         if (!scsi_eh_scmd_add(cmd, 0)) 
            scsi_finish_command(cmd); 
    } 
 }

第二次回调:

不同的 SCSI 上层模块会定义自己不同的第二次回调函数,如 SD 模块,会在sd_init_command函数中,定义自己的第二次回调函数sd_rw_intr,这个回调函数会根据 SD 模块的需要,对 SCSI 命令执行的结果做进一步的处理。清单 4 显示了 SD 模块注册第二次回调的代码。虽然各个 SCSI 上层模块可以定义自己的第二次回调函数,但是这些回调函数最终都会结束 SCSI 子系统对这个块访问请求的处理。

清单 4. SD 模块注册第二次回调的代码

static int sd_init_command(struct scsi_cmnd * SCpnt) 
 { 
    ……
    SCpnt->done = sd_rw_intr; 
    return 1; 
 }

SCSI 子系统的错误处理

由于 SCSI 底层驱动是由厂商自己实现的,在此就不予讨论。除此之外,SCSI 子系统的出错处理,主要是由 SCSI 中间层完成。在第一次回调过程中,SCSI 底层驱动将 SCSI 命令的处理结果以及获取的 SCSI 状态信息返回给 SCSI 中间层,SCSI 中间层先对 SCSI 底层驱动返回的 SCSI 命令执行的结果进行判断,若无法得到明确的结论,则对 SCSI 底层驱动返回的 SCSI 状态、感测数据等进行判断。对于判断结论为处理成功的 SCSI 命令,SCSI 中间层会直接进行第二次回调;对于判断结论为需要重试的命令,则会被加入块设备请求对列,重新被处理。这个过程可称为 SCSI 中间层对 SCSI 命令执行结果的基本判断方法。

一切看起来似乎是这么简单,但是实际上并非如此,有些错误是没有明确的判断依据的,如感测数据错误或 TIMEOUT 错误。为了解决这个问题,LINUX 内核中 SCSI 子系统引入了一个专门进行错误处理的线程,对于无法判断错误原因的 SCSI 命令,都会交由该线程进行处理。线程处理过程和两个队列密切相关,一个是错误处理队列(eh_work_q),一个是错误处理完成队列 (done_q) 。错误处理队列记录了需要进行错误处理的 SCSI 命令,错误处理完成队列记录了在错误处理过程中被处理完成的 SCSI 命令。清单 5 显示了线程对错误处理队列上记录的命令进行错误处理的过程。

清单 5. 错误处理的过程

scsi_unjam_host{ 
    ……
    if (!scsi_eh_get_sense(&eh_work_q, &eh_done_q))  
     //get sense data 
        if (!scsi_eh_abort_cmds(&eh_work_q, &eh_done_q))   
        //abort command 
        scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q);   
        //reset 
    scsi_eh_flush_done_q(&eh_done_q);   
    //complete error io on done_q 
    ……
 }

整个处理过程可归纳为四个阶段:

  • 感测数据查询****阶段
    通过查询感测数据,为处理 SCSI 命令重新提供判断依据,并按照前述基本判断方法进行判断。如果判断结果为成功或者重试,则可将该命令从错误处理队列移到错误处理完成队列。若判断失败,则命令将会继续保留在 SCSI 错误处理队列中,错误处理进入到 ABORT 阶段。
  • ABORT****阶段
    在这个阶段中,错误处理队列上的 SCSI 命令会被主动 ABORT 掉。被 ABORT 的命令,会被加入到错误处理完成队列。若 ABORT 过程结束,错误处理队列上还存在未能被处理的命令,则需进入 START STOP UNIT 阶段进行处理。
  • START STOP UNIT****阶段
    在这个阶段,START STOP UNIT[6] 命令会被发送到与错误处理队列上的命令相关的 SCSI DEVICE 上,去试图恢复 SCSI DEVICE,如果在 START STOP UNIT 阶段结束后,依旧有命令在错误处理队列上,则需要进入 RESET 阶段进行处理。
  • RESET****阶段 RESET 阶段的处理过程分三个层次:DEVICE RESET,BUS RESET 和 HOST RESET 。首先对与错误队列上的命令相关的 SCSI DEVICE,进行 RESET 操作,如果 DEVICE RESET 后,SCSI 设备能处于正常状态,则和该设备相关的错误处理队列上的错误命令,会被加入到错误处理完成队列中。若通过 DEVICE RESET 不能处理所有的错误命令,则需进入到 BUS RESET 阶段,BUS RESET 会对与错误处理队列上的命令相关的 BUS,进行 RESET 操作。若 BUS RESET 还不能成功处理所有错误处理队列上的 SCSI 命令,则会进入到 HOST RESET 阶段,HOST RESET 会对与错误处理队列上的命令相关的 HOST 进行 RESET 操作。当然,很有可能 HOST RESET 也不能成功处理所有错误命令,则只能认为错误处理队列上错误命令相关的 SCSI 设备不能被使用了。这些不能被使用的设备会被标记为不能使用状态,同时相关的错误命令都会被加入到错误处理完成队列中。

对于被加入到错误处理完成队列上的请求,若是在设备状态正确,命令重试次数小于允许次数的情况下,这些命令将被重新加入到块访问请求队列中,进行重新处理;否则,直接进行第二次回调处理,完成 SCSI 子系统对块访问请求的处理。这样,SCSI 子系统就完成了 SCSI 命令错误处理的整个过程。

结束语

本文浅析了 SCSI 子系统中的 IO 处理机制,希望对大家理解 SCSI 子系统和块设备驱动能有所帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/94151.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

PreSTU:一个专门为场景文本理解而设计的简单预训练模型

摘要:在视觉与语言(V&L)模型中,阅读和推理图像中的文本的能力往往是缺乏的。我们如何才能学习出强大的场景文本理解(STU)的V&L模型呢?本文分享自华为云社区《场景文本理解预训练PreSTU》…

C#编程基础(万字详解,这一篇就够了)

C#及其开发环境简介 C#概述 C#的编程功能 C#与.Net的关系 .Net C# C#的集成开发环境 Windows上编写C#程序 Linux/Mac OS上编写C#程序 运行第一个HelloWorld程序 C#基本语法 程序实例 C#基本语法 using关键字 class关键字 注释 成员变量 成员函数 实例化一个类…

【图像分割】粒子群优化指数熵图像分割【含Matlab源码 287期】

⛄一、图像分割简介 理论知识参考:【基础教程】基于matlab图像处理图像分割【含Matlab源码 191期】 ⛄二、部分源代码 %%无递推 clc; clear; Cimread(‘1.jpg’); Crgb2gray©; %Cimnoise(C,‘gaussian’,0,0.005); figure subplot(1,2,1) imshow© tic …

vue初级入门以及demo实现详解

vue的初级入门1,vue是什么2,vue的特点2.1,采用组件化模式2.2,声明式编码2.3,虚拟DOM Diff算法3,vue的环境的基本配置和使用3.1,在浏览器安装开发工具3.2,开发环境3.3,de…

C++--数据结构--图的相关概念及模拟实现--高阶0712

1. 图的基本概念 图(G)是由顶点(V)集合及顶点间的关系(边 E)组成的一种数据结构; 顶点:图中的结点,第i个顶点记作vi。 两个顶点vi和vj相关称作vi和vj之间有一条边。 …

04查找算法:顺序查找法、二分查找法

开始系统学习算法啦!为后面力扣和蓝桥杯的刷题做准备!这个专栏将记录自己学习算法是的笔记,包括概念,算法运行过程,以及代码实现,希望能给大家带来帮助,感兴趣的小伙伴欢迎评论区留言或者私信博…

技术分享 | 测试平台开发-前端开发之Vue.js 框架的使用(二)

首先将 Vue.js 下载到本地&#xff0c;本章就以本地的 Vue.js 为例。在本地创建一个工作区即创建一个文件夹&#xff0c;使用 vscode 打开&#xff0c;将 Vue.js 放到工作区目录下。 创建挂载元素 首先创建一个 index.html 的文件&#xff0c;使用 <script src"./vue…

educoder:Numpy图像处理

第1关&#xff1a;图像翻转 任务描述 本关任务&#xff1a;读取一副图片&#xff0c;实现图片的翻转。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.如何读取和保存图像&#xff0c;2.图像的数组表示。 彩色图像 与人脑不同&#xff0c;计算机读入一张…

给视频智能配音怎么弄?一步一步让你学会配音操作

如今我们的生活变得越来越便捷化&#xff0c;因此越来越多的新鲜事物出现在了我们的眼前&#xff0c;例如配音。随着短视频逐渐火爆起来&#xff0c;相信屏幕前的你也剪辑过自己拍摄的视频&#xff0c;并将其发到各大社交平台上吧&#xff01;但是对于视频剪辑来说&#xff0c;…

HTML CSS

一、HTML 介绍 HTML 是一门语言&#xff0c;所有的网页都是用HTML 这门语言编写出来的。HTML(HyperText Markup Language)&#xff1a;超文本标记语言&#xff1a;&#xff08;1&#xff09;超文本&#xff1a;超越了文本的限制&#xff0c;比普通文本更强大。除了文字信息&…

2022 年值得了解的基础设施即代码工具清单

云计算的出现彻底改变了每个 IT 领域。不排除 IT 基础设施。管理员不得不手动配置资源并管理大型 Excel 表格中的数据的日子已经一去不复返了。在当今动态变化的网络需求下&#xff0c;人工维护 IT 基础设施的想法非常可怕。这就是基础设施即代码工具的用武之地。 简单地说&…

【PostgreSQL的“double buffers“刷脏机制和参数】

PostgreSQL数据库使用双缓存写数据&#xff0c;shared_buffer OS page cache,下图是PG与OS内存交互的过程 ,在PostgreSQL中&#xff0c;shared_buffers所代表的内存区域可以看成是一个以8KB的block为单位的数组&#xff0c;即最小的分配单位是8KB。这正好是一个page的大小&…

手慢无,阿里云神作被《Spring Boot进阶原理实战》成功扒下,限时

又来给大家分享好书了&#xff1a;郑天民老师的 《Spring Boot进阶:原理、实战与面试题分析》&#xff0c;别问网上有没有开源版本&#xff01;问就是我也不知道&#xff0c;哈哈&#xff01;但我会有 郑天民是谁&#xff1f; 资深架构师和技术专家&#xff0c;有近15年的软件…

中英文说明书丨艾美捷CD8α体内抗体相关研究方案

艾美捷CD8α体内抗体英文说明&#xff1a; CD8a (Ly 2.2) is present on the surface of most thymocytes and mature T-lymphocyte subsets including most T-suppressor/cytotoxic cells. CD8 participates in T cell activation by binding to T cell receptor complex and…

python csv数据集处理

目录 一&#xff1a;数据集准备 二&#xff1a;加载文件 三&#xff1a;查看DataFrame的头部和尾部数据 &#xff0c;shape 四&#xff1a;统计摘要 五&#xff1a;获取数据 六&#xff1a;缺失值处理 一&#xff1a;数据集准备 可以新建txt&#xff0c;复制下面内容&…

登录处cookie验证逻辑漏洞——以熊海CMS为例

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是登录处cookie验证逻辑漏洞。 免责声明&#xff1a; 本文所介绍的内容仅做学习交流使用&#xff0c;严禁利用文中技术进行非法行为&#xff0c;否则造成一切严重后果自负&#xff01; 再次强调&#xff1a;严禁对未…

室内定位:物联网时代发展的必然需求

室内定位是指在室内环境中实现位置定位&#xff0c;主要采用无线通讯、基站定位、惯导定位等多种技术集成形成一套室内位置定位体系&#xff0c;从而实现人员、物体等在室内空间中的位置监控。 物联网时代&#xff0c;位置服务已成为人们工作生活必不可少的服务之一。蓝牙、5G、…

交换机和路由器

一台交换机有很多个端口&#xff0c;这些端口有各自的编号 计算机的网卡通过网线连接到交换机的网口上 在交换机中&#xff0c;有一张端口和MAC地址的映射表&#xff0c;称为MAC地址表&#xff0c;交换机维护这张表 交换机里的主机都是处在同一个子网里 不同子网之间是不能直…

一起Talk Android吧(第四百四十三回:UI控件之NumberPicker)

文章目录概念介绍使用方法内容总结各位看官们大家好&#xff0c;上一回中咱们说的例子是"UI控件之下拉列表:Spinner",这一回中说的例子是" UI控件之NumberPicker"。闲话休提&#xff0c;言归正转&#xff0c;让我们一起Talk Android吧&#xff01;概念介绍…

Shiro框架入门

概述 官网介绍如下&#xff1a; Apache Shiro™ is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure a…