浅析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_device
	struct list_head	__targets;    // 管理Host下的所有scsi_target
	
	struct 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;	   // 标识是否是单Lun
	unsigned 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内核源代码分析》

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

javaee idea创建maven项目,使用el和jstl

如果使用el表达式出现下图问题 解决办法 这是因为maven创建项目时&#xff0c;web.xml头部声明默认是2.3&#xff0c;这个默认jsp关闭el表达式 办法1 在每个需要用到el和jstl的页面的上面加一句: <% page isELIgnored"false" %> 方法2 修改web.xml文件开…

React 生命周期新旧对比

前言 React16.4版本之后使用了新的生命周期&#xff0c;它使用了一些新的生命周期钩子&#xff08;getDerivedStateFromProps、getSnapshotBeforeUpdate&#xff09;&#xff0c;并且即将废弃老版的3个生命周期钩子&#xff08;componentWillMount、componentWillReceiveProps…

【技术】SpringBoot Word 模板替换

SpringBoot Word 模板替换 什么是 Word 模板替换如何实现 Word 模板替换 什么是 Word 模板替换 模板一般是具有固定格式的内容&#xff0c;其中一部分需要替换。Word 模板通俗的讲是以 Word 的形式制作模板&#xff0c;固定格式和内容&#xff0c;然后将其中的一部分数据替换掉…

解决jupyter notebook可以使用pytorch而Pycharm不能使用pytorch的问题

之前我是用的这个目录下的Python 开始更新目录 1、 2、 3、

微信小程序——van-field中的left-icon属性自定义

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

Elasticsearch中倒排索引、分词器、DSL语法使用介绍

&#x1f353; 简介&#xff1a;java系列技术分享(&#x1f449;持续更新中…&#x1f525;) &#x1f353; 初衷:一起学习、一起进步、坚持不懈 &#x1f353; 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正&#x1f64f; &#x1f353; 希望这篇文章对你有所帮助,欢…

1.RabbitMQ介绍

一、MQ是什么&#xff1f;为什么使用它 MQ&#xff08;Message Queue&#xff0c;简称MQ&#xff09;被称为消息队列。 是一种用于在应用程序之间传递消息的通信方式。它是一种异步通信模式&#xff0c;允许不同的应用程序、服务或组件之间通过将消息放入队列中来进行通信。这…

三、Nginx 安装集

一、Nginx CentOS Yum 安装 1. 前置准备 # 默认情况 CentOS-7 中没有 Nginx 的源 # Nginx 官方提供了源&#xff0c;所以执行如下命令添加源 rpm -Uvh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm2. 安装 # 安装 yum insta…

1.分布式电源接入对配电网影响分析

分布式电源接入对配电网影响分析 MATLAB代码&#xff1a;分布式电源接入对配电网影响分析 关键词&#xff1a;分布式电源 配电网 评估 参考文档&#xff1a;《自写文档&#xff0c;联系我看》参考选址定容模型部分&#xff1b; 仿真平台&#xff1a;MATLAB 主要内容&a…

限流算法深入

限流定义及目的 当系统流量达到系统或下游承受能力的阈值时对系统进行限流控制以防止系统或下游挂掉&#xff0c;减少影响面。 限流组成&#xff1a;阈值及限流策略。阈值是指系统单位时间接收到的请求qps总数&#xff1b;限流策略是指限流行业触发后对应的系统行为&#xff…

【分布式技术专题】「OSS中间件系列」Minio的文件服务的存储模型及整合Java客户端访问的实战指南

Minio的元数据 数据存储 MinIO对象存储系统没有元数据数据库&#xff0c;所有的操作都是对象级别的粒度的&#xff0c;这种做法的优势是: 个别对象的失效&#xff0c;不会溢出为更大级别的系统失效。便于实现"强一致性"这个特性。此特性对于机器学习与大数据处理非…

初学者必看!我的第一个Invideo人工智能文字生成视频

这是一个使用人工智能生成视频的在线平台。 主要功能包括: - 视频脚本自动生成:可以通过输入主题,由AI自动生成视频故事剧本。 - 人声合成:支持上传脚本,AI会合成自然的人声进行朗读。 - 视频制作:有多种视频模板可选择,支持上传自己的素材,一键生成完整视频。 - 特效和增…

基于Java+SpringBoot+Vue前后端分离美食推荐商城设计和实现

博主介绍&#xff1a;✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专…

2023年7月京东护发市场数据分析(京东数据产品)

如今&#xff0c;与面部护肤相比&#xff0c;多数消费者认为头皮也需要认真对待&#xff0c;这在年轻消费群体中体现的较为明显。 随着消费者对护发理念的认同感不断加深&#xff0c;人们日常居家洗护的步骤也更加精细、使用产品品类也愈加多样化。除传统的护发素、发膜等护发…

mac使用VsCode远程连接服务器总是自动断开并要求输入密码的解决办法

在mac中使用vscode远程连接服务器&#xff0c;时常会出现自动断开并要求重新输入服务器密码的问题&#xff0c;接下来让我们来解决它&#xff1a; 1、首先&#xff0c;在本地创建公钥&#xff1a; ssh-keygen 这条命令执行之后&#xff0c;出现提示直接回车即可&#xff1b;直…

Eclipse打jar包与JavaDOC文档的生成

补充知识点——Eclipse打jar包与JavaDOC文档的生成 1、Eclipse如何打jar包&#xff0c;如何运行jar包 Java当中编写的Java代码&#xff0c;Java类、方法、接口这些东西就是项目中相关内容&#xff0c;到时候我们需要把代码提供给甲方、或者是我们需要运行我们编写的代码&…

【python知识】用 Tkinter实现“剪刀-石头-布”和“弹球游戏 ”

一、提要 Tkinter是一个Python内置模块&#xff0c;它提供了一个简单易用的界面来创建GUI。 在实现一些动态的画面、如游戏还是需要一些创新性思维的。在本文中&#xff0c;我们将使用 Tkinter 探索 Python GUI 编程。我们将介绍 Tkinter 的基础知识&#xff0c;并演示如何使用…

React笔记(一)初识React

一、React概述 1、什么是react react的官网:React 用于构建用户界面的 JavaScript 库&#xff0c;它也是一个渐进式的用于构建用户界面的javascript框架 2、主要特征 声明式&#xff1a;使用原生JS编写的页面存在着开发效率低下、性能较差的情况&#xff0c;使用react大家就…

PAT 1136 A Delayed Palindrome

个人学习记录&#xff0c;代码难免不尽人意 A B C where A is the original number, B is the reversed A, and C is their sum. A starts being the input number, and this process ends until C becomes a palindromic number – in this case we print in the last line …

图文并茂:Python Tkinter从入门到高级实战全解析

目录 介绍什么是Tkinter&#xff1f;准备工作第一个Tkinter程序界面布局事件处理补充知识点 文本输入框复选框和单选框列表框弹出对话框 综合案例&#xff1a;待办事项列表总结 介绍 欢迎来到本篇文章&#xff0c;我们将带您深入了解如何在Python中使用Tkinter库来创建图形用…