浅析Linux SCSI子系统:IO路径

文章目录

概述

SCSI子系统向上与块层对接,由块层提交的对块设备的IO请求,会由SCSI子系统转换成SCSI协议的标准命令,然后调用Scsi_Host结构的queuecommand回调下发到低层驱动执行;低层驱动会将SCSI命令和数据发送给真实的设备,并在请求完成后,调用scsi_cmd结构中的scsi_done回调,将请求响应信息返回给SCSI中层,SCSI中层完成请求响应的解析后,将结果返回给块层。

scsi_cmd:SCSI命令

struct scsi_cmnd {
	struct scsi_request req;
	struct scsi_device *device;
	struct list_head list;     // 用于链入到关联SCSI设备的SCSI命令链表中
	struct list_head eh_entry;     // 用于链入到SCSI Host的错误恢复链表中
	struct delayed_work abort_work;

	struct rcu_head rcu;

	int eh_eflags;
	unsigned long serial_number;

	unsigned long jiffies_at_alloc;

	int retries;   // SCSI命令已重试的次数
	int allowed;   // SCSI命令允许重试的次数

	unsigned char prot_op;     // DIF操作类型
	unsigned char prot_type;   // DIF保护类型
	unsigned char prot_flags;

	unsigned short cmd_len;
	enum dma_data_direction sc_data_direction;

	unsigned char *cmnd;

	struct scsi_data_buffer sdb;  // SCSI命令的数据缓冲区
	struct scsi_data_buffer *prot_sdb;     // SCSI命令的保护数据缓冲区

	unsigned underflow;	
	unsigned transfersize;

	struct request *request;

	unsigned char *sense_buffer;   // 存放sense信息的缓冲区
				
	void (*scsi_done) (struct scsi_cmnd *);    // 底层驱动完成IO请求后,调用scsi_done将结果返回给SCSI

	unsigned char *host_scribble;	

	int result;    // 存放低层驱动返回的IO状态信息
	int flags;
};

result字段

result携带了驱动或SCSI中层在完成SCSI命令处理后返回的一些结果信息,一共包含4个字段。

  • driver_byte:由SCSI中层进行设置;
  • host_byte:存放底层驱动返回的状态信息;
  • msg_byte:存放主机适配自身的一些信息;
  • status_byte:存放目标设备返回的状态信息,由SCSI协议定义。

proto_op字段

proto_op字段描述了SCSI命令的DIF操作类型,由scsi_prot_operations枚举类型定义:

enum scsi_prot_operations {
	SCSI_PROT_NORMAL = 0,

	SCSI_PROT_READ_INSERT,
	SCSI_PROT_WRITE_STRIP,
	
	SCSI_PROT_READ_STRIP,
	SCSI_PROT_WRITE_INSERT,

	SCSI_PROT_READ_PASS,
	SCSI_PROT_WRITE_PASS,
}

proto_type字段

proto_type字段描述了SCSI命令支持的DIF保护类型,由t10_dif_type枚举类型定义:

enum t10_dif_type {
	T10_PI_TYPE0_PROTECTION = 0x0,
	T10_PI_TYPE1_PROTECTION = 0x1,
	T10_PI_TYPE2_PROTECTION = 0x2,
	T10_PI_TYPE3_PROTECTION = 0x3,
}

SCSI命令下发

scsi_request_fn

scsi_dev_queue_ready

scsi_dev_queue_ready检查SCSI设备运行IO情况,确认是否允许下发新的IO。函数检查的维度有两个:

  • 检查SCSI设备的IO请求队列是否已满。若满,则不允许下发新的IO;
  • 检查SCSI设备是否设置了device_blocked。若设置,则需要等待device_blocked计数减为0时,才允许下发IO。

scsi_host_queue_ready

scsi_host_queue_ready检查主机适配器能否下发新的IO,检查逻辑与scsi_dev_queue_ready类似。

SCSI命令响应

SCSI中层在将SCSI命令请求下发给驱动时,会设置scsi_cmnd结构中的done回调函数,驱动在完成IO请求时,通过调用done回调,将IO响应信息返回给中层。

命令请求完成的软中断处理

IO请求完成的处理流程前半部分在硬中断上下文处理,后半部分的工作回切换到软中断进行处理,SCSI提供的软中断处理入口是scsi_softirq_done函数。scsi_softirq_done的工作如下:

  1. 调用scsi_decide_disposition解析scsi_cmd的返回信息,包括驱动返回状态、sense数据等,确定如何进一步处理scsi_cmd;
  2. 根据scsi_decide_disposition返回的动作,处理scsi_cmnd,分为几种情况:
    • SUCCESS:IO成功完成,将结果返回上层;
    • NEEDS_RETRY/ADD_TO_MLQUEUE:IO重试。把scsi_cmnd重新加入到块设备请求队列处理;
    • 默认情况:IO错误处理。调用scsi_en_scmd_add把scsi_cmnd加入到错误处理队列中,等待错误恢复。

scsi_softirq_done流程示意如下:

相关参考

  • 《存储技术原理分析:基于Linux 2.6内核源代码分析》
相关推荐
无所不在的物质4 分钟前
Jenkins基础教程
运维·云原生·自动化·jenkins
广而不精zhu小白15 分钟前
CentOS Stream 9 挂载Windows共享FTP文件夹
linux·windows·centos
一休哥助手21 分钟前
全面解析 Linux 系统监控与性能优化
linux·运维·性能优化
二进制杯莫停23 分钟前
掌控网络流量的利器:tcconfig
linux
LI JS@你猜啊33 分钟前
Elasticsearch 集群
大数据·服务器·elasticsearch
watl040 分钟前
【Android】unzip aar删除冲突classes再zip
android·linux·运维
团儿.1 小时前
Docker服务发现新纪元:探索Consul的无限魅力
运维·docker·云计算·服务发现·consul
赵大仁1 小时前
在 CentOS 7 上安装 Node.js 20 并升级 GCC、make 和 glibc
linux·运维·服务器·ide·ubuntu·centos·计算机基础
vvw&1 小时前
Docker Build 命令详解:在 Ubuntu 上构建 Docker 镜像教程
linux·运维·服务器·ubuntu·docker·容器·开源
李白你好1 小时前
家用无线路由器的 2.4GHz 和 5GHz
运维·网络