Index: usr.sbin/bhyve/pci_nvme.c =================================================================== --- usr.sbin/bhyve/pci_nvme.c +++ usr.sbin/bhyve/pci_nvme.c @@ -62,6 +62,7 @@ #include #include #include +#include #include #include @@ -129,6 +130,9 @@ #define NVME_DOORBELL_OFFSET offsetof(struct nvme_registers, doorbell) +#define TIMEVAL_DIFF_MILISECONDS(now, from) \ + ((now.tv_sec - from.tv_sec) * 1000 + (now.tv_usec - from.tv_usec) / 1000) + enum nvme_controller_register_offsets { NVME_CR_CAP_LOW = 0x00, NVME_CR_CAP_HI = 0x04, @@ -159,6 +163,8 @@ #define NVME_CQ_INTEN 0x01 #define NVME_CQ_INTCOAL 0x02 +#define NVME_FEAT_TIMESTAMP_TIMESTAMP_ORIGIN_SHIFT (1) + struct nvme_completion_queue { struct nvme_completion *qbase; pthread_mutex_t mtx; @@ -248,6 +254,12 @@ bool namespace_specific; }; +struct nvme_feature_data { + /* timestamp */ + uint8_t timestamp_origin; + struct timeval timestamp; +}; + #define NVME_FID_MAX (NVME_FEAT_ENDURANCE_GROUP_EVENT_CONFIGURATION + 1) struct pci_nvme_aer { @@ -290,6 +302,7 @@ struct nvme_submission_queue *submit_queues; struct nvme_feature_obj feat[NVME_FID_MAX]; + struct nvme_feature_data feat_data; enum nvme_dsm_type dataset_management; @@ -303,6 +316,8 @@ STAILQ_HEAD(, pci_nvme_aer) aer_list; uint32_t aer_count; + + struct timeval power_on_time; }; @@ -359,6 +374,14 @@ struct nvme_feature_obj *, struct nvme_command *, struct nvme_completion *); +static void nvme_feature_timestamp_get(struct pci_nvme_softc *, + struct nvme_feature_obj *, + struct nvme_command *, + struct nvme_completion *); +static void nvme_feature_timestamp_set(struct pci_nvme_softc *, + struct nvme_feature_obj *, + struct nvme_command *, + struct nvme_completion *); static __inline void cpywithpad(char *dst, size_t dst_size, const char *src, char pad) @@ -487,7 +510,8 @@ (4 << NVME_CTRLR_DATA_CQES_MIN_SHIFT); cd->nn = 1; /* number of namespaces */ - cd->oncs = 0; + cd->oncs = 1 << NVME_CTRLR_DATA_ONCS_TIMESTAMP_SHIFT; + switch (sc->dataset_management) { case NVME_DATASET_MANAGEMENT_AUTO: if (sc->nvstore.deallocate) @@ -503,6 +527,8 @@ cd->fna = 0x03; cd->power_state[0].mp = 10; + + gettimeofday(&sc->power_on_time, NULL); } /* @@ -627,6 +653,10 @@ nvme_feature_invalid_cb; sc->feat[NVME_FEAT_PREDICTABLE_LATENCY_MODE_WINDOW].get = nvme_feature_invalid_cb; + sc->feat[NVME_FEAT_TIMESTAMP].set = + nvme_feature_timestamp_set; + sc->feat[NVME_FEAT_TIMESTAMP].get = + nvme_feature_timestamp_get; } static void @@ -1376,6 +1406,55 @@ sc->num_q_is_set = true; } +static void +nvme_feature_timestamp_set(struct pci_nvme_softc *sc, + struct nvme_feature_obj *feat, + struct nvme_command *command, + struct nvme_completion *compl) +{ + uint64_t miliseconds; + + nvme_prp_memcpy(sc->nsc_pi->pi_vmctx, command->prp1, + command->prp2, (uint8_t*)&miliseconds, + sizeof(miliseconds), NVME_COPY_FROM_PRP); + + DPRINTF("%s: miliseconds: %lu", __func__, miliseconds); + miliseconds &= 0xFFFFFFFFFFFF; + + /* Set by Timestamp command */ + sc->feat_data.timestamp_origin = 1; + sc->feat_data.timestamp.tv_sec = miliseconds / 1000; + sc->feat_data.timestamp.tv_usec = (miliseconds % 1000) * 1000; +} + +static void +nvme_feature_timestamp_get(struct pci_nvme_softc *sc, + struct nvme_feature_obj *feat, + struct nvme_command *command, + struct nvme_completion *compl) +{ + uint8_t timestamp[8]; + uint64_t elapsed; + struct timeval now; + + gettimeofday(&now, NULL); + if (sc->feat_data.timestamp_origin == 0) + elapsed = TIMEVAL_DIFF_MILISECONDS(now, sc->power_on_time); + else + elapsed = TIMEVAL_DIFF_MILISECONDS(now, sc->feat_data.timestamp); + + DPRINTF("%s: elapsed: %lu", __func__, elapsed); + + memcpy(timestamp, (uint8_t*)&elapsed, sizeof(elapsed)); + timestamp[7] = 0; + timestamp[6] = sc->feat_data.timestamp_origin << + NVME_FEAT_TIMESTAMP_TIMESTAMP_ORIGIN_SHIFT; + + nvme_prp_memcpy(sc->nsc_pi->pi_vmctx, command->prp1, + command->prp2, timestamp, + sizeof(timestamp), NVME_COPY_TO_PRP); +} + static int nvme_opc_set_features(struct pci_nvme_softc *sc, struct nvme_command *command, struct nvme_completion *compl)