浅析Linux SCSI子系统:设备管理

这篇具有很好参考价值的文章主要介绍了浅析Linux SCSI子系统:设备管理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

概述

Linux SCSI子系统通过SCSI主机适配器(HBA)接入所有SCSI存储设备,在Linux系统中,可以安装多种主机适配器,SCSI中层会提供主机适配器的统一抽象,这些主机适配器的厂商提供具体的低层驱动实现;主机适配器接入到SCSI子系统后,SCSI会通过扫描或者低层驱动主动上报的方式,接入主机适配器下挂的所有SCSI存储设备。

设备管理数据结构

Linux SCSI子系统通过Scsi_Host、scsi_target和scsi_device数据结构分别来描述SCSI主机适配器、目标节点和逻辑单元,它们之间的关系如下:
浅析Linux SCSI子系统:设备管理,# 存储IO栈,linux,网络,网络协议

  • 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结构时,会在尾部申请额外的空间存储驱动的私有数据。
浅析Linux SCSI子系统:设备管理,# 存储IO栈,linux,网络,网络协议
完成Scsi_Host的结构申请后,驱动调用scsi_add_host将主机适配器添加到系统中:
浅析Linux SCSI子系统:设备管理,# 存储IO栈,linux,网络,网络协议

构建sysfs目录

添加主机适配器的过程中,一个很重要的部分就是在sysfs文件系统构建相关的节点,以支持应用程序访问SCSI子系统的相关信息。
浅析Linux SCSI子系统:设备管理,# 存储IO栈,linux,网络,网络协议

添加SCSI设备

无论是SCSI总线扫描或者是驱动发现的SCSI设备,最后都需要调用scsi_add_device接口将设备添加到系统中。scsi_add_device执行流程如下:

  • 确认SCSI目标节点在系统中是否存在,不存在就会创建新的scsi_target结构;
  • 向SCSI目标节点中添加Lun,即scsi_device。

浅析Linux SCSI子系统:设备管理,# 存储IO栈,linux,网络,网络协议

挂载Lun

scsi_probe_and_add_lun负责挂载Lun设备到系统中,它的执行流程如下:

  • 确认SCSI设备在系统中是否存在,不存在则创建新的scsi_device结构;
  • 向设备发送INQUIRY命令,设备在正常接入的情况下,会返回INQUIRY数据;
  • 解析INQUIRY数据,初始化SCSI设备信息

浅析Linux SCSI子系统:设备管理,# 存储IO栈,linux,网络,网络协议

IO请求队列初始化

分配scsi_device结构时,也会初始化设备的IO请求队列。根据主机适配器是否支持多队列,初始化函数也会不同。对于单队列,SCSI使用scsi_old_alloc_queue函数分配IO请求队列。
浅析Linux SCSI子系统:设备管理,# 存储IO栈,linux,网络,网络协议文章来源地址https://www.toymoban.com/news/detail-678677.html

相关参考

  • 《存储技术原理分析:基于Linux 2.6内核源代码分析》

到了这里,关于浅析Linux SCSI子系统:设备管理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【智能家居项目】裸机版本——网卡设备接入输入子系统 | 业务子系统 | 整体效果展示

    🐱作者:一只大喵咪1201 🐱专栏:《智能家居项目》 🔥格言: 你只管努力,剩下的交给时间! 网络子系统实现了,在我们整个项目框架中,网络子系统也输入子系统中输入设备的之一,所以现在要做的就是网络子系统接入到输入子系统中。 如上图所示,在输入子系统中增

    2024年02月08日
    浏览(44)
  • 字符设备驱动之输入子系统分析(二)

    作者: Bright-Ho 联系方式: 836665637@qq.com input 输入子系统框架分析(纯软件方面):   上一节,我们简单的描述的 什么是输入子系统 ; 什么是字符设备 ; 以及其作用 ;重点是我们讲到分析 输入子系统必须结合硬件设备来分析 ;那么这一节,我们主要讲解输入子系统的 软

    2024年02月15日
    浏览(37)
  • 字符设备驱动之输入子系统分析(一)

    作者: Bright-Ho 联系方式: 836665637@qq.com 前言背景描述: 虽然在网上看了很多有关输入子系统的资料和视频,但是真正的,系统的,全面的,来弄清输入子系统,还是要花些时间和精力的!现在我以一个初学者的角度来分析 input 输入子系统; 那么分析 input 输入子系统之前,

    2024年02月15日
    浏览(40)
  • 【智能家居项目】裸机版本——设备子系统(LED && Display && 风扇)

    🐱作者:一只大喵咪1201 🐱专栏:《智能家居项目》 🔥格言: 你只管努力,剩下的交给时间! 输入子系统中目前仅实现了按键输入,剩下的网络输入和标准输入在以后会逐步实现,今天先来实现设备子系统,包含LED设备(GPIO控制),风扇设备,OLED设备。 不同内核下是访问设

    2024年02月08日
    浏览(45)
  • HCIA-HarmonyOS设备开发认证V2.0-IOT硬件子系统-WatchDog

    看门狗(Watchdog),又称看门狗计时器(Watchdog timer),是一种硬件计时设备。一般有一个输入、一个输出,输入叫做喂狗,输出连接到系统的复位端。当系统主程序发生错误导致未及时清除看门狗计时器的计时值时,看门狗计时器就会对系统发出复位信号,使系统从悬停状态

    2024年02月21日
    浏览(52)
  • HCIA-HarmonyOS设备开发认证V2.0-IOT硬件子系统-UART

    UART 是通用异步收发传输器(Universal Asynchronous Receiver/Transmitter)的缩写,是通用串行数据总线,用于异步通信,该总线双向通信,可以实现全双工传输。UART 应用比较广泛,常用于输出打印信息,也可以外接各种模块,如 GPS、蓝牙等。 异步通信 异步通信中,数据通常以字符

    2024年02月19日
    浏览(56)
  • HCIA-HarmonyOS设备开发认证V2.0-IOT硬件子系统-ADC

    ADC(Analog to Digital Converter)模数转换器。现实生活中的所有属性(如温度、湿度、光照强度等)都是连续的,即为模拟信号;而单片机或电子计算机所能识别的信号都是离散的数字信号。此时,若是需要使用现实世界中的各种属性,就需要一种设备将模拟信号转换为数字信号

    2024年02月21日
    浏览(54)
  • HCIA-HarmonyOS设备开发认证V2.0-IOT硬件子系统-GPIO

    轻量系统设备通常需要进行外设控制,例如温湿度数据的采集、灯开关的控制,因此在完成内核开发后,需要进行设备驱动的开发。 主要Hi3861芯片介绍如何运用 IoT 硬件子系统进行轻量系统设备的驱动开发。 在轻量系统的联接模组中,智能家居远程控制能力的实现,最终是通

    2024年02月21日
    浏览(46)
  • HCIA-HarmonyOS设备开发认证V2.0-IOT硬件子系统-SPI

    SPI 是串行外设接口(Serial Peripheral Interface)是一种高速的全双工同步的通信总线。 SPI 是由 Motorola 公司开发,用于在主设备和从设备之间进行通信,常用于与闪存、实时时钟、传感器以及模数转换器等进行通信。 SPI 通信通常由主设备发起,通过以下步骤完成一次通信: 通过

    2024年02月22日
    浏览(50)
  • 【Windows 11】安装 Android子系统 和 Linux子系统

    本文使用电脑系统: 主要就是安装一个名为: 适用于Android的Windows子系统 (WSA)的软件。 首先在电脑的设置里面:时间和语言——语言和地区里面把地区改为美国。 然后到微软商店搜索: Amazon AppStore 。 安装亚马逊应用商店的时候,会首先提示你安装前面说的WSA。如此,我

    2024年02月09日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包