diff options
author | Jonathan Corbet <corbet@lwn.net> | 2008-07-14 17:29:34 -0400 |
---|---|---|
committer | Jonathan Corbet <corbet@lwn.net> | 2008-07-14 17:29:34 -0400 |
commit | 2fceef397f9880b212a74c418290ce69e7ac00eb (patch) | |
tree | d9cc09ab992825ef7fede4a688103503e3caf655 /drivers/ata/libata-eh.c | |
parent | feae1ef116ed381625d3731c5ae4f4ebcb3fa302 (diff) | |
parent | bce7f793daec3e65ec5c5705d2457b81fe7b5725 (diff) |
Merge commit 'v2.6.26' into bkl-removal
Diffstat (limited to 'drivers/ata/libata-eh.c')
-rw-r--r-- | drivers/ata/libata-eh.c | 207 |
1 files changed, 113 insertions, 94 deletions
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 62e033146bed..7894d83ea1eb 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c | |||
@@ -1308,12 +1308,7 @@ static void ata_eh_analyze_serror(struct ata_link *link) | |||
1308 | unsigned int err_mask = 0, action = 0; | 1308 | unsigned int err_mask = 0, action = 0; |
1309 | u32 hotplug_mask; | 1309 | u32 hotplug_mask; |
1310 | 1310 | ||
1311 | if (serror & SERR_PERSISTENT) { | 1311 | if (serror & (SERR_PERSISTENT | SERR_DATA)) { |
1312 | err_mask |= AC_ERR_ATA_BUS; | ||
1313 | action |= ATA_EH_RESET; | ||
1314 | } | ||
1315 | if (serror & | ||
1316 | (SERR_DATA_RECOVERED | SERR_COMM_RECOVERED | SERR_DATA)) { | ||
1317 | err_mask |= AC_ERR_ATA_BUS; | 1312 | err_mask |= AC_ERR_ATA_BUS; |
1318 | action |= ATA_EH_RESET; | 1313 | action |= ATA_EH_RESET; |
1319 | } | 1314 | } |
@@ -2047,19 +2042,11 @@ static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset, | |||
2047 | unsigned int *classes, unsigned long deadline) | 2042 | unsigned int *classes, unsigned long deadline) |
2048 | { | 2043 | { |
2049 | struct ata_device *dev; | 2044 | struct ata_device *dev; |
2050 | int rc; | ||
2051 | 2045 | ||
2052 | ata_link_for_each_dev(dev, link) | 2046 | ata_link_for_each_dev(dev, link) |
2053 | classes[dev->devno] = ATA_DEV_UNKNOWN; | 2047 | classes[dev->devno] = ATA_DEV_UNKNOWN; |
2054 | 2048 | ||
2055 | rc = reset(link, classes, deadline); | 2049 | 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 | } | 2050 | } |
2064 | 2051 | ||
2065 | static int ata_eh_followup_srst_needed(struct ata_link *link, | 2052 | static int ata_eh_followup_srst_needed(struct ata_link *link, |
@@ -2096,9 +2083,11 @@ int ata_eh_reset(struct ata_link *link, int classify, | |||
2096 | ata_reset_fn_t reset; | 2083 | ata_reset_fn_t reset; |
2097 | unsigned long flags; | 2084 | unsigned long flags; |
2098 | u32 sstatus; | 2085 | u32 sstatus; |
2099 | int rc; | 2086 | int nr_known, rc; |
2100 | 2087 | ||
2101 | /* about to reset */ | 2088 | /* |
2089 | * Prepare to reset | ||
2090 | */ | ||
2102 | spin_lock_irqsave(ap->lock, flags); | 2091 | spin_lock_irqsave(ap->lock, flags); |
2103 | ap->pflags |= ATA_PFLAG_RESETTING; | 2092 | ap->pflags |= ATA_PFLAG_RESETTING; |
2104 | spin_unlock_irqrestore(ap->lock, flags); | 2093 | spin_unlock_irqrestore(ap->lock, flags); |
@@ -2124,16 +2113,8 @@ int ata_eh_reset(struct ata_link *link, int classify, | |||
2124 | ap->ops->set_piomode(ap, dev); | 2113 | ap->ops->set_piomode(ap, dev); |
2125 | } | 2114 | } |
2126 | 2115 | ||
2127 | if (!softreset && !hardreset) { | ||
2128 | if (verbose) | ||
2129 | ata_link_printk(link, KERN_INFO, "no reset method " | ||
2130 | "available, skipping reset\n"); | ||
2131 | if (!(lflags & ATA_LFLAG_ASSUME_CLASS)) | ||
2132 | lflags |= ATA_LFLAG_ASSUME_ATA; | ||
2133 | goto done; | ||
2134 | } | ||
2135 | |||
2136 | /* prefer hardreset */ | 2116 | /* prefer hardreset */ |
2117 | reset = NULL; | ||
2137 | ehc->i.action &= ~ATA_EH_RESET; | 2118 | ehc->i.action &= ~ATA_EH_RESET; |
2138 | if (hardreset) { | 2119 | if (hardreset) { |
2139 | reset = hardreset; | 2120 | reset = hardreset; |
@@ -2141,11 +2122,6 @@ int ata_eh_reset(struct ata_link *link, int classify, | |||
2141 | } else if (softreset) { | 2122 | } else if (softreset) { |
2142 | reset = softreset; | 2123 | reset = softreset; |
2143 | ehc->i.action = ATA_EH_SOFTRESET; | 2124 | ehc->i.action = ATA_EH_SOFTRESET; |
2144 | } else { | ||
2145 | ata_link_printk(link, KERN_ERR, "BUG: no reset method, " | ||
2146 | "please report to linux-ide@vger.kernel.org\n"); | ||
2147 | dump_stack(); | ||
2148 | return -EINVAL; | ||
2149 | } | 2125 | } |
2150 | 2126 | ||
2151 | if (prereset) { | 2127 | if (prereset) { |
@@ -2165,55 +2141,71 @@ int ata_eh_reset(struct ata_link *link, int classify, | |||
2165 | "prereset failed (errno=%d)\n", rc); | 2141 | "prereset failed (errno=%d)\n", rc); |
2166 | goto out; | 2142 | goto out; |
2167 | } | 2143 | } |
2168 | } | ||
2169 | 2144 | ||
2170 | /* prereset() might have cleared ATA_EH_RESET */ | 2145 | /* prereset() might have cleared ATA_EH_RESET. If so, |
2171 | if (!(ehc->i.action & ATA_EH_RESET)) { | 2146 | * bang classes and return. |
2172 | /* prereset told us not to reset, bang classes and return */ | 2147 | */ |
2173 | ata_link_for_each_dev(dev, link) | 2148 | if (reset && !(ehc->i.action & ATA_EH_RESET)) { |
2174 | classes[dev->devno] = ATA_DEV_NONE; | 2149 | ata_link_for_each_dev(dev, link) |
2175 | rc = 0; | 2150 | classes[dev->devno] = ATA_DEV_NONE; |
2176 | goto out; | 2151 | rc = 0; |
2152 | goto out; | ||
2153 | } | ||
2177 | } | 2154 | } |
2178 | 2155 | ||
2179 | retry: | 2156 | retry: |
2157 | /* | ||
2158 | * Perform reset | ||
2159 | */ | ||
2160 | if (ata_is_host_link(link)) | ||
2161 | ata_eh_freeze_port(ap); | ||
2162 | |||
2180 | deadline = jiffies + ata_eh_reset_timeouts[try++]; | 2163 | deadline = jiffies + ata_eh_reset_timeouts[try++]; |
2181 | 2164 | ||
2182 | /* shut up during boot probing */ | 2165 | if (reset) { |
2183 | if (verbose) | 2166 | if (verbose) |
2184 | ata_link_printk(link, KERN_INFO, "%s resetting link\n", | 2167 | ata_link_printk(link, KERN_INFO, "%s resetting link\n", |
2185 | reset == softreset ? "soft" : "hard"); | 2168 | reset == softreset ? "soft" : "hard"); |
2186 | 2169 | ||
2187 | /* mark that this EH session started with reset */ | 2170 | /* mark that this EH session started with reset */ |
2188 | if (reset == hardreset) | 2171 | if (reset == hardreset) |
2189 | ehc->i.flags |= ATA_EHI_DID_HARDRESET; | 2172 | ehc->i.flags |= ATA_EHI_DID_HARDRESET; |
2190 | else | 2173 | else |
2191 | ehc->i.flags |= ATA_EHI_DID_SOFTRESET; | 2174 | ehc->i.flags |= ATA_EHI_DID_SOFTRESET; |
2192 | 2175 | ||
2193 | rc = ata_do_reset(link, reset, classes, deadline); | 2176 | rc = ata_do_reset(link, reset, classes, deadline); |
2194 | 2177 | ||
2195 | if (reset == hardreset && | 2178 | if (reset == hardreset && |
2196 | ata_eh_followup_srst_needed(link, rc, classify, classes)) { | 2179 | ata_eh_followup_srst_needed(link, rc, classify, classes)) { |
2197 | /* okay, let's do follow-up softreset */ | 2180 | /* okay, let's do follow-up softreset */ |
2198 | reset = softreset; | 2181 | reset = softreset; |
2199 | 2182 | ||
2200 | if (!reset) { | 2183 | if (!reset) { |
2201 | ata_link_printk(link, KERN_ERR, | 2184 | ata_link_printk(link, KERN_ERR, |
2202 | "follow-up softreset required " | 2185 | "follow-up softreset required " |
2203 | "but no softreset avaliable\n"); | 2186 | "but no softreset avaliable\n"); |
2204 | rc = -EINVAL; | 2187 | rc = -EINVAL; |
2205 | goto fail; | 2188 | goto fail; |
2189 | } | ||
2190 | |||
2191 | ata_eh_about_to_do(link, NULL, ATA_EH_RESET); | ||
2192 | rc = ata_do_reset(link, reset, classes, deadline); | ||
2206 | } | 2193 | } |
2207 | 2194 | ||
2208 | ata_eh_about_to_do(link, NULL, ATA_EH_RESET); | 2195 | /* -EAGAIN can happen if we skipped followup SRST */ |
2209 | rc = ata_do_reset(link, reset, classes, deadline); | 2196 | if (rc && rc != -EAGAIN) |
2197 | goto fail; | ||
2198 | } else { | ||
2199 | if (verbose) | ||
2200 | ata_link_printk(link, KERN_INFO, "no reset method " | ||
2201 | "available, skipping reset\n"); | ||
2202 | if (!(lflags & ATA_LFLAG_ASSUME_CLASS)) | ||
2203 | lflags |= ATA_LFLAG_ASSUME_ATA; | ||
2210 | } | 2204 | } |
2211 | 2205 | ||
2212 | /* -EAGAIN can happen if we skipped followup SRST */ | 2206 | /* |
2213 | if (rc && rc != -EAGAIN) | 2207 | * Post-reset processing |
2214 | goto fail; | 2208 | */ |
2215 | |||
2216 | done: | ||
2217 | ata_link_for_each_dev(dev, link) { | 2209 | ata_link_for_each_dev(dev, link) { |
2218 | /* After the reset, the device state is PIO 0 and the | 2210 | /* After the reset, the device state is PIO 0 and the |
2219 | * controller state is undefined. Reset also wakes up | 2211 | * controller state is undefined. Reset also wakes up |
@@ -2236,9 +2228,53 @@ int ata_eh_reset(struct ata_link *link, int classify, | |||
2236 | if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0) | 2228 | if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0) |
2237 | link->sata_spd = (sstatus >> 4) & 0xf; | 2229 | link->sata_spd = (sstatus >> 4) & 0xf; |
2238 | 2230 | ||
2231 | /* thaw the port */ | ||
2232 | if (ata_is_host_link(link)) | ||
2233 | ata_eh_thaw_port(ap); | ||
2234 | |||
2235 | /* postreset() should clear hardware SError. Although SError | ||
2236 | * is cleared during link resume, clearing SError here is | ||
2237 | * necessary as some PHYs raise hotplug events after SRST. | ||
2238 | * This introduces race condition where hotplug occurs between | ||
2239 | * reset and here. This race is mediated by cross checking | ||
2240 | * link onlineness and classification result later. | ||
2241 | */ | ||
2239 | if (postreset) | 2242 | if (postreset) |
2240 | postreset(link, classes); | 2243 | postreset(link, classes); |
2241 | 2244 | ||
2245 | /* clear cached SError */ | ||
2246 | spin_lock_irqsave(link->ap->lock, flags); | ||
2247 | link->eh_info.serror = 0; | ||
2248 | spin_unlock_irqrestore(link->ap->lock, flags); | ||
2249 | |||
2250 | /* Make sure onlineness and classification result correspond. | ||
2251 | * Hotplug could have happened during reset and some | ||
2252 | * controllers fail to wait while a drive is spinning up after | ||
2253 | * being hotplugged causing misdetection. By cross checking | ||
2254 | * link onlineness and classification result, those conditions | ||
2255 | * can be reliably detected and retried. | ||
2256 | */ | ||
2257 | nr_known = 0; | ||
2258 | ata_link_for_each_dev(dev, link) { | ||
2259 | /* convert all ATA_DEV_UNKNOWN to ATA_DEV_NONE */ | ||
2260 | if (classes[dev->devno] == ATA_DEV_UNKNOWN) | ||
2261 | classes[dev->devno] = ATA_DEV_NONE; | ||
2262 | else | ||
2263 | nr_known++; | ||
2264 | } | ||
2265 | |||
2266 | if (classify && !nr_known && ata_link_online(link)) { | ||
2267 | if (try < max_tries) { | ||
2268 | ata_link_printk(link, KERN_WARNING, "link online but " | ||
2269 | "device misclassified, retrying\n"); | ||
2270 | rc = -EAGAIN; | ||
2271 | goto fail; | ||
2272 | } | ||
2273 | ata_link_printk(link, KERN_WARNING, | ||
2274 | "link online but device misclassified, " | ||
2275 | "device detection might fail\n"); | ||
2276 | } | ||
2277 | |||
2242 | /* reset successful, schedule revalidation */ | 2278 | /* reset successful, schedule revalidation */ |
2243 | ata_eh_done(link, NULL, ATA_EH_RESET); | 2279 | ata_eh_done(link, NULL, ATA_EH_RESET); |
2244 | ehc->i.action |= ATA_EH_REVALIDATE; | 2280 | ehc->i.action |= ATA_EH_REVALIDATE; |
@@ -2587,7 +2623,7 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, | |||
2587 | struct ata_link *link; | 2623 | struct ata_link *link; |
2588 | struct ata_device *dev; | 2624 | struct ata_device *dev; |
2589 | int nr_failed_devs, nr_disabled_devs; | 2625 | int nr_failed_devs, nr_disabled_devs; |
2590 | int reset, rc; | 2626 | int rc; |
2591 | unsigned long flags; | 2627 | unsigned long flags; |
2592 | 2628 | ||
2593 | DPRINTK("ENTER\n"); | 2629 | DPRINTK("ENTER\n"); |
@@ -2630,7 +2666,6 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, | |||
2630 | rc = 0; | 2666 | rc = 0; |
2631 | nr_failed_devs = 0; | 2667 | nr_failed_devs = 0; |
2632 | nr_disabled_devs = 0; | 2668 | nr_disabled_devs = 0; |
2633 | reset = 0; | ||
2634 | 2669 | ||
2635 | /* if UNLOADING, finish immediately */ | 2670 | /* if UNLOADING, finish immediately */ |
2636 | if (ap->pflags & ATA_PFLAG_UNLOADING) | 2671 | if (ap->pflags & ATA_PFLAG_UNLOADING) |
@@ -2644,40 +2679,24 @@ int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset, | |||
2644 | if (ata_eh_skip_recovery(link)) | 2679 | if (ata_eh_skip_recovery(link)) |
2645 | ehc->i.action = 0; | 2680 | ehc->i.action = 0; |
2646 | 2681 | ||
2647 | /* do we need to reset? */ | ||
2648 | if (ehc->i.action & ATA_EH_RESET) | ||
2649 | reset = 1; | ||
2650 | |||
2651 | ata_link_for_each_dev(dev, link) | 2682 | ata_link_for_each_dev(dev, link) |
2652 | ehc->classes[dev->devno] = ATA_DEV_UNKNOWN; | 2683 | ehc->classes[dev->devno] = ATA_DEV_UNKNOWN; |
2653 | } | 2684 | } |
2654 | 2685 | ||
2655 | /* reset */ | 2686 | /* reset */ |
2656 | if (reset) { | 2687 | ata_port_for_each_link(link, ap) { |
2657 | /* if PMP is attached, this function only deals with | 2688 | struct ata_eh_context *ehc = &link->eh_context; |
2658 | * downstream links, port should stay thawed. | ||
2659 | */ | ||
2660 | if (!sata_pmp_attached(ap)) | ||
2661 | ata_eh_freeze_port(ap); | ||
2662 | |||
2663 | ata_port_for_each_link(link, ap) { | ||
2664 | struct ata_eh_context *ehc = &link->eh_context; | ||
2665 | 2689 | ||
2666 | if (!(ehc->i.action & ATA_EH_RESET)) | 2690 | if (!(ehc->i.action & ATA_EH_RESET)) |
2667 | continue; | 2691 | continue; |
2668 | 2692 | ||
2669 | rc = ata_eh_reset(link, ata_link_nr_vacant(link), | 2693 | rc = ata_eh_reset(link, ata_link_nr_vacant(link), |
2670 | prereset, softreset, hardreset, | 2694 | prereset, softreset, hardreset, postreset); |
2671 | postreset); | 2695 | if (rc) { |
2672 | if (rc) { | 2696 | ata_link_printk(link, KERN_ERR, |
2673 | ata_link_printk(link, KERN_ERR, | 2697 | "reset failed, giving up\n"); |
2674 | "reset failed, giving up\n"); | 2698 | goto out; |
2675 | goto out; | ||
2676 | } | ||
2677 | } | 2699 | } |
2678 | |||
2679 | if (!sata_pmp_attached(ap)) | ||
2680 | ata_eh_thaw_port(ap); | ||
2681 | } | 2700 | } |
2682 | 2701 | ||
2683 | /* the rest */ | 2702 | /* the rest */ |