Index: sys/dev/nvme/nvme_ctrlr.c =================================================================== --- sys/dev/nvme/nvme_ctrlr.c +++ sys/dev/nvme/nvme_ctrlr.c @@ -344,10 +344,10 @@ return (nvme_ctrlr_wait_for_ready(ctrlr, 1)); } -int -nvme_ctrlr_hw_reset(struct nvme_controller *ctrlr) +static void +nvme_ctrlr_disable_qpairs(struct nvme_controller *ctrlr) { - int i, err; + int i; nvme_admin_qpair_disable(&ctrlr->adminq); /* @@ -359,6 +359,14 @@ for (i = 0; i < ctrlr->num_io_queues; i++) nvme_io_qpair_disable(&ctrlr->ioq[i]); } +} + +int +nvme_ctrlr_hw_reset(struct nvme_controller *ctrlr) +{ + int err; + + nvme_ctrlr_disable_qpairs(ctrlr); DELAY(100*1000); @@ -481,7 +489,7 @@ } static int -nvme_ctrlr_destroy_qpairs(struct nvme_controller *ctrlr) +nvme_ctrlr_delete_qpairs(struct nvme_controller *ctrlr) { struct nvme_completion_poll_status status; struct nvme_qpair *qpair; @@ -820,7 +828,7 @@ } static void -nvme_ctrlr_start(void *ctrlr_arg) +nvme_ctrlr_start(void *ctrlr_arg, bool resetting) { struct nvme_controller *ctrlr = ctrlr_arg; uint32_t old_num_io_queues; @@ -833,7 +841,7 @@ * the number of I/O queues supported, so cannot reset * the adminq again here. */ - if (ctrlr->is_resetting) + if (resetting) nvme_qpair_reset(&ctrlr->adminq); for (i = 0; i < ctrlr->num_io_queues; i++) @@ -854,7 +862,7 @@ * explicit specify how many queues it will use. This value should * never change between resets, so panic if somehow that does happen. */ - if (ctrlr->is_resetting) { + if (resetting) { old_num_io_queues = ctrlr->num_io_queues; if (nvme_ctrlr_set_num_qpairs(ctrlr) != 0) { nvme_ctrlr_fail(ctrlr); @@ -894,7 +902,7 @@ if (nvme_ctrlr_set_num_qpairs(ctrlr) == 0 && nvme_ctrlr_construct_io_qpairs(ctrlr) == 0) - nvme_ctrlr_start(ctrlr); + nvme_ctrlr_start(ctrlr, false); else nvme_ctrlr_fail(ctrlr); @@ -923,7 +931,7 @@ */ pause("nvmereset", hz / 10); if (status == 0) - nvme_ctrlr_start(ctrlr); + nvme_ctrlr_start(ctrlr, true); else nvme_ctrlr_fail(ctrlr); @@ -1206,7 +1214,7 @@ if (ctrlr->is_initialized) { if (!gone) - nvme_ctrlr_destroy_qpairs(ctrlr); + nvme_ctrlr_delete_qpairs(ctrlr); for (i = 0; i < ctrlr->num_io_queues; i++) nvme_io_qpair_destroy(&ctrlr->ioq[i]); free(ctrlr->ioq, M_NVME); @@ -1306,3 +1314,71 @@ return (&ctrlr->cdata); } + +int +nvme_ctrlr_suspend(struct nvme_controller *ctrlr) +{ + + /* + * Can't touch failed controllers, so it's already suspended. + */ + if (ctrlr->is_failed) + return (0); + + /* + * To properly suspend, we need to delete the hardware I/O + * queues, and then shutdown. This weird sequence is in the + * nvme standard, and is a boiled down version of what we do + * in nvme_shutdown. Once we delete the qpairs, we have to + * disable them before shutting down. The delay is out of + * paranpoia in nvme_ctrlr_hw_reset, and is repeated here + * as is the setting is_resetting in the controller. + */ + atomic_cmpset_32(&ctrlr->is_resetting, 0, 1); + nvme_ctrlr_delete_qpairs(ctrlr); + nvme_ctrlr_disable_qpairs(ctrlr); + DELAY(100*1000); + nvme_ctrlr_shutdown(ctrlr); + atomic_cmpset_32(&ctrlr->is_resetting, 1, 0); + + return (0); +} + +int +nvme_ctrlr_resume(struct nvme_controller *ctrlr) +{ + + /* + * Can't touch failed controllers, so nothing to do to resume. + */ + if (ctrlr->is_failed) + return (0); + + /* + * Have to reset the hardware twice, just like we do on + * attach. See nmve_attach() for why. + */ + atomic_cmpset_32(&ctrlr->is_resetting, 0, 1); + if (nvme_ctrlr_hw_reset(ctrlr) != 0) + goto fail; + if (nvme_ctrlr_hw_reset(ctrlr) != 0) + goto fail; + + /* + * Now that we're reset, we can restart the controller. + */ + nvme_ctrlr_start(ctrlr, true); + atomic_cmpset_32(&ctrlr->is_resetting, 1, 0); + + return (0); +fail: + /* + * can't do I/O to the controller now, announce and fail the + * controller. However, we have to return success for the resume + * itself, due to questionable APIs. + */ + nvme_printf(ctrlr, "Failed to reset on resume, failing.\n"); + nvme_ctrlr_fail(ctrlr); + atomic_cmpset_32(&ctrlr->is_resetting, 1, 0); + return (0); +} Index: sys/dev/nvme/nvme_pci.c =================================================================== --- sys/dev/nvme/nvme_pci.c +++ sys/dev/nvme/nvme_pci.c @@ -43,6 +43,8 @@ static int nvme_pci_probe(device_t); static int nvme_pci_attach(device_t); static int nvme_pci_detach(device_t); +static int nvme_pci_suspend(device_t); +static int nvme_pci_resume(device_t); static void nvme_ctrlr_setup_interrupts(struct nvme_controller *ctrlr); @@ -51,6 +53,8 @@ DEVMETHOD(device_probe, nvme_pci_probe), DEVMETHOD(device_attach, nvme_pci_attach), DEVMETHOD(device_detach, nvme_pci_detach), + DEVMETHOD(device_suspend, nvme_pci_suspend), + DEVMETHOD(device_resume, nvme_pci_resume), DEVMETHOD(device_shutdown, nvme_shutdown), { 0, 0 } }; @@ -332,3 +336,21 @@ ctrlr->msix_enabled = 1; } + +static int +nvme_pci_suspend(device_t dev) +{ + struct nvme_controller *ctrlr; + + ctrlr = DEVICE2SOFTC(dev); + return (nvme_ctrlr_suspend(ctrlr)); +} + +static int +nvme_pci_resume(device_t dev) +{ + struct nvme_controller *ctrlr; + + ctrlr = DEVICE2SOFTC(dev); + return (nvme_ctrlr_resume(ctrlr)); +} Index: sys/dev/nvme/nvme_private.h =================================================================== --- sys/dev/nvme/nvme_private.h +++ sys/dev/nvme/nvme_private.h @@ -556,4 +556,7 @@ void nvme_ctrlr_intx_handler(void *arg); void nvme_ctrlr_poll(struct nvme_controller *ctrlr); +int nvme_ctrlr_suspend(struct nvme_controller *ctrlr); +int nvme_ctrlr_resume(struct nvme_controller *ctrlr); + #endif /* __NVME_PRIVATE_H__ */