mirror of
https://git.freebsd.org/ports.git
synced 2025-06-26 15:10:35 -04:00
450 lines
14 KiB
C++
450 lines
14 KiB
C++
--- 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
|