Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F140174606
D32831.id98464.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
8 KB
Referenced Files
None
Subscribers
None
D32831.id98464.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D32831: bhyve: implement SELF-TEST feature for NVMe controller
Attached
Detach File
Event Timeline
Log In to Comment