dsphinx.addnodesdocument)}( rawsourcechildren]( translations LanguagesNode)}(hhh](h pending_xref)}(hhh]docutils.nodesTextEnglish}parenthsba attributes}(ids]classes]names]dupnames]backrefs] refdomainstdreftypedoc reftarget /scsi/scsi_ehmodnameN classnameN refexplicitutagnamehhh ubh)}(hhh]hChinese (Traditional)}hh2sbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget /translations/zh_TW/scsi/scsi_ehmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hItalian}hhFsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget /translations/it_IT/scsi/scsi_ehmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hJapanese}hhZsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget /translations/ja_JP/scsi/scsi_ehmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hKorean}hhnsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget /translations/ko_KR/scsi/scsi_ehmodnameN classnameN refexplicituh1hhh ubh)}(hhh]hSpanish}hhsbah}(h]h ]h"]h$]h&] refdomainh)reftypeh+ reftarget /translations/sp_SP/scsi/scsi_ehmodnameN classnameN refexplicituh1hhh ubeh}(h]h ]h"]h$]h&]current_languageChinese (Simplified)uh1h hh _documenthsourceNlineNubhcomment)}(h SPDX-License-Identifier: GPL-2.0h]h SPDX-License-Identifier: GPL-2.0}hhsbah}(h]h ]h"]h$]h&] xml:spacepreserveuh1hhhhhhM/var/lib/git/docbuild/linux/Documentation/translations/zh_CN/scsi/scsi_eh.rsthKubhnote)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]h paragraph)}(hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。h]hX{此文件的目的是为让中文读者更容易阅读和理解,而不是作为一个分支。 因此, 如果您对此文件有任何意见或更新,请先尝试更新原始英文文件。 如果您发现本文档与原始文件有任何不同或者有翻译问题,请发建议或者补丁给 该文件的译者,或者请求中文文档维护者和审阅者的帮助。}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hh5Documentation/translations/zh_CN/disclaimer-zh_CN.rsthKhhubah}(h]h ]h"]h$]h&]uh1hhhhhhhhNubh field_list)}(hhh](hfield)}(hhh](h field_name)}(hOriginalh]hOriginal}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhhhKubh field_body)}(hDocumentation/scsi/scsi_eh.rst h]h)}(hDocumentation/scsi/scsi_eh.rsth]hDocumentation/scsi/scsi_eh.rst}(hhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhhubah}(h]h ]h"]h$]h&]uh1hhhubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h翻译h]h翻译}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhj hhhKubh)}(h/郝栋栋 doubled h]h)}(h.郝栋栋 doubled h](h郝栋栋 doubled <}(hj hhhNhNubh reference)}(hdoubled@leap-io-kernel.comh]hdoubled@leap-io-kernel.com}(hj*hhhNhNubah}(h]h ]h"]h$]h&]refuri!mailto:doubled@leap-io-kernel.comuh1j(hj ubh>}(hj hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1hhj ubeh}(h]h ]h"]h$]h&]uh1hhhhKhhhhubh)}(hhh](h)}(h校译h]h校译}(hjShhhNhNubah}(h]h ]h"]h$]h&]uh1hhjPhhhKubh)}(hhh]h}(h]h ]h"]h$]h&]uh1hhjPubeh}(h]h ]h"]h$]h&]uh1hhhhK hhhhubeh}(h]h ]h"]h$]h&]uh1hhhhhhhhKubhsection)}(hhh](htitle)}(hSCSI 中间层错误处理h]hSCSI 中间层错误处理}(hj}hhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjxhhhhhKubh)}(h本文档描述了SCSI中间层(mid layer)的错误处理基础架构。 关于SCSI中间层的更多信息,请参阅: Documentation/scsi/scsi_mid_low_api.rst。h]h本文档描述了SCSI中间层(mid layer)的错误处理基础架构。 关于SCSI中间层的更多信息,请参阅: Documentation/scsi/scsi_mid_low_api.rst。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjxhhubh)}(hX2目录 [1] SCSI 命令如何通过中间层传递并进入错误处理(EH) [1-1] scsi_cmnd(SCSI命令)结构体 [1-2] scmd(SCSI 命令)是如何完成的? [1-2-1] 通过scsi_done完成scmd [1-2-2] 通过超时机制完成scmd [1-3] 错误处理模块如何接管流程 [2] SCSI错误处理机制工作原理 [2-1] 基于细粒度回调的错误处理 [2-1-1] 概览 [2-1-2] scmd在错误处理流程中的传递路径 [2-1-3] 控制流分析 [2-2] 通过transportt->eh_strategy_handler()实现的错误处理 [2-2-1] transportt->eh_strategy_handler()调用前的中间层状态 [2-2-2] transportt->eh_strategy_handler()调用后的中间层状态 [2-2-3] 注意事项h]hX2目录 [1] SCSI 命令如何通过中间层传递并进入错误处理(EH) [1-1] scsi_cmnd(SCSI命令)结构体 [1-2] scmd(SCSI 命令)是如何完成的? [1-2-1] 通过scsi_done完成scmd [1-2-2] 通过超时机制完成scmd [1-3] 错误处理模块如何接管流程 [2] SCSI错误处理机制工作原理 [2-1] 基于细粒度回调的错误处理 [2-1-1] 概览 [2-1-2] scmd在错误处理流程中的传递路径 [2-1-3] 控制流分析 [2-2] 通过transportt->eh_strategy_handler()实现的错误处理 [2-2-1] transportt->eh_strategy_handler()调用前的中间层状态 [2-2-2] transportt->eh_strategy_handler()调用后的中间层状态 [2-2-3] 注意事项}hjsbah}(h]h ]h"]h$]h&]hhuh1hhjxhhhhhK'ubjw)}(hhh](j|)}(h:1. SCSI命令在中间层及错误处理中的传递流程h]h:1. SCSI命令在中间层及错误处理中的传递流程}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjhhhhhK)ubjw)}(hhh](j|)}(h1.1 scsi_cmnd结构体h]h1.1 scsi_cmnd结构体}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjhhhhhK,ubh)}(hX每个SCSI命令都由struct scsi_cmnd(简称scmd)结构体 表示。scmd包含两个list_head类型的链表节点:scmd->list 与scmd->eh_entry。其中scmd->list是用于空闲链表或设备 专属的scmd分配链表,与错误处理讨论关联不大。而 scmd->eh_entry则是专用于命令完成和错误处理链表,除非 特别说明,本文讨论中所有scmd的链表操作均通过 scmd->eh_entry实现。h]hX每个SCSI命令都由struct scsi_cmnd(简称scmd)结构体 表示。scmd包含两个list_head类型的链表节点:scmd->list 与scmd->eh_entry。其中scmd->list是用于空闲链表或设备 专属的scmd分配链表,与错误处理讨论关联不大。而 scmd->eh_entry则是专用于命令完成和错误处理链表,除非 特别说明,本文讨论中所有scmd的链表操作均通过 scmd->eh_entry实现。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK.hjhhubeh}(h] scsi-cmndah ]h"]1.1 scsi_cmnd结构体ah$]h&]uh1jvhjhhhhhK,ubjw)}(hhh](j|)}(h1.2 scmd是如何完成的?h]h1.2 scmd是如何完成的?}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjhhhhhK8ubh)}(hX%底层设备驱动(LLDD)在获取SCSI命令(scmd)后,存在两种 完成路径:底层驱动可通过调用hostt->queuecommand()时从 中间层传递的scsi_done回调函数主动完成命令,或者当命令未 及时完成时由块层(block layer)触发超时处理机制。h]hX%底层设备驱动(LLDD)在获取SCSI命令(scmd)后,存在两种 完成路径:底层驱动可通过调用hostt->queuecommand()时从 中间层传递的scsi_done回调函数主动完成命令,或者当命令未 及时完成时由块层(block layer)触发超时处理机制。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK:hjhhubjw)}(hhh](j|)}(h+1.2.1 通过scsi_done回调完成SCSI命令h]h+1.2.1 通过scsi_done回调完成SCSI命令}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjhhhhhKAubh)}(h对于所有非错误处理(EH)命令,scsi_done()是其完成回调 函数。它只调用blk_mq_complete_request()来删除块层的 定时器并触发块设备软中断(BLOCK_SOFTIRQ)。h]h对于所有非错误处理(EH)命令,scsi_done()是其完成回调 函数。它只调用blk_mq_complete_request()来删除块层的 定时器并触发块设备软中断(BLOCK_SOFTIRQ)。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKChjhhubh)}(hBLOCK_SOFTIRQ会间接调用scsi_complete(),进而调用 scsi_decide_disposition()来决定如何处理该命令。 scsi_decide_disposition()会查看scmd->result值和感 应码数据来决定如何处理命令。h]hBLOCK_SOFTIRQ会间接调用scsi_complete(),进而调用 scsi_decide_disposition()来决定如何处理该命令。 scsi_decide_disposition()会查看scmd->result值和感 应码数据来决定如何处理命令。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKGhjhhubh block_quote)}(hX>- SUCCESS 调用scsi_finish_command()来处理该命令。该函数会 执行一些维护操作,然后调用scsi_io_completion()来 完成I/O操作。scsi_io_completion()会通过调用 blk_end_request及其相关函数来通知块层该请求已完成, 如果发生错误,还会判断如何处理剩余的数据。 - NEEDS_RETRY - ADD_TO_MLQUEUE scmd被重新加入到块设备队列中。 - otherwise 调用scsi_eh_scmd_add(scmd)来处理该命令。 关于此函数的详细信息,请参见 [1-3]。 h]h bullet_list)}(hhh](h list_item)}(hXZSUCCESS 调用scsi_finish_command()来处理该命令。该函数会 执行一些维护操作,然后调用scsi_io_completion()来 完成I/O操作。scsi_io_completion()会通过调用 blk_end_request及其相关函数来通知块层该请求已完成, 如果发生错误,还会判断如何处理剩余的数据。 h](h)}(hSUCCESSh]hSUCCESS}(hj<hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKLhj8ubj,)}(hX8调用scsi_finish_command()来处理该命令。该函数会 执行一些维护操作,然后调用scsi_io_completion()来 完成I/O操作。scsi_io_completion()会通过调用 blk_end_request及其相关函数来通知块层该请求已完成, 如果发生错误,还会判断如何处理剩余的数据。 h]h)}(hX7调用scsi_finish_command()来处理该命令。该函数会 执行一些维护操作,然后调用scsi_io_completion()来 完成I/O操作。scsi_io_completion()会通过调用 blk_end_request及其相关函数来通知块层该请求已完成, 如果发生错误,还会判断如何处理剩余的数据。h]hX7调用scsi_finish_command()来处理该命令。该函数会 执行一些维护操作,然后调用scsi_io_completion()来 完成I/O操作。scsi_io_completion()会通过调用 blk_end_request及其相关函数来通知块层该请求已完成, 如果发生错误,还会判断如何处理剩余的数据。}(hjNhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKNhjJubah}(h]h ]h"]h$]h&]uh1j+hhhKNhj8ubeh}(h]h ]h"]h$]h&]uh1j6hj3ubj7)}(h NEEDS_RETRY h]h)}(h NEEDS_RETRYh]h NEEDS_RETRY}(hjlhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKThjhubah}(h]h ]h"]h$]h&]uh1j6hj3ubj7)}(hAADD_TO_MLQUEUE scmd被重新加入到块设备队列中。 h](h)}(hADD_TO_MLQUEUEh]hADD_TO_MLQUEUE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKVhjubj,)}(h,scmd被重新加入到块设备队列中。 h]h)}(h+scmd被重新加入到块设备队列中。h]h+scmd被重新加入到块设备队列中。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKXhjubah}(h]h ]h"]h$]h&]uh1j+hhhKXhjubeh}(h]h ]h"]h$]h&]uh1j6hj3ubj7)}(h|otherwise 调用scsi_eh_scmd_add(scmd)来处理该命令。 关于此函数的详细信息,请参见 [1-3]。 h](h)}(h otherwiseh]h otherwise}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKZhjubj,)}(hg调用scsi_eh_scmd_add(scmd)来处理该命令。 关于此函数的详细信息,请参见 [1-3]。 h]h)}(he调用scsi_eh_scmd_add(scmd)来处理该命令。 关于此函数的详细信息,请参见 [1-3]。h]he调用scsi_eh_scmd_add(scmd)来处理该命令。 关于此函数的详细信息,请参见 [1-3]。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK\hjubah}(h]h ]h"]h$]h&]uh1j+hhhK\hjubeh}(h]h ]h"]h$]h&]uh1j6hj3ubeh}(h]h ]h"]h$]h&]bullet-uh1j1hhhKLhj-ubah}(h]h ]h"]h$]h&]uh1j+hhhKLhjhhubeh}(h] scsi-donescsiah ]h"]+1.2.1 通过scsi_done回调完成scsi命令ah$]h&]uh1jvhjhhhhhKAubjw)}(hhh](j|)}(h1.2.2 scmd超时完成机制h]h1.2.2 scmd超时完成机制}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjhhhhhKaubh)}(haSCSI命令超时处理机制由scsi_timeout()函数实现。 当发生超时事件时,该函数h]haSCSI命令超时处理机制由scsi_timeout()函数实现。 当发生超时事件时,该函数}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKchjhhubj,)}(hXc1. 首先调用可选的hostt->eh_timed_out()回调函数。 返回值可能是以下3种情况之一: - ``SCSI_EH_RESET_TIMER`` 表示需要延长命令执行时间并重启计时器。 - ``SCSI_EH_NOT_HANDLED`` 表示eh_timed_out()未处理该命令。 此时将执行第2步的处理流程。 - ``SCSI_EH_DONE`` 表示eh_timed_out()已完成该命令。 2. 若未通过回调函数解决,系统将调用 scsi_abort_command()发起异步中止操作,该操作最多 可执行scmd->allowed + 1次。但存在三种例外情况会跳 过异步中止而直接进入第3步处理:当检测到 SCSI_EH_ABORT_SCHEDULED标志位已置位(表明该命令先 前已被中止过一次且当前重试仍失败)、当重试次数已达上 限、或当错误处理时限已到期时。在这些情况下,系统将跳 过异步中止流程而直接执行第3步处理方案。 3. 最终未解决的命令会通过scsi_eh_scmd_add(scmd)移交给 错误处理子系统,具体流程详见[1-4]章节说明。 h]henumerated_list)}(hhh](j7)}(hX首先调用可选的hostt->eh_timed_out()回调函数。 返回值可能是以下3种情况之一: - ``SCSI_EH_RESET_TIMER`` 表示需要延长命令执行时间并重启计时器。 - ``SCSI_EH_NOT_HANDLED`` 表示eh_timed_out()未处理该命令。 此时将执行第2步的处理流程。 - ``SCSI_EH_DONE`` 表示eh_timed_out()已完成该命令。 h](h)}(he首先调用可选的hostt->eh_timed_out()回调函数。 返回值可能是以下3种情况之一:h]he首先调用可选的hostt->eh_timed_out()回调函数。 返回值可能是以下3种情况之一:}(hj"hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKfhjubj,)}(hX - ``SCSI_EH_RESET_TIMER`` 表示需要延长命令执行时间并重启计时器。 - ``SCSI_EH_NOT_HANDLED`` 表示eh_timed_out()未处理该命令。 此时将执行第2步的处理流程。 - ``SCSI_EH_DONE`` 表示eh_timed_out()已完成该命令。 h]j2)}(hhh](j7)}(hX``SCSI_EH_RESET_TIMER`` 表示需要延长命令执行时间并重启计时器。 h]hdefinition_list)}(hhh]hdefinition_list_item)}(hR``SCSI_EH_RESET_TIMER`` 表示需要延长命令执行时间并重启计时器。 h](hterm)}(h``SCSI_EH_RESET_TIMER``h]hliteral)}(hjJh]hSCSI_EH_RESET_TIMER}(hjNhhhNhNubah}(h]h ]h"]h$]h&]uh1jLhjHubah}(h]h ]h"]h$]h&]uh1jFhhhKjhjBubh definition)}(hhh]h)}(h9表示需要延长命令执行时间并重启计时器。h]h9表示需要延长命令执行时间并重启计时器。}(hjfhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKjhjcubah}(h]h ]h"]h$]h&]uh1jahjBubeh}(h]h ]h"]h$]h&]uh1j@hhhKjhj=ubah}(h]h ]h"]h$]h&]uh1j;hj7ubah}(h]h ]h"]h$]h&]uh1j6hj4ubj7)}(hw``SCSI_EH_NOT_HANDLED`` 表示eh_timed_out()未处理该命令。 此时将执行第2步的处理流程。 h]j<)}(hhh]jA)}(hk``SCSI_EH_NOT_HANDLED`` 表示eh_timed_out()未处理该命令。 此时将执行第2步的处理流程。 h](jG)}(h``SCSI_EH_NOT_HANDLED``h]jM)}(hjh]hSCSI_EH_NOT_HANDLED}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jLhjubah}(h]h ]h"]h$]h&]uh1jFhhhKnhjubjb)}(hhh]h)}(hR表示eh_timed_out()未处理该命令。 此时将执行第2步的处理流程。h]hR表示eh_timed_out()未处理该命令。 此时将执行第2步的处理流程。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKmhjubah}(h]h ]h"]h$]h&]uh1jahjubeh}(h]h ]h"]h$]h&]uh1j@hhhKnhjubah}(h]h ]h"]h$]h&]uh1j;hjubah}(h]h ]h"]h$]h&]uh1j6hj4ubj7)}(hA``SCSI_EH_DONE`` 表示eh_timed_out()已完成该命令。 h]j<)}(hhh]jA)}(h;``SCSI_EH_DONE`` 表示eh_timed_out()已完成该命令。 h](jG)}(h``SCSI_EH_DONE``h]jM)}(hjh]h SCSI_EH_DONE}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jLhjubah}(h]h ]h"]h$]h&]uh1jFhhhKqhjubjb)}(hhh]h)}(h)表示eh_timed_out()已完成该命令。h]h)表示eh_timed_out()已完成该命令。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKqhjubah}(h]h ]h"]h$]h&]uh1jahjubeh}(h]h ]h"]h$]h&]uh1j@hhhKqhjubah}(h]h ]h"]h$]h&]uh1j;hjubah}(h]h ]h"]h$]h&]uh1j6hj4ubeh}(h]h ]h"]h$]h&]jjuh1j1hhhKihj0ubah}(h]h ]h"]h$]h&]uh1j+hhhKihjubeh}(h]h ]h"]h$]h&]uh1j6hjubj7)}(hX若未通过回调函数解决,系统将调用 scsi_abort_command()发起异步中止操作,该操作最多 可执行scmd->allowed + 1次。但存在三种例外情况会跳 过异步中止而直接进入第3步处理:当检测到 SCSI_EH_ABORT_SCHEDULED标志位已置位(表明该命令先 前已被中止过一次且当前重试仍失败)、当重试次数已达上 限、或当错误处理时限已到期时。在这些情况下,系统将跳 过异步中止流程而直接执行第3步处理方案。 h]h)}(hX若未通过回调函数解决,系统将调用 scsi_abort_command()发起异步中止操作,该操作最多 可执行scmd->allowed + 1次。但存在三种例外情况会跳 过异步中止而直接进入第3步处理:当检测到 SCSI_EH_ABORT_SCHEDULED标志位已置位(表明该命令先 前已被中止过一次且当前重试仍失败)、当重试次数已达上 限、或当错误处理时限已到期时。在这些情况下,系统将跳 过异步中止流程而直接执行第3步处理方案。h]hX若未通过回调函数解决,系统将调用 scsi_abort_command()发起异步中止操作,该操作最多 可执行scmd->allowed + 1次。但存在三种例外情况会跳 过异步中止而直接进入第3步处理:当检测到 SCSI_EH_ABORT_SCHEDULED标志位已置位(表明该命令先 前已被中止过一次且当前重试仍失败)、当重试次数已达上 限、或当错误处理时限已到期时。在这些情况下,系统将跳 过异步中止流程而直接执行第3步处理方案。}(hj8hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKshj4ubah}(h]h ]h"]h$]h&]uh1j6hjubj7)}(h最终未解决的命令会通过scsi_eh_scmd_add(scmd)移交给 错误处理子系统,具体流程详见[1-4]章节说明。 h]h)}(h最终未解决的命令会通过scsi_eh_scmd_add(scmd)移交给 错误处理子系统,具体流程详见[1-4]章节说明。h]h最终未解决的命令会通过scsi_eh_scmd_add(scmd)移交给 错误处理子系统,具体流程详见[1-4]章节说明。}(hjPhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhK|hjLubah}(h]h ]h"]h$]h&]uh1j6hjubeh}(h]h ]h"]h$]h&]enumtypearabicprefixhsuffix.uh1jhjubah}(h]h ]h"]h$]h&]uh1j+hhhKfhjhhubeh}(h]id2ah ]h"]1.2.2 scmd超时完成机制ah$]h&]uh1jvhjhhhhhKaubeh}(h]scmdah ]h"]1.2 scmd是如何完成的?ah$]h&]uh1jvhjhhhhhK8ubjw)}(hhh](j|)}(h1.3 异步命令中止机制h]h1.3 异步命令中止机制}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjhhhhhKubh)}(hX当命令超时触发后,系统会通过scsi_abort_command()调度异 步中止操作。若中止操作执行成功,则根据重试次数决定后续处 理:若未达最大重试限制,命令将重新下发执行;若重试次数已 耗尽,则命令最终以DID_TIME_OUT状态终止。当中止操作失败 时,系统会调用scsi_eh_scmd_add()将该命令移交错误处理子 系统,具体处理流程详见[1-4]。h]hX当命令超时触发后,系统会通过scsi_abort_command()调度异 步中止操作。若中止操作执行成功,则根据重试次数决定后续处 理:若未达最大重试限制,命令将重新下发执行;若重试次数已 耗尽,则命令最终以DID_TIME_OUT状态终止。当中止操作失败 时,系统会调用scsi_eh_scmd_add()将该命令移交错误处理子 系统,具体处理流程详见[1-4]。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]id3ah ]h"]1.3 异步命令中止机制ah$]h&]uh1jvhjhhhhhKubjw)}(hhh](j|)}(h 1.4 错误处理(EH)接管机制h]h 1.4 错误处理(EH)接管机制}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjhhhhhKubh)}(hbSCSI命令通过scsi_eh_scmd_add()函数进入错误处理流程,该函 数执行以下操作:h]hbSCSI命令通过scsi_eh_scmd_add()函数进入错误处理流程,该函 数执行以下操作:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj,)}(hX$1. 将scmd->eh_entry链接到shost->eh_cmd_q 2. 在shost->shost_state中设置SHOST_RECOVERY状态位 3. 递增shost->host_failed失败计数器 4. 当检测到shost->host_busy == shost->host_failed 时(即所有进行中命令均已失败)立即唤醒SCSI错误处理 线程。 h]j)}(hhh](j7)}(h*将scmd->eh_entry链接到shost->eh_cmd_q h]h)}(h)将scmd->eh_entry链接到shost->eh_cmd_qh]h)将scmd->eh_entry链接到shost->eh_cmd_q}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1j6hjubj7)}(h6在shost->shost_state中设置SHOST_RECOVERY状态位 h]h)}(h5在shost->shost_state中设置SHOST_RECOVERY状态位h]h5在shost->shost_state中设置SHOST_RECOVERY状态位}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1j6hjubj7)}(h(递增shost->host_failed失败计数器 h]h)}(h'递增shost->host_failed失败计数器h]h'递增shost->host_failed失败计数器}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1j6hjubj7)}(h当检测到shost->host_busy == shost->host_failed 时(即所有进行中命令均已失败)立即唤醒SCSI错误处理 线程。 h]h)}(h当检测到shost->host_busy == shost->host_failed 时(即所有进行中命令均已失败)立即唤醒SCSI错误处理 线程。h]h当检测到shost->host_busy == shost->host_failed 时(即所有进行中命令均已失败)立即唤醒SCSI错误处理 线程。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1j6hjubeh}(h]h ]h"]h$]h&]jjjkjlhjmjnuh1jhjubah}(h]h ]h"]h$]h&]uh1j+hhhKhjhhubh)}(hX如上所述,当任一scmd被加入到shost->eh_cmd_q队列时,系统 会立即置位shost_state中的SHOST_RECOVERY状态标志位,该操 作将阻止块层向对应主机控制器下发任何新的SCSI命令。在此状 态下,主机控制器上所有正在处理的scmd最终会进入以下三种状 态之一:正常完成、失败后被移入到eh_cmd_q队列、或因超时被 添加到shost->eh_cmd_q队列。h]hX如上所述,当任一scmd被加入到shost->eh_cmd_q队列时,系统 会立即置位shost_state中的SHOST_RECOVERY状态标志位,该操 作将阻止块层向对应主机控制器下发任何新的SCSI命令。在此状 态下,主机控制器上所有正在处理的scmd最终会进入以下三种状 态之一:正常完成、失败后被移入到eh_cmd_q队列、或因超时被 添加到shost->eh_cmd_q队列。}(hj>hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hXo如果所有的SCSI命令都已经完成或失败,系统中正在执行的命令 数量与失败命令数量相等( 即shost->host_busy == shost->host_failed),此时将唤 醒SCSI错误处理线程。SCSI错误处理线程一旦被唤醒,就可以确 保所有未完成命令均已标记为失败状态,并且已经被链接到 shost->eh_cmd_q队列中。h]hXo如果所有的SCSI命令都已经完成或失败,系统中正在执行的命令 数量与失败命令数量相等( 即shost->host_busy == shost->host_failed),此时将唤 醒SCSI错误处理线程。SCSI错误处理线程一旦被唤醒,就可以确 保所有未完成命令均已标记为失败状态,并且已经被链接到 shost->eh_cmd_q队列中。}(hjLhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hXA需要特别说明的是,这并不意味着底层处理流程完全静止。当底层 驱动以错误状态完成某个scmd时,底层驱动及其下层组件会立刻遗 忘该命令的所有关联状态。但对于超时命令,除非 hostt->eh_timed_out()回调函数已经明确通知底层驱动丢弃该 命令(当前所有底层驱动均未实现此功能),否则从底层驱动视角 看该命令仍处于活跃状态,理论上仍可能在某时刻完成。当然,由 于超时计时器早已触发,所有此类延迟完成都将被系统直接忽略。h]hXA需要特别说明的是,这并不意味着底层处理流程完全静止。当底层 驱动以错误状态完成某个scmd时,底层驱动及其下层组件会立刻遗 忘该命令的所有关联状态。但对于超时命令,除非 hostt->eh_timed_out()回调函数已经明确通知底层驱动丢弃该 命令(当前所有底层驱动均未实现此功能),否则从底层驱动视角 看该命令仍处于活跃状态,理论上仍可能在某时刻完成。当然,由 于超时计时器早已触发,所有此类延迟完成都将被系统直接忽略。}(hjZhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h我们将在后续章节详细讨论关于SCSI错误处理如何执行中止操作( 即强制底层驱动丢弃已超时SCSI命令)。h]h我们将在后续章节详细讨论关于SCSI错误处理如何执行中止操作( 即强制底层驱动丢弃已超时SCSI命令)。}(hjhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubeh}(h]ehah ]h"] 1.4 错误处理(eh)接管机制ah$]h&]uh1jvhjhhhhhKubeh}(h]id1ah ]h"]:1. scsi命令在中间层及错误处理中的传递流程ah$]h&]uh1jvhjxhhhhhK)ubjw)}(hhh](j|)}(h2. SCSI错误处理机制详解h]h2. SCSI错误处理机制详解}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjhhhhhKubh)}(hPSCSI底层驱动可以通过以下两种方式之一来实现SCSI错误处理。h]hPSCSI底层驱动可以通过以下两种方式之一来实现SCSI错误处理。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj,)}(hX8- 细粒度的错误处理回调机制 底层驱动可选择实现细粒度的错误处理回调函数,由SCSI中间层 主导错误恢复流程并自动调用对应的回调函数。此实现模式的详 细设计规范在[2-1]节中展开讨论。 - eh_strategy_handler()回调函数 该回调函数作为统一的错误处理入口,需要完整实现所有的恢复 操作。具体而言,它必须涵盖SCSI中间层在常规恢复过程中执行 的全部处理流程,相关实现将在[2-2]节中详细描述。 h]j2)}(hhh](j7)}(hX 细粒度的错误处理回调机制 底层驱动可选择实现细粒度的错误处理回调函数,由SCSI中间层 主导错误恢复流程并自动调用对应的回调函数。此实现模式的详 细设计规范在[2-1]节中展开讨论。 h]j<)}(hhh]jA)}(h细粒度的错误处理回调机制 底层驱动可选择实现细粒度的错误处理回调函数,由SCSI中间层 主导错误恢复流程并自动调用对应的回调函数。此实现模式的详 细设计规范在[2-1]节中展开讨论。 h](jG)}(h$细粒度的错误处理回调机制h]h$细粒度的错误处理回调机制}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jFhhhKhjubjb)}(hhh]h)}(h底层驱动可选择实现细粒度的错误处理回调函数,由SCSI中间层 主导错误恢复流程并自动调用对应的回调函数。此实现模式的详 细设计规范在[2-1]节中展开讨论。h]h底层驱动可选择实现细粒度的错误处理回调函数,由SCSI中间层 主导错误恢复流程并自动调用对应的回调函数。此实现模式的详 细设计规范在[2-1]节中展开讨论。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jahjubeh}(h]h ]h"]h$]h&]uh1j@hhhKhjubah}(h]h ]h"]h$]h&]uh1j;hjubah}(h]h ]h"]h$]h&]uh1j6hjubj7)}(hXeh_strategy_handler()回调函数 该回调函数作为统一的错误处理入口,需要完整实现所有的恢复 操作。具体而言,它必须涵盖SCSI中间层在常规恢复过程中执行 的全部处理流程,相关实现将在[2-2]节中详细描述。 h]j<)}(hhh]jA)}(hXeh_strategy_handler()回调函数 该回调函数作为统一的错误处理入口,需要完整实现所有的恢复 操作。具体而言,它必须涵盖SCSI中间层在常规恢复过程中执行 的全部处理流程,相关实现将在[2-2]节中详细描述。 h](jG)}(h!eh_strategy_handler()回调函数h]h!eh_strategy_handler()回调函数}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jFhhhKhjubjb)}(hhh]h)}(h该回调函数作为统一的错误处理入口,需要完整实现所有的恢复 操作。具体而言,它必须涵盖SCSI中间层在常规恢复过程中执行 的全部处理流程,相关实现将在[2-2]节中详细描述。h]h该回调函数作为统一的错误处理入口,需要完整实现所有的恢复 操作。具体而言,它必须涵盖SCSI中间层在常规恢复过程中执行 的全部处理流程,相关实现将在[2-2]节中详细描述。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1jahjubeh}(h]h ]h"]h$]h&]uh1j@hhhKhjubah}(h]h ]h"]h$]h&]uh1j;hjubah}(h]h ]h"]h$]h&]uh1j6hjubeh}(h]h ]h"]h$]h&]jjuh1j1hhhKhjubah}(h]h ]h"]h$]h&]uh1j+hhhKhjhhubh)}(h当错误恢复流程完成后,SCSI错误处理系统通过调用 scsi_restart_operations()函数恢复正常运行,该函数按顺序执行 以下操作:h]h当错误恢复流程完成后,SCSI错误处理系统通过调用 scsi_restart_operations()函数恢复正常运行,该函数按顺序执行 以下操作:}(hj<hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj,)}(hX1. 验证是否需要执行驱动器安全门锁定机制 2. 清除shost_state中的SHOST_RECOVERY状态标志位 3. 唤醒所有在shost->host_wait上等待的任务。如果有人调用了 scsi_block_when_processing_errors()则会发生这种情况。 (疑问:由于错误处理期间块层队列已被阻塞,为何仍需显式 唤醒?) 4. 强制激活该主机控制器下所有设备的I/O队列 h]j)}(hhh](j7)}(h7验证是否需要执行驱动器安全门锁定机制 h]h)}(h6验证是否需要执行驱动器安全门锁定机制h]h6验证是否需要执行驱动器安全门锁定机制}(hjUhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjQubah}(h]h ]h"]h$]h&]uh1j6hjNubj7)}(h5清除shost_state中的SHOST_RECOVERY状态标志位 h]h)}(h4清除shost_state中的SHOST_RECOVERY状态标志位h]h4清除shost_state中的SHOST_RECOVERY状态标志位}(hjmhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjiubah}(h]h ]h"]h$]h&]uh1j6hjNubj7)}(h唤醒所有在shost->host_wait上等待的任务。如果有人调用了 scsi_block_when_processing_errors()则会发生这种情况。 (疑问:由于错误处理期间块层队列已被阻塞,为何仍需显式 唤醒?) h]h)}(h唤醒所有在shost->host_wait上等待的任务。如果有人调用了 scsi_block_when_processing_errors()则会发生这种情况。 (疑问:由于错误处理期间块层队列已被阻塞,为何仍需显式 唤醒?)h]h唤醒所有在shost->host_wait上等待的任务。如果有人调用了 scsi_block_when_processing_errors()则会发生这种情况。 (疑问:由于错误处理期间块层队列已被阻塞,为何仍需显式 唤醒?)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1j6hjNubj7)}(h;强制激活该主机控制器下所有设备的I/O队列 h]h)}(h9强制激活该主机控制器下所有设备的I/O队列h]h9强制激活该主机控制器下所有设备的I/O队列}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjubah}(h]h ]h"]h$]h&]uh1j6hjNubeh}(h]h ]h"]h$]h&]jjjkjlhjmjnuh1jhjJubah}(h]h ]h"]h$]h&]uh1j+hhhKhjhhubjw)}(hhh](j|)}(h.2.1 基于细粒度回调的错误处理机制h]h.2.1 基于细粒度回调的错误处理机制}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjhhhhhKubjw)}(hhh](j|)}(h 2.1.1 概述h]h 2.1.1 概述}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjhhhhhKubh)}(hX如果不存在eh_strategy_handler(),SCSI中间层将负责驱动的 错误处理。错误处理(EH)的目标有两个:一是让底层驱动程序、 主机和设备不再维护已超时的SCSI命令(scmd);二是使他们准备 好接收新命令。当一个SCSI命令(scmd)被底层遗忘且底层已准备 好再次处理或拒绝该命令时,即可认为该scmd已恢复。h]hX如果不存在eh_strategy_handler(),SCSI中间层将负责驱动的 错误处理。错误处理(EH)的目标有两个:一是让底层驱动程序、 主机和设备不再维护已超时的SCSI命令(scmd);二是使他们准备 好接收新命令。当一个SCSI命令(scmd)被底层遗忘且底层已准备 好再次处理或拒绝该命令时,即可认为该scmd已恢复。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(hX,为实现这些目标,错误处理(EH)会逐步执行严重性递增的恢复 操作。部分操作通过下发SCSI命令完成,而其他操作则通过调用 以下细粒度的错误处理回调函数实现。这些回调函数可以省略, 若被省略则默认始终视为执行失败。h]hX,为实现这些目标,错误处理(EH)会逐步执行严重性递增的恢复 操作。部分操作通过下发SCSI命令完成,而其他操作则通过调用 以下细粒度的错误处理回调函数实现。这些回调函数可以省略, 若被省略则默认始终视为执行失败。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh literal_block)}(hint (* eh_abort_handler)(struct scsi_cmnd *); int (* eh_device_reset_handler)(struct scsi_cmnd *); int (* eh_bus_reset_handler)(struct scsi_cmnd *); int (* eh_host_reset_handler)(struct scsi_cmnd *);h]hint (* eh_abort_handler)(struct scsi_cmnd *); int (* eh_device_reset_handler)(struct scsi_cmnd *); int (* eh_bus_reset_handler)(struct scsi_cmnd *); int (* eh_host_reset_handler)(struct scsi_cmnd *);}hjsbah}(h]h ]h"]h$]h&]hhuh1jhhhKhjhhubh)}(hX只有在低级别的错误恢复操作无法恢复部分失败的SCSI命令 (scmd)时,才会采取更高级别的恢复操作。如果最高级别的错误 处理失败,就意味着整个错误恢复(EH)过程失败,所有未能恢复 的设备被强制下线。h]hX只有在低级别的错误恢复操作无法恢复部分失败的SCSI命令 (scmd)时,才会采取更高级别的恢复操作。如果最高级别的错误 处理失败,就意味着整个错误恢复(EH)过程失败,所有未能恢复 的设备被强制下线。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubh)}(h-在恢复过程中,需遵循以下规则:h]h-在恢复过程中,需遵循以下规则:}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjhhubj,)}(hX- 错误恢复操作针对待处理列表eh_work_q中的失败的scmds执 行。如果某个恢复操作成功恢复了一个scmd,那么该scmd会 从eh_work_q链表中移除。 需要注意的是,对某个scmd执行的单个恢复操作可能会恢复 多个scmd。例如,对某个设备执行复位操作可能会恢复该设 备上所有失败的scmd。 - 仅当低级别的恢复操作完成且eh_work_q仍然非空时,才会 触发更高级别的操作 - SCSI错误恢复机制会重用失败的scmd来发送恢复命令。对于 超时的scmd,SCSI错误处理机制会确保底层驱动在重用scmd 前已不再维护该命令。 h]j2)}(hhh](j7)}(hXj错误恢复操作针对待处理列表eh_work_q中的失败的scmds执 行。如果某个恢复操作成功恢复了一个scmd,那么该scmd会 从eh_work_q链表中移除。 需要注意的是,对某个scmd执行的单个恢复操作可能会恢复 多个scmd。例如,对某个设备执行复位操作可能会恢复该设 备上所有失败的scmd。 h](h)}(h错误恢复操作针对待处理列表eh_work_q中的失败的scmds执 行。如果某个恢复操作成功恢复了一个scmd,那么该scmd会 从eh_work_q链表中移除。h]h错误恢复操作针对待处理列表eh_work_q中的失败的scmds执 行。如果某个恢复操作成功恢复了一个scmd,那么该scmd会 从eh_work_q链表中移除。}(hj2hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj.ubh)}(h需要注意的是,对某个scmd执行的单个恢复操作可能会恢复 多个scmd。例如,对某个设备执行复位操作可能会恢复该设 备上所有失败的scmd。h]h需要注意的是,对某个scmd执行的单个恢复操作可能会恢复 多个scmd。例如,对某个设备执行复位操作可能会恢复该设 备上所有失败的scmd。}(hj@hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhj.ubeh}(h]h ]h"]h$]h&]uh1j6hj+ubj7)}(he仅当低级别的恢复操作完成且eh_work_q仍然非空时,才会 触发更高级别的操作 h]h)}(hd仅当低级别的恢复操作完成且eh_work_q仍然非空时,才会 触发更高级别的操作h]hd仅当低级别的恢复操作完成且eh_work_q仍然非空时,才会 触发更高级别的操作}(hjXhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjTubah}(h]h ]h"]h$]h&]uh1j6hj+ubj7)}(hSCSI错误恢复机制会重用失败的scmd来发送恢复命令。对于 超时的scmd,SCSI错误处理机制会确保底层驱动在重用scmd 前已不再维护该命令。 h]h)}(hSCSI错误恢复机制会重用失败的scmd来发送恢复命令。对于 超时的scmd,SCSI错误处理机制会确保底层驱动在重用scmd 前已不再维护该命令。h]hSCSI错误恢复机制会重用失败的scmd来发送恢复命令。对于 超时的scmd,SCSI错误处理机制会确保底层驱动在重用scmd 前已不再维护该命令。}(hjphhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhKhjlubah}(h]h ]h"]h$]h&]uh1j6hj+ubeh}(h]h ]h"]h$]h&]jjuh1j1hhhKhj'ubah}(h]h ]h"]h$]h&]uh1j+hhhKhjhhubh)}(hX当一个SCSI命令(scmd)被成功恢复后,错误处理逻辑会通过 scsi_eh_finish_cmd()将其从待处理队列(eh_work_q)移 至错误处理的本地完成队列(eh_done_q)。当所有scmd均恢 复完成(即eh_work_q为空时),错误处理逻辑会调用 scsi_eh_flush_done_q()对这些已恢复的scmd进行处理,即 重新尝试或错误总终止(向上层通知失败)。h]hX当一个SCSI命令(scmd)被成功恢复后,错误处理逻辑会通过 scsi_eh_finish_cmd()将其从待处理队列(eh_work_q)移 至错误处理的本地完成队列(eh_done_q)。当所有scmd均恢 复完成(即eh_work_q为空时),错误处理逻辑会调用 scsi_eh_flush_done_q()对这些已恢复的scmd进行处理,即 重新尝试或错误总终止(向上层通知失败)。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(hSCSI命令仅在满足以下全部条件时才会被重试:对应的SCSI设 备仍处于在线状态,未设置REQ_FAILFAST标志或递增后的 scmd->retries值仍小于scmd->allowed。h]hSCSI命令仅在满足以下全部条件时才会被重试:对应的SCSI设 备仍处于在线状态,未设置REQ_FAILFAST标志或递增后的 scmd->retries值仍小于scmd->allowed。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]id6ah ]h"] 2.1.1 概述ah$]h&]uh1jvhjhhhhhKubjw)}(hhh](j|)}(h72.1.2 SCSI命令在错误处理过程中的流转路径h]h72.1.2 SCSI命令在错误处理过程中的流转路径}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjhhhhhM ubj,)}(hX1. 错误完成/超时 :处理: 调用scsi_eh_scmd_add()处理scmd - 将scmd添加到shost->eh_cmd_q - 设置SHOST_RECOVERY标记位 - shost->host_failed++ :锁要求: shost->host_lock 2. 启动错误处理(EH) :操作: 将所有scmd移动到EH本地eh_work_q队列,并 清空 shost->eh_cmd_q。 :锁要求: shost->host_lock(非严格必需,仅为保持一致性) 3. scmd恢复 :操作: 调用scsi_eh_finish_cmd()完成scmd的EH - 将scmd从本地eh_work_q队列移至本地eh_done_q队列 :锁要求: 无 :并发控制: 每个独立的eh_work_q至多一个线程,确保无锁 队列的访问 4. EH完成 :操作: 调用scsi_eh_flush_done_q()重试scmd或通知上层处理 失败。此函数可以被并发调用,但每个独立的eh_work_q队 列至多一个线程,以确保无锁队列的访问。 - 从eh_done_q队列中移除scmd,清除scmd->eh_entry - 如果需要重试,调用scsi_queue_insert()重新入队scmd - 否则,调用scsi_finish_command()完成scmd - 将shost->host_failed置为零 :锁要求: 队列或完成函数会执行适当的加锁操作 h]j)}(hhh](j7)}(h错误完成/超时 :处理: 调用scsi_eh_scmd_add()处理scmd - 将scmd添加到shost->eh_cmd_q - 设置SHOST_RECOVERY标记位 - shost->host_failed++ :锁要求: shost->host_lock h](h)}(h错误完成/超时h]h错误完成/超时}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubh)}(hhh](h)}(hhh](h)}(h处理h]h处理}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhKubh)}(h}调用scsi_eh_scmd_add()处理scmd - 将scmd添加到shost->eh_cmd_q - 设置SHOST_RECOVERY标记位 - shost->host_failed++ h](h)}(h"调用scsi_eh_scmd_add()处理scmdh]h"调用scsi_eh_scmd_add()处理scmd}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubj2)}(hhh](j7)}(h将scmd添加到shost->eh_cmd_qh]h)}(hj h]h将scmd添加到shost->eh_cmd_q}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j6hjubj7)}(h设置SHOST_RECOVERY标记位h]h)}(hj h]h设置SHOST_RECOVERY标记位}(hj"hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j6hjubj7)}(hshost->host_failed++ h]h)}(hshost->host_failed++h]hshost->host_failed++}(hj9hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj5ubah}(h]h ]h"]h$]h&]uh1j6hjubeh}(h]h ]h"]h$]h&]jjuh1j1hhhMhjubeh}(h]h ]h"]h$]h&]uh1hhjubeh}(h]h ]h"]h$]h&]uh1hhhhMhjubh)}(hhh](h)}(h 锁要求h]h 锁要求}(hjbhhhNhNubah}(h]h ]h"]h$]h&]uh1hhj_hhhKubh)}(hshost->host_lock h]h)}(hshost->host_lockh]hshost->host_lock}(hjthhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjpubah}(h]h ]h"]h$]h&]uh1hhj_ubeh}(h]h ]h"]h$]h&]uh1hhhhMhjubeh}(h]h ]h"]h$]h&]uh1hhjubeh}(h]h ]h"]h$]h&]uh1j6hjubj7)}(h启动错误处理(EH) :操作: 将所有scmd移动到EH本地eh_work_q队列,并 清空 shost->eh_cmd_q。 :锁要求: shost->host_lock(非严格必需,仅为保持一致性) h](h)}(h启动错误处理(EH)h]h启动错误处理(EH)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubh)}(hhh](h)}(hhh](h)}(h操作h]h操作}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhKubh)}(hN将所有scmd移动到EH本地eh_work_q队列,并 清空 shost->eh_cmd_q。 h]h)}(hM将所有scmd移动到EH本地eh_work_q队列,并 清空 shost->eh_cmd_q。h]hM将所有scmd移动到EH本地eh_work_q队列,并 清空 shost->eh_cmd_q。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1hhjubeh}(h]h ]h"]h$]h&]uh1hhhhMhjubh)}(hhh](h)}(h 锁要求h]h 锁要求}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjhhhKubh)}(h>shost->host_lock(非严格必需,仅为保持一致性) h]h)}(h=shost->host_lock(非严格必需,仅为保持一致性)h]h=shost->host_lock(非严格必需,仅为保持一致性)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1hhjubeh}(h]h ]h"]h$]h&]uh1hhhhMhjubeh}(h]h ]h"]h$]h&]uh1hhjubeh}(h]h ]h"]h$]h&]uh1j6hjubj7)}(hscmd恢复 :操作: 调用scsi_eh_finish_cmd()完成scmd的EH - 将scmd从本地eh_work_q队列移至本地eh_done_q队列 :锁要求: 无 :并发控制: 每个独立的eh_work_q至多一个线程,确保无锁 队列的访问 h](h)}(h scmd恢复h]h scmd恢复}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj ubh)}(hhh](h)}(hhh](h)}(h操作h]h操作}(hj1 hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj. hhhKubh)}(hh调用scsi_eh_finish_cmd()完成scmd的EH - 将scmd从本地eh_work_q队列移至本地eh_done_q队列 h](h)}(h)调用scsi_eh_finish_cmd()完成scmd的EHh]h)调用scsi_eh_finish_cmd()完成scmd的EH}(hjC hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM!hj? ubj2)}(hhh]j7)}(h;将scmd从本地eh_work_q队列移至本地eh_done_q队列 h]h)}(h:将scmd从本地eh_work_q队列移至本地eh_done_q队列h]h:将scmd从本地eh_work_q队列移至本地eh_done_q队列}(hjX hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM#hjT ubah}(h]h ]h"]h$]h&]uh1j6hjQ ubah}(h]h ]h"]h$]h&]jjuh1j1hhhM#hj? ubeh}(h]h ]h"]h$]h&]uh1hhj. ubeh}(h]h ]h"]h$]h&]uh1hhhhM!hj+ ubh)}(hhh](h)}(h 锁要求h]h 锁要求}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj~ hhhKubh)}(h无 h]h)}(h无h]h无}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM%hj ubah}(h]h ]h"]h$]h&]uh1hhj~ ubeh}(h]h ]h"]h$]h&]uh1hhhhM%hj+ ubh)}(hhh](h)}(h 并发控制h]h 并发控制}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj hhhKubh)}(hJ每个独立的eh_work_q至多一个线程,确保无锁 队列的访问 h]h)}(hI每个独立的eh_work_q至多一个线程,确保无锁 队列的访问h]hI每个独立的eh_work_q至多一个线程,确保无锁 队列的访问}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM'hj ubah}(h]h ]h"]h$]h&]uh1hhj ubeh}(h]h ]h"]h$]h&]uh1hhhhM'hj+ ubeh}(h]h ]h"]h$]h&]uh1hhj ubeh}(h]h ]h"]h$]h&]uh1j6hjubj7)}(hX!EH完成 :操作: 调用scsi_eh_flush_done_q()重试scmd或通知上层处理 失败。此函数可以被并发调用,但每个独立的eh_work_q队 列至多一个线程,以确保无锁队列的访问。 - 从eh_done_q队列中移除scmd,清除scmd->eh_entry - 如果需要重试,调用scsi_queue_insert()重新入队scmd - 否则,调用scsi_finish_command()完成scmd - 将shost->host_failed置为零 :锁要求: 队列或完成函数会执行适当的加锁操作 h](h)}(hEH完成h]hEH完成}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM*hj ubh)}(hhh](h)}(hhh](h)}(h操作h]h操作}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj hhhKubh)}(hX调用scsi_eh_flush_done_q()重试scmd或通知上层处理 失败。此函数可以被并发调用,但每个独立的eh_work_q队 列至多一个线程,以确保无锁队列的访问。 - 从eh_done_q队列中移除scmd,清除scmd->eh_entry - 如果需要重试,调用scsi_queue_insert()重新入队scmd - 否则,调用scsi_finish_command()完成scmd - 将shost->host_failed置为零 h](h)}(h调用scsi_eh_flush_done_q()重试scmd或通知上层处理 失败。此函数可以被并发调用,但每个独立的eh_work_q队 列至多一个线程,以确保无锁队列的访问。h]h调用scsi_eh_flush_done_q()重试scmd或通知上层处理 失败。此函数可以被并发调用,但每个独立的eh_work_q队 列至多一个线程,以确保无锁队列的访问。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM,hj ubj,)}(h- 从eh_done_q队列中移除scmd,清除scmd->eh_entry - 如果需要重试,调用scsi_queue_insert()重新入队scmd - 否则,调用scsi_finish_command()完成scmd - 将shost->host_failed置为零 h]j2)}(hhh](j7)}(h6从eh_done_q队列中移除scmd,清除scmd->eh_entryh]h)}(hj) h]h6从eh_done_q队列中移除scmd,清除scmd->eh_entry}(hj+ hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM0hj' ubah}(h]h ]h"]h$]h&]uh1j6hj$ ubj7)}(h>如果需要重试,调用scsi_queue_insert()重新入队scmdh]h)}(hj@ h]h>如果需要重试,调用scsi_queue_insert()重新入队scmd}(hjB hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM1hj> ubah}(h]h ]h"]h$]h&]uh1j6hj$ ubj7)}(h.否则,调用scsi_finish_command()完成scmdh]h)}(hjW h]h.否则,调用scsi_finish_command()完成scmd}(hjY hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM2hjU ubah}(h]h ]h"]h$]h&]uh1j6hj$ ubj7)}(h将shost->host_failed置为零 h]h)}(h将shost->host_failed置为零h]h将shost->host_failed置为零}(hjp hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM3hjl ubah}(h]h ]h"]h$]h&]uh1j6hj$ ubeh}(h]h ]h"]h$]h&]jjuh1j1hhhM0hj ubah}(h]h ]h"]h$]h&]uh1j+hhhM0hj ubeh}(h]h ]h"]h$]h&]uh1hhj ubeh}(h]h ]h"]h$]h&]uh1hhhhM,hj ubh)}(hhh](h)}(h 锁要求h]h 锁要求}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj hhhKubh)}(h5队列或完成函数会执行适当的加锁操作 h]h)}(h3队列或完成函数会执行适当的加锁操作h]h3队列或完成函数会执行适当的加锁操作}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM5hj ubah}(h]h ]h"]h$]h&]uh1hhj ubeh}(h]h ]h"]h$]h&]uh1hhhhM5hj ubeh}(h]h ]h"]h$]h&]uh1hhj ubeh}(h]h ]h"]h$]h&]uh1j6hjubeh}(h]h ]h"]h$]h&]jjjkjlhjmjnuh1jhjubah}(h]h ]h"]h$]h&]uh1j+hhhMhjhhubeh}(h]id7ah ]h"]72.1.2 scsi命令在错误处理过程中的流转路径ah$]h&]uh1jvhjhhhhhM ubjw)}(hhh](j|)}(h2.1.3 控制流h]h2.1.3 控制流}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1j{hj hhhhhM9ubj,)}(hd通过细粒度回调机制执行的SCSI错误处理(EH)是从 scsi_unjam_host()函数开始的 h]h)}(hc通过细粒度回调机制执行的SCSI错误处理(EH)是从 scsi_unjam_host()函数开始的h]hc通过细粒度回调机制执行的SCSI错误处理(EH)是从 scsi_unjam_host()函数开始的}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM;hj ubah}(h]h ]h"]h$]h&]uh1j+hhhM;hj hhubh)}(h``scsi_unjam_host``h]jM)}(hj h]hscsi_unjam_host}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jLhj ubah}(h]h ]h"]h$]h&]uh1hhhhM>hj hhubj,)}(hX1. 持有shost->host_lock锁,将shost->eh_cmd_q中的命令移动 到本地的eh_work_q队里中,并释放host_lock锁。注意,这一步 会清空shost->eh_cmd_q。 2. 调用scsi_eh_get_sense函数。 ``scsi_eh_get_sense`` 该操作针对没有有效感知数据的错误完成命令。大部分SCSI传输协议 或底层驱动在命令失败时会自动获取感知数据(自动感知)。出于性 能原因,建议使用自动感知,推荐使用自动感知机制,因为它不仅有 助于提升性能,还能避免从发生CHECK CONDITION到执行本操作之间, 感知信息出现不同步的问题。 注意,如果不支持自动感知,那么在使用scsi_done()以错误状态完成 scmd 时,scmd->sense_buffer将包含无效感知数据。在这种情况下, scsi_decide_disposition()总是返回FAILED从而触发SCSI错误处理 (EH)。当该scmd执行到这里时,会重新获取感知数据,并再次调用 scsi_decide_disposition()进行处理。 1. 调用scsi_request_sense()发送REQUEST_SENSE命令。如果失败, 则不采取任何操作。请注意,不采取任何操作会导致对该scmd执行 更高级别的恢复操作。 2. 调用scsi_decide_disposition()处理scmd - SUCCESS scmd->retries被设置为scmd->allowed以防止 scsi_eh_flush_done_q()重试该scmd,并调用 scsi_eh_finish_cmd()。 - NEEDS_RETRY 调用scsi_eh_finish_cmd() - 其他情况 无操作。 4. 如果!list_empty(&eh_work_q),则调用scsi_eh_ready_devs()。 ``scsi_eh_ready_devs`` 该函数采取四种逐步增强的措施,使失败的设备准备好处理新的命令。 1. 调用scsi_eh_stu() ``scsi_eh_stu`` 对于每个具有有效感知数据且scsi_check_sense()判断为失败的 scmd发送START STOP UNIT(STU)命令且将start置1。注意,由 于我们明确选择错误完成的scmd,可以确定底层驱动已不再维护该 scmd,我们可以重用它进行STU。 如果STU操作成功且sdev处于离线或就绪状态,所有在sdev上失败的 scmd都会通过scsi_eh_finish_cmd()完成。 *注意* 如果hostt->eh_abort_handler()未实现或返回失败,可能 此时仍有超时的scmd,此时STU不会导致底层驱动不再维护scmd。但 是,如果STU执行成功,该函数会通过scsi_eh_finish_cmd()来完成 sdev上的所有scmd,这会导致底层驱动处于不一致的状态。看来STU 操作应仅在sdev不包含超时scmd时进行。 2. 如果!list_empty(&eh_work_q),调用scsi_eh_bus_device_reset()。 ``scsi_eh_bus_device_reset`` 此操作与scsi_eh_stu()非常相似,区别在于使用 hostt->eh_device_reset_handler()替代STU命令。此外,由于我们 没有发送SCSI命令且重置会清空该sdev上所有的scmd,所以无需筛选错 误完成的scmd。 3. 如果!list_empty(&eh_work_q),调用scsi_eh_bus_reset()。 ``scsi_eh_bus_reset`` 对于每个包含失败scmd的SCSI通道调用 hostt->eh_bus_reset_handler()。如果总线重置成功,那么该通道上 所有准备就绪或离线状态sdev上的失败scmd都会被处理处理完成。 4. 如果!list_empty(&eh_work_q),调用scsi_eh_host_reset()。 ``scsi_eh_host_reset`` 调用hostt->eh_host_reset_handler()是最终的手段。如果SCSI主机 重置成功,主机上所有就绪或离线sdev上的失败scmd都会通过错误处理 完成。 5. 如果!list_empty(&eh_work_q),调用scsi_eh_offline_sdevs()。 ``scsi_eh_offline_sdevs`` 离线所有包含未恢复scmd的所有sdev,并通过 scsi_eh_finish_cmd()完成这些scmd。 5. 调用scsi_eh_flush_done_q()。 ``scsi_eh_flush_done_q`` 此时所有的scmd都已经恢复(或放弃),并通过 scsi_eh_finish_cmd()函数加入eh_done_q队列。该函数通过 重试或显示通知上层scmd的失败来刷新eh_done_q。 h](j)}(hhh](j7)}(h持有shost->host_lock锁,将shost->eh_cmd_q中的命令移动 到本地的eh_work_q队里中,并释放host_lock锁。注意,这一步 会清空shost->eh_cmd_q。 h]h)}(h持有shost->host_lock锁,将shost->eh_cmd_q中的命令移动 到本地的eh_work_q队里中,并释放host_lock锁。注意,这一步 会清空shost->eh_cmd_q。h]h持有shost->host_lock锁,将shost->eh_cmd_q中的命令移动 到本地的eh_work_q队里中,并释放host_lock锁。注意,这一步 会清空shost->eh_cmd_q。}(hj6 hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM@hj2 ubah}(h]h ]h"]h$]h&]uh1j6hj/ ubj7)}(h!调用scsi_eh_get_sense函数。 h]h)}(h 调用scsi_eh_get_sense函数。h]h 调用scsi_eh_get_sense函数。}(hjN hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMDhjJ ubah}(h]h ]h"]h$]h&]uh1j6hj/ ubeh}(h]h ]h"]h$]h&]jjjkjlhjmjnuh1jhj+ ubh)}(h``scsi_eh_get_sense``h]jM)}(hjj h]hscsi_eh_get_sense}(hjl hhhNhNubah}(h]h ]h"]h$]h&]uh1jLhjh ubah}(h]h ]h"]h$]h&]uh1hhhhMFhj+ ubj,)}(hXO该操作针对没有有效感知数据的错误完成命令。大部分SCSI传输协议 或底层驱动在命令失败时会自动获取感知数据(自动感知)。出于性 能原因,建议使用自动感知,推荐使用自动感知机制,因为它不仅有 助于提升性能,还能避免从发生CHECK CONDITION到执行本操作之间, 感知信息出现不同步的问题。 注意,如果不支持自动感知,那么在使用scsi_done()以错误状态完成 scmd 时,scmd->sense_buffer将包含无效感知数据。在这种情况下, scsi_decide_disposition()总是返回FAILED从而触发SCSI错误处理 (EH)。当该scmd执行到这里时,会重新获取感知数据,并再次调用 scsi_decide_disposition()进行处理。 1. 调用scsi_request_sense()发送REQUEST_SENSE命令。如果失败, 则不采取任何操作。请注意,不采取任何操作会导致对该scmd执行 更高级别的恢复操作。 2. 调用scsi_decide_disposition()处理scmd - SUCCESS scmd->retries被设置为scmd->allowed以防止 scsi_eh_flush_done_q()重试该scmd,并调用 scsi_eh_finish_cmd()。 - NEEDS_RETRY 调用scsi_eh_finish_cmd() - 其他情况 无操作。 h](h)}(hX该操作针对没有有效感知数据的错误完成命令。大部分SCSI传输协议 或底层驱动在命令失败时会自动获取感知数据(自动感知)。出于性 能原因,建议使用自动感知,推荐使用自动感知机制,因为它不仅有 助于提升性能,还能避免从发生CHECK CONDITION到执行本操作之间, 感知信息出现不同步的问题。h]hX该操作针对没有有效感知数据的错误完成命令。大部分SCSI传输协议 或底层驱动在命令失败时会自动获取感知数据(自动感知)。出于性 能原因,建议使用自动感知,推荐使用自动感知机制,因为它不仅有 助于提升性能,还能避免从发生CHECK CONDITION到执行本操作之间, 感知信息出现不同步的问题。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMHhj ubh)}(hXp注意,如果不支持自动感知,那么在使用scsi_done()以错误状态完成 scmd 时,scmd->sense_buffer将包含无效感知数据。在这种情况下, scsi_decide_disposition()总是返回FAILED从而触发SCSI错误处理 (EH)。当该scmd执行到这里时,会重新获取感知数据,并再次调用 scsi_decide_disposition()进行处理。h]hXp注意,如果不支持自动感知,那么在使用scsi_done()以错误状态完成 scmd 时,scmd->sense_buffer将包含无效感知数据。在这种情况下, scsi_decide_disposition()总是返回FAILED从而触发SCSI错误处理 (EH)。当该scmd执行到这里时,会重新获取感知数据,并再次调用 scsi_decide_disposition()进行处理。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMNhj ubj)}(hhh](j7)}(h调用scsi_request_sense()发送REQUEST_SENSE命令。如果失败, 则不采取任何操作。请注意,不采取任何操作会导致对该scmd执行 更高级别的恢复操作。 h]h)}(h调用scsi_request_sense()发送REQUEST_SENSE命令。如果失败, 则不采取任何操作。请注意,不采取任何操作会导致对该scmd执行 更高级别的恢复操作。h]h调用scsi_request_sense()发送REQUEST_SENSE命令。如果失败, 则不采取任何操作。请注意,不采取任何操作会导致对该scmd执行 更高级别的恢复操作。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMThj ubah}(h]h ]h"]h$]h&]uh1j6hj ubj7)}(hXp调用scsi_decide_disposition()处理scmd - SUCCESS scmd->retries被设置为scmd->allowed以防止 scsi_eh_flush_done_q()重试该scmd,并调用 scsi_eh_finish_cmd()。 - NEEDS_RETRY 调用scsi_eh_finish_cmd() - 其他情况 无操作。 h](h)}(h)调用scsi_decide_disposition()处理scmdh]h)调用scsi_decide_disposition()处理scmd}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMXhj ubj,)}(hX-- SUCCESS scmd->retries被设置为scmd->allowed以防止 scsi_eh_flush_done_q()重试该scmd,并调用 scsi_eh_finish_cmd()。 - NEEDS_RETRY 调用scsi_eh_finish_cmd() - 其他情况 无操作。 h]j2)}(hhh](j7)}(hSUCCESS scmd->retries被设置为scmd->allowed以防止 scsi_eh_flush_done_q()重试该scmd,并调用 scsi_eh_finish_cmd()。 h]j<)}(hhh]jA)}(hSUCCESS scmd->retries被设置为scmd->allowed以防止 scsi_eh_flush_done_q()重试该scmd,并调用 scsi_eh_finish_cmd()。 h](jG)}(hSUCCESSh]hSUCCESS}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jFhhhM]hj ubjb)}(hhh]h)}(hwscmd->retries被设置为scmd->allowed以防止 scsi_eh_flush_done_q()重试该scmd,并调用 scsi_eh_finish_cmd()。h]hwscmd->retries被设置为scmd->allowed以防止 scsi_eh_flush_done_q()重试该scmd,并调用 scsi_eh_finish_cmd()。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM[hj ubah}(h]h ]h"]h$]h&]uh1jahj ubeh}(h]h ]h"]h$]h&]uh1j@hhhM]hj ubah}(h]h ]h"]h$]h&]uh1j;hj ubah}(h]h ]h"]h$]h&]uh1j6hj ubj7)}(h9NEEDS_RETRY 调用scsi_eh_finish_cmd() h]j<)}(hhh]jA)}(h'NEEDS_RETRY 调用scsi_eh_finish_cmd() h](jG)}(h NEEDS_RETRYh]h NEEDS_RETRY}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jFhhhM`hj ubjb)}(hhh]h)}(h调用scsi_eh_finish_cmd()h]h调用scsi_eh_finish_cmd()}(hj1 hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM`hj. ubah}(h]h ]h"]h$]h&]uh1jahj ubeh}(h]h ]h"]h$]h&]uh1j@hhhM`hj ubah}(h]h ]h"]h$]h&]uh1j;hj ubah}(h]h ]h"]h$]h&]uh1j6hj ubj7)}(h,其他情况 无操作。 h]j<)}(hhh]jA)}(h其他情况 无操作。 h](jG)}(h 其他情况h]h 其他情况}(hjb hhhNhNubah}(h]h ]h"]h$]h&]uh1jFhhhMchj^ ubjb)}(hhh]h)}(h 无操作。h]h 无操作。}(hjs hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMchjp ubah}(h]h ]h"]h$]h&]uh1jahj^ ubeh}(h]h ]h"]h$]h&]uh1j@hhhMchj[ ubah}(h]h ]h"]h$]h&]uh1j;hjW ubah}(h]h ]h"]h$]h&]uh1j6hj ubeh}(h]h ]h"]h$]h&]jjuh1j1hhhMZhj ubah}(h]h ]h"]h$]h&]uh1j+hhhMZhj ubeh}(h]h ]h"]h$]h&]uh1j6hj ubeh}(h]h ]h"]h$]h&]jjjkjlhjmjnuh1jhj ubeh}(h]h ]h"]h$]h&]uh1j+hhhMHhj+ ubj)}(hhh]j7)}(hA如果!list_empty(&eh_work_q),则调用scsi_eh_ready_devs()。 h]h)}(h@如果!list_empty(&eh_work_q),则调用scsi_eh_ready_devs()。h]h@如果!list_empty(&eh_work_q),则调用scsi_eh_ready_devs()。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMehj ubah}(h]h ]h"]h$]h&]uh1j6hj ubah}(h]h ]h"]h$]h&]jjjkjlhjmjnstartKuh1jhj+ ubh)}(h``scsi_eh_ready_devs``h]jM)}(hj h]hscsi_eh_ready_devs}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jLhj ubah}(h]h ]h"]h$]h&]uh1hhhhMghj+ ubj,)}(hX9该函数采取四种逐步增强的措施,使失败的设备准备好处理新的命令。 1. 调用scsi_eh_stu() ``scsi_eh_stu`` 对于每个具有有效感知数据且scsi_check_sense()判断为失败的 scmd发送START STOP UNIT(STU)命令且将start置1。注意,由 于我们明确选择错误完成的scmd,可以确定底层驱动已不再维护该 scmd,我们可以重用它进行STU。 如果STU操作成功且sdev处于离线或就绪状态,所有在sdev上失败的 scmd都会通过scsi_eh_finish_cmd()完成。 *注意* 如果hostt->eh_abort_handler()未实现或返回失败,可能 此时仍有超时的scmd,此时STU不会导致底层驱动不再维护scmd。但 是,如果STU执行成功,该函数会通过scsi_eh_finish_cmd()来完成 sdev上的所有scmd,这会导致底层驱动处于不一致的状态。看来STU 操作应仅在sdev不包含超时scmd时进行。 2. 如果!list_empty(&eh_work_q),调用scsi_eh_bus_device_reset()。 ``scsi_eh_bus_device_reset`` 此操作与scsi_eh_stu()非常相似,区别在于使用 hostt->eh_device_reset_handler()替代STU命令。此外,由于我们 没有发送SCSI命令且重置会清空该sdev上所有的scmd,所以无需筛选错 误完成的scmd。 3. 如果!list_empty(&eh_work_q),调用scsi_eh_bus_reset()。 ``scsi_eh_bus_reset`` 对于每个包含失败scmd的SCSI通道调用 hostt->eh_bus_reset_handler()。如果总线重置成功,那么该通道上 所有准备就绪或离线状态sdev上的失败scmd都会被处理处理完成。 4. 如果!list_empty(&eh_work_q),调用scsi_eh_host_reset()。 ``scsi_eh_host_reset`` 调用hostt->eh_host_reset_handler()是最终的手段。如果SCSI主机 重置成功,主机上所有就绪或离线sdev上的失败scmd都会通过错误处理 完成。 5. 如果!list_empty(&eh_work_q),调用scsi_eh_offline_sdevs()。 ``scsi_eh_offline_sdevs`` 离线所有包含未恢复scmd的所有sdev,并通过 scsi_eh_finish_cmd()完成这些scmd。 h](h)}(h]该函数采取四种逐步增强的措施,使失败的设备准备好处理新的命令。h]h]该函数采取四种逐步增强的措施,使失败的设备准备好处理新的命令。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMihj ubj)}(hhh]j7)}(h调用scsi_eh_stu() h]h)}(h调用scsi_eh_stu()h]h调用scsi_eh_stu()}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMkhj ubah}(h]h ]h"]h$]h&]uh1j6hj ubah}(h]h ]h"]h$]h&]jjjkjlhjmjnuh1jhj ubh)}(h``scsi_eh_stu``h]jM)}(hj% h]h scsi_eh_stu}(hj' hhhNhNubah}(h]h ]h"]h$]h&]uh1jLhj# ubah}(h]h ]h"]h$]h&]uh1hhhhMmhj ubj,)}(hX 对于每个具有有效感知数据且scsi_check_sense()判断为失败的 scmd发送START STOP UNIT(STU)命令且将start置1。注意,由 于我们明确选择错误完成的scmd,可以确定底层驱动已不再维护该 scmd,我们可以重用它进行STU。 如果STU操作成功且sdev处于离线或就绪状态,所有在sdev上失败的 scmd都会通过scsi_eh_finish_cmd()完成。 *注意* 如果hostt->eh_abort_handler()未实现或返回失败,可能 此时仍有超时的scmd,此时STU不会导致底层驱动不再维护scmd。但 是,如果STU执行成功,该函数会通过scsi_eh_finish_cmd()来完成 sdev上的所有scmd,这会导致底层驱动处于不一致的状态。看来STU 操作应仅在sdev不包含超时scmd时进行。 h](h)}(hX对于每个具有有效感知数据且scsi_check_sense()判断为失败的 scmd发送START STOP UNIT(STU)命令且将start置1。注意,由 于我们明确选择错误完成的scmd,可以确定底层驱动已不再维护该 scmd,我们可以重用它进行STU。h]hX对于每个具有有效感知数据且scsi_check_sense()判断为失败的 scmd发送START STOP UNIT(STU)命令且将start置1。注意,由 于我们明确选择错误完成的scmd,可以确定底层驱动已不再维护该 scmd,我们可以重用它进行STU。}(hj> hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMohj: ubh)}(h如果STU操作成功且sdev处于离线或就绪状态,所有在sdev上失败的 scmd都会通过scsi_eh_finish_cmd()完成。h]h如果STU操作成功且sdev处于离线或就绪状态,所有在sdev上失败的 scmd都会通过scsi_eh_finish_cmd()完成。}(hjL hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMthj: ubh)}(hXr*注意* 如果hostt->eh_abort_handler()未实现或返回失败,可能 此时仍有超时的scmd,此时STU不会导致底层驱动不再维护scmd。但 是,如果STU执行成功,该函数会通过scsi_eh_finish_cmd()来完成 sdev上的所有scmd,这会导致底层驱动处于不一致的状态。看来STU 操作应仅在sdev不包含超时scmd时进行。h](hemphasis)}(h*注意*h]h注意}(hj` hhhNhNubah}(h]h ]h"]h$]h&]uh1j^ hjZ ubhXj 如果hostt->eh_abort_handler()未实现或返回失败,可能 此时仍有超时的scmd,此时STU不会导致底层驱动不再维护scmd。但 是,如果STU执行成功,该函数会通过scsi_eh_finish_cmd()来完成 sdev上的所有scmd,这会导致底层驱动处于不一致的状态。看来STU 操作应仅在sdev不包含超时scmd时进行。}(hjZ hhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMwhj: ubeh}(h]h ]h"]h$]h&]uh1j+hhhMohj ubj)}(hhh]j7)}(hD如果!list_empty(&eh_work_q),调用scsi_eh_bus_device_reset()。 h]h)}(hC如果!list_empty(&eh_work_q),调用scsi_eh_bus_device_reset()。h]hC如果!list_empty(&eh_work_q),调用scsi_eh_bus_device_reset()。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhM}hj ubah}(h]h ]h"]h$]h&]uh1j6hj~ ubah}(h]h ]h"]h$]h&]jjjkjlhjmjnj Kuh1jhj ubh)}(h``scsi_eh_bus_device_reset``h]jM)}(hj h]hscsi_eh_bus_device_reset}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jLhj ubah}(h]h ]h"]h$]h&]uh1hhhhMhj ubj,)}(h此操作与scsi_eh_stu()非常相似,区别在于使用 hostt->eh_device_reset_handler()替代STU命令。此外,由于我们 没有发送SCSI命令且重置会清空该sdev上所有的scmd,所以无需筛选错 误完成的scmd。 h]h)}(h此操作与scsi_eh_stu()非常相似,区别在于使用 hostt->eh_device_reset_handler()替代STU命令。此外,由于我们 没有发送SCSI命令且重置会清空该sdev上所有的scmd,所以无需筛选错 误完成的scmd。h]h此操作与scsi_eh_stu()非常相似,区别在于使用 hostt->eh_device_reset_handler()替代STU命令。此外,由于我们 没有发送SCSI命令且重置会清空该sdev上所有的scmd,所以无需筛选错 误完成的scmd。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj ubah}(h]h ]h"]h$]h&]uh1j+hhhMhj ubj)}(hhh]j7)}(h=如果!list_empty(&eh_work_q),调用scsi_eh_bus_reset()。 h]h)}(h<如果!list_empty(&eh_work_q),调用scsi_eh_bus_reset()。h]h<如果!list_empty(&eh_work_q),调用scsi_eh_bus_reset()。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj ubah}(h]h ]h"]h$]h&]uh1j6hj ubah}(h]h ]h"]h$]h&]jjjkjlhjmjnj Kuh1jhj ubh)}(h``scsi_eh_bus_reset``h]jM)}(hj h]hscsi_eh_bus_reset}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1jLhj ubah}(h]h ]h"]h$]h&]uh1hhhhMhj ubj,)}(h对于每个包含失败scmd的SCSI通道调用 hostt->eh_bus_reset_handler()。如果总线重置成功,那么该通道上 所有准备就绪或离线状态sdev上的失败scmd都会被处理处理完成。 h]h)}(h对于每个包含失败scmd的SCSI通道调用 hostt->eh_bus_reset_handler()。如果总线重置成功,那么该通道上 所有准备就绪或离线状态sdev上的失败scmd都会被处理处理完成。h]h对于每个包含失败scmd的SCSI通道调用 hostt->eh_bus_reset_handler()。如果总线重置成功,那么该通道上 所有准备就绪或离线状态sdev上的失败scmd都会被处理处理完成。}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j+hhhMhj ubj)}(hhh]j7)}(h>如果!list_empty(&eh_work_q),调用scsi_eh_host_reset()。 h]h)}(h=如果!list_empty(&eh_work_q),调用scsi_eh_host_reset()。h]h=如果!list_empty(&eh_work_q),调用scsi_eh_host_reset()。}(hj%hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj!ubah}(h]h ]h"]h$]h&]uh1j6hjubah}(h]h ]h"]h$]h&]jjjkjlhjmjnj Kuh1jhj ubh)}(h``scsi_eh_host_reset``h]jM)}(hjAh]hscsi_eh_host_reset}(hjChhhNhNubah}(h]h ]h"]h$]h&]uh1jLhj?ubah}(h]h ]h"]h$]h&]uh1hhhhMhj ubj,)}(h调用hostt->eh_host_reset_handler()是最终的手段。如果SCSI主机 重置成功,主机上所有就绪或离线sdev上的失败scmd都会通过错误处理 完成。 h]h)}(h调用hostt->eh_host_reset_handler()是最终的手段。如果SCSI主机 重置成功,主机上所有就绪或离线sdev上的失败scmd都会通过错误处理 完成。h]h调用hostt->eh_host_reset_handler()是最终的手段。如果SCSI主机 重置成功,主机上所有就绪或离线sdev上的失败scmd都会通过错误处理 完成。}(hjZhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjVubah}(h]h ]h"]h$]h&]uh1j+hhhMhj ubj)}(hhh]j7)}(hA如果!list_empty(&eh_work_q),调用scsi_eh_offline_sdevs()。 h]h)}(h@如果!list_empty(&eh_work_q),调用scsi_eh_offline_sdevs()。h]h@如果!list_empty(&eh_work_q),调用scsi_eh_offline_sdevs()。}(hjuhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjqubah}(h]h ]h"]h$]h&]uh1j6hjnubah}(h]h ]h"]h$]h&]jjjkjlhjmjnj Kuh1jhj ubh)}(h``scsi_eh_offline_sdevs``h]jM)}(hjh]hscsi_eh_offline_sdevs}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jLhjubah}(h]h ]h"]h$]h&]uh1hhhhMhj ubj,)}(ha离线所有包含未恢复scmd的所有sdev,并通过 scsi_eh_finish_cmd()完成这些scmd。 h]h)}(h`离线所有包含未恢复scmd的所有sdev,并通过 scsi_eh_finish_cmd()完成这些scmd。h]h`离线所有包含未恢复scmd的所有sdev,并通过 scsi_eh_finish_cmd()完成这些scmd。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j+hhhMhj ubeh}(h]h ]h"]h$]h&]uh1j+hhhMihj+ ubj)}(hhh]j7)}(hX 调用scsi_eh_flush_done_q()。 ``scsi_eh_flush_done_q`` 此时所有的scmd都已经恢复(或放弃),并通过 scsi_eh_finish_cmd()函数加入eh_done_q队列。该函数通过 重试或显示通知上层scmd的失败来刷新eh_done_q。 h](h)}(h调用scsi_eh_flush_done_q()。h]h调用scsi_eh_flush_done_q()。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubj,)}(h``scsi_eh_flush_done_q`` 此时所有的scmd都已经恢复(或放弃),并通过 scsi_eh_finish_cmd()函数加入eh_done_q队列。该函数通过 重试或显示通知上层scmd的失败来刷新eh_done_q。 h](h)}(h``scsi_eh_flush_done_q``h]jM)}(hjh]hscsi_eh_flush_done_q}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jLhjubah}(h]h ]h"]h$]h&]uh1hhhhMhjubj,)}(h此时所有的scmd都已经恢复(或放弃),并通过 scsi_eh_finish_cmd()函数加入eh_done_q队列。该函数通过 重试或显示通知上层scmd的失败来刷新eh_done_q。 h]h)}(h此时所有的scmd都已经恢复(或放弃),并通过 scsi_eh_finish_cmd()函数加入eh_done_q队列。该函数通过 重试或显示通知上层scmd的失败来刷新eh_done_q。h]h此时所有的scmd都已经恢复(或放弃),并通过 scsi_eh_finish_cmd()函数加入eh_done_q队列。该函数通过 重试或显示通知上层scmd的失败来刷新eh_done_q。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j+hhhMhjubeh}(h]h ]h"]h$]h&]uh1j+hhhMhjubeh}(h]h ]h"]h$]h&]uh1j6hjubah}(h]h ]h"]h$]h&]jjjkjlhjmjnj Kuh1jhj+ ubeh}(h]h ]h"]h$]h&]uh1j+hhhM@hj hhubeh}(h]id8ah ]h"]2.1.3 控制流ah$]h&]uh1jvhjhhhhhM9ubeh}(h]id5ah ]h"].2.1 基于细粒度回调的错误处理机制ah$]h&]uh1jvhjhhhhhKubjw)}(hhh](j|)}(h@2.2 基于transportt->eh_strategy_handler()的错误处理机制h]h@2.2 基于transportt->eh_strategy_handler()的错误处理机制}(hj7hhhNhNubah}(h]h ]h"]h$]h&]uh1j{hj4hhhhhMubh)}(hX在该机制中,transportt->eh_strategy_handler()替代 scsi_unjam_host()的被调用,并负责整个错误恢复过程。该处理 函数完成后应该确保底层驱动不再维护任何失败的scmd并且将设备 设置为就绪(准备接收新命令)或离线状态。此外,该函数还应该 执行SCSI错误处理的维护任务,以维护SCSI中间层的数据完整性。 换句话说,eh_strategy_handler()必须实现[2-1-2]中除第1步 外的所有步骤。h]hX在该机制中,transportt->eh_strategy_handler()替代 scsi_unjam_host()的被调用,并负责整个错误恢复过程。该处理 函数完成后应该确保底层驱动不再维护任何失败的scmd并且将设备 设置为就绪(准备接收新命令)或离线状态。此外,该函数还应该 执行SCSI错误处理的维护任务,以维护SCSI中间层的数据完整性。 换句话说,eh_strategy_handler()必须实现[2-1-2]中除第1步 外的所有步骤。}(hjEhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj4hhubjw)}(hhh](j|)}(hF2.2.1 transportt->eh_strategy_handler()调用前的SCSI中间层状态h]hF2.2.1 transportt->eh_strategy_handler()调用前的SCSI中间层状态}(hjVhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjShhhhhMubj,)}(hX 进入该处理函数时,以下条件成立。 - 每个失败的scmd的eh_flags字段已正确设置。 - 每个失败的scmd通过scmd->eh_entry链接到scmd->eh_cmd_q队列。 - 已设置SHOST_RECOVERY标志。 - `shost->host_failed == shost->host_busy`。 h](h)}(h0进入该处理函数时,以下条件成立。h]h0进入该处理函数时,以下条件成立。}(hjhhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjdubj2)}(hhh](j7)}(h7每个失败的scmd的eh_flags字段已正确设置。 h]h)}(h6每个失败的scmd的eh_flags字段已正确设置。h]h6每个失败的scmd的eh_flags字段已正确设置。}(hj}hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjyubah}(h]h ]h"]h$]h&]uh1j6hjvubj7)}(hH每个失败的scmd通过scmd->eh_entry链接到scmd->eh_cmd_q队列。 h]h)}(hG每个失败的scmd通过scmd->eh_entry链接到scmd->eh_cmd_q队列。h]hG每个失败的scmd通过scmd->eh_entry链接到scmd->eh_cmd_q队列。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j6hjvubj7)}(h!已设置SHOST_RECOVERY标志。 h]h)}(h 已设置SHOST_RECOVERY标志。h]h 已设置SHOST_RECOVERY标志。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j6hjvubj7)}(h,`shost->host_failed == shost->host_busy`。 h]h)}(h+`shost->host_failed == shost->host_busy`。h](htitle_reference)}(h(`shost->host_failed == shost->host_busy`h]h&shost->host_failed == shost->host_busy}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1jhjubh。}(hjhhhNhNubeh}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j6hjvubeh}(h]h ]h"]h$]h&]jjuh1j1hhhMhjdubeh}(h]h ]h"]h$]h&]uh1j+hhhMhjShhubeh}(h]#transportt-eh-strategy-handler-scsiah ]h"]F2.2.1 transportt->eh_strategy_handler()调用前的scsi中间层状态ah$]h&]uh1jvhj4hhhhhMubjw)}(hhh](j|)}(hF2.2.2 transportt->eh_strategy_handler()调用后的SCSI中间层状态h]hF2.2.2 transportt->eh_strategy_handler()调用后的SCSI中间层状态}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjhhhhhMubj,)}(hXn从该处理函数退出时,以下条件成立。 - shost->host_failed为零。 - shost->eh_cmd_q被清空。 - 每个scmd->eh_entry被清空。 - 对每个scmd必须调用scsi_queue_insert()或scsi_finish_command()。 注意,该处理程序可以使用scmd->retries(剩余重试次数)和 scmd->allowed(允许重试次数)限制重试次数。 h](h)}(h3从该处理函数退出时,以下条件成立。h]h3从该处理函数退出时,以下条件成立。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubj2)}(hhh](j7)}(hshost->host_failed为零。 h]h)}(hshost->host_failed为零。h]hshost->host_failed为零。}(hj'hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj#ubah}(h]h ]h"]h$]h&]uh1j6hj ubj7)}(hshost->eh_cmd_q被清空。 h]h)}(hshost->eh_cmd_q被清空。h]hshost->eh_cmd_q被清空。}(hj?hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhj;ubah}(h]h ]h"]h$]h&]uh1j6hj ubj7)}(h!每个scmd->eh_entry被清空。 h]h)}(h 每个scmd->eh_entry被清空。h]h 每个scmd->eh_entry被清空。}(hjWhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjSubah}(h]h ]h"]h$]h&]uh1j6hj ubj7)}(h对每个scmd必须调用scsi_queue_insert()或scsi_finish_command()。 注意,该处理程序可以使用scmd->retries(剩余重试次数)和 scmd->allowed(允许重试次数)限制重试次数。 h]h)}(h对每个scmd必须调用scsi_queue_insert()或scsi_finish_command()。 注意,该处理程序可以使用scmd->retries(剩余重试次数)和 scmd->allowed(允许重试次数)限制重试次数。h]h对每个scmd必须调用scsi_queue_insert()或scsi_finish_command()。 注意,该处理程序可以使用scmd->retries(剩余重试次数)和 scmd->allowed(允许重试次数)限制重试次数。}(hjohhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjkubah}(h]h ]h"]h$]h&]uh1j6hj ubeh}(h]h ]h"]h$]h&]jjuh1j1hhhMhjubeh}(h]h ]h"]h$]h&]uh1j+hhhMhjhhubeh}(h]id9ah ]h"]F2.2.2 transportt->eh_strategy_handler()调用后的scsi中间层状态ah$]h&]uh1jvhj4hhhhhMubjw)}(hhh](j|)}(h2.2.3 注意事项h]h2.2.3 注意事项}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1j{hjhhhhhMubj,)}(hX- 需明确已超时的scmd在底层仍处于活跃状态,因此在操作这些 scmd前,必须确保底层已彻底不再维护。 - 访问或修改shost数据结构时,必须持有shost->host_lock锁 以维持数据一致性。 - 错误处理完成后,每个故障设备必须彻底清除所有活跃SCSI命 令(scmd)的关联状态。 - 错误处理完成后,每个故障设备必须被设置为就绪(准备接收 新命令)或离线状态。 h]j2)}(hhh](j7)}(h需明确已超时的scmd在底层仍处于活跃状态,因此在操作这些 scmd前,必须确保底层已彻底不再维护。 h]h)}(h需明确已超时的scmd在底层仍处于活跃状态,因此在操作这些 scmd前,必须确保底层已彻底不再维护。h]h需明确已超时的scmd在底层仍处于活跃状态,因此在操作这些 scmd前,必须确保底层已彻底不再维护。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j6hjubj7)}(hb访问或修改shost数据结构时,必须持有shost->host_lock锁 以维持数据一致性。 h]h)}(ha访问或修改shost数据结构时,必须持有shost->host_lock锁 以维持数据一致性。h]ha访问或修改shost数据结构时,必须持有shost->host_lock锁 以维持数据一致性。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j6hjubj7)}(hp错误处理完成后,每个故障设备必须彻底清除所有活跃SCSI命 令(scmd)的关联状态。 h]h)}(ho错误处理完成后,每个故障设备必须彻底清除所有活跃SCSI命 令(scmd)的关联状态。h]ho错误处理完成后,每个故障设备必须彻底清除所有活跃SCSI命 令(scmd)的关联状态。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j6hjubj7)}(hr错误处理完成后,每个故障设备必须被设置为就绪(准备接收 新命令)或离线状态。 h]h)}(hp错误处理完成后,每个故障设备必须被设置为就绪(准备接收 新命令)或离线状态。h]hp错误处理完成后,每个故障设备必须被设置为就绪(准备接收 新命令)或离线状态。}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjubah}(h]h ]h"]h$]h&]uh1j6hjubeh}(h]h ]h"]h$]h&]jjuh1j1hhhMhjubah}(h]h ]h"]h$]h&]uh1j+hhhMhjhhubh)}(hTejun Heo htejun@gmail.comh](h Tejun Heo }(hjhhhNhNubj))}(hhtejun@gmail.comh]hhtejun@gmail.com}(hj#hhhNhNubah}(h]h ]h"]h$]h&]refurimailto:htejun@gmail.comuh1j(hjubeh}(h]h ]h"]h$]h&]uh1hhhhMhjhhubh)}(h11th September 2005h]h11th September 2005}(hj9hhhNhNubah}(h]h ]h"]h$]h&]uh1hhhhMhjhhubeh}(h]id10ah ]h"]2.2.3 注意事项ah$]h&]uh1jvhj4hhhhhMubeh}(h]transportt-eh-strategy-handlerah ]h"]@2.2 基于transportt->eh_strategy_handler()的错误处理机制ah$]h&]uh1jvhjhhhhhMubeh}(h]id4ah ]h"]2. scsi错误处理机制详解ah$]h&]uh1jvhjxhhhhhKubeh}(h]scsiah ]h"]scsi 中间层错误处理ah$]h&]uh1jvhhhhhhhKubeh}(h]h ]h"]h$]h&]sourcehuh1hcurrent_sourceN current_lineNsettingsdocutils.frontendValues)}(j{N generatorN datestampN source_linkN source_urlN toc_backlinksentryfootnote_backlinksK sectnum_xformKstrip_commentsNstrip_elements_with_classesN strip_classesN report_levelK halt_levelKexit_status_levelKdebugNwarning_streamN tracebackinput_encoding utf-8-siginput_encoding_error_handlerstrictoutput_encodingutf-8output_encoding_error_handlerjerror_encodingutf-8error_encoding_error_handlerbackslashreplace language_codeenrecord_dependenciesNconfigN id_prefixhauto_id_prefixid dump_settingsNdump_internalsNdump_transformsNdump_pseudo_xmlNexpose_internalsNstrict_visitorN_disable_configN_sourceh _destinationN _config_files]7/var/lib/git/docbuild/linux/Documentation/docutils.confafile_insertion_enabled raw_enabledKline_length_limitM'pep_referencesN pep_base_urlhttps://peps.python.org/pep_file_url_templatepep-%04drfc_referencesN rfc_base_url&https://datatracker.ietf.org/doc/html/ tab_widthKtrim_footnote_reference_spacesyntax_highlightlong smart_quotessmartquotes_locales]character_level_inline_markupdoctitle_xform docinfo_xformKsectsubtitle_xform image_loadinglinkembed_stylesheetcloak_email_addressessection_self_linkenvNubreporterNindirect_targets]substitution_defs}substitution_names}refnames}refids}nameids}(jdjajjjjjjjjjzjwjjj{jxj\jYj1j.jjj j j)j&jTjQjjjjjLjIu nametypes}(jdjjjjjzjj{j\j1jj j)jTjjjLuh}(jajxjjjjjjjjjwjjjjxjjYjj.jjjj jj&j jQj4jjSjjjIju footnote_refs} citation_refs} autofootnotes]autofootnote_refs]symbol_footnotes]symbol_footnote_refs] footnotes] citations]autofootnote_startKsymbol_footnote_startK id_counter collectionsCounter}jK sRparse_messages](hsystem_message)}(hhh]h)}(h:Enumerated list start value not ordinal-1: "4" (ordinal 4)h]h>Enumerated list start value not ordinal-1: “4” (ordinal 4)}(hjhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjubah}(h]h ]h"]h$]h&]levelKtypeINFOsourcehlineKuh1jhj+ ubj)}(hhh]h)}(h:Enumerated list start value not ordinal-1: "2" (ordinal 2)h]h>Enumerated list start value not ordinal-1: “2” (ordinal 2)}(hj hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj ubah}(h]h ]h"]h$]h&]levelKtypejsourcehlineKuh1jhj ubj)}(hhh]h)}(h:Enumerated list start value not ordinal-1: "3" (ordinal 3)h]h>Enumerated list start value not ordinal-1: “3” (ordinal 3)}(hj(hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj%ubah}(h]h ]h"]h$]h&]levelKtypejsourcehlineKuh1jhj ubj)}(hhh]h)}(h:Enumerated list start value not ordinal-1: "4" (ordinal 4)h]h>Enumerated list start value not ordinal-1: “4” (ordinal 4)}(hjChhhNhNubah}(h]h ]h"]h$]h&]uh1hhj@ubah}(h]h ]h"]h$]h&]levelKtypejsourcehlineKuh1jhj ubj)}(hhh]h)}(h:Enumerated list start value not ordinal-1: "5" (ordinal 5)h]h>Enumerated list start value not ordinal-1: “5” (ordinal 5)}(hj^hhhNhNubah}(h]h ]h"]h$]h&]uh1hhj[ubah}(h]h ]h"]h$]h&]levelKtypejsourcehlineKuh1jhj ubj)}(hhh]h)}(h:Enumerated list start value not ordinal-1: "5" (ordinal 5)h]h>Enumerated list start value not ordinal-1: “5” (ordinal 5)}(hjyhhhNhNubah}(h]h ]h"]h$]h&]uh1hhjvubah}(h]h ]h"]h$]h&]levelKtypejsourcehlineKuh1jhj+ ubetransform_messages] transformerN include_log]1Documentation/translations/zh_CN/scsi/scsi_eh.rst(NNNNta decorationNhhub.