git: 004ce88ad1ef - main - mpr: Add workaround for too few slots being automatically scanned

From: Warner Losh <imp_at_FreeBSD.org>
Date: Sun, 31 Aug 2025 19:01:33 UTC
The branch main has been updated by imp:

URL: https://cgit.FreeBSD.org/src/commit/?id=004ce88ad1efd42a1d7d5692849b4aa6906178fc

commit 004ce88ad1efd42a1d7d5692849b4aa6906178fc
Author:     Peter Eriksson <pen@lysator.liu.se>
AuthorDate: 2025-08-31 18:58:56 +0000
Commit:     Warner Losh <imp@FreeBSD.org>
CommitDate: 2025-08-31 19:01:09 +0000

    mpr: Add workaround for too few slots being automatically scanned
    
    This patch adds a /boot/loader.conf setting that makes it possibly to
    override the detected number of slots in storage enclosures. Some (yes
    I'm looking at you HPE D6020!) reports less available slots that there
    actually are (the D6020 seems to report 18 but actually has 35 per
    drawer). This causes the mpr driver to have problems detecting/managing
    all drives in a multienclosure setting. For the D6020 this occurs when
    connecting two or more fully equipped (140 drives) enclosures to one
    controller...
    
    This problem can be "fixed" by adding the following to /boot/loader.conf
    and rebooting:
        hw.mpr.encl_min_slots="35"
    
    Note: I (Warner) don't have this hardware to see if there's some way to
    fix the detection, so I'm committing this as a stop-gap. It's a no-op if
    no tunable is set.
    
    PR: 271238
    Reivewed by: imp
---
 sys/dev/mpr/mpr.c         | 10 ++++++++++
 sys/dev/mpr/mpr_mapping.c | 18 ++++++++++++++++++
 sys/dev/mpr/mprvar.h      |  1 +
 3 files changed, 29 insertions(+)

diff --git a/sys/dev/mpr/mpr.c b/sys/dev/mpr/mpr.c
index d1c572e40669..262d6b58b705 100644
--- a/sys/dev/mpr/mpr.c
+++ b/sys/dev/mpr/mpr.c
@@ -1729,6 +1729,7 @@ mpr_get_tunables(struct mpr_softc *sc)
 	sc->enable_ssu = MPR_SSU_ENABLE_SSD_DISABLE_HDD;
 	sc->spinup_wait_time = DEFAULT_SPINUP_WAIT;
 	sc->use_phynum = 1;
+	sc->encl_min_slots = 0;
 	sc->max_reqframes = MPR_REQ_FRAMES;
 	sc->max_prireqframes = MPR_PRI_REQ_FRAMES;
 	sc->max_replyframes = MPR_REPLY_FRAMES;
@@ -1748,6 +1749,7 @@ mpr_get_tunables(struct mpr_softc *sc)
 	TUNABLE_INT_FETCH("hw.mpr.enable_ssu", &sc->enable_ssu);
 	TUNABLE_INT_FETCH("hw.mpr.spinup_wait_time", &sc->spinup_wait_time);
 	TUNABLE_INT_FETCH("hw.mpr.use_phy_num", &sc->use_phynum);
+	TUNABLE_INT_FETCH("hw.mpr.encl_min_slots", &sc->encl_min_slots);
 	TUNABLE_INT_FETCH("hw.mpr.max_reqframes", &sc->max_reqframes);
 	TUNABLE_INT_FETCH("hw.mpr.max_prireqframes", &sc->max_prireqframes);
 	TUNABLE_INT_FETCH("hw.mpr.max_replyframes", &sc->max_replyframes);
@@ -1797,6 +1799,10 @@ mpr_get_tunables(struct mpr_softc *sc)
 	    device_get_unit(sc->mpr_dev));
 	TUNABLE_INT_FETCH(tmpstr, &sc->use_phynum);
 
+	snprintf(tmpstr, sizeof(tmpstr), "dev.mpr.%d.encl_min_slots",
+	    device_get_unit(sc->mpr_dev));
+	TUNABLE_INT_FETCH(tmpstr, &sc->encl_min_slots);
+
 	snprintf(tmpstr, sizeof(tmpstr), "dev.mpr.%d.max_reqframes",
 	    device_get_unit(sc->mpr_dev));
 	TUNABLE_INT_FETCH(tmpstr, &sc->max_reqframes);
@@ -1951,6 +1957,10 @@ mpr_setup_sysctl(struct mpr_softc *sc)
 	SYSCTL_ADD_UQUAD(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
 	    OID_AUTO, "prp_page_alloc_fail", CTLFLAG_RD,
 	    &sc->prp_page_alloc_fail, "PRP page allocation failures");
+
+	SYSCTL_ADD_INT(sysctl_ctx, SYSCTL_CHILDREN(sysctl_tree),
+	    OID_AUTO, "encl_min_slots", CTLFLAG_RW, &sc->encl_min_slots, 0,
+	    "force enclosure minimum slots");
 }
 
 static struct mpr_debug_string {
diff --git a/sys/dev/mpr/mpr_mapping.c b/sys/dev/mpr/mpr_mapping.c
index f9a9ac1c53d0..38aa4dfc7ef2 100644
--- a/sys/dev/mpr/mpr_mapping.c
+++ b/sys/dev/mpr/mpr_mapping.c
@@ -2785,6 +2785,8 @@ mpr_mapping_enclosure_dev_status_change_event(struct mpr_softc *sc,
 		 * DPM, if it's being used.
 		 */
 		if (enc_idx != MPR_ENCTABLE_BAD_IDX) {
+			u16 new_num_slots;
+
 			et_entry = &sc->enclosure_table[enc_idx];
 			if (et_entry->init_complete &&
 			    !et_entry->missing_count) {
@@ -2796,6 +2798,17 @@ mpr_mapping_enclosure_dev_status_change_event(struct mpr_softc *sc,
 			et_entry->enc_handle = le16toh(event_data->
 			    EnclosureHandle);
 			et_entry->start_slot = le16toh(event_data->StartSlot);
+			new_num_slots = le16toh(event_data->NumSlots);
+			if (new_num_slots < sc->encl_min_slots) {
+				mpr_dprint(sc, MPR_MAPPING, "%s: Enclosure %d num_slots %d, overriding with %d.\n",
+					   __func__, enc_idx, new_num_slots, sc->encl_min_slots);
+				new_num_slots = sc->encl_min_slots;
+			}
+			if (et_entry->num_slots != new_num_slots) {
+				mpr_dprint(sc, MPR_MAPPING, "%s: Enclosure %d old num_slots %d, new %d.\n",
+					   __func__, enc_idx, et_entry->num_slots, sc->encl_min_slots);
+				et_entry->num_slots = new_num_slots;
+			}
 			saved_phy_bits = et_entry->phy_bits;
 			et_entry->phy_bits |= le32toh(event_data->PhyBits);
 			if (saved_phy_bits != et_entry->phy_bits)
@@ -2858,6 +2871,11 @@ mpr_mapping_enclosure_dev_status_change_event(struct mpr_softc *sc,
 			et_entry->start_index = MPR_MAPTABLE_BAD_IDX;
 			et_entry->dpm_entry_num = MPR_DPM_BAD_IDX;
 			et_entry->num_slots = le16toh(event_data->NumSlots);
+			if (et_entry->num_slots < sc->encl_min_slots) {
+				mpr_dprint(sc, MPR_ERROR | MPR_MAPPING, "%s: Enclosure %d num_slots is %d, overriding with %d.\n",
+					   __func__, enc_idx, et_entry->num_slots, sc->encl_min_slots);
+				et_entry->num_slots = sc->encl_min_slots;
+			}
 			et_entry->start_slot = le16toh(event_data->StartSlot);
 			et_entry->phy_bits = le32toh(event_data->PhyBits);
 		}
diff --git a/sys/dev/mpr/mprvar.h b/sys/dev/mpr/mprvar.h
index 0f1743f4266e..93f3fbffe079 100644
--- a/sys/dev/mpr/mprvar.h
+++ b/sys/dev/mpr/mprvar.h
@@ -366,6 +366,7 @@ struct mpr_softc {
 	int				spinup_wait_time;
 	int				use_phynum;
 	int				dump_reqs_alltypes;
+	int                             encl_min_slots;
 	uint64_t			chain_alloc_fail;
 	uint64_t			prp_page_alloc_fail;
 	struct sysctl_ctx_list		sysctl_ctx;