diff --git a/sys/dev/nvme/nvme.c b/sys/dev/nvme/nvme.c index eacd0cc49974..2e598e8066b7 100644 --- a/sys/dev/nvme/nvme.c +++ b/sys/dev/nvme/nvme.c @@ -157,30 +157,14 @@ nvme_shutdown(void) { device_t *devlist; struct nvme_controller *ctrlr; - union cc_register cc; - union csts_register csts; int dev, devcount; if (devclass_get_devices(nvme_devclass, &devlist, &devcount)) return; for (dev = 0; dev < devcount; dev++) { - /* - * Only notify controller of shutdown when a real shutdown is - * in process, not when a module unload occurs. It seems at - * least some controllers (Chatham at least) don't let you - * re-enable the controller after shutdown notification has - * been received. - */ ctrlr = DEVICE2SOFTC(devlist[dev]); - cc.raw = nvme_mmio_read_4(ctrlr, cc); - cc.bits.shn = NVME_SHN_NORMAL; - nvme_mmio_write_4(ctrlr, cc, cc.raw); - csts.raw = nvme_mmio_read_4(ctrlr, csts); - while (csts.bits.shst != NVME_SHST_COMPLETE) { - DELAY(5); - csts.raw = nvme_mmio_read_4(ctrlr, csts); - } + nvme_ctrlr_shutdown(ctrlr); } free(devlist, M_TEMP); diff --git a/sys/dev/nvme/nvme.h b/sys/dev/nvme/nvme.h index 9df75da046b7..f904933a20af 100644 --- a/sys/dev/nvme/nvme.h +++ b/sys/dev/nvme/nvme.h @@ -170,27 +170,30 @@ struct nvme_registers union cap_lo_register cap_lo; union cap_hi_register cap_hi; - uint32_t vs; /* version */ - uint32_t intms; /* interrupt mask set */ - uint32_t intmc; /* interrupt mask clear */ + uint32_t vs; /* version */ + uint32_t intms; /* interrupt mask set */ + uint32_t intmc; /* interrupt mask clear */ /** controller configuration */ union cc_register cc; - uint32_t reserved1; - uint32_t csts; /* controller status */ - uint32_t reserved2; + uint32_t reserved1; + + /** controller status */ + union csts_register csts; + + uint32_t reserved2; /** admin queue attributes */ union aqa_register aqa; - uint64_t asq; /* admin submission queue base addr */ - uint64_t acq; /* admin completion queue base addr */ - uint32_t reserved3[0x3f2]; + uint64_t asq; /* admin submission queue base addr */ + uint64_t acq; /* admin completion queue base addr */ + uint32_t reserved3[0x3f2]; struct { - uint32_t sq_tdbl; /* submission queue tail doorbell */ - uint32_t cq_hdbl; /* completion queue head doorbell */ + uint32_t sq_tdbl; /* submission queue tail doorbell */ + uint32_t cq_hdbl; /* completion queue head doorbell */ } doorbell[1] __packed; } __packed; diff --git a/sys/dev/nvme/nvme_ctrlr.c b/sys/dev/nvme/nvme_ctrlr.c index 1338f153225c..48457820540b 100644 --- a/sys/dev/nvme/nvme_ctrlr.c +++ b/sys/dev/nvme/nvme_ctrlr.c @@ -1117,6 +1117,21 @@ nvme_ctrlr_destruct(struct nvme_controller *ctrlr, device_t dev) { int i; + /* + * Notify the controller of a shutdown, even though this is due to + * a driver unload, not a system shutdown (this path is not invoked + * during shutdown). This ensures the controller receives a + * shutdown notification in case the system is shutdown before + * reloading the driver. + * + * Chatham does not let you re-enable the controller after shutdown + * notification has been received, so do not send it in this case. + * This is OK because Chatham does not depend on the shutdown + * notification anyways. + */ + if (pci_get_devid(ctrlr->dev) != CHATHAM_PCI_ID) + nvme_ctrlr_shutdown(ctrlr); + nvme_ctrlr_disable(ctrlr); taskqueue_free(ctrlr->taskqueue); @@ -1162,6 +1177,26 @@ nvme_ctrlr_destruct(struct nvme_controller *ctrlr, device_t dev) pci_release_msi(dev); } +void +nvme_ctrlr_shutdown(struct nvme_controller *ctrlr) +{ + union cc_register cc; + union csts_register csts; + int ticks = 0; + + cc.raw = nvme_mmio_read_4(ctrlr, cc); + cc.bits.shn = NVME_SHN_NORMAL; + nvme_mmio_write_4(ctrlr, cc, cc.raw); + csts.raw = nvme_mmio_read_4(ctrlr, csts); + while ((csts.bits.shst != NVME_SHST_COMPLETE) && (ticks++ < 5*hz)) { + pause("nvme shn", 1); + csts.raw = nvme_mmio_read_4(ctrlr, csts); + } + if (csts.bits.shst != NVME_SHST_COMPLETE) + nvme_printf(ctrlr, "did not complete shutdown within 5 seconds " + "of notification\n"); +} + void nvme_ctrlr_submit_admin_request(struct nvme_controller *ctrlr, struct nvme_request *req) diff --git a/sys/dev/nvme/nvme_private.h b/sys/dev/nvme/nvme_private.h index 10643f22178c..f0f44537976c 100644 --- a/sys/dev/nvme/nvme_private.h +++ b/sys/dev/nvme/nvme_private.h @@ -433,6 +433,7 @@ void nvme_completion_poll_cb(void *arg, const struct nvme_completion *cpl); int nvme_ctrlr_construct(struct nvme_controller *ctrlr, device_t dev); void nvme_ctrlr_destruct(struct nvme_controller *ctrlr, device_t dev); +void nvme_ctrlr_shutdown(struct nvme_controller *ctrlr); int nvme_ctrlr_hw_reset(struct nvme_controller *ctrlr); void nvme_ctrlr_reset(struct nvme_controller *ctrlr); /* ctrlr defined as void * to allow use with config_intrhook. */