diff options
Diffstat (limited to 'drivers/ata/libata-eh.c')
-rw-r--r-- | drivers/ata/libata-eh.c | 52 |
1 files changed, 42 insertions, 10 deletions
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 06a92c58a49d..751dad0138ae 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c | |||
@@ -2047,19 +2047,11 @@ static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset, | |||
2047 | unsigned int *classes, unsigned long deadline) | 2047 | unsigned int *classes, unsigned long deadline) |
2048 | { | 2048 | { |
2049 | struct ata_device *dev; | 2049 | struct ata_device *dev; |
2050 | int rc; | ||
2051 | 2050 | ||
2052 | ata_link_for_each_dev(dev, link) | 2051 | ata_link_for_each_dev(dev, link) |
2053 | classes[dev->devno] = ATA_DEV_UNKNOWN; | 2052 | classes[dev->devno] = ATA_DEV_UNKNOWN; |
2054 | 2053 | ||
2055 | rc = reset(link, classes, deadline); | 2054 | return reset(link, classes, deadline); |
2056 | |||
2057 | /* convert all ATA_DEV_UNKNOWN to ATA_DEV_NONE */ | ||
2058 | ata_link_for_each_dev(dev, link) | ||
2059 | if (classes[dev->devno] == ATA_DEV_UNKNOWN) | ||
2060 | classes[dev->devno] = ATA_DEV_NONE; | ||
2061 | |||
2062 | return rc; | ||
2063 | } | 2055 | } |
2064 | 2056 | ||
2065 | static int ata_eh_followup_srst_needed(struct ata_link *link, | 2057 | static int ata_eh_followup_srst_needed(struct ata_link *link, |
@@ -2096,7 +2088,7 @@ int ata_eh_reset(struct ata_link *link, int classify, | |||
2096 | ata_reset_fn_t reset; | 2088 | ata_reset_fn_t reset; |
2097 | unsigned long flags; | 2089 | unsigned long flags; |
2098 | u32 sstatus; | 2090 | u32 sstatus; |
2099 | int rc; | 2091 | int nr_known, rc; |
2100 | 2092 | ||
2101 | /* | 2093 | /* |
2102 | * Prepare to reset | 2094 | * Prepare to reset |
@@ -2245,9 +2237,49 @@ int ata_eh_reset(struct ata_link *link, int classify, | |||
2245 | if (ata_is_host_link(link)) | 2237 | if (ata_is_host_link(link)) |
2246 | ata_eh_thaw_port(ap); | 2238 | ata_eh_thaw_port(ap); |
2247 | 2239 | ||
2240 | /* postreset() should clear hardware SError. Although SError | ||
2241 | * is cleared during link resume, clearing SError here is | ||
2242 | * necessary as some PHYs raise hotplug events after SRST. | ||
2243 | * This introduces race condition where hotplug occurs between | ||
2244 | * reset and here. This race is mediated by cross checking | ||
2245 | * link onlineness and classification result later. | ||
2246 | */ | ||
2248 | if (postreset) | 2247 | if (postreset) |
2249 | postreset(link, classes); | 2248 | postreset(link, classes); |
2250 | 2249 | ||
2250 | /* clear cached SError */ | ||
2251 | spin_lock_irqsave(link->ap->lock, flags); | ||
2252 | link->eh_info.serror = 0; | ||
2253 | spin_unlock_irqrestore(link->ap->lock, flags); | ||
2254 | |||
2255 | /* Make sure onlineness and classification result correspond. | ||
2256 | * Hotplug could have happened during reset and some | ||
2257 | * controllers fail to wait while a drive is spinning up after | ||
2258 | * being hotplugged causing misdetection. By cross checking | ||
2259 | * link onlineness and classification result, those conditions | ||
2260 | * can be reliably detected and retried. | ||
2261 | */ | ||
2262 | nr_known = 0; | ||
2263 | ata_link_for_each_dev(dev, link) { | ||
2264 | /* convert all ATA_DEV_UNKNOWN to ATA_DEV_NONE */ | ||
2265 | if (classes[dev->devno] == ATA_DEV_UNKNOWN) | ||
2266 | classes[dev->devno] = ATA_DEV_NONE; | ||
2267 | else | ||
2268 | nr_known++; | ||
2269 | } | ||
2270 | |||
2271 | if (classify && !nr_known && ata_link_online(link)) { | ||
2272 | if (try < max_tries) { | ||
2273 | ata_link_printk(link, KERN_WARNING, "link online but " | ||
2274 | "device misclassified, retrying\n"); | ||
2275 | rc = -EAGAIN; | ||
2276 | goto fail; | ||
2277 | } | ||
2278 | ata_link_printk(link, KERN_WARNING, | ||
2279 | "link online but device misclassified, " | ||
2280 | "device detection might fail\n"); | ||
2281 | } | ||
2282 | |||
2251 | /* reset successful, schedule revalidation */ | 2283 | /* reset successful, schedule revalidation */ |
2252 | ata_eh_done(link, NULL, ATA_EH_RESET); | 2284 | ata_eh_done(link, NULL, ATA_EH_RESET); |
2253 | ehc->i.action |= ATA_EH_REVALIDATE; | 2285 | ehc->i.action |= ATA_EH_REVALIDATE; |