Send a shutdown notification in the driver unload path, to ensure

notification gets sent in cases where system shuts down with driver
unloaded.

Sponsored by:	Intel
Reviewed by:	carl
MFC after:	3 days
This commit is contained in:
Jim Harris 2013-08-13 21:47:08 +00:00
parent 88d961f32e
commit 56183abc2b
4 changed files with 51 additions and 28 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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)

View File

@ -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. */