diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index b8c3f9e6af8994820c30b40889154f87511014e0..a44982d6a42deb13d6f590f86da8ba9cadfe0505 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -4225,7 +4225,7 @@ void ata_std_postreset(struct ata_link *link, unsigned int *classes) * RETURNS: * 1 if @dev matches @new_class and @new_id, 0 otherwise. */ -static int ata_dev_same_device(struct ata_device *dev, unsigned int new_class, +int ata_dev_same_device(struct ata_device *dev, unsigned int new_class, const u16 *new_id) { const u16 *old_id = dev->id; @@ -7407,3 +7407,4 @@ EXPORT_SYMBOL_GPL(ata_cable_ignore); EXPORT_SYMBOL_GPL(ata_cable_sata); EXPORT_SYMBOL_GPL(ata_host_get); EXPORT_SYMBOL_GPL(ata_host_put); +EXPORT_SYMBOL_GPL(ata_dev_same_device); diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 64a958a99f6a89896c16354502c34a397d18a1df..1c36493602a43aad87f70ee8fcddf6bb7504985f 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -632,6 +632,22 @@ static int sas_get_ata_command_set(struct domain_device *dev) return ata_dev_classify(&tf); } +static void sas_ata_store_id(struct domain_device *dev) +{ + struct ata_device *ata_dev = sas_to_ata_dev(dev); + unsigned char model[ATA_ID_PROD_LEN + 1]; + unsigned char serial[ATA_ID_SERNO_LEN + 1]; + + /* store the ata device's class and id */ + memcpy(dev->sata_dev.id, ata_dev->id, ATA_ID_WORDS); + dev->sata_dev.class = ata_dev->class; + + ata_id_c_string(ata_dev->id, model, ATA_ID_PROD, sizeof(model)); + ata_id_c_string(ata_dev->id, serial, ATA_ID_SERNO, sizeof(serial)); + + sas_ata_printk(KERN_INFO, dev, "model:%s serial:%s\n", model, serial); +} + void sas_probe_sata(struct asd_sas_port *port) { struct domain_device *dev, *n; @@ -656,6 +672,8 @@ void sas_probe_sata(struct asd_sas_port *port) */ if (ata_dev_disabled(sas_to_ata_dev(dev))) sas_fail_probe(dev, __func__, -ENODEV); + else + sas_ata_store_id(dev); } } diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index bf5196fe693c516f9ba54073296af123f955553c..ec1f30a14b76290dbaa2987e08329a9982b7ea5b 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -2053,9 +2053,37 @@ static int sas_rediscover_dev(struct domain_device *dev, int phy_id, bool last) action = ", needs recovery"; SAS_DPRINTK("ex %016llx phy 0x%x broadcast flutter%s\n", SAS_ADDR(dev->sas_addr), phy_id, action); + + /* the phy attached address will be updated by sas_ex_phy_discover() + * and sometimes become abnormal + */ + if (SAS_ADDR(phy->attached_sas_addr) != SAS_ADDR(sas_addr) || + SAS_ADDR(phy->attached_sas_addr) == 0) { + /* if attached_sas_addr become abnormal, we must set the + * original address back so that the device can be unregistered + */ + memcpy(phy->attached_sas_addr, sas_addr, SAS_ADDR_SIZE); + SAS_DPRINTK("phy address(%016llx) abnormal, origin:%016llx\n", + SAS_ADDR(phy->attached_sas_addr), + SAS_ADDR(sas_addr)); + goto unregister; + } + + + if (ata_dev) { + struct ata_device *adev = sas_to_ata_dev(ata_dev); + unsigned int class = ata_dev->sata_dev.class; + u16 *id = ata_dev->sata_dev.id; + + /* to see if the disk is replaced with another one */ + if (!ata_dev_same_device(adev, class, id)) + goto unregister; + } + return res; } +unregister: /* we always have to delete the old device when we went here */ SAS_DPRINTK("ex %016llx phy 0x%x replace %016llx\n", SAS_ADDR(dev->sas_addr), phy_id, diff --git a/include/linux/libata.h b/include/linux/libata.h index 38c95d66ab12f1465fe02c9b62752a8887976dae..b3381000a909a7c05bd84b87e1fe08db7ef53d1f 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1145,6 +1145,8 @@ extern int sata_scr_write(struct ata_link *link, int reg, u32 val); extern int sata_scr_write_flush(struct ata_link *link, int reg, u32 val); extern bool ata_link_online(struct ata_link *link); extern bool ata_link_offline(struct ata_link *link); +extern int ata_dev_same_device(struct ata_device *dev, unsigned int new_class, + const u16 *new_id); #ifdef CONFIG_PM extern int ata_host_suspend(struct ata_host *host, pm_message_t mesg); extern void ata_host_resume(struct ata_host *host); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index 3de3b10da19a9eee24904a5604bb74744e8eafe9..258d5d8c71c8a61d7b78421354047b17680fbde9 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -164,6 +164,7 @@ struct sata_device { struct ata_host *ata_host; struct smp_resp rps_resp ____cacheline_aligned; /* report_phy_sata_resp */ u8 fis[ATA_RESP_FIS_SIZE]; + u16 id[ATA_ID_WORDS]; }; struct ssp_device {