我的编程空间,编程开发者的网络收藏夹
学习永远不晚

如何分析Linux内核SCSI IO子系统

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

如何分析Linux内核SCSI IO子系统

如何分析Linux内核SCSI IO子系统,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

概述

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 内核对于三种请求提交方式的处理过程。

如何分析Linux内核SCSI IO子系统
图 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 命令记录了命令描述块 (CDB),感测数据缓存 (SENSE BUFFER),IO 超时时间等 SCSI 相关的信息和 SCSI 子系统处理命令需要的一些其他信息,如回调函数等。清单 2 显示了这个命令的主要结构。

清单 2. 主要结构

struct scsi_cmnd {

……

void (*done) (struct scsi_cmnd *);

……

int retries;

int timeout_per_command;

……

enum dma_data_direction sc_data_direction;

……

unsigned char cmnd[MAX_COMMAND_SIZE];

void *request_buffer;

struct request *request;

……

unsigned char sense_buffer[SCSI_SENSE_BUFFERSIZE];

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 命令错误处理的整个过程。

关于如何分析Linux内核SCSI IO子系统问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注编程网行业资讯频道了解更多相关知识。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

如何分析Linux内核SCSI IO子系统

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

如何分析Linux内核SCSI IO子系统

如何分析Linux内核SCSI IO子系统,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。概述LINUX 内核中 SCSI 子系统由 SCSI 上层,中间层和底层驱动模块 [
2023-06-17

Linux系统中如何分析内核

本篇文章给大家分享的是有关Linux系统中如何分析内核,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。在Linux系统中,内核是一个很重要的部分,属于Linux系统中的核心程序。
2023-06-28

如何分析linux系统内核bsp

这期内容当中小编将会给大家带来有关如何分析linux系统内核bsp,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。BSP简介:Board Support Package顾名思义,就是板级支持包,说白了就是最
2023-06-28

如何分析Linux系统内核的作用和功能

这篇文章主要为大家分析了如何分析Linux系统内核的作用和功能的相关知识点,内容详细易懂,操作细节合理,具有一定参考价值。如果感兴趣的话,不妨跟着跟随小编一起来看看,下面跟着小编一起深入学习“如何分析Linux系统内核的作用和功能”的知识吧
2023-06-28

如何分析C++ 系统IO流

本篇文章为大家展示了如何分析C++ 系统IO流,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。前言:本次讲解一个小知识点,也是最常见的一个知识点:iostream;不管编写什么程序,必然会使用到IO流
2023-06-21

如何解析Linux系统架构中的内核

如何解析Linux系统架构中的内核,相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。概述Linux系统一般有4个主要部分组成,内核、shell、文件系统和应用程序。内核、shell
2023-06-16

Linux系统中内核调试的示例分析

这篇文章主要介绍了Linux系统中内核调试的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。调试是软件开发过程中一个必不可少的环节,在 Linux 内核开发的过程中也不
2023-06-12

怎么进行Linux系统内核架构分析

这期内容当中小编将会给大家带来有关怎么进行Linux系统内核架构分析,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。Linux系统中内核是一个非常重要的一部分,那么Linux内核具体是什么样子呢?下面本篇文
2023-06-28

如何分析Linux内核源码do_fork

本篇文章为大家展示了如何分析Linux内核源码do_fork,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。我们都知道进程是Linux内核中最为重要的一个抽象概念,那么我们平时在fork一个进程时,该
2023-06-16

如何进行java的io系统分析

这篇文章将为大家详细讲解有关如何进行java的io系统分析,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。一. Input和Output1. stream代表的是任何有能力产出数据的数据源,或
2023-06-03

如何分析Linux内核双向链表

这篇文章将为大家详细讲解有关如何分析Linux内核双向链表,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。Linux中双向链表是指将双向链表节点嵌套在其它的结构体中;在遍历链表的时候,根据双链
2023-06-28

如何理解Linux系统IO分析工具的iotop参数

这篇文章主要讲解了“如何理解Linux系统IO分析工具的iotop参数”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何理解Linux系统IO分析工具的iotop参数”吧!简介:iotop
2023-06-13

如何实现LINUX系统2.4内核升级到2.6内核

本篇内容介绍了“如何实现LINUX系统2.4内核升级到2.6内核”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一. 在升级前必须对以下的组件
2023-06-10

Linux系统如何查看内核代码

要查看Linux系统的内核代码,可以按照以下步骤进行操作:1. 下载内核源代码:- 可以从官方网站上下载最新的内核源代码,网址为 https://www.kernel.org/- 也可以通过Git命令克隆Linux内核的源代码仓库,命令如下
2023-10-18

Linux系统如何查看内核版本

这篇“Linux系统如何查看内核版本”文章,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要参考一下,对于“Linux系统如何查看内核版本”,小编整理了以下知识点,请大家跟着小编的步伐一步一步的慢慢理解,接下来就让我们
2023-06-28

Ubuntu系统中如何升级Linux内核

这篇文章主要介绍“Ubuntu系统中如何升级Linux内核”,在日常操作中,相信很多人在Ubuntu系统中如何升级Linux内核问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Ubuntu系统中如何升级Linu
2023-06-13

Linux操作系统的NTFS和内核的示例分析

今天就跟大家聊聊有关Linux操作系统的NTFS和内核的示例分析,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。传统编译内核模块的方法繁琐而费时,这里介绍一种快速编译所需要内核模块的新
2023-06-17

如何分析Linux系统umask

本篇文章为大家展示了如何分析Linux系统umask,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。umask的主要作用就是指定在建立文件时预设的权限掩码,简单点说就是为了控制默认权限的。语  法:u
2023-06-28

如何分析Linux系统BSP

小编今天带大家了解如何分析Linux系统BSP,文中知识点介绍的非常详细。觉得有帮助的朋友可以跟着小编一起浏览文章的内容,希望能够帮助更多想解决这个问题的朋友找到问题的答案,下面跟着小编一起深入学习“如何分析Linux系统BSP”的知识吧。
2023-06-28

如何在Linux系统上安装Linux内核头文件

本篇内容主要讲解“如何在Linux系统上安装Linux内核头文件”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“如何在Linux系统上安装Linux内核头文件”吧!当你在编译一个设备驱动模块时,你
2023-06-13

编程热搜

目录