免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3290 | 回复: 1
打印 上一主题 下一主题

about linux scsi module error handling [复制链接]

论坛徽章:
1
操作系统版块每日发帖之星
日期:2016-06-13 06:20:00
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-08-06 19:21 |只看该作者 |倒序浏览

                                                                               
此文直接由WORD贴过来.若想格式和图片效果更好,请下载PDF(附在此文最下面).( 此站第一篇文章,heh )
About Linux module



      

1 Linux error handling thread
When
can work


Linux have a error handling thread called
scsi_error_handler, it will call eh_strategy_handler function pointer if have
otherwise it call scsi_unjam_host to do error handling.

flow 1
scsi_error_handler
        |
        |
        |---------à  eh_strategy_handler  or
                  
scsi_unjam_host


The below is an important function
scsi_error_handler that handle error handling, the marked red part shows some
policy related to error handling

When error handling start
  
1)      host_failed == host_busy and host_failed != 0   or
2)      host_failed == host_busy and host_eh_scheduled != 0.


The above two conditions meet any one error
handling can start. host_failed means the failed scmd request count, the
host_busy means working request count. So the necessary condition is all
working requests failed it can start error handling.  The other two is if host_failed is not equal
0 or host_eh_scheduled is not 0.

host_eh_scheduled is a variable that to let
driver to motivate scsi_error_handler by call function scsi_schedule_eh.  When user call scsi_scheduler_eh function the
host_eh_sceduled would increase one. So the aim it to let scsi_error_handler
thread can run if failed request number equal to busy request number.

When doing recovery the normal command also
can’t be send to LLDD from mid layer. scsi_request_fn function would check the
shost status. when doing recovery the host status set to
1)            
SHOST_RECOVERY
2)            
SHOST_CANCEL_RECOVERY
3)            
SHOST_DEL_RECOVERY


function scsi_error_handler 1
int
scsi_error_handler(void *data)
{
       struct Scsi_Host *shost = data;

       /*
        *
We use TASK_INTERRUPTIBLE so that the thread is not
        *
counted against the load average as a running process.
        *
We never actually get interrupted because kthread_run
        *
disables singal delivery for the created thread.
        */
       set_current_state(TASK_INTERRUPTIBLE);
       while (!kthread_should_stop()) {
              if
((shost->host_failed == 0 && shost->host_eh_scheduled == 0) ||
                  shost->host_failed !=
shost->host_busy) {
                     SCSI_LOG_ERROR_RECOVERY(1,
                            printk("Error
handler scsi_eh_%d sleeping\n",
                                   shost->host_no));
                     schedule();
                     set_current_state(TASK_INTERRUPTIBLE);
                     continue;
              }

              __set_current_state(TASK_RUNNING);
              SCSI_LOG_ERROR_RECOVERY(1,
                     printk("Error handler
scsi_eh_%d waking up\n",
                            shost->host_no));

              /*
               * We have a host that is failing for some
reason.  Figure out
               * what we need to do to get it up and online
again (if we can).
               * If we fail, we end up taking the thing
offline.
               */
              if
(shost->transportt->eh_strategy_handler)
                     shost->transportt->eh_strategy_handler(shost);
              else
                     scsi_unjam_host(shost);

              /*
               * Note - if the above fails completely, the
action is to take
               * individual devices offline and flush the
queue of any
               * outstanding requests that may have been
pending.  When we
               * restart, we restart any I/O to any other
devices on the bus
               * which are still online.
               */
              scsi_restart_operations(shost);
              set_current_state(TASK_INTERRUPTIBLE);
       }
       __set_current_state(TASK_RUNNING);

       SCSI_LOG_ERROR_RECOVERY(1,
              printk("Error handler
scsi_eh_%d exiting\n", shost->host_no));
       shost->ehandler = NULL;
       return 0;
}

What
to do

The linux scsi error handler thread just
call a function pointer eh_strategy_handler or scsi_unjam_host. The
eh_strategy_handler function pointer common not implement by LLDD. So the
scsi_unjam_host stand out to do the job. This function is important so we would
talk about it later.

Because when doing failed request recovery
the normal operation is blocked so after doing recovery need to restart the
operation so function scsi_restart_operation function called.


scsi_unjam_host
In Linux document have a detail description
for this function. The link is

./Document/scsi/scsi_eh.txt

Below I also refer the function implement
and the kernel document to describe the function. I think it is very detail. I
don’t want to talk about the thing that the document already talked. However
there also may something else to talk.

In host template we have some function
pointer to implement that related to error handling. It includes
1)     
int (* eh_abort_handler)(struct
scsi_cmnd *);
2)     
int (*
eh_device_reset_handler)(struct scsi_cmnd *);
3)     
int (*
eh_bus_reset_handler)(struct scsi_cmnd *);
4)     
int (*
eh_host_reset_handler)(struct scsi_cmnd *);

These 4 function is different level error
handling from easy to complex. When doing error handling while no normal
request handling, it tries below layer error handler, if it can succeed handle
all error request no need more level handler stand out. And error handling can
finish. Otherwise it need to using higher level error handling function to
handle.

Here we should follow the function. Simply
saying that there are two type error request.
1)      
time out request
2)      
no succeed handle by LLDD
request but returned with error sense information.
For different type error request, though it
all need be handled in scsi_unjam_host function, however there error mark is
different so there error handle way is different.

The major different related mark including
1)       SCSI_EH_CANCEL_CMD.
2)       SENSE error request.
The SCSI_EH_CANCEL_CMD mark now is only add
to time out request. The sense error request is related the scmd that return
with not correct sense.

Below would talked about that under
paragraph. First let’s make sure on concept that what error handling aim. Just
like two type error request so error handling aim existed 2.
1)       clear time out scmd in LLDD. Because time out request is handling in
LLDD, the scmd is still remembered in LLDD driver. So the error handling want
the LLDD forgot the time out scmd in LLDD.
And cancel the jobs that doing for handling the time out scmd in LLDD driver.
2)       Found hardware have error, so reset hardware to let it work
normally.


function scsi_unjam_host 2
static void
scsi_unjam_host(struct Scsi_Host *shost)
{
       unsigned long flags;
       LIST_HEAD(eh_work_q);
       LIST_HEAD(eh_done_q);

       spin_lock_irqsave(shost->host_lock,
flags);
       list_splice_init(&shost->eh_cmd_q,
&eh_work_q);
       spin_unlock_irqrestore(shost->host_lock,
flags);

       SCSI_LOG_ERROR_RECOVERY(1,
scsi_eh_prt_fail_stats(shost, &eh_work_q));




   



   


       if (!scsi_eh_get_sense(&eh_work_q,
&eh_done_q))
              if
(!scsi_eh_abort_cmds(&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);
}

  


  
   



reference scsi_eh.txt 1
>

    1. Lock shost->host_lock, splice_init
shost->eh_cmd_q into local
       eh_work_q and unlock host_lock.  Note that shost->eh_cmd_q is
       cleared by this action.

    2. Invoke scsi_eh_get_sense.

    >

       This action is taken for each
error-completed
       (!SCSI_EH_CANCEL_CMD) commands without
valid sense data.  Most
       SCSI transports/LLDDs automatically
acquire sense data on
       command failures (autosense).  Autosense is recommended for
       performance reasons and as sense
information could get out of
       sync inbetween occurrence of CHECK
CONDITION and this action.

       Note that if autosense is not supported,
scmd->sense_buffer
       contains invalid sense data when
error-completing the scmd
       with scsi_done().  scsi_decide_disposition() always returns
       FAILED in such cases thus invoking SCSI
EH.  When the scmd
       reaches here, sense data is acquired and
       scsi_decide_disposition() is called
again.

       1. Invoke scsi_request_sense() which
issues REQUEST_SENSE
           command.  If fails, no action.  Note that taking no action
           causes higher-severity recovery to
be taken for the scmd.

       2. Invoke scsi_decide_disposition() on
the scmd

         
- SUCCESS
              scmd->retries is set to
scmd->allowed preventing
              scsi_eh_flush_done_q() from
retrying the scmd and
              scsi_eh_finish_cmd() is invoked.

         
- NEEDS_RETRY
              scsi_eh_finish_cmd() invoked

         
- otherwise
              No action.

    3. If !list_empty(&eh_work_q), invoke
scsi_eh_abort_cmds().

    >

       This action is taken for each timed out
command.
       hostt->eh_abort_handler() is invoked
for each scmd.  The
       handler returns SUCCESS if it has
succeeded to make LLDD and
       all related hardware forget about the
scmd.

       If a timedout scmd is successfully
aborted and the sdev is
       either offline or ready, scsi_eh_finish_cmd()
is invoked for
       the scmd.
Otherwise, the scmd is left in eh_work_q for
       higher-severity actions.

       Note that both offline and ready status
mean that the sdev is
       ready to process new scmds, where
processing also implies
       immediate failing; thus, if a sdev is in
one of the two
       states, no further recovery action is
needed.

       Device readiness is tested using
scsi_eh_tur() which issues
       TEST_UNIT_READY command.  Note that the scmd must have been
       aborted successfully before reusing it
for TEST_UNIT_READY.

    4. If !list_empty(&eh_work_q), invoke
scsi_eh_ready_devs()

    >

       This function takes four increasingly
more severe measures to
       make failed sdevs ready for new commands.

       1. Invoke scsi_eh_stu()

       >

         
For each sdev which has failed scmds with valid sense data
         
of which scsi_check_sense()'s verdict is FAILED,
         
START_STOP_UNIT command is issued w/ start=1.  Note that
         
as we explicitly choose error-completed scmds, it is known
         
that lower layers have forgotten about the scmd and we can
         
reuse it for STU.

         
If STU succeeds and the sdev is either offline or ready,
         
all failed scmds on the sdev are EH-finished with
         
scsi_eh_finish_cmd().

         
*NOTE* If hostt->eh_abort_handler() isn't implemented or
         
failed, we may still have timed out scmds at this point
         
and STU doesn't make lower layers forget about those
         
scmds.  Yet, this function
EH-finish all scmds on the sdev
         
if STU succeeds leaving lower layers in an inconsistent
         
state.  It seems that STU action
should be taken only when
         
a sdev has no timed out scmd.

       2. If !list_empty(&eh_work_q), invoke
scsi_eh_bus_device_reset().

       >

         
This action is very similar to scsi_eh_stu() except that,
         
instead of issuing STU, hostt->eh_device_reset_handler()
         
is used.  Also, as we're not
issuing SCSI commands and
         
resetting clears all scmds on the sdev, there is no need
         
to choose error-completed scmds.

       3. If !list_empty(&eh_work_q), invoke
scsi_eh_bus_reset()

       >

         
hostt->eh_bus_reset_handler() is invoked for each channel
         
with failed scmds.  If bus reset
succeeds, all failed
         
scmds on all ready or offline sdevs on the channel are
         
EH-finished.

       4. If !list_empty(&eh_work_q), invoke
scsi_eh_host_reset()

       >

         
This is the last resort.
hostt->eh_host_reset_handler()
         
is invoked.  If host reset
succeeds, all failed scmds on
         
all ready or offline sdevs on the host are EH-finished.

       5. If !list_empty(&eh_work_q), invoke
scsi_eh_offline_sdevs()

       >

         
Take all sdevs which still have unrecovered scmds offline
         
and EH-finish the scmds.

    5. Invoke scsi_eh_flush_done_q().

       >

         
At this point all scmds are recovered (or given up) and
         
put on eh_done_q by scsi_eh_finish_cmd().  This function
         
flushes eh_done_q by either retrying or notifying upper
         
layer of failure of the scmds.




1.1 time out request error handle path
Previously I have said that error handle
aim for time out request is to let LLDD to forgot that scmd after an time out
scmd insert to error queue. (By the way time out request also may be handled by
other way, I would describe later. Here only talked error handling thread how
to handle time out request) the error handling aim is to let LLDD to
forgot.  So it mark SCSI_EH_CANCEL_CMD to
the time out scmd. And insert in to error queue.
In scsi_unjam_host function. It would tries
1)       abort the request ( to make it forgot ) if failed
2)       try to reset device to make hardware forgot scmd. ( because
scmd->done function already be ignore, so let hardware forgot that is the
left thing ), if failed
3)       try to reset bus, if failed
4)       try to reset host., if failed
5)       mark the device (that still have failing request)is offline.  

The other important function related to
time out request eh_timed_out function
1.2 error return request handle path
The error return request would not try to
abort request because it already return by LLDD driver. So it just try to get
correct sense.
1)       try to get sense
2)       try to send start stop unit to enable the device
3)       try to reset device to make hardware forgot scmd. ( because
scmd->done function already be ignore, so let hardware forgot that is the
left thing ), if failed
4)       try to reset bus, if failed
5)       try to reset host., if failed
6)       mark the device (that still have failing request)is offline.  

1.3 compare the two error request type handling
The two path is only different at previous time
out scmd need to let LLDD forgot and sense error request need to re-get sense.

If failed, they all try to reset hardware
until reset return succeed. Because they thinks if abort or get sense fail the
hardware may have problem so reset. hardware

 SHAPE 
\* MERGEFORMAT


  
  
   
   
   
   
   
   
   
   
   
   
   
   
  
  
  

  
  
  

  
   
   
   
     
     
     time
     out scmd
     
     
   
   
   

  

  
   
   
   
     
     
     SCSI_EH_CANCEL_CMD
     
     
   
   
   

  
   
   
   
     
     
     Abort
     request
     
     
   
   
   

  

  
   
   
   
     
     
     returned failed scmd
     
     
   
   
   

  

  
   
   
   
     
     
     sense is not valid
     
     
   
   
   

  
   
   
   
     
     
     get
     sense
     
     
   
   
   

  


   此处缺少图,若要看图请下载 pdf

1.4 The fate of error request
 SHAPE 
\* MERGEFORMAT


  
  
  

  
   
   
   
     
     
     any faile
     
     
   
   
   

  
   
   
   
     
     
     device
     on line
     
     
   
   
   

  

  
   
   
   
     
     
     have
     retry opptunity
     
     
   
   
   

  
   
   
   
     
     
     reinsert
     to request queue
     
     
   
   
   

  


  

  
   
   
   
     
     
     finish the request
     
     
   
   
   

  

  
   
   
   
     
     
     both true
     
     
   
   
   


  此处缺少图,若要看图请下载 pdf

After the error request handled finished,
it would insert to an done_q. When scsi_eh_flush_done_q called, all the scmd in
the done_q would be handled. The scmd in done_q have two results. one is to
reinsert to request queue to retry. If it retry count didn’t run out and the
device still on line. The other result is just finish the request.

So the error handling aim in that thread we
could see is just make hardware works fine, then may retry or fail the error
request.
1.5 others related to error handling thread
1) the scmd to do error handing such as
send START_STOP_UNIT cmd is reusing the failed scmd.



2 time out scmd before enter in error queue.
Time out is an very common error situation.
Above talked about how to handle time out scmd in scsi_error_handler thread. In
fact not all time out request would be handled by scsi_error_handler thread
handle.  Many of them would handled by
others.

In linux scsi host and transport templates
structure both have time out scmd handler function pointer. both name
eh_timed_out. Both of them are optional.

Simply saying, that
1)      
eh_timed_out function if not
available it would return  EH_NOT_HANDLED. And insert to time out scmd
into error queue.
2)      
if eh_timed_out avail, it have
3 return value, EH_HANDLED,EH_RESET_TIMER,EH_NOT_HANDLED
3)      
EH_HANDLED_HANDLED lead to
finish to command
4)      
EH_ RESET_TIMER lead to
enlarger the time out time value.
5)      
EH_NOT_HANDLED lead to the scmd
request insert to error queue

And when I check code I
found EH_RESET_TIMER is not cause retry count increase not like kernel Document
saying

So here shows that only EH_NOT_HANDLED time
out scmd would enter in error queue.

Below reference paragraphs have detail
introduction.

reference scmd time out related 2
[1-2-2]
Completing a scmd w/ timeout

The timeout handler is scsi_times_out().  When a timeout occurs, this
function

1. invokes optional hostt->eh_timed_out()
callback.  Return value can
    be one of

    - EH_HANDLED
        This indicates that eh_timed_out()
dealt with the timeout.  The
        scmd is passed to __scsi_done() and
thus linked into per-cpu
        scsi_done_q.  Normal command completion described in
[1-2-1]
        follows.

    - EH_RESET_TIMER
        This indicates that more time is
required to finish the
        command.  Timer is restarted.  This action is
counted as a
        retry and only
allowed scmd->allowed + 1(!) times.  Once the
        limit is reached, action for EH_NOT_HANDLED
is taken instead.

        *NOTE* This action is racy as the LLDD
could finish the scmd
        after the timeout has expired but
before it's added back.  In
        such cases, scsi_done() would think
that timeout has occurred
        and return without doing anything.  We lose completion and the
        command will time out again.

    - EH_NOT_HANDLED
        This is the same as when eh_timed_out()
callback doesn't exist.
        Step #2 is taken.

2. scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD)
is invoked for the
    command.
See [1-3] for more information.




3 the mpt2sas error handling introduction
LSI mpt2SAS driver is for the LSI message
passing technology related controller driver. I thinks it just like our Loki,
it includes embedded cpus.

For error handling part it have below
functions.
reference lsi error handling related
functions 3
        .eh_abort_handler               = scsih_abort,
        .eh_device_reset_handler        = scsih_dev_reset,
        .eh_host_reset_handler          = scsih_host_reset,

scsih_abort function do belows things
1)      if the scmd have no hardware related just call scmd->done();
2)     
if the scmd have hardware
related, get a message and set it to MPI2_SCSITASKMGMT_TASKTYPE_ABORT_TASK and
send it to hba firmware. to cancel related command.

scsih_dev_reset function do belows things
1)      if the scmd have no hardware related just call scmd->done();
2)      find the related device to the scmd.
3)     
and get a message and set it to
MPI2_SCSITASKMGMT_TASKTYPE_TARGET_RESET and send it to hba firmware to reset
the device.
scsih_host_reset function do below things
1)     
reset the hba controller.

During hba hardware reset. It would block
by using completion mechanism to wait for hba restart.

               

       
        文件:About Linux module error handling.pdf
        大小:72KB
        下载:
下载
       
               
               
v\:* {behavior:url(#default#VML);}
o\:* {behavior:url(#default#VML);}
w\:* {behavior:url(#default#VML);}
.shape {behavior:url(#default#VML);}

  Normal
  0
  
  7.8 pt
  0
  2
  
  false
  false
  false
  
   
   
   
   
   
   
   
   
   
   
   
   
  
  MicrosoftInternetExplorer4



/* Style Definitions */
table.MsoNormalTable
        {mso-style-name:"Table Normal";
        mso-tstyle-rowband-size:0;
        mso-tstyle-colband-size:0;
        mso-style-noshow:yes;
        mso-style-parent:"";
        mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
        mso-para-margin:0cm;
        mso-para-margin-bottom:.0001pt;
        mso-pagination:widow-orphan;
        font-size:10.0pt;
        font-family:"Times New Roman";
        mso-ansi-language:#0400;
        mso-fareast-language:#0400;
        mso-bidi-language:#0400;}
table.MsoTableGrid
        {mso-style-name:"Table Grid";
        mso-tstyle-rowband-size:0;
        mso-tstyle-colband-size:0;
        border:solid windowtext 1.0pt;
        mso-border-alt:solid windowtext .5pt;
        mso-padding-alt:0cm 5.4pt 0cm 5.4pt;
        mso-border-insideh:.5pt solid windowtext;
        mso-border-insidev:.5pt solid windowtext;
        mso-para-margin:0cm;
        mso-para-margin-bottom:.0001pt;
        text-align:justify;
        text-justify:inter-ideograph;
        mso-pagination:none;
        font-size:10.0pt;
        font-family:"Times New Roman";
        mso-ansi-language:#0400;
        mso-fareast-language:#0400;
        mso-bidi-language:#0400;}


  

               
               
               
               
               
               
               
               
               
               

本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/102470/showart_2020067.html

论坛徽章:
0
2 [报告]
发表于 2014-01-17 18:05 |只看该作者
very good
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP