summaryrefslogtreecommitdiff
path: root/drivers/scsi/libsas/sas_expander.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-29 11:55:36 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-29 11:55:36 -0700
commit08dbfad3f5040f5bdb6c529da20d6d4e81fefd72 (patch)
tree94767b4d009979fd40f7c1deab59bb578c88529b /drivers/scsi/libsas/sas_expander.c
parentcf72cbb39da84b6f02f90c07f33b102fc10b16f0 (diff)
parent12e67eb89eb2b9516685c744d3f7de0a2d1bd701 (diff)
downloadlinux-stable-master.tar.gz
linux-stable-master.zip
Merge tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsiHEADmaster
Pull more SCSI updates from Martin Petersen: "Remaining updates for the 7.3 merge window. The only core change is enabling context analysis for the SCSI layer and UFS. The remaining changes are either bug fixes or hardening" * tag 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/mkp/scsi: (26 commits) scsi: snic: Fix SCSI host leak on workqueue allocation failure scsi: MAINTAINERS: Update my email address scsi: MAINTAINERS: Leave the cumana_1 and oak drivers to the RISCPC maintainers scsi: leapraid: Standardize NCQ priority sysfs attributes scsi: leapraid: Serialize firmware log mmap with teardown scsi: leapraid: Balance host references for firmware log VMAs scsi: lpfc: Remove unnnecessary NULL check scsi: qla2xxx: Fix an loop timeout test scsi: qla2xxx: Fix an error code in qla_get_tmf() scsi: ibmvfc: Fix use of uninitialized rport in ibmvfc_do_work() scsi: core: Enable context analysis for hosts.o scsi: lpfc: Replace strlcat() with sysfs_emit_at() in the sysfs show functions scsi: lpfc: Replace strlcat() with seq_buf in the debugfs dump helpers scsi: lpfc: Replace strlcat() with seq_buf in lpfc_rx_monitor_report() scsi: lpfc: Replace strlcat() with scnprintf() in lpfc_vport_symbolic_node_name() scsi: lpfc: Replace strlcat() with seq_buf in lpfc_info() scsi: core: Enable context analysis scsi: core: Protect host state changes with the host lock scsi: core: Add lock context annotations scsi: core: Pass the SCSI host pointer directly to scanning functions ...
Diffstat (limited to 'drivers/scsi/libsas/sas_expander.c')
-rw-r--r--drivers/scsi/libsas/sas_expander.c96
1 files changed, 78 insertions, 18 deletions
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c
index f471ab464a78..811c9eb4fef1 100644
--- a/drivers/scsi/libsas/sas_expander.c
+++ b/drivers/scsi/libsas/sas_expander.c
@@ -345,11 +345,10 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id,
SAS_ADDR(phy->attached_sas_addr), type);
}
-/* check if we have an existing attached ata device on this expander phy */
-struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id)
+/* Return the domain device attached to an expander phy */
+struct domain_device *sas_ex_phy_to_dev(struct domain_device *ex_dev, int phy_id)
{
struct ex_phy *ex_phy = &ex_dev->ex_dev.ex_phy[phy_id];
- struct domain_device *dev;
struct sas_rphy *rphy;
if (!ex_phy->port)
@@ -359,7 +358,13 @@ struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id)
if (!rphy)
return NULL;
- dev = sas_find_dev_by_rphy(rphy);
+ return sas_find_dev_by_rphy(rphy);
+}
+
+/* Check if we have an existing attached ata device on this expander phy */
+struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id)
+{
+ struct domain_device *dev = sas_ex_phy_to_dev(ex_dev, phy_id);
if (dev && dev_is_sata(dev))
return dev;
@@ -1958,6 +1963,72 @@ static bool dev_type_flutter(enum sas_device_type new, enum sas_device_type old)
return false;
}
+static void sas_rediscover_ex_phy(struct domain_device *dev, int phy_id,
+ bool last)
+{
+ struct expander_device *ex = &dev->ex_dev;
+ struct ex_phy *phy = &ex->ex_phy[phy_id];
+
+ phy->phy_change_count = -1;
+ ex->ex_change_count = -1;
+ sas_unregister_devs_sas_addr(dev, phy_id, last);
+ sas_discover_event(dev->port, DISCE_REVALIDATE_DOMAIN);
+}
+
+static bool sas_dev_is_flutter(struct domain_device *dev, int phy_id,
+ u8 *sas_addr, enum sas_device_type type)
+{
+ struct expander_device *ex = &dev->ex_dev;
+ struct ex_phy *phy = &ex->ex_phy[phy_id];
+ struct domain_device *child_dev;
+ char *action = "";
+ int res;
+
+ if (SAS_ADDR(sas_addr) != SAS_ADDR(phy->attached_sas_addr) ||
+ !dev_type_flutter(type, phy->attached_dev_type))
+ return false;
+
+ res = sas_ex_phy_discover(dev, phy_id);
+ if (res)
+ return false;
+
+ child_dev = sas_ex_phy_to_dev(dev, phy_id);
+ if (!child_dev)
+ goto out;
+
+ if (dev_is_sata(child_dev) &&
+ phy->attached_dev_type == SAS_SATA_PENDING) {
+ action = ", needs recovery";
+ goto out;
+ }
+
+ if (SAS_ADDR(child_dev->sas_addr) != SAS_ADDR(phy->attached_sas_addr)) {
+ pr_info("ex %016llx phy%02d sas_addr changed from %016llx to %016llx\n",
+ SAS_ADDR(dev->sas_addr), phy_id,
+ SAS_ADDR(child_dev->sas_addr),
+ SAS_ADDR(phy->attached_sas_addr));
+ /*
+ * Device unregistering relies on address matching. Restore
+ * attached_sas_addr back to the original address so that the old
+ * device can be unregistered later
+ */
+ memcpy(phy->attached_sas_addr, child_dev->sas_addr, SAS_ADDR_SIZE);
+ return false;
+ }
+
+ if (child_dev->linkrate != phy->linkrate) {
+ pr_info("ex %016llx phy%02d linkrate changed from %d to %d\n",
+ SAS_ADDR(dev->sas_addr), phy_id,
+ child_dev->linkrate, phy->linkrate);
+ return false;
+ }
+
+out:
+ pr_debug("ex %016llx phy%02d broadcast flutter%s\n",
+ SAS_ADDR(dev->sas_addr), phy_id, action);
+ return true;
+}
+
static int sas_rediscover_dev(struct domain_device *dev, int phy_id,
bool last, int sibling)
{
@@ -2011,27 +2082,16 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id,
if (res == 0)
sas_set_ex_phy(dev, phy_id, disc_resp);
goto out_free_resp;
- } else if (SAS_ADDR(sas_addr) == SAS_ADDR(phy->attached_sas_addr) &&
- dev_type_flutter(type, phy->attached_dev_type)) {
- struct domain_device *ata_dev = sas_ex_to_ata(dev, phy_id);
- char *action = "";
-
- sas_ex_phy_discover(dev, phy_id);
+ }
- if (ata_dev && phy->attached_dev_type == SAS_SATA_PENDING)
- action = ", needs recovery";
- pr_debug("ex %016llx phy%02d broadcast flutter%s\n",
- SAS_ADDR(dev->sas_addr), phy_id, action);
+ if (sas_dev_is_flutter(dev, phy_id, sas_addr, type))
goto out_free_resp;
- }
/* we always have to delete the old device when we went here */
pr_info("ex %016llx phy%02d replace %016llx\n",
SAS_ADDR(dev->sas_addr), phy_id,
SAS_ADDR(phy->attached_sas_addr));
- sas_unregister_devs_sas_addr(dev, phy_id, last);
-
- res = sas_discover_new(dev, phy_id);
+ sas_rediscover_ex_phy(dev, phy_id, last);
out_free_resp:
kfree(disc_resp);
return res;