git: e8a7efd92181 - main - nvme: set the controller Timestamp feature

From: Abdelkader Boudih <seuros_at_FreeBSD.org>
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;