Index: sys/dev/nvme/nvme_ctrlr.c =================================================================== --- sys/dev/nvme/nvme_ctrlr.c +++ sys/dev/nvme/nvme_ctrlr.c @@ -1166,23 +1166,30 @@ nvme_ctrlr_reset_task(void *arg, int pending) { struct nvme_controller *ctrlr = arg; - int status; + int err; - nvme_ctrlr_devctl_log(ctrlr, "RESET", "resetting controller"); - status = nvme_ctrlr_hw_reset(ctrlr); - /* - * Use pause instead of DELAY, so that we yield to any nvme interrupt - * handlers on this CPU that were blocked on a qpair lock. We want - * all nvme interrupts completed before proceeding with restarting the - * controller. - * - * XXX - any way to guarantee the interrupt handlers have quiesced? - */ - pause("nvmereset", hz / 10); - if (status == 0) - nvme_ctrlr_start(ctrlr, true); - else + if (!ctrlr->is_failing) { + nvme_ctrlr_devctl_log(ctrlr, "RESET", "resetting controller"); + err = nvme_ctrlr_hw_reset(ctrlr); + /* + * Use pause instead of DELAY, so that we yield to any nvme + * interrupt handlers on this CPU that were blocked on a qpair + * lock. We want all nvme interrupts completed before proceeding + * with restarting the controller. + * + * XXX - any way to guarantee the interrupt handlers have quiesced? + */ + pause("nvmereset", hz / 10); + if (err == 0) + nvme_ctrlr_start(ctrlr, true); + else { + nvme_ctrlr_devctl_log(ctrlr, "FAILED", "failing controller"); + nvme_ctrlr_fail(ctrlr); + } + } else { + nvme_ctrlr_devctl_log(ctrlr, "GONE", "failing controller"); nvme_ctrlr_fail(ctrlr); + } atomic_cmpset_32(&ctrlr->is_resetting, 1, 0); } @@ -1445,6 +1452,7 @@ TASK_INIT(&ctrlr->fail_req_task, 0, nvme_ctrlr_fail_req_task, ctrlr); STAILQ_INIT(&ctrlr->fail_req); ctrlr->is_failed = false; + ctrlr->is_failing = false; make_dev_args_init(&md_args); md_args.mda_devsw = &nvme_ctrlr_cdevsw; Index: sys/dev/nvme/nvme_private.h =================================================================== --- sys/dev/nvme/nvme_private.h +++ sys/dev/nvme/nvme_private.h @@ -164,6 +164,7 @@ RECOVERY_START, /* Deadline has passed, start recovering */ RECOVERY_RESET, /* This pass, initiate reset of controller */ RECOVERY_WAITING, /* waiting for the reset to complete */ + RECOVERY_FAILED, /* recovery unable to revive device */ }; struct nvme_qpair { struct nvme_controller *ctrlr; @@ -316,6 +317,7 @@ u_int fail_on_reset; bool is_failed; + bool is_failing; bool is_dying; STAILQ_HEAD(, nvme_request) fail_req; Index: sys/dev/nvme/nvme_qpair.c =================================================================== --- sys/dev/nvme/nvme_qpair.c +++ sys/dev/nvme/nvme_qpair.c @@ -908,7 +908,7 @@ sbintime_t now; bool idle; uint32_t csts; - uint8_t cfs; + uint8_t cfs = 0; mtx_lock(&qpair->lock); idle = TAILQ_EMPTY(&qpair->outstanding_tr); @@ -970,6 +970,11 @@ * controller and hope for the best. */ csts = nvme_mmio_read_4(ctrlr, csts); + if (csts == NVME_GONE) { + nvme_printf(ctrlr, "Controller is gone, possibly due to hot unplug event\n"); + qpair->recovery_state = RECOVERY_FAILED; + goto again; + } cfs = (csts >> NVME_CSTS_REG_CFS_SHIFT) & NVME_CSTS_REG_CFS_MASK; if (cfs) { nvme_printf(ctrlr, "Controller in fatal status, resetting\n"); @@ -991,8 +996,7 @@ break; case RECOVERY_RESET: nvme_printf(ctrlr, "Resetting controller due to a timeout%s.\n", - (csts == NVME_GONE) ? " and possible hot unplug" : - (cfs ? " and fatal error status" : "")); + cfs ? " and fatal error status" : ""); nvme_printf(ctrlr, "RECOVERY_WAITING\n"); qpair->recovery_state = RECOVERY_WAITING; nvme_ctrlr_reset(ctrlr); @@ -1000,6 +1004,12 @@ case RECOVERY_WAITING: nvme_printf(ctrlr, "waiting\n"); break; + case RECOVERY_FAILED: + nvme_printf(ctrlr, "recovery failed\n"); + ctrlr->is_failing = true; + mtx_unlock(&qpair->lock); + nvme_ctrlr_reset(ctrlr); + break; } /*