diff options
Diffstat (limited to 'drivers/scsi/libata-eh.c')
-rw-r--r-- | drivers/scsi/libata-eh.c | 128 |
1 files changed, 126 insertions, 2 deletions
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index b9df49a36214..4b6aa30f4d68 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c | |||
@@ -47,6 +47,8 @@ | |||
47 | 47 | ||
48 | static void __ata_port_freeze(struct ata_port *ap); | 48 | static void __ata_port_freeze(struct ata_port *ap); |
49 | static void ata_eh_finish(struct ata_port *ap); | 49 | static void ata_eh_finish(struct ata_port *ap); |
50 | static void ata_eh_handle_port_suspend(struct ata_port *ap); | ||
51 | static void ata_eh_handle_port_resume(struct ata_port *ap); | ||
50 | 52 | ||
51 | static void ata_ering_record(struct ata_ering *ering, int is_io, | 53 | static void ata_ering_record(struct ata_ering *ering, int is_io, |
52 | unsigned int err_mask) | 54 | unsigned int err_mask) |
@@ -262,6 +264,9 @@ void ata_scsi_error(struct Scsi_Host *host) | |||
262 | repeat: | 264 | repeat: |
263 | /* invoke error handler */ | 265 | /* invoke error handler */ |
264 | if (ap->ops->error_handler) { | 266 | if (ap->ops->error_handler) { |
267 | /* process port resume request */ | ||
268 | ata_eh_handle_port_resume(ap); | ||
269 | |||
265 | /* fetch & clear EH info */ | 270 | /* fetch & clear EH info */ |
266 | spin_lock_irqsave(ap->lock, flags); | 271 | spin_lock_irqsave(ap->lock, flags); |
267 | 272 | ||
@@ -274,12 +279,15 @@ void ata_scsi_error(struct Scsi_Host *host) | |||
274 | 279 | ||
275 | spin_unlock_irqrestore(ap->lock, flags); | 280 | spin_unlock_irqrestore(ap->lock, flags); |
276 | 281 | ||
277 | /* invoke EH. if unloading, just finish failed qcs */ | 282 | /* invoke EH, skip if unloading or suspended */ |
278 | if (!(ap->pflags & ATA_PFLAG_UNLOADING)) | 283 | if (!(ap->pflags & (ATA_PFLAG_UNLOADING | ATA_PFLAG_SUSPENDED))) |
279 | ap->ops->error_handler(ap); | 284 | ap->ops->error_handler(ap); |
280 | else | 285 | else |
281 | ata_eh_finish(ap); | 286 | ata_eh_finish(ap); |
282 | 287 | ||
288 | /* process port suspend request */ | ||
289 | ata_eh_handle_port_suspend(ap); | ||
290 | |||
283 | /* Exception might have happend after ->error_handler | 291 | /* Exception might have happend after ->error_handler |
284 | * recovered the port but before this point. Repeat | 292 | * recovered the port but before this point. Repeat |
285 | * EH in such case. | 293 | * EH in such case. |
@@ -2101,3 +2109,119 @@ void ata_do_eh(struct ata_port *ap, ata_prereset_fn_t prereset, | |||
2101 | ata_eh_recover(ap, prereset, softreset, hardreset, postreset); | 2109 | ata_eh_recover(ap, prereset, softreset, hardreset, postreset); |
2102 | ata_eh_finish(ap); | 2110 | ata_eh_finish(ap); |
2103 | } | 2111 | } |
2112 | |||
2113 | /** | ||
2114 | * ata_eh_handle_port_suspend - perform port suspend operation | ||
2115 | * @ap: port to suspend | ||
2116 | * | ||
2117 | * Suspend @ap. | ||
2118 | * | ||
2119 | * LOCKING: | ||
2120 | * Kernel thread context (may sleep). | ||
2121 | */ | ||
2122 | static void ata_eh_handle_port_suspend(struct ata_port *ap) | ||
2123 | { | ||
2124 | unsigned long flags; | ||
2125 | int rc = 0; | ||
2126 | |||
2127 | /* are we suspending? */ | ||
2128 | spin_lock_irqsave(ap->lock, flags); | ||
2129 | if (!(ap->pflags & ATA_PFLAG_PM_PENDING) || | ||
2130 | ap->pm_mesg.event == PM_EVENT_ON) { | ||
2131 | spin_unlock_irqrestore(ap->lock, flags); | ||
2132 | return; | ||
2133 | } | ||
2134 | spin_unlock_irqrestore(ap->lock, flags); | ||
2135 | |||
2136 | WARN_ON(ap->pflags & ATA_PFLAG_SUSPENDED); | ||
2137 | |||
2138 | /* suspend */ | ||
2139 | ata_eh_freeze_port(ap); | ||
2140 | |||
2141 | if (ap->ops->port_suspend) | ||
2142 | rc = ap->ops->port_suspend(ap, ap->pm_mesg); | ||
2143 | |||
2144 | /* report result */ | ||
2145 | spin_lock_irqsave(ap->lock, flags); | ||
2146 | |||
2147 | ap->pflags &= ~ATA_PFLAG_PM_PENDING; | ||
2148 | if (rc == 0) | ||
2149 | ap->pflags |= ATA_PFLAG_SUSPENDED; | ||
2150 | else | ||
2151 | ata_port_schedule_eh(ap); | ||
2152 | |||
2153 | if (ap->pm_result) { | ||
2154 | *ap->pm_result = rc; | ||
2155 | ap->pm_result = NULL; | ||
2156 | } | ||
2157 | |||
2158 | spin_unlock_irqrestore(ap->lock, flags); | ||
2159 | |||
2160 | return; | ||
2161 | } | ||
2162 | |||
2163 | /** | ||
2164 | * ata_eh_handle_port_resume - perform port resume operation | ||
2165 | * @ap: port to resume | ||
2166 | * | ||
2167 | * Resume @ap. | ||
2168 | * | ||
2169 | * This function also waits upto one second until all devices | ||
2170 | * hanging off this port requests resume EH action. This is to | ||
2171 | * prevent invoking EH and thus reset multiple times on resume. | ||
2172 | * | ||
2173 | * On DPM resume, where some of devices might not be resumed | ||
2174 | * together, this may delay port resume upto one second, but such | ||
2175 | * DPM resumes are rare and 1 sec delay isn't too bad. | ||
2176 | * | ||
2177 | * LOCKING: | ||
2178 | * Kernel thread context (may sleep). | ||
2179 | */ | ||
2180 | static void ata_eh_handle_port_resume(struct ata_port *ap) | ||
2181 | { | ||
2182 | unsigned long timeout; | ||
2183 | unsigned long flags; | ||
2184 | int i, rc = 0; | ||
2185 | |||
2186 | /* are we resuming? */ | ||
2187 | spin_lock_irqsave(ap->lock, flags); | ||
2188 | if (!(ap->pflags & ATA_PFLAG_PM_PENDING) || | ||
2189 | ap->pm_mesg.event != PM_EVENT_ON) { | ||
2190 | spin_unlock_irqrestore(ap->lock, flags); | ||
2191 | return; | ||
2192 | } | ||
2193 | spin_unlock_irqrestore(ap->lock, flags); | ||
2194 | |||
2195 | /* spurious? */ | ||
2196 | if (!(ap->pflags & ATA_PFLAG_SUSPENDED)) | ||
2197 | goto done; | ||
2198 | |||
2199 | if (ap->ops->port_resume) | ||
2200 | rc = ap->ops->port_resume(ap); | ||
2201 | |||
2202 | /* give devices time to request EH */ | ||
2203 | timeout = jiffies + HZ; /* 1s max */ | ||
2204 | while (1) { | ||
2205 | for (i = 0; i < ATA_MAX_DEVICES; i++) { | ||
2206 | struct ata_device *dev = &ap->device[i]; | ||
2207 | unsigned int action = ata_eh_dev_action(dev); | ||
2208 | |||
2209 | if ((dev->flags & ATA_DFLAG_SUSPENDED) && | ||
2210 | !(action & ATA_EH_RESUME)) | ||
2211 | break; | ||
2212 | } | ||
2213 | |||
2214 | if (i == ATA_MAX_DEVICES || time_after(jiffies, timeout)) | ||
2215 | break; | ||
2216 | msleep(10); | ||
2217 | } | ||
2218 | |||
2219 | done: | ||
2220 | spin_lock_irqsave(ap->lock, flags); | ||
2221 | ap->pflags &= ~(ATA_PFLAG_PM_PENDING | ATA_PFLAG_SUSPENDED); | ||
2222 | if (ap->pm_result) { | ||
2223 | *ap->pm_result = rc; | ||
2224 | ap->pm_result = NULL; | ||
2225 | } | ||
2226 | spin_unlock_irqrestore(ap->lock, flags); | ||
2227 | } | ||