Page MenuHomeFreeBSD

D32831.id98464.diff
No OneTemporary

D32831.id98464.diff

Index: sys/dev/nvme/nvme.h
===================================================================
--- sys/dev/nvme/nvme.h
+++ sys/dev/nvme/nvme.h
@@ -1394,23 +1394,25 @@
_Static_assert(sizeof(struct nvme_command_effects_page) == 4096,
"bad size for nvme_command_effects_page");
+struct nvme_device_self_test_result {
+ uint8_t status;
+ uint8_t segment_num;
+ uint8_t valid_diag_info;
+ uint8_t rsvd3;
+ uint64_t poh;
+ uint32_t nsid;
+ /* Define as an array to simplify alignment issues */
+ uint8_t failing_lba[8];
+ uint8_t status_code_type;
+ uint8_t status_code;
+ uint8_t vendor_specific[2];
+} __packed;
+
struct nvme_device_self_test_page {
- uint8_t curr_operation;
- uint8_t curr_compl;
- uint8_t rsvd2[2];
- struct {
- uint8_t status;
- uint8_t segment_num;
- uint8_t valid_diag_info;
- uint8_t rsvd3;
- uint64_t poh;
- uint32_t nsid;
- /* Define as an array to simplify alignment issues */
- uint8_t failing_lba[8];
- uint8_t status_code_type;
- uint8_t status_code;
- uint8_t vendor_specific[2];
- } __packed result[20];
+ uint8_t curr_operation;
+ uint8_t curr_compl;
+ uint8_t rsvd2[2];
+ struct nvme_device_self_test_result result[20];
} __packed __aligned(4);
_Static_assert(sizeof(struct nvme_device_self_test_page) == 564,
Index: usr.sbin/bhyve/pci_nvme.c
===================================================================
--- usr.sbin/bhyve/pci_nvme.c
+++ usr.sbin/bhyve/pci_nvme.c
@@ -62,6 +62,7 @@
#include <sys/errno.h>
#include <sys/types.h>
#include <net/ieee_oui.h>
+#include <sys/time.h>
#include <assert.h>
#include <pthread.h>
@@ -184,6 +185,33 @@
NVME_STOR_RAM = 1,
};
+#define SELF_TEST_SHORT_DURATION (60)
+#define SELF_TEST_EXTENDED_DURATION (300)
+
+#define SELF_TEST_IN_PROGRESS_SHORT(ops, start, now) \
+ (ops == 0x1 && (now.tv_sec - start.tv_sec) < SELF_TEST_SHORT_DURATION)
+#define SELF_TEST_IN_PROGRESS_EXTENDED(ops, start, now) \
+ (ops == 0x2 && (now.tv_sec - start.tv_sec) < SELF_TEST_EXTENDED_DURATION)
+#define SELF_TEST_IN_PROGRESS(ops, start, now) \
+ (SELF_TEST_IN_PROGRESS_SHORT(ops, start, now) || \
+ SELF_TEST_IN_PROGRESS_EXTENDED(ops, start, now))
+
+#define SELF_TEST_FINISH_SHORT(ops, start, now) \
+ (ops == 0x1 && (now.tv_sec - start.tv_sec) >= SELF_TEST_SHORT_DURATION)
+#define SELF_TEST_FINISH_EXTENDED(ops, start, now) \
+ (ops == 0x2 && (now.tv_sec - start.tv_sec) >= SELF_TEST_EXTENDED_DURATION)
+#define SELF_TEST_FINISH(ops, start, now) \
+ (SELF_TEST_FINISH_SHORT(ops, start, now) || \
+ SELF_TEST_FINISH_EXTENDED(ops, start, now))
+
+#define SELF_TEST_CACULATE_PROGRESS_SHORT(start, now) \
+ (((now.tv_sec - start.tv_sec) * 100 / SELF_TEST_SHORT_DURATION) & 0x7F)
+#define SELF_TEST_CACULATE_PROGRESS_EXTENDED(start, now) \
+ (((now.tv_sec - start.tv_sec) * 100 / SELF_TEST_EXTENDED_DURATION) & 0x7F)
+
+#define TIME_DIFF_IN_HOURS(start, now) \
+ ((now.tv_sec - start.tv_sec) / 3600)
+
struct pci_nvme_blockstore {
enum nvme_storage_type type;
void *ctx;
@@ -267,6 +295,7 @@
struct nvme_error_information_entry err_log;
struct nvme_health_information_page health_log;
struct nvme_firmware_page fw_log;
+ struct nvme_device_self_test_page self_log;
struct pci_nvme_blockstore nvstore;
@@ -301,6 +330,10 @@
uint32_t read_dunits_remainder;
uint32_t write_dunits_remainder;
+ struct timeval power_on_time;
+ struct timeval self_test_start_time;
+ struct nvme_device_self_test_result self_test_result;
+
STAILQ_HEAD(, pci_nvme_aer) aer_list;
uint32_t aer_count;
};
@@ -309,6 +342,7 @@
static struct pci_nvme_ioreq *pci_nvme_get_ioreq(struct pci_nvme_softc *);
static void pci_nvme_release_ioreq(struct pci_nvme_softc *, struct pci_nvme_ioreq *);
static void pci_nvme_io_done(struct blockif_req *, int);
+static void nvme_self_test_make_progress(struct pci_nvme_softc *);
/* Controller Configuration utils */
#define NVME_CC_GET_EN(cc) \
@@ -467,7 +501,8 @@
cd->ver = 0x00010300;
- cd->oacs = 1 << NVME_CTRLR_DATA_OACS_FORMAT_SHIFT;
+ cd->oacs = (1 << NVME_CTRLR_DATA_OACS_FORMAT_SHIFT) |
+ (1 << NVME_CTRLR_DATA_OACS_SELFTEST_SHIFT);
cd->acl = 2;
cd->aerl = 4;
@@ -503,6 +538,11 @@
cd->fna = 0x03;
cd->power_state[0].mp = 10;
+
+ /* miniutes to complete extended device self-test */
+ cd->edstt = SELF_TEST_EXTENDED_DURATION / 60;
+
+ gettimeofday(&sc->power_on_time, NULL);
}
/*
@@ -600,6 +640,7 @@
memset(&sc->err_log, 0, sizeof(sc->err_log));
memset(&sc->health_log, 0, sizeof(sc->health_log));
memset(&sc->fw_log, 0, sizeof(sc->fw_log));
+ memset(&sc->self_log, 0, sizeof(sc->self_log));
/* Set read/write remainder to round up according to spec */
sc->read_dunits_remainder = 999;
@@ -1113,6 +1154,13 @@
MIN(logsize, sizeof(sc->fw_log)),
NVME_COPY_TO_PRP);
break;
+ case NVME_LOG_DEVICE_SELF_TEST:
+ nvme_self_test_make_progress(sc);
+ nvme_prp_memcpy(sc->nsc_pi->pi_vmctx, command->prp1,
+ command->prp2, (uint8_t *)&sc->self_log,
+ MIN(logsize, sizeof(sc->self_log)),
+ NVME_COPY_TO_PRP);
+ break;
default:
DPRINTF("%s get log page %x command not supported",
__func__, logpage);
@@ -1442,6 +1490,95 @@
return (0);
}
+static void
+nvme_insert_self_test_log(struct nvme_device_self_test_result* result,
+ struct nvme_device_self_test_page* self_log)
+{
+ uint8_t i;
+
+ DPRINTF("%s", __func__);
+ /*
+ * Newest log entry always inserts to position 0.
+ * move old ones to next position.
+ * the last log entry will be abandoned
+ */
+ for (i = 19; i > 0; i--) {
+ memcpy((uint8_t*)&self_log->result[i], (uint8_t*)&self_log->result[i - 1],
+ sizeof(struct nvme_device_self_test_result));
+ }
+ memcpy(&self_log->result[0], result, sizeof(struct nvme_device_self_test_result));
+ memset(result, 0, sizeof(struct nvme_device_self_test_result));
+}
+
+static void
+nvme_self_test_make_progress(struct pci_nvme_softc* sc)
+{
+ struct timeval now;
+
+ gettimeofday(&now, NULL);
+ DPRINTF("%s, self test duration %ld", __func__, now.tv_sec - sc->self_test_start_time.tv_sec);
+
+ if (SELF_TEST_IN_PROGRESS_SHORT(sc->self_log.curr_operation, sc->self_test_start_time, now)) {
+ sc->self_log.curr_compl = SELF_TEST_CACULATE_PROGRESS_SHORT(sc->self_test_start_time, now);
+ } else if (SELF_TEST_IN_PROGRESS_EXTENDED(sc->self_log.curr_operation, sc->self_test_start_time, now)) {
+ sc->self_log.curr_compl = SELF_TEST_CACULATE_PROGRESS_EXTENDED(sc->self_test_start_time, now);
+ } else if (SELF_TEST_FINISH(sc->self_log.curr_operation, sc->self_test_start_time, now)) {
+ sc->self_log.curr_operation = 0;
+ sc->self_log.curr_compl = 0;
+ nvme_insert_self_test_log(&sc->self_test_result, &sc->self_log); /* insert log entry */
+ }
+}
+
+static int
+nvme_opc_device_self_test(struct pci_nvme_softc* sc, struct nvme_command* command,
+ struct nvme_completion* compl)
+{
+ uint32_t nsid = command->nsid;
+ uint8_t stc = command->cdw10 & 0xF;
+ struct timeval now;
+
+ DPRINTF("%s stc %d", __func__, stc);
+ if (nsid != 0 && nsid != 1 && nsid != 0xFFFFFFFF) {
+ pci_nvme_status_genc(&compl->status,
+ NVME_SC_INVALID_NAMESPACE_OR_FORMAT);
+ return (1);
+ }
+
+ gettimeofday(&now, NULL);
+
+ switch (stc) {
+ case 0x1: /* Start a short device self-test operation */
+ /* FALLTHROUGH */
+ case 0x2: /* Start an extended device self-test operation */
+ if (SELF_TEST_IN_PROGRESS(sc->self_log.curr_operation, sc->self_test_start_time, now)) {
+ pci_nvme_status_tc(&compl->status, NVME_SCT_COMMAND_SPECIFIC,
+ NVME_SC_SELF_TEST_IN_PROGRESS);
+ return (1);
+ }
+ nvme_self_test_make_progress(sc);
+ gettimeofday(&sc->self_test_start_time, NULL);
+ sc->self_log.curr_operation = stc;
+
+ sc->self_test_result.status = stc << 4;
+ sc->self_test_result.poh = TIME_DIFF_IN_HOURS(sc->power_on_time, now);
+ break;
+ case 0xF: /* Abort device self-test operation */
+ if (SELF_TEST_IN_PROGRESS(sc->self_log.curr_operation, sc->self_test_start_time, now)) {
+ sc->self_log.curr_operation = 0; /* clear current operation */
+ sc->self_test_result.status |= 0x1; /* aborted */
+ nvme_insert_self_test_log(&sc->self_test_result, &sc->self_log);
+ }
+ break;
+ default:
+ pci_nvme_status_genc(&compl->status, NVME_SC_INVALID_FIELD);
+ return (1);
+ break;
+ }
+ pci_nvme_status_genc(&compl->status, NVME_SC_SUCCESS);
+
+ return (0);
+}
+
static int
nvme_opc_format_nvm(struct pci_nvme_softc* sc, struct nvme_command* command,
struct nvme_completion* compl)
@@ -1617,6 +1754,10 @@
NVME_SCT_COMMAND_SPECIFIC,
NVME_SC_INVALID_FIRMWARE_SLOT);
break;
+ case NVME_OPC_DEVICE_SELF_TEST:
+ DPRINTF("%s command DEVICE_SELF_TEST", __func__);
+ nvme_opc_device_self_test(sc, cmd, &compl);
+ break;
case NVME_OPC_ASYNC_EVENT_REQUEST:
DPRINTF("%s command ASYNC_EVENT_REQ", __func__);
nvme_opc_async_event_req(sc, cmd, &compl);

File Metadata

Mime Type
text/plain
Expires
Mon, Dec 22, 3:02 AM (13 h, 49 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
27132539
Default Alt Text
D32831.id98464.diff (8 KB)

Event Timeline