一,SCSI设备上报过程:硬盘上线过程

SATA盘AHCI控制器初始化过程:
ahci_init()
 ->pci_module_init(&ahci_pci_driver);
static struct pci_driver ahci_pci_driver = {
.name = DRV_NAME,
.id_table = ahci_pci_tbl,
.probe = ahci_init_one,
.remove = ata_pci_remove_one,
};
ahci_init_one(struct pci_dev*pdev,struct pci_device_id*ent)
-->scsi_host_alloc(sht,privsize) //分配一个SCSI控制器 struct scsi_host
//并创建了一个SCSI控制器的错误处理线程:shost->ehandler=kthread_run(scsi_error_handler,"scsi_eh_%d",shost->host_no)
-->scsi_add_host(host,dev); //向系统中添加SCSI 控制器
-->scsi_scan_host(host)   //扫描此SCSI控制器
---->scsi_scan_channel(shost, channel, id, lun, rescan); //扫描所有的总线CHANNEL
------> scsi_scan_target(shost, channel, order_id, lun, rescan);  //扫描所有的目标器
-------->scsi_probe_and_add_lun //扫描目标器下的逻辑设备lun
---------->scsi_alloc_sdev(host, channel, id, lun, hostdata); //分配scsi逻辑设备 struct scsi_device 
//在此指定了设备的总线类型为scsi_bus_type
//并调用了 scsi_alloc_queue(sdev) 为SCSI设备分配了请求队列
// 设置请求队列的unplug超时为3ms, 超时函数blk_unplug_timeout-->generic_unplug_device
---------->scsi_allocate_request(sdev, GFP_ATOMIC);
------------->scsi_probe_lun 
------------->scsi_add_lun(sdev, result, &bflags);
--------------->device_add()  //把设备添加到所属总线的设备列表
------------------>bus_add_device(dev)
--------------------->device_attach(dev) //总线尝试关联设备与驱动
------------------------->driver_probe_device
------------------------->drv->probe(dev);

init_sd() //sd块设备驱动初始化
scsi_register_driver(&sd_template.gendrv)
driver_attach(drv); //把驱动加入总线驱动列表

static struct scsi_driver sd_template = {
.owner = THIS_MODULE,
.gendrv = {
.name = "sd",
.probe = sd_probe,
.remove = sd_remove,
.shutdown = sd_shutdown,
},
.rescan = sd_rescan,
.init_command = sd_init_command,
.issue_flush = sd_issue_flush,
};

sd_probe(dev)  //设置SCSI设备的超时时间,struct scsi_device->timout=30ms
gd=alloc_disk(16)
设备磁盘的超时为30ms(stuct scsi_device.timeout=30*HZ),
add_disk(gd)<----scsi_alloc_queue()
向系统添回块设备。磁盘上线完成

二,磁盘IO过程

scsi_alloc_queue(sdev)
q->make_request_fn=__make_request
q->request_fn = scsi_request_fn
q->prep_rq_fn = scsi_prep_fn

2.1 不同的IO提交方式:                  设置不同的完成回调函数

submit_bh()//   --->bio->bi_end_io =end_bio_bh_io_sync
   swap_readpage()  --->bio->bi_end_io = end_swap_bio_read 
1,submit_bio(int rw,struct bio*bio)//向块层提交BIO的通用接口 
2,-->generic_make_request(bio); //通用块层BIO提交函数
3,---->__generic_make_request(struct bio *bio)
4,------>q->make_request_fn(q,bio)  //提交bio到请求队列

__make_request(q,bio);

//如果队列为空,就分配一个新请求req,plug到请求队列,设置定时器3ms,等待超时unplug

elv_merge(q,&req,bio)   //IO调度算法,检查bio是否可以合入已有请求,可以向前/向后合并
5.1------>get_request_wait(q,rw,bio)  //不能合并时,获取一个新的请求
----------->get_request(q,rw,bio,GFP_NOIO)
------------->blk_alloc_request(q,rw,gfp_mask) //分配一个新的请求,并初始化
                 mempool_alloc(q->rq.rq_pool, gfp_mask);
                 blk_rq_init(q,rq);
5.2------>init_request_from_bio(req,bio); //用bio初始化一个新请求
5.3------>  __elv_add_request(q, rq, where); //把新请求加入请求队列
    q->elevator->ops->elevator_add_req_fn(q, rq); //IO调度算法,向请求队列中加入新请求
5.4------> __blk_run_queue(q) /或unplug超时 __generic_unplug_device(q); //激活请求队列
------------>q->request_fn(q); //把请求队列提交给SCSI中间 
==============以上为SCSI上层(SCSI设备驱动层)==============
6,----------> scsi_request_fn(q)  //SCSI中间层处理请求

7.1 ----------->req=blk_peek_request(q) 或 rq=elv_next_request(q)  //从请求队列中获取一个请求

----------------->q->prep_rq_fn(q, rq);//对获取到的请求进行预处理,
---------------------scsi_prep_fn(q,rq)

--------------------->scsi_get_command(sdev, GFP_ATOMIC);

//分配SCSI命令struct scsi_cmnd,并初始化,req->special=cmnd

--------------------->cmd = __scsi_get_command(dev->host, gfp_mask); //分配SCSI命令结构
                          -->kmem_cache_alloc(shost->cmd_pool->slab,gfp_mask | shost->cmd_pool->gfp_mask);
                             scsi_init_io(cmd) //初始化SCSI命令结构中的sg(分散聚合表)
                             drv->init_command(cmd) //驱动初始化SCSI命令
                          -->sd_init_command(cmd) //磁盘驱动初始化SCSI命令
                         scmd->cmnd[]  构建SCSI CDB,
                        设置超时时间cmd->timeout_per_command=scsi设备超时时间30ms
                       设置SCSI命令的回函数cmd->done=sd_rw_intr()  或scsi_done()  
7.2,------------>scsi_dispatch_cmd(cmd); //分发请求,把SCSI命令提交给SCSI控制器

scsi_add_timer(cmd, cmd->timeout_per_command,scsi_times_out);

//设置SCSI命令的超时处理函数30ms

8,----------------->host->hostt->queuecommand(cmd, scsi_done);
=============以上为SCSI中间层(SCSI协议层)=================

本层为SCSI控制器的驱动,由控制器厂商实现驱动,一盘为了扩展
SATA盘AHCI控制器:  ahci_sht->queuecommand=ata_scsi_queuecmd(cmd,scsi_done)
SAS盘SAS控制器:PMC(如pm8001)pm8001_sht->queuecommand=sas_queuecommand(cmd,scsi_done)
LSI(如mpt2sas,mpt3sas) scsih_driver_template->queuecommand=_scsi_qcmd(cmd,scsi_done)

=========以上为SCSI低层(SCSI传输层/SCSI控制器驱动层)==========

2.1,IO返回过程与错误处理

SCSI低层命令返回: 命令返回与错误处理
scsi_done():
scsi_delete_timer(scmd)//删除定时器
__scsi_done(scmd)
30ms超时:scsi_times_out(scmd):
scmd->device->host->hostt->eh_timed_out(scmd)
   如果成功处理,则__scsi_done(scmd)
或(重试) 重新设置定时器,再等30ms

__scsi_done(scmd):
//把scmd添加到scsi_done_q的全局链表
//触发软中断SCSI_SOFTIRQ
--->scsi_softirq()    或者  scsi_softirq_done()
遍历处理scsi_done_q链表中的scmd,
disposition = scsi_decide_disposition(cmd);
switch (disposition) {
case SUCCESS:

scsi_finish_command(cmd);/* 结束命令 */

//调用SCSI命令的回调函数 sd_rw_intr(cmd) 或 scsi_done(cmd)

//最终都会调用 bio->bi_end_io
    break;
case NEEDS_RETRY:

scsi_retry_command(cmd);/* 立即重试命令 */

//把SCSI请求重新插入请求队列 scsi_queue_insert(cmd,)

break;
case ADD_TO_MLQUEUE:
    scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);/* 延时重试命令 */
    break;
default:
    if (!scsi_eh_scmd_add(cmd, 0))/* 进入SCSI控制器的错误处理流程.唤配shost->ehandler错误处理线程 */
    scsi_finish_command(cmd);/* 不能进行错误处理,强制结束这个SCSI命令 */
}

scsi_error_handler():
if (shost->hostt->eh_strategy_handler) /* 主机适配器定义了错误恢复处理回调 */
rtn = shost->hostt->eh_strategy_handler(shost);
else
scsi_unjam_host(shost);/* 默认的错误恢复函数 */

scsi_unjam_host():

if (!scsi_eh_get_sense(&eh_work_q, &eh_done_q))/* 发送用于错误恢复的SCSI命令 */
if (!scsi_eh_abort_cmds(&eh_work_q, &eh_done_q))/* 放弃故障的命令 */
scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q);

static void scsi_eh_ready_devs(struct Scsi_Host *shost,
      struct list_head *work_q,
      struct list_head *done_q)
{
if (!scsi_eh_stu(shost, work_q, done_q))/* 发送命令重启设备 */
if (!scsi_eh_bus_device_reset(shost, work_q, done_q))/* 复位逻辑设备 */
if (!scsi_eh_bus_reset(shost, work_q, done_q))/* 复位总线通道 */
if (!scsi_eh_host_reset(work_q, done_q))/* 复位主机适配器 */
scsi_eh_offline_sdevs(work_q, done_q);/* 使SCSI设备离线 */
}

SCSI设备IO过程:磁盘上线与IO过程相关推荐

  1. 实战:如何对磁盘和网络IO进行评估、监控、定位和优化?

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 后台回复"k8s",可领取k8s资料 生产中经常遇到一 ...

  2. linux 磁盘资源管理以及IO

    柱面:Cylinder :一个环形磁道的面Cylinder =Track,早期的磁盘一个面上有1024个磁柱面:现在磁盘(服务器上的磁盘)做的很大,一个磁面上可以达到十几万的柱面数. 磁道:Track ...

  3. 18 操作系统第五章 设备管理 IO设备的基本概念和分类 IO控制器 IO控制方式 IO软件层次结构 IO核心子系统 假脱机技术 设备的分配与回收 缓冲区管理

    文章目录 1 IO设备的基本概念和分类 1.1 什么是I/O设备 1.2 I/O设备分类 2 IO控制器 2.1 I/O设备组成 2.2 I/O控制器功能 2.3 I/O控制器的组成 2.4 寄存器编 ...

  4. 网络IO和磁盘IO详解

    网络IO和磁盘IO详解 1. 缓存IO 缓存I/O又被称作标准I/O,大多数文件系统的默认I/O操作都是缓存I/O.在Linux的缓存I/O机制中,数据先从磁盘复制到内核空间的缓冲区,然后从内核空间缓 ...

  5. 网络io和磁盘io_在磁盘IO上,第1部分:IO的风味

    网络io和磁盘io 10月,我将在纽约参加O'Reilly Velocity会议,并发表"当我们谈论磁盘IO时我们谈论的话题"的演讲 . 我决定将我的一些准备笔记发布为一系列博客文 ...

  6. 北航操作系统课程-第九次作业-设备管理-IO与磁盘

    北航操作系统课程-第九次作业-设备管理-IO与磁盘 北京航空航天大学计算机学院-2020春操作系统课程 题目作者为北航计算机学院操作系统课程组,答案为博主原创.水平有限,无法保证作答正确性,如有错误敬 ...

  7. linux下测试磁盘的读写IO速度-简易方法

    linux下测试磁盘的读写IO速度-简易方法 参考资料: https://blog.csdn.net/zqtsx/article/details/25487185 一:使用hdparm命令 这是一个是 ...

  8. 网络io和磁盘io cpu_在Windows 7中使用任务栏仪表监视CPU,内存和磁盘IO

    网络io和磁盘io cpu It can be annoying having to launch Task Manager or Resource Monitor every time you ju ...

  9. 快速云:五分钟了解几款磁盘测试与IO查看的工具

    这期来讲一下云服务器中linux架构下,跟磁盘相关的几款测试工具,包括运维经常使用的IO测试工具FIO,已经简单轻量化的测试工具DD,以及一个IO相关状态的查看工具IOSTAT,以下笔者就为您介绍这款 ...

  10. 网络与IO知识扫盲(三):从系统调用的角度,剖析 Socket 的连接过程、BIO 的连接过程

    Socket的连接过程.TCP的一些参数 前置知识 用到的命令 netstat -natp 查看网络连接和占用的端口 tcpdump -nn -i eth0 port 9090 开监听抓取数据包 ls ...

最新文章

  1. SAP CO模块权限控制
  2. java.lang.String cannot be cast to org.apache.flink.table.data.StringData
  3. delphi开发LINUX程序,DELPHI开发LINUX包
  4. php判断url参数为空,PHP检查url链接是否已经有参数的简单示例
  5. 用shell查看关键数据
  6. python语言标识符命名规则_python标识符命名规范是什么
  7. rundeck入门-基础知识
  8. 读SRE Google运维解密有感(一)
  9. 【火炉炼AI】机器学习045-对股票数据进行隐马尔科夫建模
  10. 基于单片机的电子秤(数码管)系统设计(#0416)
  11. U盘文件夹类型变成应用程序
  12. 智能家居APP的竞品分析报告(米家)
  13. 前端开发SEO搜索引擎优化方案
  14. 对于最小二乘法的解释
  15. mysql获取最大天数_mysql如何查询两个日期之间最大的连续登录天数
  16. 德莱联盟(判断两点是否相交 nyist)
  17. Android实现应用内多语言切换
  18. hiveserver2 和beeline_Beeline连接Hiveserver2错误
  19. 率土之滨鸿蒙之初,实用主义大菜刀攻略:从赛季初到赛季末
  20. 帝国网站服务器,帝国CMS程序服务器迁移的方法

热门文章

  1. java jsch执行脚本_JSch远程执行脚本
  2. idea打包 jar文件
  3. ArcGIS矢量化并进行拓扑检查(附练习数据下载)
  4. VB.NET实现文件上传下载
  5. html按钮调用php函数,如何在html按钮上执行php函数点击
  6. 【Django】(一)django的下载、安装、配置及创建项目等
  7. MATLAB中 histogram 和 imhist 的区别
  8. 网易云课堂Python Flask框架全栈开发
  9. 使用python读写xlsx格式中的数据【xlrd、pywin32】
  10. 【FFmpeg】srs引入ffmpeg转码