We found that in the scenario where the expander device is connected to a wide port, a physical link connected to the wide port link down and re-establish the link at a lower link rate, while the expander device link rate and all expander PHY link rates maintain the original link rate, the following error occurs: [175712.419423] hisi_sas_v3_hw 0000:74:02.0: erroneous completion iptt=2985 task=00000000268357f1 dev id=10 exp 0x500e004aaaaaaa1f phy9 addr=500e004aaaaaaa09 CQ hdr: 0x102b 0xa0ba9 0x1000 0x20000 Error info: 0x200 0x0 0x0 0x0 After analysis, it is concluded that: when the physical link is re-established, the link rate of the expander device and the expander PHY are not updated. As a result, the expander PHY attached to a SATA PHY is using link rate greater than the physical PHY link rate. Therefore, add support for check whether the link rate of physical PHY which is connected to the port changes after the phy up occur, if the link rate of the newly established physical phy is lower than the link rate of the port, a smaller link rate is transmitted to the port and update the device link rate that needs to be updated in port->dev_list. Signed-off-by: Yihang Li <liyihang9@xxxxxxxxxx> --- drivers/scsi/libsas/sas_discover.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index 6998560812f2..e453d94fbd30 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -164,6 +164,20 @@ static int sas_get_port_device(struct asd_sas_port *port) return 0; } +static void sas_check_port_linkrate(struct asd_sas_port *port) +{ + struct asd_sas_phy *phy; + u32 link_rate = port->linkrate; + + list_for_each_entry(phy, &port->phy_list, port_phy_el) + link_rate = min(link_rate, phy->linkrate); + + if (port->linkrate != link_rate) { + port->linkrate = link_rate; + sas_update_linkrate(port); + } +} + /* ---------- Discover and Revalidate ---------- */ int sas_notify_lldd_dev_found(struct domain_device *dev) @@ -435,8 +449,10 @@ static void sas_discover_domain(struct work_struct *work) clear_bit(DISCE_DISCOVER_DOMAIN, &port->disc.pending); - if (port->port_dev) + if (port->port_dev) { + sas_check_port_linkrate(port); return; + } error = sas_get_port_device(port); if (error) -- 2.30.0