浅析Linux SCSI子系统:设备管理
文章目录
- 概述
- 设备管理数据结构
- scsi_host_template:SCSI主机适配器模板
- scsi_host:SCSI主机适配器
- 主机适配器支持DIF
- scsi_target:SCSI目标节点
- scsi_device:SCSI设备
- 添加主机适配器
- 构建sysfs目录
- 添加SCSI设备
- 挂载Lun
- IO请求队列初始化
- 相关参考
概述
Linux SCSI子系统通过SCSI主机适配器(HBA)接入所有SCSI存储设备,在Linux系统中,可以安装多种主机适配器,SCSI中层会提供主机适配器的统一抽象,这些主机适配器的厂商提供具体的低层驱动实现;主机适配器接入到SCSI子系统后,SCSI会通过扫描或者低层驱动主动上报的方式,接入主机适配器下挂的所有SCSI存储设备。
设备管理数据结构
Linux SCSI子系统通过Scsi_Host、scsi_target和scsi_device数据结构分别来描述SCSI主机适配器、目标节点和逻辑单元,它们之间的关系如下:
- Linux系统支持安装多个主机适配器,所有接入的主机适配器在SCSI中层都会有对应的Scsi_Host结构。Scsi_Host结构描述了SCSI主机适配器的通用属性和方法,由低层驱动根据scsi_host_template进行创建并注册到SCSI子系统中;
- Scsi_Host维护了两个设备链表:target链表和device链表,其中target链表管理所有的scsi_targe结构,device链表管理所有的scsi_device结构。
scsi_host_template:SCSI主机适配器模板
scsi_host_template描述了SCSI主机适配器的公共属性和接口,包括主机队列深度、命令处理回调、错误处理回调等。低层驱动自定义SCSI主机适配器模板,SCSI中层会提供接口由低层驱动调用,根据SCSI主机适配器模板信息生成相应的Scsi_Host实例。
struct scsi_host_template {struct module *module;const char *name;int (* queuecommand)(struct Scsi_Host *, struct scsi_cmnd *); // SCSI命令下发接口int (* eh_abort_handler)(struct scsi_cmnd *); // 错误恢复:取消指定的SCSI命令int (* eh_device_reset_handler)(struct scsi_cmnd *); // 错误恢复:复位SCSI设备int (* eh_target_reset_handler)(struct scsi_cmnd *); // 错误恢复:复位SCSI目标节点int (* eh_bus_reset_handler)(struct scsi_cmnd *); // 错误恢复:复位SCSI总线int (* eh_host_reset_handler)(struct scsi_cmnd *); // 错误恢复:复位SCSI主机适配器int (* slave_alloc)(struct scsi_device *); // 添加SCSI设备时,中层调用让驱动传递设备私有数据int (* slave_configure)(struct scsi_device *); void (* slave_destroy)(struct scsi_device *); // 删除SCSI设备时,中层调用让驱动释放设备私有数据int (* target_alloc)(struct scsi_target *); // 添加SCSI目标时,中层调用让驱动传递设备私有数据void (* target_destroy)(struct scsi_target *); // 删除SCSI目标时,中层让驱动释放设备私有数据int (* scan_finished)(struct Scsi_Host *, unsigned long);void (* scan_start)(struct Scsi_Host *);int (* change_queue_depth)(struct scsi_device *, int); // 调整SCSI设备的队列深度int (* map_queues)(struct Scsi_Host *shost);enum blk_eh_timer_return (*eh_timed_out)(struct scsi_cmnd *); // 低层驱动自定义IO超时处理策略int (*host_reset)(struct Scsi_Host *shost, int reset_type);int can_queue;int this_id;unsigned short sg_tablesize;unsigned short sg_prot_tablesize;unsigned int max_sectors;unsigned long dma_boundary;short cmd_per_lun;unsigned char present;int tag_alloc_policy;unsigned track_queue_depth:1;unsigned supported_mode:2;unsigned unchecked_isa_dma:1;unsigned use_clustering:1;unsigned emulated:1;unsigned skip_settle_delay:1;unsigned no_write_same:1;unsigned force_blk_mq:1;unsigned int max_host_blocked;
};
scsi_host:SCSI主机适配器
SCSI主机适配器通常也是PCI设备,由内核的PCI子系统负责扫描接入。
struct Scsi_Host {struct list_head __devices; // 管理Host下的所有scsi_devicestruct list_head __targets; // 管理Host下的所有scsi_targetstruct list_head starved_list;struct list_head eh_cmd_q;struct task_struct * ehandler; struct completion * eh_action; wait_queue_head_t host_wait;struct scsi_host_template *hostt; // 指向主机适配器模板的指针struct scsi_transport_template *transportt;union {struct blk_queue_tag *bqt;struct blk_mq_tag_set tag_set;};atomic_t host_busy; atomic_t host_blocked;unsigned int host_failed; unsigned int host_eh_scheduled; unsigned int host_no; int eh_deadline;unsigned long last_reset;unsigned int max_channel;unsigned int max_id;u64 max_lun;unsigned int unique_id;unsigned short max_cmd_len;int this_id;int can_queue;short cmd_per_lun;short unsigned int sg_tablesize;short unsigned int sg_prot_tablesize;unsigned int max_sectors;unsigned long dma_boundary;unsigned nr_hw_queues;...char work_q_name[20];struct workqueue_struct *work_q;struct workqueue_struct *tmf_work_q;unsigned int max_host_blocked;unsigned int prot_capabilities;unsigned char prot_guard_type;enum scsi_host_state shost_state;struct device shost_gendev, shost_dev;void *shost_data;unsigned long hostdata[0] __attribute__ ((aligned (sizeof(unsigned long)))); // 可用于存储低层驱动私有数据
}
主机适配器支持DIF
Scsi_Host的prot_capabilities字段描述了SCSI主机适配器支持DIF的能力。
enum scsi_host_prot_capabilities {SHOST_DIF_TYPE1_PROTECTION = 1 << 0, /* T10 DIF Type 1 */SHOST_DIF_TYPE2_PROTECTION = 1 << 1, /* T10 DIF Type 2 */SHOST_DIF_TYPE3_PROTECTION = 1 << 2, /* T10 DIF Type 3 */SHOST_DIX_TYPE0_PROTECTION = 1 << 3, /* DIX between OS and HBA only */SHOST_DIX_TYPE1_PROTECTION = 1 << 4, /* DIX with DIF Type 1 */SHOST_DIX_TYPE2_PROTECTION = 1 << 5, /* DIX with DIF Type 2 */SHOST_DIX_TYPE3_PROTECTION = 1 << 6, /* DIX with DIF Type 3 */
}
scsi_target:SCSI目标节点
struct scsi_target {struct scsi_device *starget_sdev_user;struct list_head siblings; // 用于挂接在Host的__targets链表中struct list_head devices; // 管理目标节点下的所有SCSI设备的链表struct device dev;struct kref reap_ref; unsigned int channel;unsigned int id; unsigned int create:1; unsigned int single_lun:1; // 标识是否是单Lununsigned int pdt_1f_for_no_lun:1; unsigned int no_report_luns:1;unsigned int expecting_lun_change:1; atomic_t target_busy;atomic_t target_blocked;unsigned int can_queue;unsigned int max_target_blocked;char scsi_level;enum scsi_target_state state;void *hostdata; // 驱动私有数据unsigned long starget_data[0]; // 驱动私有数据
}
scsi_device:SCSI设备
在SCSI子系统的语义中,SCSI设备对应的才是逻辑单元的概念,也就是我们常说的Lun。
struct scsi_device {struct Scsi_Host *host;struct request_queue *request_queue; // IO请求队列struct list_head siblings; // 用于链接到Host的__devices链表struct list_head same_target_siblings; atomic_t device_busy;atomic_t device_blocked; spinlock_t list_lock;struct list_head cmd_list; // 下发到设备的SCSI命令链表struct list_head starved_entry;unsigned short queue_depth; unsigned short max_queue_depth; unsigned short last_queue_full_depth; unsigned short last_queue_full_count; unsigned long last_queue_full_time;unsigned long queue_ramp_up_period;unsigned long last_queue_ramp_up;unsigned int id, channel;u64 lun;unsigned int manufacturer; unsigned sector_size; // 扇区大小void *hostdata; unsigned char type;char scsi_level;char inq_periph_qual; ...struct list_head event_list;struct work_struct event_work;unsigned int max_device_blocked; atomic_t iorequest_cnt;atomic_t iodone_cnt;atomic_t ioerr_cnt;struct device sdev_gendev,sdev_dev;struct execute_work ew; /* used to get process context on put */struct work_struct requeue_work;struct scsi_device_handler *handler;void *handler_data;unsigned char access_state;struct mutex state_mutex;enum scsi_device_state sdev_state;struct task_struct *quiesced_by;unsigned long sdev_data[0];
}
添加主机适配器
驱动添加主机适配器前,需要先调用scsi_alloc_host分配Scsi_Host结构。scsi_alloc_host根据驱动填写的主机适配器模板对Scsi_Host结构进行初始化,同时允许驱动传入特定的size,这样SCSI中层在分配Scsi_Host结构时,会在尾部申请额外的空间存储驱动的私有数据。
完成Scsi_Host的结构申请后,驱动调用scsi_add_host将主机适配器添加到系统中:
构建sysfs目录
添加主机适配器的过程中,一个很重要的部分就是在sysfs文件系统构建相关的节点,以支持应用程序访问SCSI子系统的相关信息。
添加SCSI设备
无论是SCSI总线扫描或者是驱动发现的SCSI设备,最后都需要调用scsi_add_device接口将设备添加到系统中。scsi_add_device执行流程如下:
- 确认SCSI目标节点在系统中是否存在,不存在就会创建新的scsi_target结构;
- 向SCSI目标节点中添加Lun,即scsi_device。
挂载Lun
scsi_probe_and_add_lun负责挂载Lun设备到系统中,它的执行流程如下:
- 确认SCSI设备在系统中是否存在,不存在则创建新的scsi_device结构;
- 向设备发送INQUIRY命令,设备在正常接入的情况下,会返回INQUIRY数据;
- 解析INQUIRY数据,初始化SCSI设备信息
IO请求队列初始化
分配scsi_device结构时,也会初始化设备的IO请求队列。根据主机适配器是否支持多队列,初始化函数也会不同。对于单队列,SCSI使用scsi_old_alloc_queue函数分配IO请求队列。
相关参考
- 《存储技术原理分析:基于Linux 2.6内核源代码分析》
相关文章:

浅析Linux SCSI子系统:设备管理
文章目录 概述设备管理数据结构scsi_host_template:SCSI主机适配器模板scsi_host:SCSI主机适配器主机适配器支持DIF scsi_target:SCSI目标节点scsi_device:SCSI设备 添加主机适配器构建sysfs目录 添加SCSI设备挂载LunIO请求队列初…...

爬虫逆向实战(二十五)--某矿采购公告
一、数据接口分析 主页地址:某矿 1、抓包 通过抓包可以发现数据接口是cgxj/by-lx-page 2、判断是否有加密参数 请求参数是否加密? 通过查看“载荷”模块可以发现有一个param的加密参数 请求头是否加密? 无响应是否加密? 无c…...

DPLL 算法之分裂策略
前言 DPLL算法确实是基于树(或二叉树)的回溯搜索算法,它用于解决布尔可满足性问题(SAT问题)。下面我会分析您提到的DPLL算法中的分裂策略,以及它是如何在搜索过程中起作用的。 DPLL算法中的分裂策略是用于在…...

Jmeter+ServerAgent
一、Jmeter 下载 https://jmeter.apache.org/download_jmeter.cgi选择Binaries二进制下载 apache-jmeter-5.6.2.tgz 修改配置文件 jmeter下的bin目录,打开jmeter.properties 文件 languagezh_CN启动命令 cd apache-jmeter-5.6/bin sh jmeter二、ServerAgent 监…...

打破数据孤岛!时序数据库 TDengine 与创意物联感知平台完成兼容性互认
新型物联网实现良好建设的第一要务就是打破信息孤岛,将数据汇聚在平台统一处理,实现数据共享,放大物联终端的行业价值,实现系统开放性,以此营造丰富的行业应用环境。在此背景下,物联感知平台应运而生&#…...

ubuntu22安装和部署Kettle8.2
前提 kettle是纯java编写的etl开源工具,目前kettle7和kettle8都需要java8或者以上才能正常运行。所以运行kettle前先检查java环境是否正确配置,java版本是否是8或者以上。 kettle安装 1、创建kettle目录,并将kettle的zip包解压到kettle目…...

修复 Ubuntu Linux 中的“找不到命令‘python’”错误
在ubuntu 22.04版本中使用 callstack backtrace.txt 回溯错误点是碰到了该问题。 参考文章:链接 ubuntu22.04版本中默认只安装了python3版本 查看python各个版本安装情况,在终端输入命令: type python python2 python3如果安装了对应的版本…...

【业务功能篇86】微服务-springcloud-系统性能压力测试-jmeter-性能优化-JVM参数调优
系统性能压力测试 一、压力测试 压力测试是给软件不断加压,强制其在极限的情况下运行,观察它可以运行到何种程度,从而发现性能缺陷,是通过搭建与实际环境相似的测试环境,通过测试程序在同一时间内或某一段时间内&…...

mysql的登录与退出
mysql是c/s架构,意味着同时要有客户端和服务端 1 找到客户端。mysql.exe的安装目录 打开命令行 2 输入对应的服务器的ip,如果是本地,就是Localhost,如果是远程服务器,那就输入对应ip/域名。并且指定mysql监听的端口 …...

SOLIDWORKS工程图转DWG图层映射技巧
DWG格式的图纸在工程制图中有着非常重要的地位,工程实践中常常就需要将SOLIDWORKS工程图进行转换。对于两者之间数据衔接的妥善处理,是提升工作效率的有效手段。基于此目的,本次我们将介绍数据衔接的一个有效解决方案:图层数据的映…...

PMAC与Modbus主站进行Modbus Tcp通讯
PMAC与Modbus主站进行Modbus Tcp通讯 创建modbus通讯参数 在项目的PMAC Script Language\Global Includes下创建一个名为00_Modbus_Para.pmh的pmh文件。 Modbus[0].Config.ServerPort 0 Modbus[0].Config.ConnectTimeOut 6000 Modbus[0].Config.SendRecvTimeOut 0 Modbu…...

MyBatis分页插件PageHelper的使用及MyBatis的特殊符号---详细介绍
一,分页的概念 分页是一种将大量数据或内容分割成多个页面以便逐页显示的方式。在分页中,数据被分割成一定数量的页,每页显示一部分数据或内容,用户可以通过翻页或跳分页是一种将大量数据或内容分割成多个页面以便逐页显示的方式。…...

Qt(C++)计算一段程序执行经过的时间
一、前言 在许多应用程序和系统中,需要对经过的时间进行计算和记录。例如 可能想要测量某个操作的执行时间,或者记录一个过程中经过的时间以进行性能分析。在这些场景下,准确地计时是非常重要的。 Qt提供了一个功能强大的计时器类QElapsedTimer,可以方便地记录经过的时间…...

UnionTech OS(统信桌面操作系统)安装 g++ 和 cmake
文章目录 前言一、debian 10简介二、安装 g三、安装cmake参考资料 前言 统信桌面操作系统支持x86、龙芯、申威、鲲鹏、飞腾、兆芯等国产CPU平台,基于debian 10.x 的稳定版本,长期维护的统一内核版本(4.19)。 一、debian 10简介 Debian 10 是一款广泛使…...

php_webshell免杀--从0改造你的AntSword
0x00 前言: 为什么会有改造蚁剑的想法,之前看到有做冰蝎的流量加密,来看到绕过waf,改造一些弱特征,通过流量转换,跳过密钥交互。 但是,冰蝎需要反编译去改造源码,再进行修复bug&am…...

RocketMQ mqadmin java springboot python 调用笔记
命令 mqadmin命令列表 yeqiangyeqiang-MS-7B23:/opt/rocketmq-all-5.1.3-bin-release$ sh bin/mqadmin The most commonly used mqadmin commands are:updateTopic Update or create topicdeleteTopic Delete topic from broker and NameServer.…...

Java aspose 将HTML导出成Excel文件
1.需求 有一批表格的html文件,需要将这些表格导出成excel文件 2.代码 使用第三方库 aspose ByteArrayInputStream htmlIs new ByteArrayInputStream(htmlBuilder.toString().getBytes()); // 将html字符串构建成输入流 LoadOptions lo new LoadOptions(LoadFo…...

原生微信小程序 动态(横向,纵向)公告(广告)栏
先看一下动态效果 Y轴滚动公告的原理是swiper组件在页面中的Y轴滚动,属性vertical,其余属性也设置一下autoplay circular interval"3000" X轴滚动的原理是,利用动画效果,将内容从右往左过渡过去 wxml: &l…...

pandas和polars简单的对比分析
pandas pandas是基于python写的,底层的数据结构是Numpy数据(ndarray)。pandas自身有两个核心的数据结构:DataFrame和Series,前者是二维的表格数据结构,后者是一维标签化数组。 polars polars是用Rust(一种系统级编程…...

Feign远程调用的使用
假设已配好nacos服务:并且已配好userservice、orderservice,点击跳转 Feign是一个声明式的http客户端,官方地址:https://github.com/OpenFeign/feign,其作用就是在程序中帮助我们优雅的实现http请求的发送,…...

Postman API测试之道:不止于点击,更在于策略
引言:API测试的重要性 在当今的软件开发中,API已经成为了一个不可或缺的部分。它们是软件组件之间交互的桥梁,确保数据的流动和功能的实现。因此,对API的测试显得尤为重要,它不仅关乎功能的正确性,还涉及到…...

5G 数字乡村数字农业农村大数据中心项目农业大数据建设方案PPT
导读:原文《5G 数字乡村数字农业农村大数据中心项目农业大数据建设方案PPT》(获取来源见文尾),本文精选其中精华及架构部分,逻辑清晰、内容完整,为快速形成售前方案提供参考。以下是部分内容, 喜…...

Golang Gorm 一对多的添加
一对多的添加有两种情况: 一种是添加用户的时候同时创建文章其次是创建文章关联已经存在的用户。 package mainimport ("gorm.io/driver/mysql""gorm.io/gorm" )// User 用户表 一个用户拥有多篇文章 type User struct {ID int64Name …...

图像扭曲之锯齿
源码: void wave_sawtooth(cv::Mat& src,cv::Mat& dst,double amplitude,double wavelength) {dst.create(src.rows, src.cols, CV_8UC3);dst.setTo(0);double xAmplitude amplitude;double yAmplitude amplitude;int xWavelength wavelength;int yWave…...

【分布式技术专题】「OSS中间件系列」Minio的文件服务的存储模型及整合Java客户端访问的实战指南
Minio的元数据 数据存储 MinIO对象存储系统没有元数据数据库,所有的操作都是对象级别的粒度的,这种做法的优势是: 个别对象的失效,不会溢出为更大级别的系统失效。便于实现"强一致性"这个特性。此特性对于机器学习与大数据处理非…...

构建个人博客_Obsidian_github.io_hexo
1 初衷 很早就开始分享文档,以技术类的为主,一开始是 MSN,博客,随着平台的更替,后来又用了 CSDN,知乎,简书…… 再后来是 Obsidian,飞书,Notion,常常有以下困…...

烟花厂人员作业释放静电行为检测算法
烟花厂人员作业释放静电行为检测算法通过pythonyolo系列算法模型框架,烟花厂人员作业释放静电行为检测算法在工厂车间入口处能够及时捕捉到人员是否触摸静电释放仪。一旦检测到人员进入时没有触摸静电释放仪,系统将自动触发告警。Python是一种由Guido va…...

ARTS挑战第二周-T:PHP数组相关操作
array_combine() 函数 合并两个数组 array_combine()传入2个参数,使用方法如下 array_combine(array $keys, array $values): array 返回一个 array,用来自 keys 数组的值作为键名,来自 values 数组的值作为相应的值。 array_key_exists() 函…...

【如何对公司网络进行限速?一个案例详解】
有不少朋友问到了关于企业网络QoS配置,这个确实在实际网络应用中非常多,基本上大部分企业或个人都用到这个功能,本期我们详细了解下QoS如何对宽带进行限制,QoS如何企业中应用。 一、什么是QoS? Qos是用来解决网络延迟和阻塞等问…...

服务器安全-修改默认ssh端口
防火墙先打开指定端口,要不修改后连不上(端口需要在65535之内) firewall-cmd --list-ports firewall-cmd --add-port54111/tcp --permanent firewall-cmd --reload-------------------- 先让两个端口同时存在,等配置成功后关闭22端口 vim /etc/ssh/sshd_config重启sshd service…...