Linux 内核scsi磁盘的错误恢复机制 与线程

 1 使用kthread_create创建线程:
    struct task_struct *kthread_create(int (*threadfn)(void *data),
                                       void *data,
       const char *namefmt, ...);
这个函数可以像printk一样传入某种格式的线程名
线程创建后,不会马上运行,而是需要将kthread_create() 返回的task_struct指针传给wake_up_process(),然后通过此函数运行线程。
2. 当然,还有一个创建并启动线程的函数:kthread_run
   struct task_struct *kthread_run(int (*threadfn)(void *data),
                                    void *data,
                                    const char *namefmt, ...);
3. 线程一旦启动起来后,会一直运行,除非该线程主动调用do_exit函数,或者其他的进程调用kthread_stop函数,结束线程的运行。
    int kthread_stop(struct task_struct *thread);
kthread_stop() 通过发送信号给线程。
如果线程函数正在处理一个非常重要的任务,它不会被中断的。当然如果线程函数永远不返回并且不检查信号,它将永远都不会停止。
参考:Kernel threads made easy

简单的说,就是linux内核为每个下发的scsi磁盘命令加个定时器,如果超时了下面底层驱动还没有处理完这个scsi命令的话,就开始触发scsi_unjam_host开始的动作。这个函数会进行一些逐渐提高等级的恢复操作,然后看你这个底层的scsi host是不是可以恢复到正常状态了。
 默认的策略是这样的,不过好像也可以在自己的scsi host里面自定义恢复策略。
  scsi_eh_abort_cmds        //先是取消这个超时命令,调用scsi host的接口通知下层取消
      if (!scsi_eh_stu(shost, work_q, done_q))    //START_STOP_UNIT command  让这个磁盘重新就绪?
                 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))  //重启整个host,整个host上面的所有磁盘估计都要重启了。
                                        scsi_eh_offline_sdevs(work_q, done_q);  //
 
 
 
 
  这里的scsi错误指的不是 scsi命令返回的错误结果,而是scsi命令被分发给下层驱动之后,驱动超时了也没有返回一个结果回来,就是scsi命令在底层挂住了停止响应了。
 
  以前linux内核会为每个scsi磁盘命令建一个 timer的,使用scsi_add_timer这个函数来作的。 不过后来好像2.6.28开始改了办法了,因为他们发现这样可能导致创建的timer 太多,影响性能吧。作磁盘测试的时候,可以达到每秒3000个磁盘操作,每个超时设为100秒的时候,都会导致创建很多timer了。 应该是把这个timer计时放到block层来做了,每个队列 queue里面所有磁盘指令现在共用一个timer,然后每次执行一个磁盘指令的时候,修改这个timer的超时为所有指令的最小超时时间。而不是为每个指令都建立一个新的timer。
 这个新的机制应该是在lwn的这篇文章里面有描述的,不过不知道怎么回事今天lwn在我这里打不开。
 Block layer: solid-state storage, timeouts, affinity, and more  http://lwn.net/Articles/303270/
 
timer的超时处理函数最后调到这个scsi_eh_scmd_add函数,就会唤醒scsi的错误恢复进程
  82/**
  83 * scsi_eh_scmd_add - add scsi cmd to error handling.
  84 * @scmd:       scmd to run eh on.
  85 * @eh_flag:    optional SCSI_EH flag.
  86 *
  87 * Return value:
  88 *      0 on failure.
  89 */
  90int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag)
  91{
  92        struct Scsi_Host *shost = scmd->device->host;
  93        unsigned long flags;
  94        int ret = 0;
  95
  96        if (!shost->ehandler)
  97                return 0;
  98
  99        spin_lock_irqsave(shost->host_lock, flags);
 100        if (scsi_host_set_state(shost, SHOST_RECOVERY))
 101                if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY))
 102                        goto out_unlock;
 103
 104        ret = 1;
 105        scmd->eh_eflags |= eh_flag;
 106        list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);
 107        shost->host_failed++;
 108        scsi_eh_wakeup(shost);     唤醒超时处理线程
 109 out_unlock:
 110        spin_unlock_irqrestore(shost->host_lock, flags);
 111        return ret;
 112}
 

 
 
 //scsi把这个scsi_times_out函数注册为block层的超时处理函数了。
  114/**
 115 * scsi_times_out - Timeout function for normal scsi commands.
 116 * @req:        request that is timing out.
 117 *
 118 * Notes:
 119 *     We do not need to lock this.  There is the potential for a race
 120 *     only in that the normal completion handling might run, but if the
 121 *     normal completion function determines that the timer has already
 122 *     fired, then it mustn't do anything.
 123 */
 124enum blk_eh_timer_return scsi_times_out(struct request *req)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值