aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libata-eh.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/scsi/libata-eh.c')
-rw-r--r--drivers/scsi/libata-eh.c128
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
48static void __ata_port_freeze(struct ata_port *ap); 48static void __ata_port_freeze(struct ata_port *ap);
49static void ata_eh_finish(struct ata_port *ap); 49static void ata_eh_finish(struct ata_port *ap);
50static void ata_eh_handle_port_suspend(struct ata_port *ap);
51static void ata_eh_handle_port_resume(struct ata_port *ap);
50 52
51static void ata_ering_record(struct ata_ering *ering, int is_io, 53static 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 */
2122static 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 */
2180static 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}