The diff below enables smartmontools (smartctl & smartd) support for NVMe disks on -current kernels with the latest nvme(4) passthrough commits.
Obvious features tested and work for me on various amd64 boxes and my m1 macmini. ian@ has also succesfully tested it. Thoughts? OK? -- .... Ken Index: Makefile =================================================================== RCS file: /cvs/ports/sysutils/smartmontools/Makefile,v diff -u -p -u -p -r1.50 Makefile --- Makefile 27 Sep 2023 17:16:34 -0000 1.50 +++ Makefile 26 May 2024 14:54:25 -0000 @@ -2,6 +2,7 @@ COMMENT= control and monitor storage sy # XXX at update time check whether C++11 is actually needed DISTNAME= smartmontools-7.4 +REVISION= 0 CATEGORIES= sysutils HOMEPAGE= https://www.smartmontools.org/ Index: patches/patch-openbsd_nvme_ioctl_h =================================================================== RCS file: patches/patch-openbsd_nvme_ioctl_h diff -N patches/patch-openbsd_nvme_ioctl_h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-openbsd_nvme_ioctl_h 26 May 2024 14:54:25 -0000 @@ -0,0 +1,78 @@ +Index: openbsd_nvme_ioctl.h +--- openbsd_nvme_ioctl.h.orig ++++ openbsd_nvme_ioctl.h +@@ -0,0 +1,74 @@ ++/* ++ * Copyright (c) 2024 Kenneth R Westerback <k...@openbsd.org> ++ * ++ * Permission to use, copy, modify, and distribute this software for any ++ * purpose with or without fee is hereby granted, provided that the above ++ * copyright notice and this permission notice appear in all copies. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF ++ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ++ */ ++ ++#define NVME_PASSTHROUGH_CMD _IOWR('n', 0, struct nvme_pt_cmd) ++ ++struct nvme_pt_status { ++ int ps_dv_unit; ++ int ps_nsid; ++ int ps_flags; ++#define NVME_CQE_SCT(_f) ((_f) & (0x07 << 9)) ++#define NVME_CQE_SCT_GENERIC (0x00 << 9) ++#define NVME_CQE_SC(_f) ((_f) & (0xff << 1)) ++#define NVME_CQE_SC_SUCCESS (0x00 << 1) ++ uint32_t ps_csts; ++ uint32_t ps_cc; ++}; ++ ++#define BIO_MSG_COUNT 5 ++#define BIO_MSG_LEN 128 ++ ++struct bio_msg { ++ int bm_type; ++ char bm_msg[BIO_MSG_LEN]; ++}; ++ ++struct bio_status { ++ char bs_controller[16]; ++ int bs_status; ++ int bs_msg_count; ++ struct bio_msg bs_msgs[BIO_MSG_COUNT]; ++}; ++ ++struct bio { ++ void *bio_cookie; ++ struct bio_status bio_status; ++}; ++ ++struct nvme_pt_cmd { ++ /* Commands may arrive via /dev/bio. */ ++ struct bio pt_bio; ++ ++ /* The sqe fields that the caller may specify. */ ++ uint8_t pt_opcode; ++ uint32_t pt_nsid; ++ uint32_t pt_cdw10; ++ uint32_t pt_cdw11; ++ uint32_t pt_cdw12; ++ uint32_t pt_cdw13; ++ uint32_t pt_cdw14; ++ uint32_t pt_cdw15; ++ ++ caddr_t pt_status; ++ uint32_t pt_statuslen; ++ ++ caddr_t pt_databuf; /* User space address. */ ++ uint32_t pt_databuflen; /* Length of buffer. */ ++}; ++ ++#define nvme_completion_is_error(_flags) \ ++ ((NVME_CQE_SC(_flags) != NVME_CQE_SC_SUCCESS) \ ++ || (NVME_CQE_SCT(_flags) != NVME_CQE_SCT_GENERIC)) Index: patches/patch-os_openbsd_cpp =================================================================== RCS file: patches/patch-os_openbsd_cpp diff -N patches/patch-os_openbsd_cpp --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-os_openbsd_cpp 26 May 2024 14:54:25 -0000 @@ -0,0 +1,196 @@ +Index: os_openbsd.cpp +--- os_openbsd.cpp.orig ++++ os_openbsd.cpp +@@ -14,6 +14,7 @@ + + #include "atacmds.h" + #include "scsicmds.h" ++#include "nvmecmds.h" + #include "utility.h" + #include "os_openbsd.h" + +@@ -22,11 +23,29 @@ + #include <sys/stat.h> + #include <util.h> + ++// based on OpenBSD "/usr/include/dev/ic/nvmeio.h" && "/usr/include/dev/biovar.h" ++#include "openbsd_nvme_ioctl.h" // NVME_PASSTHROUGH_CMD, nvme_completion_is_error + const char * os_openbsd_cpp_cvsid = "$Id: os_openbsd.cpp 5393 2022-05-29 05:08:10Z dpgilbert $" + OS_OPENBSD_H_CVSID; + + #define ARGUSED(x) ((void)(x)) + ++bool sd_is_nvme(const char *dev) ++{ ++ struct nvme_pt_cmd pt; ++ memset(&pt, 0, sizeof(pt)); ++ pt.pt_opcode = smartmontools::nvme_admin_identify; ++ ++ int fd = ::open(dev, O_RDWR); ++ if (fd == -1) ++ return false; ++ ++ int status = ioctl(fd, NVME_PASSTHROUGH_CMD, &pt); ++ close(fd); ++ ++ return status != -1 || errno != ENOTTY; ++} ++ + ///////////////////////////////////////////////////////////////////////////// + + namespace os_openbsd { // No need to publish anything, name provided for Doxygen +@@ -209,6 +228,80 @@ bool openbsd_ata_device::ata_pass_through(const ata_cm + } + + ///////////////////////////////////////////////////////////////////////////// ++/// NVMe support ++ ++class openbsd_nvme_device ++: public /*implements*/ nvme_device, ++ public /*extends*/ openbsd_smart_device ++{ ++public: ++ openbsd_nvme_device(smart_interface * intf, const char * dev_name, ++ const char * req_type, unsigned nsid); ++ ++ virtual bool open() override; ++ ++ virtual bool nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) override; ++}; ++ ++openbsd_nvme_device::openbsd_nvme_device(smart_interface * intf, const char * dev_name, ++ const char * req_type, unsigned nsid) ++: smart_device(intf, dev_name, "nvme", req_type), ++ nvme_device(nsid), ++ openbsd_smart_device() ++{ ++} ++ ++bool openbsd_nvme_device::open() ++{ ++ const char *dev = get_dev_name(); ++ int fd; ++ ++ if ((fd = ::open(dev, O_RDWR)) == -1) { ++ set_err(errno, "can't open sd device"); ++ return false; ++ } ++ ++ set_fd(fd); ++ ++ return true; ++} ++ ++bool openbsd_nvme_device::nvme_pass_through(const nvme_cmd_in & in, nvme_cmd_out & out) ++{ ++ struct nvme_pt_cmd pt; ++ struct nvme_pt_status ps; ++ ++ memset(&ps, 0, sizeof(ps)); ++ memset(&pt.pt_bio, 0, sizeof(pt.pt_bio)); ++ ++ pt.pt_opcode = in.opcode; ++ pt.pt_nsid = in.nsid; ++ pt.pt_databuf = (caddr_t)in.buffer; ++ pt.pt_databuflen = in.size; ++ pt.pt_cdw10 = in.cdw10; ++ pt.pt_cdw11 = in.cdw11; ++ pt.pt_cdw12 = in.cdw12; ++ pt.pt_cdw13 = in.cdw13; ++ pt.pt_cdw14 = in.cdw14; ++ pt.pt_cdw15 = in.cdw15; ++ pt.pt_status = (char *)&ps; ++ pt.pt_statuslen = sizeof(ps); ++ ++ int status = ioctl(get_fd(), NVME_PASSTHROUGH_CMD, &pt); ++ ++ if (status == -1) ++ return set_err(errno, "NVME_PASSTHROUGH_CMD: %s", strerror(errno)); ++ ++ out.result = 0; // cqe.cdw0 (Command specific result) is not provided ++ ++ if (nvme_completion_is_error(ps.ps_flags)) ++ return set_nvme_err(out, nvme_completion_is_error(ps.ps_flags)); ++ ++ return true; ++} ++ ++ ++///////////////////////////////////////////////////////////////////////////// + /// Standard SCSI support + + class openbsd_scsi_device +@@ -381,6 +474,9 @@ class openbsd_smart_interface (protected) + + virtual scsi_device * get_scsi_device(const char * name, const char * type) override; + ++ virtual nvme_device * get_nvme_device(const char * name, const char * type, ++ unsigned nsid) override; ++ + virtual smart_device * autodetect_smart_device(const char * name) override; + + virtual smart_device * get_custom_smart_device(const char * name, const char * type) override; +@@ -434,6 +530,11 @@ scsi_device * openbsd_smart_interface::get_scsi_device + return new openbsd_scsi_device(this, name, type); + } + ++nvme_device * openbsd_smart_interface::get_nvme_device(const char * name, const char * type, unsigned nsid) ++{ ++ return new openbsd_nvme_device(this, name, type, nsid); ++} ++ + int openbsd_smart_interface::get_dev_names(char ***names, const char *prefix) + { + char *disknames, *p, **mp; +@@ -504,6 +605,7 @@ bool openbsd_smart_interface::scan_smart_devices(smart + + bool scan_ata = !*type || !strcmp(type, "ata"); + bool scan_scsi = !*type || !strcmp(type, "scsi") || !strcmp(type, "sat"); ++ bool scan_nvme = !*type || !strcmp(type, "nvme"); + + // Make namelists + char * * atanames = 0; int numata = 0; +@@ -517,7 +619,7 @@ bool openbsd_smart_interface::scan_smart_devices(smart + + char * * scsinames = 0; int numscsi = 0; + char * * scsitapenames = 0; int numscsitape = 0; +- if (scan_scsi) { ++ if (scan_scsi || scan_nvme) { + numscsi = get_dev_names(&scsinames, net_dev_scsi_disk); + if (numscsi < 0) { + set_err(ENOMEM); +@@ -541,9 +643,17 @@ bool openbsd_smart_interface::scan_smart_devices(smart + if(numata) free(atanames); + + for (i = 0; i < numscsi; i++) { +- scsi_device * scsidev = new openbsd_scsi_device(this, scsinames[i], type, true /*scanning*/); +- if (scsidev) +- devlist.push_back(scsidev); ++ if (sd_is_nvme(scsinames[i])) { ++ if (scan_nvme) { ++ nvme_device * nvmedev = new openbsd_nvme_device(this, scsinames[i], type, true /*scanning*/); ++ if (nvmedev) ++ devlist.push_back(nvmedev); ++ } ++ } else if (scan_scsi) { ++ scsi_device * scsidev = new openbsd_scsi_device(this, scsinames[i], type, true /*scanning*/); ++ if (scsidev) ++ devlist.push_back(scsidev); ++ } + free(scsinames[i]); + } + if(numscsi) free(scsinames); +@@ -588,8 +698,11 @@ smart_device * openbsd_smart_interface::autodetect_sma + // XXX get USB vendor ID, product ID and version from sd(4)/umass(4). + // XXX check sat device via get_usb_dev_type_by_id(). + +- // No USB bridge found, assume regular SCSI or SAT device +- return get_scsi_device(name, ""); ++ // No USB bridge found, decide if it's NVME or regular SCSI or SAT device ++ if (sd_is_nvme(name)) ++ return get_nvme_device(name, "nvme", 0); ++ else ++ return get_scsi_device(name, ""); + } + if (!strncmp(net_dev_scsi_tape, test_name, strlen(net_dev_scsi_tape))) + return get_scsi_device(name, "scsi"); Index: patches/patch-smartctl_8_in =================================================================== RCS file: patches/patch-smartctl_8_in diff -N patches/patch-smartctl_8_in --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-smartctl_8_in 26 May 2024 14:54:25 -0000 @@ -0,0 +1,170 @@ +Index: smartctl.8.in +--- smartctl.8.in.orig ++++ smartctl.8.in +@@ -233,11 +233,11 @@ in the smartmontools database (see \*(Aq\-v\*(Aq optio + drive model family may also be printed. + If \*(Aq\-n\*(Aq (see below) is specified, the power mode of the drive is + printed. +-.\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %IF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .Sp + [NVMe] For NVMe devices the information is obtained from the Identify + Controller and the Identify Namespace data structure. +-.\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %ENDIF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .TP + .B \-\-identify[=[w][nvb]] + [ATA only] Prints an annotated table of the IDENTIFY DEVICE data. +@@ -266,12 +266,12 @@ the SMART options which require support for 48-bit ATA + For SCSI, this is equivalent to + .br + \*(Aq\-H \-i \-A \-l error \-l selftest\*(Aq. +-.\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %IF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .Sp + For NVMe, this is equivalent to + .br + \*(Aq\-H \-i \-c \-A \-l error \-l selftest\*(Aq. +-.\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %ENDIF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .TP + .B \-x, \-\-xall + Prints all SMART and non-SMART information about the device. +@@ -290,12 +290,12 @@ For SCSI disks, this is equivalent to + \-l defects \-l envrep \-l genstats \-l ssd \-l zdevstat\*(Aq + .br + and for SCSI tape drives and changers, add \*(Aq\-l tapedevstat\*(Aq. +-.\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %IF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .Sp + For NVMe, this is equivalent to + .br + \*(Aq\-H \-i \-c \-A \-l error \-l selftest\*(Aq. +-.\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %ENDIF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .TP + .B \-\-scan + Scans for devices and prints each device name, device type and protocol +@@ -1184,11 +1184,11 @@ Prefailure SMART Attribute value is less than or equal + [SCSI tape drive or changer] The TapeAlert status is obtained by reading the + TapeAlert log page, but only if this option is given twice (see + \fBTAPE DRIVES\fP for the rationale). +-.\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %IF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .Sp + [NVMe] NVMe status is obtained by reading the "Critical Warning" byte from + the SMART/Health Information log. +-.\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %ENDIF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .TP + .B \-c, \-\-capabilities + [ATA] Prints only the generic SMART capabilities. These +@@ -1197,11 +1197,11 @@ respond to some of the different SMART commands. For + shows if the device logs errors, if it supports offline surface + scanning, and so on. If the device can carry out self-tests, this + option also shows the estimated time required to run those tests. +-.\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %IF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .Sp + [NVMe] Prints various NVMe device capabilities obtained from the Identify + Controller and the Identify Namespace data structure. +-.\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %ENDIF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .TP + .B \-A, \-\-attributes + [ATA] Prints only the vendor specific SMART Attributes. The Attributes +@@ -1298,11 +1298,11 @@ and start-stop cycle counter log pages. + Certain vendor specific attributes are listed if recognised. + The attributes are output in a relatively free format (compared with ATA + disk attributes). +-.\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %IF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .Sp + [NVMe] For NVMe devices the attributes are obtained from the SMART/Health + Information log. +-.\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %ENDIF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .TP + .B \-f FORMAT, \-\-format=FORMAT + [ATA only] Selects the output format of the attributes: +@@ -1407,7 +1407,7 @@ receives a command which is not implemented or is not + \- [SCSI] prints the error counter log pages for reads, write and verifies. + The verify row is only output if it has an element other than zero. + .Sp +-.\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %IF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .I error[,NUM] + \- [NVMe] prints the NVMe Error Information log. + Only the 16 most recent log entries are printed by default. +@@ -1419,7 +1419,7 @@ Note that the contents of this log is not preserved ac + controller resets, but the value of \*(AqError Information Log Entries\*(Aq + from SMART/Health Information log is. + .Sp +-.\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %ENDIF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .I xerror[,NUM][,error] + \- [ATA only] prints the Extended Comprehensive SMART error log + (General Purpose Log address 0x03). Unlike the Summary SMART error +@@ -1472,12 +1472,12 @@ If provided, the SCSI Sense Key (SK), Additional Sense + Additional Sense Code Qualifier (ASCQ) are also printed. The self tests + can be run using the \*(Aq\-t\*(Aq option described below (using the ATA + test terminology). +-.\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %IF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .Sp + .I selftest + \- [NVMe: NEW EXPERIMENTAL SMARTCTL 7.4 FEATURE] + prints the NVMe self-test log. +-.\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %ENDIF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .Sp + .I xselftest[,NUM][,selftest] + \- [ATA only] prints the Extended SMART self-test log (General Purpose +@@ -1663,7 +1663,7 @@ This command: + writes a binary representation of the one sector log 0x11 + (SATA Phy Event Counters) to file log.bin. + .Sp +-.\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %IF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .I nvmelog,PAGE,SIZE + \- [NVMe only] prints a hex dump of the first SIZE bytes from the NVMe + log with identifier PAGE. +@@ -1672,7 +1672,7 @@ SIZE is a hexadecimal number in the range from 0x4 to + \fBWARNING: Do not specify the identifier of an unknown log page. + Reading a log page may have undesirable side effects.\fP + .Sp +-.\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %ENDIF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .I ssd + \- [ATA] prints the Solid State Device Statistics log page. + This has the same effect as \*(Aq\-l devstat,7\*(Aq, see above. +@@ -2130,12 +2130,12 @@ with other disks use the \*(Aq\-c\*(Aq option to monit + .Sp + .I short + \- [SCSI] runs the "Background short" self-test. +-.\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %IF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .Sp + .I short + \- [NVMe: NEW EXPERIMENTAL SMARTCTL 7.4 FEATURE] + runs the "Short" self-test for current namespace. +-.\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %ENDIF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .Sp + .I long + \- [ATA] runs SMART Extended Self Test (tens of minutes to several hours). +@@ -2146,12 +2146,12 @@ below). + .Sp + .I long + \- [SCSI] runs the "Background long" self-test. +-.\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %IF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .Sp + .I long + \- [NVMe: NEW EXPERIMENTAL SMARTCTL 7.4 FEATURE] + runs the "Extended" self-test for current namespace. +-.\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %ENDIF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .Sp + .I conveyance + \- [ATA only] runs a SMART Conveyance Self Test (minutes). This Index: patches/patch-smartd_8_in =================================================================== RCS file: patches/patch-smartd_8_in diff -N patches/patch-smartd_8_in --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ patches/patch-smartd_8_in 26 May 2024 14:54:25 -0000 @@ -0,0 +1,17 @@ +Index: smartd.8.in +--- smartd.8.in.orig ++++ smartd.8.in +@@ -458,11 +458,11 @@ this option are: + .I scsiioctl + \- report only ioctl() transactions with SCSI devices. + .Sp +-.\" %IF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %IF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + .I nvmeioctl + \- report only ioctl() transactions with NVMe devices. + .Sp +-.\" %ENDIF OS Darwin FreeBSD Linux NetBSD Windows Cygwin ++.\" %ENDIF OS Darwin FreeBSD Linux NetBSD OpenBSD Windows Cygwin + Any argument may include a positive integer to specify the level of + detail that should be reported. The argument should be followed by a + comma then the integer with no spaces. For example, \fIataioctl,2\fP