SCSI设备IO过程:磁盘上线与IO过程

一,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设备离线 */
}
  •                     <li class="tool-item tool-active is-like "><a href="javascript:;"><svg class="icon" aria-hidden="true">
                            <use xlink:href="#csdnc-thumbsup"></use>
                        </svg><span class="name">点赞</span>
                        <span class="count">1</span>
                        </a></li>
                        <li class="tool-item tool-active is-collection "><a href="javascript:;" data-report-click="{&quot;mod&quot;:&quot;popu_824&quot;}"><svg class="icon" aria-hidden="true">
                            <use xlink:href="#icon-csdnc-Collection-G"></use>
                        </svg><span class="name">收藏</span></a></li>
                        <li class="tool-item tool-active is-share"><a href="javascript:;"><svg class="icon" aria-hidden="true">
                            <use xlink:href="#icon-csdnc-fenxiang"></use>
                        </svg>分享</a></li>
                        <!--打赏开始-->
                                                <!--打赏结束-->
                                                <li class="tool-item tool-more">
                            <a>
                            <svg t="1575545411852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M179.176 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5718"></path><path d="M509.684 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5719"></path><path d="M846.175 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5720"></path></svg>
                            </a>
                            <ul class="more-box">
                                <li class="item"><a class="article-report">文章举报</a></li>
                            </ul>
                        </li>
                                            </ul>
                </div>
                            </div>
            <div class="person-messagebox">
                <div class="left-message"><a href="https://blog.csdn.net/cgm88s">
                    <img src="https://profile.csdnimg.cn/0/9/C/3_cgm88s" class="avatar_pic" username="cgm88s">
                                            <img src="https://g.csdnimg.cn/static/user-reg-year/1x/10.png" class="user-years">
                                    </a></div>
                <div class="middle-message">
                                        <div class="title"><span class="tit"><a href="https://blog.csdn.net/cgm88s" data-report-click="{&quot;mod&quot;:&quot;popu_379&quot;}" target="_blank">_银叶_</a></span>
                                            </div>
                    <div class="text"><span>发布了75 篇原创文章</span> · <span>获赞 22</span> · <span>访问量 31万+</span></div>
                </div>
                                <div class="right-message">
                                            <a href="https://im.csdn.net/im/main.html?userName=cgm88s" target="_blank" class="btn btn-sm btn-red-hollow bt-button personal-letter">私信
                        </a>
                                                            <a class="btn btn-sm attented bt-button personal-watch" data-report-click="{&quot;mod&quot;:&quot;popu_379&quot;}">已关注</a>
                                    </div>
                            </div>
                    </div>
    
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值