summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/ic/nvme.c50
-rw-r--r--sys/include/dev/ic/nvmevar.h83
2 files changed, 133 insertions, 0 deletions
diff --git a/sys/dev/ic/nvme.c b/sys/dev/ic/nvme.c
index fe04caf..c65d7e0 100644
--- a/sys/dev/ic/nvme.c
+++ b/sys/dev/ic/nvme.c
@@ -309,6 +309,35 @@ nvme_poll_submit_cmd(struct nvme_queue *q, struct nvme_cmd cmd)
return 0;
}
+/*
+ * Get NVMe log page
+ *
+ * @ctrl: NVMe controller to target
+ * @buf: Data buffer
+ * @lid: Log identifier
+ * @len: Length (in bytes)
+ */
+static int
+nvme_get_logpage(struct nvme_ctrl *ctrl, void *buf, uint8_t lid, uint32_t len)
+{
+ struct nvme_cmd cmd = {0};
+ struct nvme_get_logpage_cmd *cmdp;
+
+ if (!is_4k_aligned(buf)) {
+ return -1;
+ }
+
+ cmdp = &cmd.get_logpage;
+ cmdp->opcode = NVME_OP_GET_LOGPAGE;
+ cmdp->nsid = 0xFFFFFFFF;
+ cmdp->lid = lid;
+ cmdp->numdl = len / 4;
+ cmdp->numdu = 0;
+ cmdp->prp1 = VIRT_TO_PHYS(buf);
+ cmdp->prp2 = 0;
+ return nvme_poll_submit_cmd(&ctrl->adminq, cmd);
+}
+
static int
nvme_identify(struct nvme_ctrl *ctrl, void *buf, uint32_t nsid, uint8_t cns)
{
@@ -549,6 +578,7 @@ nvme_init_ctrl(struct nvme_bar *bar)
uint16_t mqes;
uint8_t *nsids;
struct nvme_ctrl ctrl = { .bar = bar };
+ struct nvme_smart_data *smart;
struct nvme_queue *adminq;
struct nvme_id *id;
@@ -572,14 +602,21 @@ nvme_init_ctrl(struct nvme_bar *bar)
return error;
}
+ smart = dynalloc_memalign(sizeof(*smart), 0x1000);
+ if (smart == NULL) {
+ return -ENOMEM;
+ }
+
id = dynalloc_memalign(sizeof(*id), 0x1000);
if (id == NULL) {
+ dynfree(smart);
return -ENOMEM;
}
nsids = dynalloc_memalign(0x1000, 0x1000);
if (nsids == NULL) {
dynfree(id);
+ dynfree(smart);
return -ENOMEM;
}
@@ -587,6 +624,18 @@ nvme_init_ctrl(struct nvme_bar *bar)
nvme_log_ctrl_id(id);
nvme_identify(&ctrl, nsids, 0, ID_CNS_NSID_LIST);
+ /*
+ * Attempt to read some SMART data but don't bother
+ * if it fails in any way.
+ */
+ error = nvme_get_logpage(&ctrl, smart, NVME_LOGPAGE_SMART, sizeof(*smart));
+ if (error == 0) {
+ if (smart->temp != 0 && smart->temp > 283)
+ pr_trace("temp: %d K\n", smart->temp);
+
+ pr_trace("%d%% used\n", smart->percent_used);
+ }
+
ctrl.sqes = id->sqes >> 4;
ctrl.cqes = id->cqes >> 4;
@@ -613,6 +662,7 @@ nvme_init_ctrl(struct nvme_bar *bar)
dynfree(id);
dynfree(nsids);
+ dynfree(smart);
return 0;
}
diff --git a/sys/include/dev/ic/nvmevar.h b/sys/include/dev/ic/nvmevar.h
index eab8b52..3b06ad0 100644
--- a/sys/include/dev/ic/nvmevar.h
+++ b/sys/include/dev/ic/nvmevar.h
@@ -34,6 +34,7 @@
/* Admin commands */
#define NVME_OP_CREATE_IOSQ 0x01
+#define NVME_OP_GET_LOGPAGE 0x02
#define NVME_OP_CREATE_IOCQ 0x05
#define NVME_OP_IDENTIFY 0x06
@@ -45,6 +46,67 @@
#define NVME_OP_WRITE 0x01
#define NVME_OP_READ 0x02
+/* Log page identifiers */
+#define NVME_LOGPAGE_SMART 0x02
+
+/*
+ * S.M.A.R.T health / information log
+ *
+ * See section 5.16.1.3, figure 207 of the
+ * NVMe base spec (rev 2.0a)
+ *
+ * @cwarn: Critical warning
+ * @temp: Composite tempature (kelvin)
+ * @avail_spare: Available spare (in percentage)
+ * @avail_spare_thr: Available spare threshold
+ * @percent_used: Estimate NVMe life used percentage
+ * @end_cwarn: Endurance group critical warning summary
+ * @data_units_read: Number of 512 byte data units read
+ * @data_units_written: Number of 512 byte data units written
+ * @host_reads: Number of host read commands completed
+ * @host_writes: Number of host write commands completed
+ * @ctrl_busy_time: Controller busy time
+ * @power_cycles: Number of power cycles
+ * @power_on_hours: Number of power on hours
+ * @unsafe_shutdowns: Number of unsafe shutdowns
+ * @media_errors: Media and data integrity errors
+ * @n_errlog_entries: Number of error log info entries
+ * @warning_temp_time: Warning composite tempature time
+ * @critical_comp_time: Critical composite tempature time
+ * @temp_sensor: Tempature sensor <n> data
+ * @temp1_trans_cnt: Tempature 1 transition count
+ * @temp2_trans_cnt: Tempature 2 transition count
+ * @temp1_total_time: Total time for tempature 1
+ * @temp2_total_time: Total time for tempature 2
+ */
+struct nvme_smart_data {
+ uint8_t cwarn;
+ uint16_t temp;
+ uint8_t avail_spare;
+ uint8_t avail_spare_thr;
+ uint8_t percent_used;
+ uint8_t end_cwarn;
+ uint8_t reserved[25];
+ uint8_t data_units_read[16];
+ uint8_t data_units_written[16];
+ uint8_t host_reads[16];
+ uint8_t host_writes[16];
+ uint8_t ctrl_busy_time[16];
+ uint8_t power_cycles[16];
+ uint8_t power_on_hours[16];
+ uint8_t unsafe_shutdowns[16];
+ uint8_t media_errors[16];
+ uint8_t n_errlog_entries[16];
+ uint32_t warning_temp_time;
+ uint32_t critical_comp_time;
+ uint16_t temp_sensor[8];
+ uint32_t temp1_trans_cnt;
+ uint32_t temp2_trans_cnt;
+ uint32_t temp1_total_time;
+ uint32_t temp2_total_time;
+ uint8_t reserved1[280];
+};
+
struct nvme_identify_cmd {
uint8_t opcode;
uint8_t flags;
@@ -98,6 +160,26 @@ struct nvme_create_iosq_cmd {
uint64_t unused3[2];
};
+/* Get log page */
+struct nvme_get_logpage_cmd {
+ uint8_t opcode;
+ uint8_t flags;
+ uint16_t cid;
+ uint32_t nsid;
+ uint64_t unused[2];
+ uint64_t prp1;
+ uint64_t prp2;
+ uint8_t lid;
+ uint8_t lsp;
+ uint16_t numdl;
+ uint16_t numdu;
+ uint16_t lsi;
+ uint64_t lpo;
+ uint8_t unused1[3];
+ uint8_t csi;
+ uint32_t unused2;
+};
+
/* Read/write */
struct nvme_rw_cmd {
uint8_t opcode;
@@ -123,6 +205,7 @@ struct nvme_cmd {
struct nvme_identify_cmd identify;
struct nvme_create_iocq_cmd create_iocq;
struct nvme_create_iosq_cmd create_iosq;
+ struct nvme_get_logpage_cmd get_logpage;
struct nvme_rw_cmd rw;
};
};