diff --git a/sys/dev/nvme/nvme_qpair.c b/sys/dev/nvme/nvme_qpair.c --- a/sys/dev/nvme/nvme_qpair.c +++ b/sys/dev/nvme/nvme_qpair.c @@ -891,6 +891,18 @@ { struct nvme_tracker *tr; + /* + * We are called both to tear down a partial object, and to destroy an + * object that's fully formed. If the recovery lock is initialized, then + * we could race nvme_qpair_timeout if we don't hold the recovery lock + * while we set timer_armed to false. If it's not initialized, + * timer_armed is guaranteed to be false and there's nothing to race. + */ + if (mtx_initialized(&qpair->recovery)) { + mtx_lock(&qpair->recovery); + qpair->timer_armed = false; + mtx_unlock(&qpair->recovery); + } callout_drain(&qpair->timer); if (qpair->tag) { @@ -1039,6 +1051,19 @@ return; } + /* + * Shutdown condition: We set qpair->timer_armed to false in + * nvme_qpair_destroy before calling callout_drain. When we call that, + * this routine might get called one last time. Exit w/o setting a + * timeout. None of the watchdog stuff needs to be done since we're + * destroying the qpair. + */ + if (!qpair->timer_armed) { + nvme_printf(qpair->ctrlr, + "Timeout fired during nvme_qpair_destroy\n"); + return; + } + switch (qpair->recovery_state) { case RECOVERY_NONE: /*