一、引言:企业级存储系统的核心引擎
drivers/scsi/
目录是Linux内核中 SCSI(Small Computer System Interface)子系统的完整实现 ,它构建了从物理存储设备到块设备抽象层的核心桥梁。
在Linux 2.6内核时期,SCSI子系统正处于 从传统并行SCSI向串行SCSI(SAS、FC、iSCSI)转型 的关键阶段,同时也是 SCSI中间层架构成熟 的重要时期。
SCSI子系统不仅要处理硬盘、磁带机、光驱等传统SCSI设备,还要应对RAID控制器、存储区域网络(SAN)、iSCSI、SAS等现代存储技术。
理解这个目录,就是理解Linux如何支持企业级存储设备,如何在保持高性能的同时处理复杂的错误恢复,以及如何通过统一的驱动模型管理多样化的存储硬件。
该目录包含超过100个子目录和数百个文件,总计超过50万行代码,构建了从 SCSI核心、主机适配器驱动、设备驱动、到传输层协议 的完整存储生态。
在2.6版本中,SCSI子系统实现了重大架构演进: 成熟的SCSI中间层、统一的上层块设备接口、完善的错误处理机制 ,并为后续的 多路径I/O、存储虚拟化、NVMe 等技术奠定了基础。
二、目录宏观架构:分层与模块化设计 2.1 目录结构概览
drivers/scsi/
目录采用严格的分层架构,将不同功能的代码模块分离到独立子目录中:
drivers/scsi/
├── scsi.c # SCSI核心(初始化和退出)
├── scsi_error.c # 错误处理与恢复
├── scsi_lib.c # SCSI库函数(请求队列、命令构建)
├── scsi_scan.c # 设备扫描和探测
├── scsi_devinfo.c # 设备信息管理
├── scsi_proc.c # /proc/scsi接口
├── scsi_sysfs.c # sysfs接口(/sys/class/scsi_*)
├── scsi_ioctl.c # ioctl处理
├── scsi_mod.c # SCSI模块支持
├── scsi_dh.c # 设备处理器框架
├── constants.c # SCSI常量定义
├── hosts.c # 主机适配器管理
├── sd.c # SCSI磁盘驱动(sd_mod,最重要的设备驱动)
├── sr.c # SCSI光盘驱动(sr_mod)
├── st.c # SCSI磁带驱动
├── sg.c # 通用SCSI设备(sg,直通访问)
├── ch.c # SCSI更换器(自动换片机)
├── osd/ # 对象存储设备
├── aacraid/ # Adaptec RAID控制器
├── aic7xxx/ # Adaptec AIC-7xxx系列
├── arcmsr/ # Areca RAID控制器
├── bfa/ # Brocade FC适配器
├── bnx2i/ # Broadcom iSCSI
├── cxgb3i/ # Chelsio iSCSI
├── device_handler/ # 多路径设备处理器
│ ├── scsi_dh_alua.c # ALUA(非对称逻辑单元访问)
│ ├── scsi_dh_emc.c # EMC多路径
│ ├── scsi_dh_hp_sw.c # HP多路径
│ └── ...
├── fcoe/ # 光纤通道 over Ethernet
├── hpsa.c # HP Smart Array
├── ibmvscsi/ # IBM虚拟SCSI
├── iscsi_tcp/ # iSCSI TCP传输
├── libfc/ # 光纤通道库
├── libsas/ # SAS库
├── lpfc/ # Emulex光纤通道
├── megaraid/ # LSI MegaRAID
├── mpt2sas/ # LSI MPT SAS 2.0
├── mpt3sas/ # LSI MPT SAS 3.0
├── qla2xxx/ # QLogic光纤通道
├── scsi_transport_fc/ # FC传输属性
├── scsi_transport_iscsi/ # iSCSI传输属性
├── scsi_transport_sas/ # SAS传输属性
├── scsi_transport_spi/ # SPI传输属性
├── smartpqi/ # Microsemi Smart PQI
├── storvsc/ # Hyper-V存储
├── sym53c8xx_2/ # LSI 53C8xx/53C1010
├── ufs/ # 通用闪存存储
└── ... # 其他厂商驱动
2.2 SCSI软件栈分层架构
块设备层(Block Layer)
↓
--- SCSI上层驱动 ---
↓
SCSI磁盘驱动(sd.c)、磁带驱动(st.c)、光盘驱动(sr.c)
↓
--- SCSI中间层(Mid-Level) ---
↓
SCSI核心(scsi.c)、错误处理、设备扫描、命令处理
↓
--- SCSI底层驱动(Low-Level Drivers) ---
↓
主机适配器驱动(HBA驱动:qla2xxx, lpfc, mpt2sas等)
↓
--- 物理传输层 ---
↓
FC、SAS、iSCSI、并行SCSI等物理连接
↓
物理存储设备(硬盘、磁带、光驱等)
三、核心数据结构:SCSI世界的抽象模型
3.1 SCSI命令结构:
struct scsi_cmnd
这是SCSI子系统最核心的数据结构,代表一个SCSI命令:
struct scsi_cmnd {
struct scsi_device *device; /* 目标设备 */
struct list_head list; /* 链表节点 */
/* 命令状态 */
int eh_eflags; /* 错误处理标志 */
unsigned short cmd_len; /* CDB长度 */
unsigned char cmnd[16]; /* CDB(命令描述块) */
/* 数据缓冲区 */
unsigned buffers; /* 缓冲区计数 */
struct request *request; /* 关联的块层请求 */
unsigned char *buffer; /* 数据缓冲区指针 */
unsigned bufflen; /* 缓冲区长度 */
unsigned resid_len; /* 剩余长度(出错时) */
/* 传输控制 */
unsigned underflow; /* 下溢处理 */
unsigned transfersize; /* 传输大小 */
struct scsi_data_buffer sdb; /* 散点-聚集列表 */
/* 完成回调 */
void (*done)(struct scsi_cmnd *); /* 完成回调函数 */
/* 定时器和超时 */
struct timer_list eh_timeout; /* 错误处理超时 */
unsigned long jiffies_at_alloc; /* 分配时的jiffies */
/* 结果和状态 */
int result; /* 命令结果 */
unsigned char scsi_status; /* SCSI状态字节 */
unsigned char tag; /* 命令标签 */
/* 私有数据 */
void *host_scribble; /* 主机驱动私有数据 */
unsigned int host_scribble_len; /* 私有数据长度 */
/* 标志 */
unsigned int cmd_per_lun; /* 每逻辑单元命令数 */
unsigned char tag_supported:1; /* 是否支持标签命令 */
unsigned char tag_type:3; /* 标签类型 */
unsigned char use_sg:4; /* 散点-聚集条目数 */
};
关键字段解析
:
cmnd
:CDB(Command Descriptor Block),包含实际的SCSI命令
sdb
:散点-聚集列表,支持分散的内存缓冲区
done
:命令完成回调函数,异步处理的核心
host_scribble
:主机适配器驱动私有数据区 3.2 SCSI设备结构:
struct scsi_device
代表一个SCSI逻辑单元(LUN):
struct scsi_device {
struct Scsi_Host *host; /* 所属主机适配器 */
struct request_queue *request_queue; /* 请求队列 */
/* 设备标识 */
u16 id; /* SCSI ID */
u16 lun; /* 逻辑单元号 */
u64 unique_id; /* 唯一标识符 */
/* 设备能力 */
unsigned inquiry_len; /* INQUIRY数据长度 */
unsigned char inquiry[INQUIRY_BUFFER_LEN]; /* INQUIRY数据 */
unsigned char vendor[9]; /* 厂商字符串 */
unsigned char model[17]; /* 型号字符串 */
unsigned char rev[5]; /* 修订版本 */
/* 设备类型 */
unsigned type:5; /* 设备类型(0x00=磁盘等) */
unsigned removable:1; /* 是否可移动 */
unsigned changed:1; /* 介质是否改变 */
unsigned busy:1; /* 设备是否忙 */
unsigned lockable:1; /* 是否可锁定 */
unsigned locked:1; /* 是否已锁定 */
/* 队列管理 */
unsigned queue_depth; /* 队列深度 */
unsigned short device_busy; /* 设备繁忙计数 */
struct scsi_target *sdev_target; /* 目标设备 */
/* 设备模型集成 */
struct device sdev_gendev; /* 通用设备结构 */
struct device sdev_dev; /* 类设备结构 */
/* 电源管理 */
struct list_head same_target_siblings; /* 同目标兄弟设备 */
struct list_head siblings; /* 兄弟设备列表 */
/* 异步事件 */
struct async_scan_data *async_scan_data; /* 异步扫描数据 */
/* 多路径 */
struct scsi_dh_data *scsi_dh_data; /* 设备处理器数据 */
};
3.3 SCSI主机结构:
struct Scsi_Host
代表一个SCSI主机适配器(HBA):
struct Scsi_Host {
/* 主机适配器特性 */
struct module *hostt; /* 主机模板 */
unsigned int unique_id; /* 唯一标识符 */
unsigned int host_no; /* 主机号 */
unsigned int max_channel; /* 最大通道数 */
unsigned int max_id; /* 最大目标ID */
unsigned int max_lun; /* 最大LUN */
/* 命令处理 */
unsigned int can_queue; /* 最大命令数 */
unsigned int cmd_per_lun; /* 每LUN最大命令数 */
unsigned short sg_tablesize; /* 散点-聚集表大小 */
unsigned short max_sectors; /* 最大扇区数 */
unsigned long dma_boundary; /* DMA边界 */
/* 工作队列 */
struct workqueue_struct *work_q; /* 工作队列 */
char work_q_name[20]; /* 工作队列名称 */
/* 设备模型 */
struct device shost_dev; /* 主机设备结构 */
struct device shost_gendev; /* 通用主机设备 */
/* 链表管理 */
struct list_head __devices; /* 设备链表 */
struct list_head __targets; /* 目标设备链表 */
/* 主机模板操作 */
struct scsi_host_template *hostt; /* 主机模板 */
/* 私有数据 */
void *hostdata; /* 主机驱动私有数据 */
char hostdata[0] ____cacheline_aligned; /* 内联私有数据 */
/* 统计信息 */
unsigned int host_busy; /* 主机繁忙计数 */
unsigned int host_failed; /* 主机失败计数 */
unsigned int host_eh_scheduled; /* 错误处理调度计数 */
/* 锁 */
spinlock_t host_lock; /* 主机锁 */
struct mutex scan_mutex; /* 扫描互斥锁 */
/* 异步事件 */
struct list_head starved_list; /* 饥饿设备列表 */
unsigned int starved_list_len; /* 饥饿列表长度 */
};
3.4 主机模板结构:
struct scsi_host_template
主机适配器驱动通过此模板注册:
struct scsi_host_template {
struct module *module; /* 所属模块 */
const char *name; /* 驱动名称 */
/* 探测和移除 */
int (*detect)(struct scsi_host_template *); /* 探测函数 */
int (*release)(struct Scsi_Host *); /* 释放函数 */
/* 命令处理 */
int (*queuecommand)(struct scsi_cmnd *, void (*done)(struct scsi_cmnd *));
int (*eh_abort_handler)(struct scsi_cmnd *); /* 中止处理 */
int (*eh_device_reset_handler)(struct scsi_cmnd *); /* 设备重置 */
int (*eh_bus_reset_handler)(struct scsi_cmnd *); /* 总线重置 */
int (*eh_host_reset_handler)(struct scsi_cmnd *); /* 主机重置 */
/* 中断处理 */
irqreturn_t (*irq_handler)(int, void *); /* 中断处理程序 */
/* 同步点 */
int (*slave_configure)(struct scsi_device *); /* 从设备配置 */
int (*slave_destroy)(struct scsi_device *); /* 从设备销毁 */
/* 超时 */
int (*change_queue_depth)(struct scsi_device *, int); /* 改变队列深度 */
/* 杂项 */
unsigned int supported_mode:2; /* 支持模式 */
unsigned int unchecked_isa_dma:1; /* 无检查ISA DMA */
unsigned int use_clustering:1; /* 使用聚集 */
unsigned int emulated:1; /* 是否模拟 */
/* DMA */
unsigned int dma_boundary; /* DMA边界 */
/* 命令标志 */
unsigned int cmd_size; /* 命令结构大小 */
struct scsi_host_cmd_pool *cmd_pool; /* 命令池 */
/* 设备模型 */
char *proc_name; /* /proc名称 */
struct proc_dir_entry *proc_dir; /* /proc目录 */
};
四、SCSI中间层:核心处理逻辑
4.1 命令提交流程
scsi_lib.c
处理从块层到SCSI层的命令转换:
static int scsi_setup_cmnd(struct scsi_device *sdev, struct request *req)
{
struct scsi_cmnd *cmd = req->special;
int ret = BLKPREP_OK;
/* 1. 初始化命令 */
memset(cmd, 0, sizeof(*cmd));
/* 2. 设置设备指针 */
cmd->device = sdev;
/* 3. 构建CDB */
if (req->cmd_type == REQ_TYPE_BLOCK_PC) {
/* 直通命令(sg) */
memcpy(cmd->cmnd, req->cmd, req->cmd_len);
cmd->cmd_len = req->cmd_len;
} else {
/* 块设备请求,转换为SCSI命令 */
cmd->cmnd[0] = req->cmd[0];
if (req->cmd[0] == READ_10)
cmd->cmnd[1] = 0;
else if (req->cmd[0] == WRITE_10)
cmd->cmnd[1] = 0;
/* 设置LBA和长度 */
put_unaligned_be32(blk_rq_pos(req), &cmd->cmnd[2]);
put_unaligned_be16(blk_rq_sectors(req), &cmd->cmnd[7]);
cmd->cmd_len = 10;
}
/* 4. 设置数据缓冲区 */
if (rq_data_dir(req) == WRITE) {
cmd->sc_data_direction = DMA_TO_DEVICE;
} else {
cmd->sc_data_direction = DMA_FROM_DEVICE;
}
/* 5. 设置完成回调 */
cmd->done = scsi_done;
return ret;
}
4.2 命令完成处理
void scsi_done(struct scsi_cmnd *cmd)
{
struct request *req = cmd->request;
int result = cmd->result;
/* 1. 更新统计 */
cmd->device->device_busy--;
/* 2. 处理结果 */
if (result) {
/* 错误处理 */
if (driver_byte(result) & DRIVER_SENSE)
scsi_log_completion(cmd, SCAN_MEDIUM_ERROR);
else
scsi_log_completion(cmd, SCAN_HOST_BYTE_ERROR);
}
/* 3. 调用请求完成回调 */
if (req->end_io) {
req->end_io(req, result);
} else {
/* 默认完成处理 */
__blk_end_request_all(req, result);
}
/* 4. 释放命令 */
scsi_release_buffers(cmd);
scsi_free_sgtables(cmd);
scsi_put_command(cmd);
}
4.3 设备扫描
scsi_scan.c
负责发现和配置SCSI设备:
int scsi_scan_host(struct Scsi_Host *shost)
{
struct async_scan_data *data;
int ret;
/* 1. 分配异步扫描数据 */
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
/* 2. 初始化异步扫描 */
init_completion(&data->prev_finished);
data->shost = shost;
/* 3. 启动异步扫描线程 */
ret = async_schedule(scsi_scan_host_async, data);
if (ret) {
kfree(data);
return ret;
}
return 0;
}
static void scsi_scan_host_async(void *data, async_cookie_t cookie)
{
struct async_scan_data *async_data = data;
struct Scsi_Host *shost = async_data->shost;
int id, channel;
/* 4. 扫描所有通道和目标 */
for (channel = 0; channel <= shost->max_channel; channel++) {
for (id = 0; id <= shost->max_id; id++) {
scsi_scan_target(&shost->shost_gendev, channel, id,
SCAN_WILD_CARD, 1);
}
}
/* 5. 完成扫描 */
complete(&async_data->prev_finished);
kfree(async_data);
}
五、SCSI磁盘驱动:
sd.c
5.1 驱动初始化
static int __init init_sd(void)
{
int majors = 0, i, err;
/* 1. 为每个可能的SCSI磁盘分配主设备号 */
for (i = 0; i < SD_MAJORS; i++) {
if (register_blkdev(sd_major(i), "sd") == 0)
majors++;
}
if (!majors)
return -ENODEV;
/* 2. 注册SCSI驱动模板 */
err = scsi_register_driver(&sd_template.gendrv);
if (err)
goto err_out;
return 0;
err_out:
while (--majors >= 0)
unregister_blkdev(sd_major(majors), "sd");
return err;
}
5.2 设备探测
static int sd_probe(struct device *dev)
{
struct scsi_device *sdp = to_scsi_device(dev);
struct gendisk *gd;
struct sd_disk *sdkp;
int index;
int error;
/* 1. 检查设备类型 */
if (sdp->type != TYPE_DISK && sdp->type != TYPE_MOD)
return -ENODEV;
/* 2. 分配sd_disk结构 */
sdkp = kzalloc(sizeof(*sdkp), GFP_KERNEL);
if (!sdkp)
return -ENOMEM;
/* 3. 分配gendisk结构 */
gd = alloc_disk(SD_MINORS);
if (!gd) {
error = -ENOMEM;
goto out_free;
}
/* 4. 初始化gendisk */
gd->major = sd_major(sdp->index);
gd->first_minor = sdp->index * SD_MINORS;
gd->minors = SD_MINORS;
gd->fops = &sd_fops;
gd->private_data = sdkp;
/* 5. 设置磁盘名 */
sprintf(gd->disk_name, "sd%c", 'a' + (sdp->index % 26));
/* 6. 初始化请求队列 */
gd->queue = sdp->request_queue;
blk_queue_prep_rq(gd->queue, sd_prep_fn);
/* 7. 读取容量信息 */
error = sd_read_capacity(sdkp, sdp);
if (error)
goto out_put;
/* 8. 添加到系统 */
add_disk(gd);
/* 9. 保存反向指针 */
sdp->sdev_gendev.parent = get_device(dev);
sdp->sdev_gendev.driver_data = sdkp;
return 0;
out_put:
put_disk(gd);
out_free:
kfree(sdkp);
return error;
}
5.3 请求处理
static int sd_init_command(struct scsi_cmnd *cmd)
{
struct request *rq = cmd->request;
struct scsi_device *sdp = cmd->device;
u32 lba = blk_rq_pos(rq);
u32 blocks = blk_rq_sectors(rq);
/* 1. 设置CDB */
if (blocks > 0xffff) {
/* 使用16字节CDB */
cmd->cmnd[0] = (rq_data_dir(rq) == WRITE) ? WRITE_16 : READ_16;
put_unaligned_be64(lba, &cmd->cmnd[2]);
put_unaligned_be32(blocks, &cmd->cmnd[10]);
cmd->cmd_len = 16;
} else if (blocks > 0xff) {
/* 使用10字节CDB */
cmd->cmnd[0] = (rq_data_dir(rq) == WRITE) ? WRITE_10 : READ_10;
put_unaligned_be32(lba, &cmd->cmnd[2]);
put_unaligned_be16(blocks, &cmd->cmnd[7]);
cmd->cmd_len = 10;
} else {
/* 使用6字节CDB */
cmd->cmnd[0] = (rq_data_dir(rq) == WRITE) ? WRITE_6 : READ_6;
cmd->cmnd[1] = (lba >> 16) & 0x1f;
cmd->cmnd[2] = (lba >> 8) & 0xff;
cmd->cmnd[3] = lba & 0xff;
cmd->cmnd[4] = blocks;
cmd->cmd_len = 6;
}
/* 2. 设置数据方向 */
if (rq_data_dir(rq) == WRITE)
cmd->sc_data_direction = DMA_TO_DEVICE;
else
cmd->sc_data_direction = DMA_FROM_DEVICE;
/* 3. 设置完成回调 */
cmd->done = sd_done;
return 0;
}
六、错误处理与恢复
6.1 错误处理框架
scsi_error.c
实现复杂的错误恢复机制:
void scsi_eh_scmd_add(struct scsi_cmnd *scmd)
{
struct Scsi_Host *shost = scmd->device->host;
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) {
/* 已计划中止,不重复添加 */
spin_unlock_irqrestore(shost->host_lock, flags);
return;
}
/* 标记命令需要错误处理 */
scmd->eh_eflags |= SCSI_EH_ABORT_SCHEDULED;
/* 添加到错误处理列表 */
list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);
shost->host_failed++;
spin_unlock_irqrestore(shost->host_lock, flags);
/* 调度错误处理 */
scsi_schedule_eh(shost);
}
6.2 错误处理线程
static int scsi_error_handler(void *data)
{
struct Scsi_Host *shost = data;
/* 设置线程名称 */
daemonize("%s_eh", shost->hostt->name);
allow_signal(SIGKILL);
while (true) {
struct scsi_cmnd *scmd, *next;
LIST_HEAD(work_q);
/* 1. 等待错误处理事件 */
wait_event_interruptible(shost->host_wait,
shost->host_failed ||
kthread_should_stop());
if (kthread_should_stop())
break;
spin_lock_irq(shost->host_lock);
/* 2. 收集需要处理的命令 */
list_splice_init(&shost->eh_cmd_q, &work_q);
shost->host_failed = 0;
shost->host_eh_scheduled = 0;
spin_unlock_irq(shost->host_lock);
/* 3. 处理每个失败的命令 */
list_for_each_entry_safe(scmd, next, &work_q, eh_entry) {
list_del_init(&scmd->eh_entry);
/* 执行错误恢复 */
scsi_eh_abort_cmnds(shost, &work_q, scmd);
}
}
return 0;
}
6.3 命令中止
int scsi_abort_command(struct scsi_cmnd *scmd)
{
struct scsi_device *sdev = scmd->device;
struct Scsi_Host *shost = sdev->host;
unsigned long timeout;
int rtn;
/* 1. 调用主机适配器的中止处理程序 */
if (!shost->hostt->eh_abort_handler)
return FAILED;
/* 2. 执行中止 */
rtn = shost->hostt->eh_abort_handler(scmd);
if (rtn == SUCCESS) {
/* 等待命令完成 */
timeout = jiffies + shost->eh_abort_timeout;
while (time_before(jiffies, timeout)) {
if (!scsi_cmd_is_active(scmd))
return SUCCESS;
msleep(1);
}
}
return FAILED;
}
七、主机适配器驱动示例:QLogic光纤通道
7.1 驱动初始化
static int __init qla2x00_module_init(void)
{
int ret;
/* 1. 注册PCI驱动 */
ret = pci_register_driver(&qla2x00_pci_driver);
if (ret) {
printk(KERN_WARNING
"qla2xxx: PCI driver registration failed: %d\n", ret);
return ret;
}
/* 2. 创建调试文件系统 */
qla2x00_create_debugfs_root();
/* 3. 初始化全局锁 */
spin_lock_init(&qla2x00_global_lock);
return 0;
}
7.2 命令队列
static int qla2x00_queuecommand(struct scsi_cmnd *cmd,
void (*done)(struct scsi_cmnd *))
{
srb_t *sp;
fc_port_t *fcport;
int rval;
/* 1. 分配SRB(SCSI Request Block) */
sp = qla2x00_get_sp(cmd->device->host, cmd, done);
if (!sp)
return SCSI_MLQUEUE_HOST_BUSY;
/* 2. 获取FC端口 */
fcport = (fc_port_t *)cmd->device->hostdata;
/* 3. 构建FCP命令 */
rval = qla2x00_build_scsi_cmd(sp, fcport);
if (rval != QLA_SUCCESS) {
qla2x00_rel_sp(sp);
return SCSI_MLQUEUE_HOST_BUSY;
}
/* 4. 提交到硬件队列 */
rval = qla2x00_start_scsi(sp);
if (rval != QLA_SUCCESS) {
qla2x00_rel_sp(sp);
cmd->result = DID_ERROR << 16;
done(cmd);
return 0;
}
return 0;
}
7.3 中断处理
static irqreturn_t qla2x00_intr_handler(int irq, void *dev_id)
{
struct qla_hw_data *ha = dev_id;
uint32_t stat;
/* 1. 读取中断状态 */
stat = RD_REG_DWORD(&ha->iobase->istatus);
/* 2. 检查是否为本适配器中断 */
if (!(stat & ISP_INTERRUPT_MASK))
return IRQ_NONE;
/* 3. 处理响应队列 */
if (stat & RISC_INT) {
if (ha->flags.fw_dump_reading)
qla2x00_fw_dump(ha);
else
qla2x00_process_response_queue(ha);
}
/* 4. 处理完成队列 */
if (stat & MBX_INTERRUPT) {
qla2x00_mbx_completion(ha, MS_MASK);
}
return IRQ_HANDLED;
}
八、2.6内核SCSI子系统的演进
8.1 相对于2.4的架构改进
特性2.4内核2.6内核
设备模型集成基本集成深度集成,完善的sysfs支持错误处理相对简单完善的错误恢复框架(SCSI EH)多路径支持有限支持设备处理器框架,支持ALUA等传输层支持并行SCSI为主FC、iSCSI、SAS完整支持性能优化基本队列标记命令队列(TCQ),优化调度电源管理基本支持完善的电源管理框架热插拔有限支持完整的热插拔支持
8.2 关键技术创新
SCSI中间层成熟
:清晰的上下层分离,提高代码重用性
统一的错误处理
:标准化错误恢复流程,提高系统可靠性
多路径I/O框架
:支持存储高可用性和负载均衡
传输抽象层
:支持多种物理传输(FC、iSCSI、SAS)
设备处理器框架
:可插拔的设备特定处理模块
异步扫描
:加速大型存储系统的设备发现
8.3 性能优化
命令聚集
:合并小的I/O请求,提高吞吐量
标记命令队列
:支持设备端的命令重新排序
中断合并
:减少中断频率,提高CPU效率
DMA映射优化
:支持64位寻址,提高大内存系统性能
缓存优化
:优化数据结构布局,提高缓存命中率
九、总结:企业级存储的软件基石
drivers/scsi/
目录是Linux内核 企业级存储能力 的核心体现。
它将复杂的存储硬件、多样的传输协议、复杂的错误处理,通过分层的软件架构统一管理。
在2.6版本中,这个子系统完成了从"功能实现"到"企业级可靠"的关键演进。
SCSI子系统的设计体现了几个核心工程原则: 严格分层 :硬件细节隐藏在HBA驱动中,SCSI协议处理在中间层,块设备接口在上层 错误隔离 :完善的错误处理框架,防止单个设备故障影响整个系统 性能与可靠性平衡 :在追求高性能的同时,不牺牲数据完整性 硬件抽象 :统一的接口支持多种存储技术和厂商设备 可扩展性 :模块化设计支持新硬件和新协议 通过分析
drivers/scsi/
,我们看到的不仅是存储驱动的实现,更是 企业级存储系统的设计哲学 。
它为Linux在数据中心、云计算、高性能计算等领域的存储应用提供了坚实基础,是现代操作系统存储子系统的典范之作。
