git: e8a7efd92181 - main - nvme: set the controller Timestamp feature
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 04 Oct 2026 17:41:48 UTC
The branch main has been updated by seuros:
URL: https://cgit.FreeBSD.org/src/commit/?id=e8a7efd92181720e9d7a7ef50fe3a00294911027
commit e8a7efd92181720e9d7a7ef50fe3a00294911027
Author: Abdelkader Boudih <seuros@FreeBSD.org>
AuthorDate: 2026-10-04 17:41:17 +0000
Commit: Abdelkader Boudih <seuros@FreeBSD.org>
CommitDate: 2026-10-04 17:41:17 +0000
nvme: set the controller Timestamp feature
Controllers that report ONCS.TIMESTAMP keep a millisecond clock that the
host is expected to seed
Reviewed by: imp, adrian
Differential Revision: https://reviews.freebsd.org/D59997
---
sys/dev/nvme/nvme.h | 19 ++++++++++++++
sys/dev/nvme/nvme_ctrlr.c | 60 +++++++++++++++++++++++++++++++++++++++++++++
sys/dev/nvme/nvme_private.h | 3 +++
3 files changed, 82 insertions(+)
diff --git a/sys/dev/nvme/nvme.h b/sys/dev/nvme/nvme.h
index 5b6ee1026678..ae31170f5379 100644
--- a/sys/dev/nvme/nvme.h
+++ b/sys/dev/nvme/nvme.h
@@ -1175,6 +1175,25 @@ enum nvme_activate_action {
NVME_AA_ACTIVATE = 0x2,
};
+/* Timestamp - Data Structure for Set Features / Get Features */
+struct nvme_timestamp_data {
+ uint8_t tstmp[6]; /* milliseconds since 1970-01-01 UTC */
+ uint8_t tstmps; /* attributes, get features only */
+ uint8_t rsvd7;
+} __packed;
+
+_Static_assert(sizeof(struct nvme_timestamp_data) == 8,
+ "bad size for nvme_timestamp_data");
+
+/** Timestamp attributes */
+#define NVME_TIMESTAMP_TSTMPS_SYNCH_SHIFT (0)
+#define NVME_TIMESTAMP_TSTMPS_SYNCH_MASK (0x1)
+#define NVME_TIMESTAMP_TSTMPS_ORIGIN_SHIFT (1)
+#define NVME_TIMESTAMP_TSTMPS_ORIGIN_MASK (0x7)
+
+#define NVME_TIMESTAMP_ORIGIN_RESET (0x0)
+#define NVME_TIMESTAMP_ORIGIN_SET_FEATURES (0x1)
+
struct nvme_power_state {
/** Maximum Power */
uint16_t mp; /* Maximum Power */
diff --git a/sys/dev/nvme/nvme_ctrlr.c b/sys/dev/nvme/nvme_ctrlr.c
index ab8b0aa37a35..1197cf460c83 100644
--- a/sys/dev/nvme/nvme_ctrlr.c
+++ b/sys/dev/nvme/nvme_ctrlr.c
@@ -885,6 +885,54 @@ out:
free(data, M_NVME);
}
+/* XXX revisit the ordering if the clock ever comes up earlier. */
+static void
+nvme_ctrlr_set_timestamp(struct nvme_controller *ctrlr)
+{
+ struct nvme_completion_poll_status status;
+ struct nvme_timestamp_data *ts;
+ struct timespec now;
+ uint64_t ms;
+ int i;
+
+ ts = malloc(sizeof(*ts), M_NVME, M_WAITOK | M_ZERO);
+ getnanotime(&now);
+ ms = (uint64_t)now.tv_sec * 1000 + now.tv_nsec / 1000000;
+ for (i = 0; i < nitems(ts->tstmp); i++)
+ ts->tstmp[i] = (ms >> (i * 8)) & 0xff;
+
+ status.done = 0;
+ nvme_ctrlr_cmd_set_feature(ctrlr, NVME_FEAT_TIMESTAMP, 0, 0, 0, 0, 0,
+ ts, sizeof(*ts), nvme_completion_poll_cb, &status);
+ nvme_completion_poll(&status);
+ if (nvme_completion_is_error(&status.cpl) && bootverbose)
+ nvme_printf(ctrlr, "failed to set the timestamp\n");
+ free(ts, M_NVME);
+}
+
+static void
+nvme_ctrlr_timestamp_mountroot(void *arg)
+{
+ struct nvme_controller *ctrlr = arg;
+
+ if (ctrlr->is_failed || ctrlr->is_failed_admin || ctrlr->is_dying)
+ return;
+ nvme_ctrlr_set_timestamp(ctrlr);
+}
+
+static void
+nvme_ctrlr_configure_timestamp(struct nvme_controller *ctrlr)
+{
+ if (NVMEV(NVME_CTRLR_DATA_ONCS_TIMESTAMP, ctrlr->cdata.oncs) == 0)
+ return;
+
+ /* The mountroot handler registered at attach does the initial set. */
+ if (!root_mounted())
+ return;
+
+ nvme_ctrlr_set_timestamp(ctrlr);
+}
+
static void
nvme_ctrlr_configure_int_coalescing(struct nvme_controller *ctrlr)
{
@@ -1153,6 +1201,7 @@ nvme_ctrlr_start(void *ctrlr_arg, bool resetting)
nvme_ctrlr_configure_aer(ctrlr);
nvme_ctrlr_configure_apst(ctrlr);
nvme_ctrlr_configure_int_coalescing(ctrlr);
+ nvme_ctrlr_configure_timestamp(ctrlr);
for (i = 0; i < ctrlr->num_io_queues; i++)
nvme_io_qpair_enable(&ctrlr->ioq[i]);
@@ -1194,6 +1243,12 @@ nvme_ctrlr_start_config_hook(void *arg)
nvme_printf(ctrlr,
"ignoring Apple-internal namespaces above NSID 1\n");
+ if (!root_mounted() &&
+ NVMEV(NVME_CTRLR_DATA_ONCS_TIMESTAMP, ctrlr->cdata.oncs) != 0)
+ ctrlr->timestamp_tag = EVENTHANDLER_REGISTER(mountroot,
+ nvme_ctrlr_timestamp_mountroot, ctrlr,
+ EVENTHANDLER_PRI_ANY);
+
ctrlr->is_initialized = true;
child = device_add_child(ctrlr->dev, NULL, DEVICE_UNIT_ANY);
device_set_ivars(child, ctrlr);
@@ -1851,6 +1906,11 @@ nvme_ctrlr_destruct(struct nvme_controller *ctrlr, device_t dev)
ctrlr->is_dying = true;
+ if (ctrlr->timestamp_tag != NULL) {
+ EVENTHANDLER_DEREGISTER(mountroot, ctrlr->timestamp_tag);
+ ctrlr->timestamp_tag = NULL;
+ }
+
if (ctrlr->resource == NULL)
goto nores;
if (!mtx_initialized(&ctrlr->adminq.lock))
diff --git a/sys/dev/nvme/nvme_private.h b/sys/dev/nvme/nvme_private.h
index c7095bc34334..6f228cd1427b 100644
--- a/sys/dev/nvme/nvme_private.h
+++ b/sys/dev/nvme/nvme_private.h
@@ -33,6 +33,7 @@
#include <sys/bio.h>
#include <sys/bus.h>
#include <sys/counter.h>
+#include <sys/eventhandler.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
@@ -329,6 +330,8 @@ struct nvme_controller {
bool isr_warned;
bool is_initialized;
+ eventhandler_tag timestamp_tag;
+
/* Host Memory Buffer */
int hmb_nchunks;
size_t hmb_chunk;