Download raw body.
Enable OpenBSD NVMe disk support in smartmontools
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 <krw@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
Enable OpenBSD NVMe disk support in smartmontools