aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOleksii Samorukov <samm@FreeBSD.org>2021-11-24 07:15:14 +0000
committerOleksii Samorukov <samm@FreeBSD.org>2021-11-24 07:28:58 +0000
commit40a957b3db989c9d820563005df61464f8a0312d (patch)
treed6a089f03f84833888cd898e34b83f58d0aa2134
parentcb629f23f15dd63b8d329a9c149b723b16b56170 (diff)
downloadports-40a957b3db989c9d820563005df61464f8a0312d.tar.gz
ports-40a957b3db989c9d820563005df61464f8a0312d.zip
sysutils/smartmontools: Implement monitoring of the devices behind mrsas RAID
PR: 212211 Reported by: stesin@gmail.com
-rw-r--r--sysutils/smartmontools/Makefile2
-rw-r--r--sysutils/smartmontools/files/patch-os__freebsd.cpp450
-rw-r--r--sysutils/smartmontools/files/patch-os__freebsd.h167
-rw-r--r--sysutils/smartmontools/files/patch-smartctl.8.in47
-rw-r--r--sysutils/smartmontools/files/patch-smartd.conf.5.in56
-rw-r--r--sysutils/smartmontools/files/smart.in3
6 files changed, 724 insertions, 1 deletions
diff --git a/sysutils/smartmontools/Makefile b/sysutils/smartmontools/Makefile
index 6b043b9757e7..6600ade6e8fe 100644
--- a/sysutils/smartmontools/Makefile
+++ b/sysutils/smartmontools/Makefile
@@ -2,7 +2,7 @@
PORTNAME= smartmontools
DISTVERSION= 7.2
-PORTREVISION= 2
+PORTREVISION= 3
CATEGORIES= sysutils
MASTER_SITES= SF
diff --git a/sysutils/smartmontools/files/patch-os__freebsd.cpp b/sysutils/smartmontools/files/patch-os__freebsd.cpp
new file mode 100644
index 000000000000..a4abc95ad6b5
--- /dev/null
+++ b/sysutils/smartmontools/files/patch-os__freebsd.cpp
@@ -0,0 +1,450 @@
+--- os_freebsd.cpp.orig 2020-12-12 21:36:48 UTC
++++ os_freebsd.cpp
+@@ -26,6 +26,7 @@
+ #endif
+ #include <sys/stat.h>
+ #include <unistd.h>
++#include <sys/uio.h>
+ #include <glob.h>
+ #include <stddef.h>
+ #include <paths.h>
+@@ -199,6 +200,9 @@ static const char smartctl_examples[] =
+ " smartctl -a --device=areca,3/1 /dev/arcmsr0\n"
+ " (Prints all SMART information for 3rd disk in the 1st enclosure \n"
+ " on first ARECA RAID controller)\n"
++ " smartctl -a --device=megaraid,3 /dev/mrsas0\n"
++ " (Prints all SMART information for 3rd disk\n"
++ " on first LSI RAID controller)\n"
+
+ ;
+
+@@ -761,7 +765,240 @@ bool freebsd_escalade_device::ata_pass_through(const a
+ return true;
+ }
+
++/////////////////////////////////////////////////////////////////////////////
++/// LSI MegaRAID support
+
++class freebsd_megaraid_device
++: public /* implements */ scsi_device,
++ public /* extends */ freebsd_smart_device
++{
++public:
++ freebsd_megaraid_device(smart_interface *intf, const char *name,
++ unsigned int tgt);
++
++ virtual ~freebsd_megaraid_device();
++
++ virtual smart_device * autodetect_open() override;
++
++ virtual bool open() override;
++ virtual bool close() override;
++
++ virtual bool scsi_pass_through(scsi_cmnd_io *iop) override;
++
++private:
++ unsigned int m_disknum;
++ unsigned int m_hba;
++ int m_fd;
++
++ bool (freebsd_megaraid_device::*pt_cmd)(int cdblen, void *cdb, int dataLen, void *data,
++ int senseLen, void *sense, int report, int direction, int timeout);
++ bool megasas_cmd(int cdbLen, void *cdb, int dataLen, void *data,
++ int senseLen, void *sense, int report, int direction, int timeout);
++};
++
++freebsd_megaraid_device::freebsd_megaraid_device(smart_interface *intf,
++ const char *dev_name, unsigned int tgt)
++ : smart_device(intf, dev_name, "megaraid", "megaraid"),
++ freebsd_smart_device(),
++ m_disknum(tgt), m_hba(0),
++ m_fd(-1), pt_cmd(0)
++{
++ set_info().info_name = strprintf("%s [megaraid_disk_%02d]", dev_name, m_disknum);
++ set_info().dev_type = strprintf("megaraid,%d", tgt);
++}
++
++freebsd_megaraid_device::~freebsd_megaraid_device()
++{
++ if (m_fd >= 0)
++ ::close(m_fd);
++}
++
++smart_device * freebsd_megaraid_device::autodetect_open()
++{
++ int report = scsi_debugmode;
++
++ // Open device
++ if (!open())
++ return this;
++
++ // The code below is based on smartd.cpp:SCSIFilterKnown()
++ if (strcmp(get_req_type(), "megaraid"))
++ return this;
++
++ // Get INQUIRY
++ unsigned char req_buff[64] = {0, };
++ int req_len = 36;
++ if (scsiStdInquiry(this, req_buff, req_len)) {
++ close();
++ set_err(EIO, "INQUIRY failed");
++ return this;
++ }
++
++ int avail_len = req_buff[4] + 5;
++ int len = (avail_len < req_len ? avail_len : req_len);
++ if (len < 36)
++ return this;
++
++ if (report)
++ pout("Got MegaRAID inquiry.. %s\n", req_buff+8);
++
++ // Use INQUIRY to detect type
++ {
++ // SAT?
++ ata_device * newdev = smi()->autodetect_sat_device(this, req_buff, len);
++ if (newdev) // NOTE: 'this' is now owned by '*newdev'
++ return newdev;
++ }
++
++ // Nothing special found
++ return this;
++}
++
++bool freebsd_megaraid_device::open()
++{
++ /* Open Device IOCTL node */
++ if ((m_fd = ::open(get_dev_name(), O_RDWR)) >= 0) {
++ pt_cmd = &freebsd_megaraid_device::megasas_cmd;
++ }
++ else {
++ int err = errno;
++ freebsd_smart_device::close();
++ return set_err(err, "cannot open %s",get_dev_name());
++ }
++ set_fd(m_fd);
++ return true;
++}
++
++bool freebsd_megaraid_device::close()
++{
++ if (m_fd >= 0)
++ ::close(m_fd);
++ m_fd = -1; m_hba = 0; pt_cmd = 0;
++ set_fd(m_fd);
++ return true;
++}
++
++bool freebsd_megaraid_device::scsi_pass_through(scsi_cmnd_io *iop)
++{
++ int report = scsi_debugmode;
++
++ if (report > 0) {
++ int k, j;
++ const unsigned char * ucp = iop->cmnd;
++ const char * np;
++ char buff[256];
++ const int sz = (int)sizeof(buff);
++
++ np = scsi_get_opcode_name(ucp[0]);
++ j = snprintf(buff, sz, " [%s: ", np ? np : "<unknown opcode>");
++ for (k = 0; k < (int)iop->cmnd_len; ++k)
++ j += snprintf(&buff[j], (sz > j ? (sz - j) : 0), "%02x ", ucp[k]);
++ if ((report > 1) &&
++ (DXFER_TO_DEVICE == iop->dxfer_dir) && (iop->dxferp)) {
++ int trunc = (iop->dxfer_len > 256) ? 1 : 0;
++
++ snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n Outgoing "
++ "data, len=%d%s:\n", (int)iop->dxfer_len,
++ (trunc ? " [only first 256 bytes shown]" : ""));
++ dStrHex(iop->dxferp, (trunc ? 256 : iop->dxfer_len) , 1);
++ }
++ else
++ snprintf(&buff[j], (sz > j ? (sz - j) : 0), "]\n");
++ pout("%s", buff);
++ }
++
++ // Controller rejects Test Unit Ready
++ if (iop->cmnd[0] == 0x00)
++ return true;
++
++ if (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 || iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16) {
++ // Controller does not return ATA output registers in SAT sense data
++ if (iop->cmnd[2] & (1 << 5)) // chk_cond
++ return set_err(ENOSYS, "ATA return descriptor not supported by controller firmware");
++ }
++ // SMART WRITE LOG SECTOR causing media errors
++ if ((iop->cmnd[0] == SAT_ATA_PASSTHROUGH_16 // SAT16 WRITE LOG
++ && iop->cmnd[14] == ATA_SMART_CMD && iop->cmnd[3]==0 && iop->cmnd[4] == ATA_SMART_WRITE_LOG_SECTOR) ||
++ (iop->cmnd[0] == SAT_ATA_PASSTHROUGH_12 // SAT12 WRITE LOG
++ && iop->cmnd[9] == ATA_SMART_CMD && iop->cmnd[3] == ATA_SMART_WRITE_LOG_SECTOR))
++ {
++ if(!failuretest_permissive)
++ return set_err(ENOSYS, "SMART WRITE LOG SECTOR may cause problems, try with -T permissive to force");
++ }
++ if (pt_cmd == NULL)
++ return false;
++ return (this->*pt_cmd)(iop->cmnd_len, iop->cmnd,
++ iop->dxfer_len, iop->dxferp,
++ iop->max_sense_len, iop->sensep, report, iop->dxfer_dir, iop->timeout);
++}
++
++bool freebsd_megaraid_device::megasas_cmd(int cdbLen, void *cdb,
++ int dataLen, void *data,
++ int senseLen, void * sense, int /*report*/, int dxfer_dir, int timeout)
++{
++ struct mfi_pass_frame * pthru;
++ struct mfi_ioc_packet uio;
++
++ pthru = (struct mfi_pass_frame *)&uio.mfi_frame.raw;
++ memset(&uio, 0, sizeof(uio));
++
++ pthru->header.cmd = MFI_CMD_PD_SCSI_IO;
++ pthru->header.cmd_status = 0;
++ pthru->header.scsi_status = 0x0;
++ pthru->header.target_id = m_disknum;
++ pthru->header.lun_id = 0; // FIXME, should be bus number?
++
++ pthru->header.sense_len = senseLen;
++ pthru->sense_addr_lo = (uintptr_t)sense ;
++ pthru->sense_addr_hi = (uintptr_t)((uint64_t)sense >> 32);
++
++ pthru->header.cdb_len = cdbLen;
++ pthru->header.timeout = timeout;
++ switch (dxfer_dir) {
++ case DXFER_FROM_DEVICE:
++ pthru->header.flags = MFI_FRAME_DIR_READ;
++ break;
++ case DXFER_TO_DEVICE:
++ pthru->header.flags = MFI_FRAME_DIR_WRITE;
++ break;
++ case DXFER_NONE:
++ pthru->header.flags = MFI_FRAME_DIR_NONE;
++ break;
++ }
++
++ if (dataLen > 0) {
++ uio.mfi_sge_count = 1;
++ uio.mfi_sgl_off = offsetof(struct mfi_pass_frame,sgl);
++ uio.mfi_sgl[0].iov_base = data;
++ uio.mfi_sgl[0].iov_len = dataLen;
++
++ pthru->header.sg_count = 1;
++ pthru->header.data_len = dataLen;
++ // tested on amd64 kernel in native and 32bit mode
++ pthru->sgl.sg64[0].addr = (intptr_t)data;
++ pthru->sgl.sg64[0].len = (uint32_t)dataLen;
++ }
++ memcpy(pthru->cdb, cdb, cdbLen);
++
++ uio.mfi_adapter_no = m_hba;
++ uio.mfi_sense_len = senseLen;
++ uio.mfi_sense_off = offsetof(struct mfi_pass_frame, sense_addr_lo);
++
++ errno = 0;
++ int rc = ioctl(m_fd, MFI_CMD, &uio);
++
++ if (pthru->header.cmd_status || rc != 0) {
++ if (pthru->header.cmd_status == 12) {
++ return set_err(EIO, "megasas_cmd: Device %d does not exist\n", m_disknum);
++ }
++ return set_err((errno ? errno : EIO), "megasas_cmd result: %d.%d = %d/%d",
++ m_hba, m_disknum, errno,
++ pthru->header.cmd_status);
++ }
++ return true;
++}
++
++
+ /////////////////////////////////////////////////////////////////////////////
+ /// Implement Highpoint RAID support with old functions
+
+@@ -1401,6 +1638,15 @@ smart_device * freebsd_scsi_device::autodetect_open()
+ return this;
+ }
+
++ // DELL?
++ if (!memcmp(req_buff + 8, "DELL PERC", 12) || !memcmp(req_buff + 8, "MegaRAID", 8)
++ || !memcmp(req_buff + 16, "PERC H", 6) || !memcmp(req_buff + 8, "LSI\0",4)
++ ) {
++ close();
++ set_err(EINVAL, "DELL or MegaRaid controller, use '-d megaraid,N'");
++ return this;
++ }
++
+ // SAT or USB, skip MFI controllers because of bugs
+ {
+ smart_device * newdev = smi()->autodetect_sat_device(this, req_buff, len);
+@@ -1453,6 +1699,10 @@ class freebsd_smart_interface (protected)
+ virtual std::string get_valid_custom_dev_types_str();
+ private:
+ bool get_nvme_devlist(smart_device_list & devlist, const char * type);
++ bool get_dev_megaraid(smart_device_list & devlist);
++ int megaraid_pd_add_list(const char * devname, smart_device_list & devlist);
++ int megaraid_dcmd_cmd(const char * devname, uint32_t opcode, void *buf,
++ size_t bufsize, uint8_t *mbox, size_t mboxlen, uint8_t *statusp);
+ };
+
+
+@@ -1775,6 +2025,9 @@ bool freebsd_smart_interface::scan_smart_devices(smart
+ }
+ }
+
++ // add devices from LSI MegaRaid controllers
++ get_dev_megaraid(devlist);
++
+ if (scan_nvme)
+ get_nvme_devlist(devlist, type);
+ return true;
+@@ -1800,6 +2053,114 @@ bool freebsd_smart_interface::get_nvme_devlist(smart_d
+ return true;
+ }
+
++// getting devices from LSI SAS MegaRaid, if available
++bool freebsd_smart_interface::get_dev_megaraid(smart_device_list & devlist)
++{
++ /* Scanning of disks on MegaRaid device */
++ char ctrlpath[64];
++
++ // trying to add devices on first 32 buses, same as StorCLI does
++ for(unsigned i = 0; i <=32; i++) {
++ sprintf(ctrlpath, "%s%u", MFI_CTRLR_PREFIX, i);
++ megaraid_pd_add_list(ctrlpath, devlist);
++ sprintf(ctrlpath, "%s%u", MRSAS_CTRLR_PREFIX, i);
++ megaraid_pd_add_list(ctrlpath, devlist);
++ }
++ return true;
++}
++
++int
++freebsd_smart_interface::megaraid_dcmd_cmd(const char * devname, uint32_t opcode, void *buf,
++ size_t bufsize, uint8_t *mbox, size_t mboxlen, uint8_t *statusp)
++{
++ struct mfi_ioc_packet ioc;
++ struct mfi_dcmd_frame * dcmd;
++
++ if ((mbox != NULL && (mboxlen == 0 || mboxlen > MFI_MBOX_SIZE)) ||
++ (mbox == NULL && mboxlen != 0))
++ {
++ errno = EINVAL;
++ return (-1);
++ }
++
++ memset(&ioc, 0, sizeof(ioc));
++ dcmd = (struct mfi_dcmd_frame *)&ioc.mfi_frame.raw;
++
++ if (mbox)
++ memcpy(dcmd->mbox, mbox, mboxlen);
++ dcmd->header.cmd = MFI_CMD_DCMD;
++ dcmd->header.data_len = bufsize;
++ dcmd->opcode = opcode;
++
++ if (bufsize > 0) {
++ ioc.mfi_sge_count = 1;
++ ioc.mfi_sgl_off = offsetof(struct mfi_dcmd_frame,sgl);
++ ioc.mfi_sgl[0].iov_base = buf;
++ ioc.mfi_sgl[0].iov_len = bufsize;
++ dcmd->header.sg_count = 1;
++ dcmd->header.data_len = bufsize;
++ // tested on amd64 kernel in native and 32bit mode
++ dcmd->sgl.sg64[0].addr = (intptr_t)buf;
++ dcmd->sgl.sg64[0].len = (uint32_t)bufsize;
++ }
++
++ int fd;
++ if ((fd = ::open(devname, O_RDWR)) < 0) {
++ return (errno);
++ }
++ // We are using MFI_CMD as it seems to be supported by all LSI BSD drivers
++ int r = ioctl(fd, MFI_CMD, &ioc);
++ ::close(fd);
++ if (r < 0) {
++ return (r);
++ }
++
++ if (statusp != NULL)
++ *statusp = dcmd->header.cmd_status;
++ else if (dcmd->header.cmd_status != MFI_STAT_OK) {
++ fprintf(stderr, "command %x returned error status %x\n",
++ opcode, dcmd->header.cmd_status);
++ errno = EIO;
++ return (-1);
++ }
++ return (0);
++}
++
++int
++freebsd_smart_interface::megaraid_pd_add_list(const char * devname, smart_device_list & devlist)
++{
++ /*
++ * Keep fetching the list in a loop until we have a large enough
++ * buffer to hold the entire list.
++ */
++ mfi_pd_list * list = 0;
++ for (unsigned list_size = 1024; ; ) {
++ list = reinterpret_cast<mfi_pd_list *>(realloc(list, list_size));
++ if (!list)
++ throw std::bad_alloc();
++ memset(list, 0, list_size);
++ if (megaraid_dcmd_cmd(devname, MFI_DCMD_PD_GET_LIST, list, list_size, NULL, 0,
++ NULL) < 0)
++ {
++ free(list);
++ return (-1);
++ }
++ if (list->size <= list_size)
++ break;
++ list_size = list->size;
++ }
++
++ // adding all SCSI devices
++ for (unsigned i = 0; i < list->count; i++) {
++ if(list->addr[i].scsi_dev_type)
++ continue; /* non disk device found */
++ smart_device * dev = new freebsd_megaraid_device(this, devname, list->addr[i].device_id);
++ devlist.push_back(dev);
++ }
++ free(list);
++ return (0);
++}
++
+ #if (FREEBSDVER < 800000) // without this build fail on FreeBSD 8
+ static char done[USB_MAX_DEVICES];
+
+@@ -2034,9 +2395,16 @@ smart_device * freebsd_smart_interface::autodetect_sma
+ }
+ }
+ // device is LSI raid supported by mfi driver
+- if(!strncmp("/dev/mfid", test_name, strlen("/dev/mfid")))
+- set_err(EINVAL, "To monitor disks on LSI RAID load mfip.ko module and run 'smartctl -a /dev/passX' to show SMART information");
++ if(!strncmp("/dev/mfid", test_name, strlen("/dev/mfid"))) {
++ set_err(EINVAL, "To access disks on LSI RAID load mfip.ko and use /dev/passX or use -d 'megaraid,N' with /dev/mfiX devices");
++ return 0;
++ }
+
++ if(!strncmp(MFI_CTRLR_PREFIX, test_name, strlen(MFI_CTRLR_PREFIX)) || !strncmp(MRSAS_CTRLR_PREFIX, test_name, strlen(MRSAS_CTRLR_PREFIX))) {
++ set_err(EINVAL, "To access disks on %s use '-d megaraid,N' device type", test_name);
++ return 0;
++ }
++
+ // form /dev/nvme* or nvme*
+ if(!strncmp("/dev/nvme", test_name, strlen("/dev/nvme")))
+ return new freebsd_nvme_device(this, name, "", 0 /* use default nsid */);
+@@ -2142,12 +2510,16 @@ smart_device * freebsd_smart_interface::get_custom_sma
+ return new freebsd_areca_ata_device(this, name, disknum, encnum);
+ }
+
++ if (sscanf(type, "megaraid,%d", &disknum) == 1) {
++ return new freebsd_megaraid_device(this, name, disknum);
++ }
++
+ return 0;
+ }
+
+ std::string freebsd_smart_interface::get_valid_custom_dev_types_str()
+ {
+- return "3ware,N, hpt,L/M/N, cciss,N, areca,N/E"
++ return "3ware,N, hpt,L/M/N, cciss,N, areca,N/E, megaraid,N"
+ #if FREEBSDVER > 800100
+ ", atacam"
+ #endif
diff --git a/sysutils/smartmontools/files/patch-os__freebsd.h b/sysutils/smartmontools/files/patch-os__freebsd.h
new file mode 100644
index 000000000000..7bace7bc234d
--- /dev/null
+++ b/sysutils/smartmontools/files/patch-os__freebsd.h
@@ -0,0 +1,167 @@
+--- os_freebsd.h.orig 2018-08-19 18:45:53 UTC
++++ os_freebsd.h
+@@ -606,4 +606,164 @@ HPT_PASS_THROUGH_HEADER, *PHPT_PASS_THROUGH_HEADER;
+ #define __unused __attribute__ ((__unused__))
+ #endif
+
++// MFI definition from the kernel sources, see sys/dev/mfi
++
++#define MFI_STAT_OK 0x00
++#define MFI_DCMD_PD_GET_LIST 0x02010000
++
++#define MFI_CTRLR_PREFIX "/dev/mfi"
++#define MRSAS_CTRLR_PREFIX "/dev/mrsas"
++
++/*
++ * MFI Frame flags
++ */
++#define MFI_FRAME_POST_IN_REPLY_QUEUE 0x0000
++#define MFI_FRAME_DONT_POST_IN_REPLY_QUEUE 0x0001
++#define MFI_FRAME_SGL32 0x0000
++#define MFI_FRAME_SGL64 0x0002
++#define MFI_FRAME_SENSE32 0x0000
++#define MFI_FRAME_SENSE64 0x0004
++#define MFI_FRAME_DIR_NONE 0x0000
++#define MFI_FRAME_DIR_WRITE 0x0008
++#define MFI_FRAME_DIR_READ 0x0010
++#define MFI_FRAME_DIR_BOTH 0x0018
++#define MFI_FRAME_IEEE_SGL 0x0020
++#define MFI_FRAME_FMT "\20" \
++ "\1NOPOST" \
++ "\2SGL64" \
++ "\3SENSE64" \
++ "\4WRITE" \
++ "\5READ" \
++ "\6IEEESGL"
++
++/* MFI Commands */
++typedef enum {
++ MFI_CMD_INIT = 0x00,
++ MFI_CMD_LD_READ,
++ MFI_CMD_LD_WRITE,
++ MFI_CMD_LD_SCSI_IO,
++ MFI_CMD_PD_SCSI_IO,
++ MFI_CMD_DCMD,
++ MFI_CMD_ABORT,
++ MFI_CMD_SMP,
++ MFI_CMD_STP
++} mfi_cmd_t;
++
++/* Scatter Gather elements */
++struct mfi_sg32 {
++ uint32_t addr;
++ uint32_t len;
++} __packed;
++
++struct mfi_sg64 {
++ uint64_t addr;
++ uint32_t len;
++} __packed;
++
++struct mfi_sg_skinny {
++ uint64_t addr;
++ uint32_t len;
++ uint32_t flag;
++} __packed;
++
++union mfi_sgl {
++ struct mfi_sg32 sg32[1];
++ struct mfi_sg64 sg64[1];
++ struct mfi_sg_skinny sg_skinny[1];
++} __packed;
++
++/* Message frames. All messages have a common header */
++struct mfi_frame_header {
++ uint8_t cmd;
++ uint8_t sense_len;
++ uint8_t cmd_status;
++ uint8_t scsi_status;
++ uint8_t target_id;
++ uint8_t lun_id;
++ uint8_t cdb_len;
++ uint8_t sg_count;
++ uint32_t context;
++ /*
++ * pad0 is MSI Specific. Not used by Driver. Zero the value before
++ * sending the command to f/w.
++ */
++ uint32_t pad0;
++ uint16_t flags;
++#define MFI_FRAME_DATAOUT 0x08
++#define MFI_FRAME_DATAIN 0x10
++ uint16_t timeout;
++ uint32_t data_len;
++} __packed;
++
++#define MFI_PASS_FRAME_SIZE 48
++struct mfi_pass_frame {
++ struct mfi_frame_header header;
++ uint32_t sense_addr_lo;
++ uint32_t sense_addr_hi;
++ uint8_t cdb[16];
++ union mfi_sgl sgl;
++} __packed;
++
++#define MFI_DCMD_FRAME_SIZE 40
++#define MFI_MBOX_SIZE 12
++
++struct mfi_dcmd_frame {
++ struct mfi_frame_header header;
++ uint32_t opcode;
++ uint8_t mbox[MFI_MBOX_SIZE];
++ union mfi_sgl sgl;
++} __packed;
++
++#define MAX_IOCTL_SGE 16
++struct mfi_ioc_packet {
++ uint16_t mfi_adapter_no;
++ uint16_t mfi_pad1;
++ uint32_t mfi_sgl_off;
++ uint32_t mfi_sge_count;
++ uint32_t mfi_sense_off;
++ uint32_t mfi_sense_len;
++ union {
++ uint8_t raw[128];
++ struct mfi_frame_header hdr;
++ } mfi_frame;
++
++ struct iovec mfi_sgl[MAX_IOCTL_SGE];
++} __packed;
++
++#ifdef COMPAT_FREEBSD32
++struct mfi_ioc_packet32 {
++ uint16_t mfi_adapter_no;
++ uint16_t mfi_pad1;
++ uint32_t mfi_sgl_off;
++ uint32_t mfi_sge_count;
++ uint32_t mfi_sense_off;
++ uint32_t mfi_sense_len;
++ union {
++ uint8_t raw[128];
++ struct mfi_frame_header hdr;
++ } mfi_frame;
++
++ struct iovec32 mfi_sgl[MAX_IOCTL_SGE];
++} __packed;
++#endif
++
++struct mfi_pd_address {
++ uint16_t device_id;
++ uint16_t encl_device_id;
++ uint8_t encl_index;
++ uint8_t slot_number;
++ uint8_t scsi_dev_type; /* 0 = disk */
++ uint8_t connect_port_bitmap;
++ uint64_t sas_addr[2];
++} __packed;
++
++#define MAX_SYS_PDS 240
++struct mfi_pd_list {
++ uint32_t size;
++ uint32_t count;
++ struct mfi_pd_address addr[MAX_SYS_PDS];
++} __packed;
++
++#define MFI_CMD _IOWR('M', 1, struct mfi_ioc_packet)
++
+ #endif /* OS_FREEBSD_H_ */
diff --git a/sysutils/smartmontools/files/patch-smartctl.8.in b/sysutils/smartmontools/files/patch-smartctl.8.in
new file mode 100644
index 000000000000..cc0164ddc90a
--- /dev/null
+++ b/sysutils/smartmontools/files/patch-smartctl.8.in
@@ -0,0 +1,47 @@
+--- smartctl.8.in.orig 2020-12-21 18:34:31 UTC
++++ smartctl.8.in
+@@ -478,24 +478,40 @@ this device type is for NVMe disks that are behind a R
+ bridge.
+ .Sp
+ .\" %ENDIF NOT OS Darwin
+-.\" %IF OS Linux
++.\" %IF OS Linux FreeBSD
+ .I marvell
+ \- [Linux only] interact with SATA disks behind Marvell chip-set
+ controllers (using the Marvell rather than libata driver).
+ .Sp
+ .I megaraid,N
+-\- [Linux only] the device consists of one or more SCSI/SAS disks connected
++\- [FreeBSD and Linux only] the device consists of one or more SCSI/SAS disks connected
+ to a MegaRAID controller. The non-negative integer N (in the range of 0 to
+-127 inclusive) denotes which disk on the controller is monitored.
++127 inclusive) denotes which disk on the controller is monitored. This interface
++will also work for Dell PERC controllers.
+ Use syntax such as:
++.\" %ENDIF OS Linux FreeBSD
++.\" %IF OS ALL
++FreeBSD:
++.\" %ENDIF OS ALL
++.\" %IF OS FreeBSD
+ .br
++\fBsmartctl \-a \-d megaraid,2 /dev/mfi0\fP
++.br
++\fBsmartctl \-a \-d megaraid,0 /dev/mrsas0\fP
++.br
++.Sp
++.\" %ENDIF OS FreeBSD
++.\" %IF OS ALL
++Linux:
++.\" %ENDIF OS ALL
++.\" %IF OS Linux
++.br
+ \fBsmartctl \-a \-d megaraid,2 /dev/sda\fP
+ .br
+ \fBsmartctl \-a \-d megaraid,0 /dev/sdb\fP
+ .br
+ \fBsmartctl \-a \-d megaraid,0 /dev/bus/0\fP
+ .br
+-This interface will also work for Dell PERC controllers.
+ It is possible to set RAID device name as /dev/bus/N, where N is a SCSI bus
+ number.
+ .Sp
diff --git a/sysutils/smartmontools/files/patch-smartd.conf.5.in b/sysutils/smartmontools/files/patch-smartd.conf.5.in
new file mode 100644
index 000000000000..3544992b45b4
--- /dev/null
+++ b/sysutils/smartmontools/files/patch-smartd.conf.5.in
@@ -0,0 +1,56 @@
+--- smartd.conf.5.in.orig 2020-11-23 18:25:16 UTC
++++ smartd.conf.5.in
+@@ -116,15 +116,27 @@ Section below!
+ # may become common with SATA disks in SAS and FC
+ # environments.
+ /dev/sda \-a \-d sat
+-.\" %IF OS Linux
++.\" %IF OS FreeBSD Linux
+ #
+-# Three disks connected to a MegaRAID controller
++# Disks connected to a MegaRAID controller
+ # Start short self\-tests daily between 1\-2, 2\-3, and
+ # 3\-4 am.
++.\" %ENDIF OS FreeBSD Linux
++.\" %IF OS Linux
++# Linux:
+ /dev/sda \-d megaraid,0 \-a \-s S/../.././01
+ /dev/sda \-d megaraid,1 \-a \-s S/../.././02
+ /dev/sda \-d megaraid,2 \-a \-s S/../.././03
+ /dev/bus/0 \-d megaraid,2 \-a \-s S/../.././03
++.\" %ENDIF OS Linux
++.\" %IF OS FreeBSD
++# FreeBSD:
++/dev/mfi0 \-d megaraid,0 \-a \-s S/../.././01
++/dev/mfi0 \-d megaraid,1 \-a \-s S/../.././02
++/dev/mfi0 \-d megaraid,2 \-a \-s S/../.././03
++/dev/mrsas0 \-d megaraid,2 \-a \-s S/../.././03
++.\" %ENDIF OS FreeBSD
++.\" %IF OS Linux
+ #
+ # Three disks connected to an AacRaid controller
+ # Start short self\-tests daily between 1\-2, 2\-3, and
+@@ -463,18 +475,19 @@ bridge.
+ \- [Linux only] interact with SATA disks behind Marvell chip-set
+ controllers (using the Marvell rather than libata driver).
+ .Sp
++.\" %ENDIF OS FreeBSD Linux
++.\" %IF OS FreeBSD Linux
+ .I megaraid,N
+-\- [Linux only] the device consists of one or more SCSI/SAS disks connected
++\- [Linux and FreeBSD only] the device consists of one or more SCSI/SAS disks connected
+ to a MegaRAID controller. The non-negative integer N (in the range of 0 to
+ 127 inclusive) denotes which disk on the controller is monitored.
+ This interface will also work for Dell PERC controllers.
+ In log files and email messages this disk will be identified as
+ megaraid_disk_XXX with XXX in the range from 000 to 127 inclusive.
+-It is possible to set RAID device name as /dev/bus/N, where N is a SCSI bus
+-number.
++
+ Please see the \fBsmartctl\fP(8) man page for further details.
+ .Sp
+-.\" %ENDIF OS Linux
++.\" %ENDIF OS FreeBSD Linux
+ .\" %IF OS Linux Windows Cygwin
+ .I aacraid,H,L,ID
+ \- [Linux, Windows and Cygwin only] the device consists of one or more
diff --git a/sysutils/smartmontools/files/smart.in b/sysutils/smartmontools/files/smart.in
index d02bc706d447..f9ceea2bfad1 100644
--- a/sysutils/smartmontools/files/smart.in
+++ b/sysutils/smartmontools/files/smart.in
@@ -48,6 +48,9 @@ case "${daily_status_smart_enable}" in
arcmsr*) devflags="-dareca,${device##arcmsr[0-9]*,}"
device="/dev/${device%,[0-9]*}"
;;
+ mrsas*) devflags="-dmegaraid,${device##mrsas[0-9]*,}"
+ device="/dev/${device%,[0-9]*}"
+ ;;
/*) ;;
*) device="/dev/${device}"
;;