git: 40a957b3db98 - main - sysutils/smartmontools: Implement monitoring of the devices behind mrsas RAID

From: Oleksii Samorukov <samm_at_FreeBSD.org>
Date: Wed, 24 Nov 2021 07:29:19 UTC
The branch main has been updated by samm:

URL: https://cgit.FreeBSD.org/ports/commit/?id=40a957b3db989c9d820563005df61464f8a0312d

commit 40a957b3db989c9d820563005df61464f8a0312d
Author:     Oleksii Samorukov <samm@FreeBSD.org>
AuthorDate: 2021-11-24 07:15:14 +0000
Commit:     Oleksii Samorukov <samm@FreeBSD.org>
CommitDate: 2021-11-24 07:28:58 +0000

    sysutils/smartmontools: Implement monitoring of the devices behind mrsas RAID
    
    PR:             212211
    Reported by:    stesin@gmail.com
---
 sysutils/smartmontools/Makefile                    |   2 +-
 sysutils/smartmontools/files/patch-os__freebsd.cpp | 450 +++++++++++++++++++++
 sysutils/smartmontools/files/patch-os__freebsd.h   | 167 ++++++++
 sysutils/smartmontools/files/patch-smartctl.8.in   |  47 +++
 .../smartmontools/files/patch-smartd.conf.5.in     |  56 +++
 sysutils/smartmontools/files/smart.in              |   3 +
 6 files changed, 724 insertions(+), 1 deletion(-)

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}"
 			        ;;