Download raw body.
nvme: use RTD3E hint in nvme_shutdown
nvme(4) currently has an arbitrary 4s timeout for an nvme controller to
finish processing a Shutdown command. The NVMe spec provides for
controllers to advertise a recommended minimum timeout (RTD3E) to wait,
so we can make this more intelligent.
This diff adds support for reading RTD3E out of the Identify response
data structure. It then clamps it between our existing default of 4s and
60s (chosen from looking at how Linux does it).
(Note: If the controller doesn't report an RTD3E, it should read as 0
per Figure 251 in NVMe spec 1.4, so we'll fallback to 4s. The area in
the Identify data was originally reserved and this diff fills in that
missing 16 byte gap in nvmereg.h.)
It just so happens the Samsung NVMe in this X1 Carbon of mine advertises
10s(!!) for RTD3E, so our 4s is definitely on the short end.
ok?
diffstat refs/heads/master refs/heads/nvme-power-states
M sys/dev/ic/nvme.c | 16+ 4-
M sys/dev/ic/nvmereg.h | 6+ 1-
2 files changed, 22 insertions(+), 5 deletions(-)
diff refs/heads/master refs/heads/nvme-power-states
commit - d2504b08cd8cde859ded65a3f88dbbb85545b86e
commit + 7802f75ac16de25fda682d084af6b1309ecf6b0b
blob - 62299a4de6268fdedb94e7cf8d6aae0da7035c18
blob + 460d43144083eb5814f1aa726eab20a25973f369
--- sys/dev/ic/nvme.c
+++ sys/dev/ic/nvme.c
@@ -160,6 +160,8 @@ static const struct nvme_ops nvme_ops = {
#define NVME_TIMO_IDENT 10000 /* ms to probe/identify */
#define NVME_TIMO_DELAYNS 10 /* ns to delay() in poll loop */
+#define SECS_TO_MICROS(s) (s * 1000 * 1000)
+
/*
* Some controllers, at least Apple NVMe, always require split
* transfers, so don't use bus_space_{read,write}_8() on LP64.
@@ -522,8 +524,8 @@ nvme_scsi_probe(struct scsi_link *link)
int
nvme_shutdown(struct nvme_softc *sc)
{
- u_int32_t cc, csts;
- int i;
+ u_int32_t cc, csts, rtd3e;
+ int64_t us = SECS_TO_MICROS(4);
nvme_write4(sc, NVME_INTMC, 0);
@@ -537,7 +539,16 @@ nvme_shutdown(struct nvme_softc *sc)
SET(cc, NVME_CC_SHN(NVME_CC_SHN_NORMAL));
nvme_write4(sc, NVME_CC, cc);
- for (i = 0; i < 4000; i++) {
+ if (sc->sc_identify.rtd3_entry) {
+ /*
+ * Controller advertises a D3 entry latency. Clamp it to a
+ * range from our default timeout to one minute.
+ */
+ rtd3e = lemtoh32(&sc->sc_identify.rtd3_entry);
+ us = MIN(MAX(us, rtd3e), SECS_TO_MICROS(60));
+ }
+
+ do {
nvme_barrier(sc, 0, sc->sc_ios,
BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
csts = nvme_read4(sc, NVME_CSTS);
@@ -545,7 +556,8 @@ nvme_shutdown(struct nvme_softc *sc)
return (0);
delay(1000);
- }
+ us -= 1000;
+ } while (us >= 0);
printf("%s: unable to shutdown, disabling\n", DEVNAME(sc));
blob - 2a28c6af83ef74fce326515381d774767fd3152d
blob + b29b37e6159099860beb0f85af9c4f408cd2bc3d
--- sys/dev/ic/nvmereg.h
+++ sys/dev/ic/nvmereg.h
@@ -290,8 +290,13 @@ struct nvm_identify_controller {
Namespace Sharing Capabilities */
u_int8_t mdts; /* Maximum Data Transfer Size */
u_int16_t cntlid; /* Controller ID */
+ u_int32_t version; /* NVMe Specification Version */
- u_int8_t _reserved1[16];
+ u_int32_t rtd3_resume; /* D3 Resume Latency (microseconds) */
+ u_int32_t rtd3_entry; /* D3 Entry Latency (microseconds) */
+
+ u_int32_t oaes; /* Optional Async. Events Supported */
+
u_int32_t ctratt;
#define NVM_ID_CTRL_CTRATT_FMT "\020" \
"\016DELEG" "\017DEVNVM" "\020ELBAS" "\005ENDURGRPS" \
nvme: use RTD3E hint in nvme_shutdown