讨论下Linux下SCSI栈的IO错误处理策略

讨论下Linux下SCSI栈的IO错误处理策略

(一)尽快报错  or  尽力重试?

先讨论一个问题:如果上级交给的任务暂时无法完成,那么我们应该:

(策略1)应该尽快向上报错(abort),等待上级做出下一步的指令。

(策略2)暂不报告失败,而是带着未完成的任务,去修复和重试。

用电影《大决战》中的情节来举例,锦州外围某部队丢了阵地后没有向上报告,而是尽力把阵地夺了回来。

讨论下Linux下SCSI栈的IO错误处理策略

 

 讨论下Linux下SCSI栈的IO错误处理策略

(二)Linux的SCSI对错误的处理策略

目前Linux的SCSI栈,默认的策略是暂时不要向上报错,而是修复和重试。希望能对上级屏蔽底层错误,返回一个重试后的成功的结果。

 

如果SCSI设备驱动完整的注册了各个修复函数(scsi_en_XX_reset ),

那么底层回错的结果就是, 从下向上,逐级修复, 最后把堆积的IO再提交一遍。

如果SCSI设备驱动没有注册scsi_en_XX_reset函数,那么就没有相关修复处理。

 

static void scsi_unjam_host(struct Scsi_Host *shost)

{

    略 。。。

    if (!scsi_eh_get_sense(&eh_work_q, &eh_done_q))

          scsi_eh_ready_devs(shost, &eh_work_q, &eh_done_q);

    略 。。。

    scsi_eh_flush_done_q(&eh_done_q);

}

 

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_target_reset(shost, work_q, done_q))

           if (!scsi_eh_bus_reset(shost, work_q, done_q))

             if (!scsi_eh_host_reset(shost, work_q, done_q))

                 scsi_eh_offline_sdevs(work_q,done_q);

}

这段代码看的我心惊胆战。

这个反复重试的策略,对单机系统是合理的,但是对分布式系统不合理。

本来分布式系统可以选择这个盘,也可以选择那个盘,但肯定不会选择故障的盘。早点让上级知道,能更快的把后续读写任务切到其他盘。然后故障盘再执行硬件修复(比如Smart查询,坏道扫描,硬盘上下电)就从容了。再说,如果遇到硬件故障,简单的重试大概率还是失败。

 

(三)学习结论:

(策略1)如果是集中式的单控系统,读写任务迁移困难,那么遇到困难先别急着abort,还是尽量重试吧。

(策略2)如果是分布式系统,读写任务可以迁移,那么应该尽快abort,因为分布式系统的可靠性从来不是构筑在单设备可靠性之上,而是用冗余来弥补单设备可靠性的不足。如果底层早点报错,那么上层就能早点切换。

 

##############################

相关函数序列如下:

(1)SCSI层的提交,然后等待硬盘执行完毕后返回中断

scsi_queue_rq

   => scsi_dispatch_cmd

      ==> Scsi_Host->hostt->queuecommand

      ==> scsi_cmnd->scsi_done

 

scsi_execute_req

   => scsi_execute

     ==> __scsi_execute

        ===> blk_execute_rq

 

(2)硬盘返回处理:返回成功 or 返回失败 or 不按时返回

(2.1)中断前半部-硬中断回调

handle_level_irq

  _base_interrupt

       mpt_callbacks

       _scsih_io_done

           scsi_cmnd->scsi_done

               scsi_mq_done

                    blk_mq_complete_request

                       blk_mq_complete_request_remote

                          blk_mq_trigger_softirq

                              raise_softirq_irqoff

                              // 放到软中断中去处理:BLOCK_SOFTIRQ

 

(2.2)中断后半部-软中断回调

 do_softirq

   do_softirq_own_stack

     _do_softirq

       softirq_action->action

       // subsys_initcall(blk_softirq_init)

       // BLOCK_SOFTIRQ    <---> blk_done_softirq

       blk_done_softirq

            request_queue->softirq_done_fn

            scsi_softirq_done

                 scsi_decide_disposition

                        //  SCSI操作完成

                       (A)scsi_finish_command

                            scsi_io_completion

                                scsi_end_request

                                   blk_update_request

                                       req_bio_endio

                                            bio_endio

                                                 bio->bi_end_io

                                                      end_bio_bh_io_sync

 

                                blk_finish_request

                                  request->end_io

                                  // blk_end_sync_rq

                           scsi_run_queue

                     (B) scsi_queue_insert

                            _scsi_queue_insert

                              blk_requeue_request

                                 elv_requeue_request

                              kblockd_schedule_work

                     (C) scsi_eh_scmd_add

 

(2.3)返回错误的处理 

我再强调下结论,分布式系统要尽快返回结果。

先abort,然后再修复底层故障。

不要一边Hung住未完成的IO,一边来修复底层故障!

  scsi_error_handler //while循环任务   

    (A)eh_strategy_handler //自定义处理策略

       # ata_scsi_error

   

    (B)scsi_unjam_host  //SCSI默认处理策略

      scsi_eh_ready_devs

         scsi_eh_bus_device_reset

         scsi_eh_target_reset

         scsi_eh_bus_reset

         scsi_eh_host_reset

         scsi_eh_flush_done_q

(3)超时检查

blk_mq_check_expired

    blk_mq_rq_timed_out

       request->q->mq_ops->timeout

           scsi_timeout

               scsi_times_out

                   scsi_eh_scmd_add

                       scsi_eh_wakeup

                           Scsi_Host->ehandler

 

讨论下Linux下SCSI栈的IO错误处理策略

上一篇:Ubuntu18.04安装教程(一)


下一篇:Ubuntu安装redis