git: afb60897a15c - main - mpsutil: Add Show Discovery command
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 11 Sep 2026 05:32:40 UTC
The branch main has been updated by imp:
URL: https://cgit.FreeBSD.org/src/commit/?id=afb60897a15c37cb5dbc45e2cabfac58b681550f
commit afb60897a15c37cb5dbc45e2cabfac58b681550f
Author: Warner Losh <imp@FreeBSD.org>
AuthorDate: 2026-09-11 05:13:14 +0000
Commit: Warner Losh <imp@FreeBSD.org>
CommitDate: 2026-09-11 05:13:14 +0000
mpsutil: Add Show Discovery command
Prints the current discovry tables for the driver.
Sponsored by: Netflix
Assisted by: Claude (sonet-5)
Differential Revision: https://reviews.freebsd.org/D58528
---
usr.sbin/mpsutil/mps_show.c | 205 ++++++++++++++++++++++++++++++++++++++++++++
usr.sbin/mpsutil/mpsutil.8 | 14 +++
2 files changed, 219 insertions(+)
diff --git a/usr.sbin/mpsutil/mps_show.c b/usr.sbin/mpsutil/mps_show.c
index 378d5380aa76..cf1ae4f5005b 100644
--- a/usr.sbin/mpsutil/mps_show.c
+++ b/usr.sbin/mpsutil/mps_show.c
@@ -630,6 +630,211 @@ show_devices(int ac, char **av)
}
MPS_COMMAND(show, devices, show_devices, "", "Show attached devices");
+static const struct {
+ uint32_t mask;
+ const char *name;
+} discovery_status_bits[] = {
+ { MPI2_SASIOUNIT0_DS_MAX_ENCLOSURES_EXCEED, "MaxEnclosuresExceeded" },
+ { MPI2_SASIOUNIT0_DS_MAX_EXPANDERS_EXCEED, "MaxExpandersExceeded" },
+ { MPI2_SASIOUNIT0_DS_MAX_DEVICES_EXCEED, "MaxDevicesExceeded" },
+ { MPI2_SASIOUNIT0_DS_MAX_TOPO_PHYS_EXCEED, "MaxTopoPhysExceeded" },
+ { MPI2_SASIOUNIT0_DS_DOWNSTREAM_INITIATOR, "DownstreamInitiator" },
+ { MPI2_SASIOUNIT0_DS_MULTI_SUBTRACTIVE_SUBTRACTIVE, "MultiSubtractiveToSubtractive" },
+ { MPI2_SASIOUNIT0_DS_EXP_MULTI_SUBTRACTIVE, "ExpMultiSubtractive" },
+ { MPI2_SASIOUNIT0_DS_MULTI_PORT_DOMAIN, "MultiPortDomain" },
+ { MPI2_SASIOUNIT0_DS_TABLE_TO_SUBTRACTIVE_LINK, "TableToSubtractiveLink" },
+ { MPI2_SASIOUNIT0_DS_UNSUPPORTED_DEVICE, "UnsupportedDevice" },
+ { MPI2_SASIOUNIT0_DS_TABLE_LINK, "TableLink" },
+ { MPI2_SASIOUNIT0_DS_SUBTRACTIVE_LINK, "SubtractiveLink" },
+ { MPI2_SASIOUNIT0_DS_SMP_CRC_ERROR, "SmpCrcError" },
+ { MPI2_SASIOUNIT0_DS_SMP_FUNCTION_FAILED, "SmpFunctionFailed" },
+ { MPI2_SASIOUNIT0_DS_INDEX_NOT_EXIST, "RouteIndexNotExist" },
+ { MPI2_SASIOUNIT0_DS_OUT_ROUTE_ENTRIES, "RouteTableFull" },
+ { MPI2_SASIOUNIT0_DS_SMP_TIMEOUT, "SmpTimeout" },
+ { MPI2_SASIOUNIT0_DS_MULTIPLE_PORTS, "MultiplePortsSameAddress" },
+ { MPI2_SASIOUNIT0_DS_UNADDRESSABLE_DEVICE, "UnaddressableDevice" },
+ { MPI2_SASIOUNIT0_DS_LOOP_DETECTED, "LoopDetected" },
+};
+
+static void
+snprint_discovery_status(char *buf, size_t buflen, uint32_t ds)
+{
+ unsigned i;
+ int first = 1;
+
+ buf[0] = '\0';
+ if (ds == 0) {
+ strlcpy(buf, "-", buflen);
+ return;
+ }
+ for (i = 0; i < nitems(discovery_status_bits); i++) {
+ if ((ds & discovery_status_bits[i].mask) == 0)
+ continue;
+ if (!first)
+ strlcat(buf, ",", buflen);
+ strlcat(buf, discovery_status_bits[i].name, buflen);
+ first = 0;
+ }
+}
+
+static const char *
+get_access_status(uint8_t status)
+{
+
+ switch (status) {
+ case MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS:
+ return ("Ok");
+ case MPI2_SAS_DEVICE0_ASTATUS_SATA_INIT_FAILED:
+ return ("SATAInitFailed");
+ case MPI2_SAS_DEVICE0_ASTATUS_SATA_CAPABILITY_FAILED:
+ return ("SATACapabilityFailed");
+ case MPI2_SAS_DEVICE0_ASTATUS_SATA_AFFILIATION_CONFLICT:
+ return ("SATAAffiliationConflict");
+ case MPI2_SAS_DEVICE0_ASTATUS_SATA_NEEDS_INITIALIZATION:
+ return ("SATANeedsInit");
+ case MPI2_SAS_DEVICE0_ASTATUS_ROUTE_NOT_ADDRESSABLE:
+ return ("RouteNotAddressable");
+ case MPI2_SAS_DEVICE0_ASTATUS_SMP_ERROR_NOT_ADDRESSABLE:
+ return ("SmpErrorNotAddressable");
+ case MPI2_SAS_DEVICE0_ASTATUS_DEVICE_BLOCKED:
+ return ("DeviceBlocked");
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_UNKNOWN:
+ return ("SATAInitFailUnknown");
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_AFFILIATION_CONFLICT:
+ return ("SATAInitFailAffiliation");
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_DIAG:
+ return ("SATAInitFailDiag");
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_IDENTIFICATION:
+ return ("SATAInitFailID");
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_CHECK_POWER:
+ return ("SATAInitFailPower");
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_PIO_SN:
+ return ("SATAInitFailPIO");
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_MDMA_SN:
+ return ("SATAInitFailMDMA");
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_UDMA_SN:
+ return ("SATAInitFailUDMA");
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_ZONING_VIOLATION:
+ return ("SATAInitFailZoning");
+ case MPI2_SAS_DEVICE0_ASTATUS_SIF_NOT_ADDRESSABLE:
+ return ("SATAInitFailNotAddressable");
+ default:
+ return ("Reserved");
+ }
+}
+
+/*
+ * Dump firmware-maintained DiscoveryStatus (SAS IO Unit Page 0, per PHY) and
+ * AccessStatus (SAS Device Page 0, per device) fields used by mpr(4)/mps(4)
+ * during discovery.
+ */
+static int
+show_discovery(int ac, char **av)
+{
+ MPI2_CONFIG_PAGE_SASIOUNIT_0 *sas0;
+ MPI2_SAS_IO_UNIT0_PHY_DATA *phy;
+ MPI2_CONFIG_PAGE_SAS_DEV_0 *device;
+ uint16_t IOCStatus, handle;
+ uint32_t ds;
+ uint16_t flags;
+ uint8_t astatus;
+ char dsbuf[256];
+ int fd, error, i, nsuspect, in_progress, disabled, suspect;
+
+ fd = mps_open(mps_unit);
+ if (fd < 0) {
+ error = errno;
+ warn("mps_open");
+ return (error);
+ }
+
+ sas0 = mps_read_extended_config_page(fd,
+ MPI2_CONFIG_EXTPAGETYPE_SAS_IO_UNIT,
+ MPI2_SASIOUNITPAGE0_PAGEVERSION, 0, 0, &IOCStatus);
+ if (sas0 == NULL) {
+ error = errno;
+ warn("Error retrieving SAS IO Unit page 0: %s",
+ mps_ioc_status(IOCStatus));
+ close(fd);
+ return (error);
+ }
+
+ nsuspect = 0;
+ printf("%-4s%-5s%-11s%-9s%-6s%-10s%-10s%s\n",
+ "Phy", "Port", "PortStatus", "Disabled", "Rate", "AttHandle",
+ "CtlHandle", "DiscoveryStatus");
+ for (i = 0; i < sas0->NumPhys; i++) {
+ phy = &sas0->PhyData[i];
+ ds = le32toh(phy->DiscoveryStatus);
+ in_progress = (phy->PortFlags &
+ MPI2_SASIOUNIT0_PORTFLAGS_DISCOVERY_IN_PROGRESS) != 0;
+ disabled = (phy->PhyFlags &
+ MPI2_SASIOUNIT0_PHYFLAGS_PHY_DISABLED) != 0;
+
+ snprint_discovery_status(dsbuf, sizeof(dsbuf), ds);
+ printf("%-4d%-5u%-11s%-9s%-6s0x%-8x0x%-8x%s%s\n",
+ i, phy->Port, in_progress ? "InProgress" : "Idle",
+ disabled ? "Y" : "N", get_device_speed(phy->NegotiatedLinkRate),
+ le16toh(phy->AttachedDevHandle),
+ le16toh(phy->ControllerDevHandle), dsbuf,
+ (ds != 0 || in_progress) ? " <== SUSPECT" : "");
+ if (ds != 0 || in_progress)
+ nsuspect++;
+ }
+ printf("\n");
+
+ printf("%-8s%-10s%-17s%-6s%-6s%-8s%s\n",
+ "Handle", "Parent", "SAS Address", "Enc", "Slot", "Flags",
+ "AccessStatus");
+ handle = 0xffff;
+ for (;;) {
+ device = mps_read_extended_config_page(fd,
+ MPI2_CONFIG_EXTPAGETYPE_SAS_DEVICE,
+ MPI2_SASDEVICE0_PAGEVERSION, 0,
+ MPI2_SAS_DEVICE_PGAD_FORM_GET_NEXT_HANDLE | handle,
+ &IOCStatus);
+ if (device == NULL) {
+ if (IOCStatus == MPI2_IOCSTATUS_CONFIG_INVALID_PAGE)
+ break;
+ error = errno;
+ warn("Error retrieving device page: %s",
+ mps_ioc_status(IOCStatus));
+ free(sas0);
+ close(fd);
+ return (error);
+ }
+ handle = le16toh(device->DevHandle);
+
+ flags = le16toh(device->Flags);
+ astatus = device->AccessStatus;
+ suspect = (astatus != MPI2_SAS_DEVICE0_ASTATUS_NO_ERRORS) ||
+ (flags & MPI2_SAS_DEVICE0_FLAGS_UNSUPPORTED_DEVICE) ||
+ !(flags & MPI2_SAS_DEVICE0_FLAGS_DEVICE_PRESENT);
+
+ printf("0x%-6x0x%-8x%08x%08x %-6u%-6u0x%-6x%s%s\n",
+ handle, le16toh(device->ParentDevHandle),
+ le32toh(device->SASAddress.High),
+ le32toh(device->SASAddress.Low),
+ le16toh(device->EnclosureHandle), le16toh(device->Slot),
+ flags, get_access_status(astatus),
+ suspect ? " <== SUSPECT" : "");
+
+ if (suspect)
+ nsuspect++;
+ free(device);
+ }
+ printf("\n");
+
+ if (nsuspect != 0)
+ printf("*** %d suspect PHY/device entries found ***\n", nsuspect);
+
+ free(sas0);
+ close(fd);
+ return (0);
+}
+MPS_COMMAND(show, discovery, show_discovery, "",
+ "Show firmware DiscoveryStatus/AccessStatus per PHY and device");
+
static int
show_enclosures(int ac, char **av)
{
diff --git a/usr.sbin/mpsutil/mpsutil.8 b/usr.sbin/mpsutil/mpsutil.8
index 6bfcdf5f9dd7..402031542f16 100644
--- a/usr.sbin/mpsutil/mpsutil.8
+++ b/usr.sbin/mpsutil/mpsutil.8
@@ -51,6 +51,9 @@
.Cm show devices
.Nm
.Op Fl u Ar unit
+.Cm show discovery
+.Nm
+.Op Fl u Ar unit
.Cm show enclosures
.Nm
.Op Fl u Ar unit
@@ -124,6 +127,17 @@ Displays a summary of all adapters.
Displays all devices, expanders and enclosures.
.It Cm show devices
Displays all devices.
+.It Cm show discovery
+Displays the firmware DiscoveryStatus field for each PHY
+.Po Dq SAS IO Unit Page 0 Pc
+and the AccessStatus field for each device
+.Po Dq SAS Device Page 0 Pc .
+Entries with a nonzero DiscoveryStatus, a non-Ok AccessStatus, or a PHY
+still showing discovery in progress are marked
+.Dq SUSPECT .
+Useful for finding which PHY or device a firmware discovery stall was
+attributed to, since these fields aren't otherwise exposed and can remain
+set after the boot completes.
.It Cm show expanders
Displays all expanders.
.It Cm show enclosures