aboutsummaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authorWarner Losh <imp@FreeBSD.org>2024-04-08 16:54:12 +0000
committerWarner Losh <imp@FreeBSD.org>2024-05-11 18:09:51 +0000
commitc1fccf0f60cad746e8f88d971d0ec9f36d691ccc (patch)
tree3d98c9e0290b2baa8f721efebf6b57e9bbcc22a0 /sbin
parente84a75f936028bf3947f72f11eac4e25baf91b3a (diff)
downloadsrc-c1fccf0f60cad746e8f88d971d0ec9f36d691ccc.tar.gz
src-c1fccf0f60cad746e8f88d971d0ec9f36d691ccc.zip
nvmecontrol: Implement telemetry-log command.
This produces the same data as the Linux nvme-cli 'nvme telemetry-log' command. It extracts the telemetry log from drive. This is a variable length log, so we read the first page and find out how much of the log to grab. There's 3 levels of details available, and we grab the level of detail specified on the command line. Sponsored by: Netflix
Diffstat (limited to 'sbin')
-rw-r--r--sbin/nvmecontrol/Makefile1
-rw-r--r--sbin/nvmecontrol/nvmecontrol.818
-rw-r--r--sbin/nvmecontrol/telemetry.c182
3 files changed, 200 insertions, 1 deletions
diff --git a/sbin/nvmecontrol/Makefile b/sbin/nvmecontrol/Makefile
index 81674475ba1f..2abcd72dedfa 100644
--- a/sbin/nvmecontrol/Makefile
+++ b/sbin/nvmecontrol/Makefile
@@ -26,6 +26,7 @@ SRCS+= reset.c
SRCS+= resv.c
SRCS+= sanitize.c
SRCS+= selftest.c
+SRCS+= telemetry.c
CFLAGS+= -I${SRCTOP}/lib/libnvmf
MAN= nvmecontrol.8
LDFLAGS+= -rdynamic
diff --git a/sbin/nvmecontrol/nvmecontrol.8 b/sbin/nvmecontrol/nvmecontrol.8
index 6f7b45aac607..713fcf092d64 100644
--- a/sbin/nvmecontrol/nvmecontrol.8
+++ b/sbin/nvmecontrol/nvmecontrol.8
@@ -33,7 +33,7 @@
.\"
.\" Author: Jim Harris <jimharris@FreeBSD.org>
.\"
-.Dd May 3, 2024
+.Dd May 10, 2024
.Dt NVMECONTROL 8
.Os
.Sh NAME
@@ -243,6 +243,11 @@
.Op Fl Q Ar entries
.Aq Ar device-id
.Aq Ar address
+.Nm
+.Ic telemetry-log
+.Fl O Ar output-file
+.Op Fl d Ar data-area
+.Aq Ar device-id
.Sh DESCRIPTION
NVM Express (NVMe) is a storage protocol standard for SSDs and other
high-speed storage devices over PCI Express as well as remote storage
@@ -806,6 +811,17 @@ The flags have the same meaning for the new association as described above
for the
.Cm connect
command.
+.Ss telemetry-log
+Extract the telemetry log associated with
+.Ar device-id ,
+using the specified parameters:
+.Bl -tag -width 6n
+.It Fl O Ar output-file
+Output file for the data.
+This parameter is mandatory.
+.It Fl d Ar data-area
+The data area is either 1, 2 or 3.
+.El
.Sh DEVICE NAMES
Where
.Aq Ar namespace-id
diff --git a/sbin/nvmecontrol/telemetry.c b/sbin/nvmecontrol/telemetry.c
new file mode 100644
index 000000000000..df83382d50ed
--- /dev/null
+++ b/sbin/nvmecontrol/telemetry.c
@@ -0,0 +1,182 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (C) 2024 Netflix, Inc
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/ioccom.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <sys/endian.h>
+
+#include "nvmecontrol.h"
+
+/* Tables for command line parsing */
+
+static cmd_fn_t telemetry_log;
+
+#define NONE 0xffffffffu
+static struct options {
+ const char *outfn;
+ const char *dev;
+ uint8_t da;
+} opt = {
+ .outfn = NULL,
+ .dev = NULL,
+ .da = 3,
+};
+
+static const struct opts telemetry_log_opts[] = {
+#define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc }
+ OPT("output-file", 'O', arg_string, opt, outfn,
+ "output file for telemetry data"),
+ OPT("data-area", 'd', arg_uint8, opt, da,
+ "output file for telemetry data"),
+ { NULL, 0, arg_none, NULL, NULL }
+};
+#undef OPT
+
+static const struct args telemetry_log_args[] = {
+ { arg_string, &opt.dev, "<controller id|namespace id>" },
+ { arg_none, NULL, NULL },
+};
+
+static struct cmd telemetry_log_cmd = {
+ .name = "telemetry-log",
+ .fn = telemetry_log,
+ .descr = "Retrieves telemetry log pages from drive",
+ .ctx_size = sizeof(opt),
+ .opts = telemetry_log_opts,
+ .args = telemetry_log_args,
+};
+
+CMD_COMMAND(telemetry_log_cmd);
+
+/* End of tables for command line parsing */
+
+/*
+ * Note: Even though this is a logpage, it's variable size and tricky
+ * to get with some weird options, so it's its own command.
+ */
+
+static void
+telemetry_log(const struct cmd *f, int argc, char *argv[])
+{
+ int fd, fdout;
+ char *path;
+ uint32_t nsid;
+ uint64_t size;
+ uint64_t off;
+ uint32_t chunk;
+ struct nvme_controller_data cdata;
+ bool can_telemetry;
+ struct nvme_telemetry_log_page tlp, buf;
+
+ if (arg_parse(argc, argv, f))
+ return;
+ if (opt.da < 1 || opt.da > 3)
+ errx(EX_USAGE, "Data area %d is not in the range 1-3\n", opt.da);
+ if (opt.outfn == NULL)
+ errx(EX_USAGE, "No output file specified");
+
+ open_dev(opt.dev, &fd, 0, 1);
+ get_nsid(fd, &path, &nsid);
+ if (nsid == 0) {
+ nsid = NVME_GLOBAL_NAMESPACE_TAG;
+ } else {
+ close(fd);
+ open_dev(path, &fd, 0, 1);
+ }
+ free(path);
+
+ if (read_controller_data(fd, &cdata))
+ errx(EX_IOERR, "Identify request failed");
+
+ can_telemetry = NVMEV(NVME_CTRLR_DATA_LPA_TELEMETRY, cdata.lpa);
+ if (!can_telemetry)
+ errx(EX_UNAVAILABLE, "Drive does not support telemetry");
+ if (nsid != NVME_GLOBAL_NAMESPACE_TAG)
+ errx(EX_UNAVAILABLE, "Cannot operate on namespace");
+
+ fdout = open(opt.outfn, O_WRONLY | O_CREAT, 0664);
+ if (fdout == -1)
+ err(EX_IOERR, "Can't create %s", opt.outfn);
+
+ /* Read the log page */
+ size = sizeof(tlp);
+ off = 0;
+ read_logpage(fd, NVME_LOG_TELEMETRY_HOST_INITIATED, nsid, 0, 0, 0,
+ off, 0, 0, 0, &tlp, size);
+ switch(opt.da) {
+ case 1:
+ size = letoh(tlp.da1_last);
+ break;
+ case 2:
+ size = letoh(tlp.da2_last);
+ break;
+ case 3:
+ size = letoh(tlp.da3_last);
+ break;
+ default:
+ errx(EX_USAGE, "Impossible data area %d", opt.da);
+ }
+ size = (size + 1) * 512; /* The count of additional pages */
+ chunk = 4096;
+
+ printf("Extracting %llu bytes\n", (unsigned long long)size);
+ do {
+ if (chunk > size)
+ chunk = size;
+ read_logpage(fd, NVME_LOG_TELEMETRY_HOST_INITIATED, nsid, 0, 0, 0,
+ off, 0, 0, 0, &buf, chunk);
+ if (off == 0) {
+ /*
+ * Sanity check to make sure that the generation number
+ * didn't change between the two reads.
+ */
+ if (tlp.hi_gen != buf.hi_gen)
+ warnx(
+ "Generation number changed from %d to %d",
+ tlp.hi_gen, buf.hi_gen);
+ }
+ if (write(fdout, &buf, chunk) != chunk)
+ err(EX_IOERR, "Error writing %s", opt.outfn);
+ off += chunk;
+ size -= chunk;
+ } while (size > 0);
+
+ close(fdout);
+ close(fd);
+ exit(0);
+}