diff options
-rw-r--r-- | drivers/scsi/libata-core.c | 26 | ||||
-rw-r--r-- | drivers/scsi/libata-eh.c | 103 | ||||
-rw-r--r-- | include/linux/libata.h | 4 |
3 files changed, 131 insertions, 2 deletions
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c index 9c97783462d6..63857a90ac28 100644 --- a/drivers/scsi/libata-core.c +++ b/drivers/scsi/libata-core.c | |||
@@ -987,6 +987,12 @@ unsigned ata_exec_internal(struct ata_device *dev, | |||
987 | 987 | ||
988 | spin_lock_irqsave(&ap->host_set->lock, flags); | 988 | spin_lock_irqsave(&ap->host_set->lock, flags); |
989 | 989 | ||
990 | /* no internal command while frozen */ | ||
991 | if (ap->flags & ATA_FLAG_FROZEN) { | ||
992 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | ||
993 | return AC_ERR_SYSTEM; | ||
994 | } | ||
995 | |||
990 | /* initialize internal qc */ | 996 | /* initialize internal qc */ |
991 | 997 | ||
992 | /* XXX: Tag 0 is used for drivers with legacy EH as some | 998 | /* XXX: Tag 0 is used for drivers with legacy EH as some |
@@ -2565,8 +2571,11 @@ void ata_std_postreset(struct ata_port *ap, unsigned int *classes) | |||
2565 | sata_scr_write(ap, SCR_ERROR, serror); | 2571 | sata_scr_write(ap, SCR_ERROR, serror); |
2566 | 2572 | ||
2567 | /* re-enable interrupts */ | 2573 | /* re-enable interrupts */ |
2568 | if (ap->ioaddr.ctl_addr) /* FIXME: hack. create a hook instead */ | 2574 | if (!ap->ops->error_handler) { |
2569 | ata_irq_on(ap); | 2575 | /* FIXME: hack. create a hook instead */ |
2576 | if (ap->ioaddr.ctl_addr) | ||
2577 | ata_irq_on(ap); | ||
2578 | } | ||
2570 | 2579 | ||
2571 | /* is double-select really necessary? */ | 2580 | /* is double-select really necessary? */ |
2572 | if (classes[0] != ATA_DEV_NONE) | 2581 | if (classes[0] != ATA_DEV_NONE) |
@@ -2681,6 +2690,8 @@ int ata_drive_probe_reset(struct ata_port *ap, ata_probeinit_fn_t probeinit, | |||
2681 | { | 2690 | { |
2682 | int rc = -EINVAL; | 2691 | int rc = -EINVAL; |
2683 | 2692 | ||
2693 | ata_eh_freeze_port(ap); | ||
2694 | |||
2684 | if (probeinit) | 2695 | if (probeinit) |
2685 | probeinit(ap); | 2696 | probeinit(ap); |
2686 | 2697 | ||
@@ -2725,6 +2736,9 @@ int ata_drive_probe_reset(struct ata_port *ap, ata_probeinit_fn_t probeinit, | |||
2725 | if (rc == 0) { | 2736 | if (rc == 0) { |
2726 | if (postreset) | 2737 | if (postreset) |
2727 | postreset(ap, classes); | 2738 | postreset(ap, classes); |
2739 | |||
2740 | ata_eh_thaw_port(ap); | ||
2741 | |||
2728 | if (classes[0] == ATA_DEV_UNKNOWN) | 2742 | if (classes[0] == ATA_DEV_UNKNOWN) |
2729 | rc = -ENODEV; | 2743 | rc = -ENODEV; |
2730 | } | 2744 | } |
@@ -4039,6 +4053,10 @@ static struct ata_queued_cmd *ata_qc_new(struct ata_port *ap) | |||
4039 | struct ata_queued_cmd *qc = NULL; | 4053 | struct ata_queued_cmd *qc = NULL; |
4040 | unsigned int i; | 4054 | unsigned int i; |
4041 | 4055 | ||
4056 | /* no command while frozen */ | ||
4057 | if (unlikely(ap->flags & ATA_FLAG_FROZEN)) | ||
4058 | return NULL; | ||
4059 | |||
4042 | /* the last tag is reserved for internal command. */ | 4060 | /* the last tag is reserved for internal command. */ |
4043 | for (i = 0; i < ATA_MAX_QUEUE - 1; i++) | 4061 | for (i = 0; i < ATA_MAX_QUEUE - 1; i++) |
4044 | if (!test_and_set_bit(i, &ap->qactive)) { | 4062 | if (!test_and_set_bit(i, &ap->qactive)) { |
@@ -4953,6 +4971,7 @@ int ata_device_add(const struct ata_probe_ent *ent) | |||
4953 | 4971 | ||
4954 | ata_chk_status(ap); | 4972 | ata_chk_status(ap); |
4955 | host_set->ops->irq_clear(ap); | 4973 | host_set->ops->irq_clear(ap); |
4974 | ata_eh_freeze_port(ap); /* freeze port before requesting IRQ */ | ||
4956 | count++; | 4975 | count++; |
4957 | } | 4976 | } |
4958 | 4977 | ||
@@ -5385,5 +5404,8 @@ EXPORT_SYMBOL_GPL(ata_scsi_device_resume); | |||
5385 | EXPORT_SYMBOL_GPL(ata_eng_timeout); | 5404 | EXPORT_SYMBOL_GPL(ata_eng_timeout); |
5386 | EXPORT_SYMBOL_GPL(ata_port_schedule_eh); | 5405 | EXPORT_SYMBOL_GPL(ata_port_schedule_eh); |
5387 | EXPORT_SYMBOL_GPL(ata_port_abort); | 5406 | EXPORT_SYMBOL_GPL(ata_port_abort); |
5407 | EXPORT_SYMBOL_GPL(ata_port_freeze); | ||
5408 | EXPORT_SYMBOL_GPL(ata_eh_freeze_port); | ||
5409 | EXPORT_SYMBOL_GPL(ata_eh_thaw_port); | ||
5388 | EXPORT_SYMBOL_GPL(ata_eh_qc_complete); | 5410 | EXPORT_SYMBOL_GPL(ata_eh_qc_complete); |
5389 | EXPORT_SYMBOL_GPL(ata_eh_qc_retry); | 5411 | EXPORT_SYMBOL_GPL(ata_eh_qc_retry); |
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index 037a561809f5..cb4e2b8d32d9 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c | |||
@@ -291,6 +291,109 @@ int ata_port_abort(struct ata_port *ap) | |||
291 | return nr_aborted; | 291 | return nr_aborted; |
292 | } | 292 | } |
293 | 293 | ||
294 | /** | ||
295 | * __ata_port_freeze - freeze port | ||
296 | * @ap: ATA port to freeze | ||
297 | * | ||
298 | * This function is called when HSM violation or some other | ||
299 | * condition disrupts normal operation of the port. Frozen port | ||
300 | * is not allowed to perform any operation until the port is | ||
301 | * thawed, which usually follows a successful reset. | ||
302 | * | ||
303 | * ap->ops->freeze() callback can be used for freezing the port | ||
304 | * hardware-wise (e.g. mask interrupt and stop DMA engine). If a | ||
305 | * port cannot be frozen hardware-wise, the interrupt handler | ||
306 | * must ack and clear interrupts unconditionally while the port | ||
307 | * is frozen. | ||
308 | * | ||
309 | * LOCKING: | ||
310 | * spin_lock_irqsave(host_set lock) | ||
311 | */ | ||
312 | static void __ata_port_freeze(struct ata_port *ap) | ||
313 | { | ||
314 | WARN_ON(!ap->ops->error_handler); | ||
315 | |||
316 | if (ap->ops->freeze) | ||
317 | ap->ops->freeze(ap); | ||
318 | |||
319 | ap->flags |= ATA_FLAG_FROZEN; | ||
320 | |||
321 | DPRINTK("ata%u port frozen\n", ap->id); | ||
322 | } | ||
323 | |||
324 | /** | ||
325 | * ata_port_freeze - abort & freeze port | ||
326 | * @ap: ATA port to freeze | ||
327 | * | ||
328 | * Abort and freeze @ap. | ||
329 | * | ||
330 | * LOCKING: | ||
331 | * spin_lock_irqsave(host_set lock) | ||
332 | * | ||
333 | * RETURNS: | ||
334 | * Number of aborted commands. | ||
335 | */ | ||
336 | int ata_port_freeze(struct ata_port *ap) | ||
337 | { | ||
338 | int nr_aborted; | ||
339 | |||
340 | WARN_ON(!ap->ops->error_handler); | ||
341 | |||
342 | nr_aborted = ata_port_abort(ap); | ||
343 | __ata_port_freeze(ap); | ||
344 | |||
345 | return nr_aborted; | ||
346 | } | ||
347 | |||
348 | /** | ||
349 | * ata_eh_freeze_port - EH helper to freeze port | ||
350 | * @ap: ATA port to freeze | ||
351 | * | ||
352 | * Freeze @ap. | ||
353 | * | ||
354 | * LOCKING: | ||
355 | * None. | ||
356 | */ | ||
357 | void ata_eh_freeze_port(struct ata_port *ap) | ||
358 | { | ||
359 | unsigned long flags; | ||
360 | |||
361 | if (!ap->ops->error_handler) | ||
362 | return; | ||
363 | |||
364 | spin_lock_irqsave(&ap->host_set->lock, flags); | ||
365 | __ata_port_freeze(ap); | ||
366 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | ||
367 | } | ||
368 | |||
369 | /** | ||
370 | * ata_port_thaw_port - EH helper to thaw port | ||
371 | * @ap: ATA port to thaw | ||
372 | * | ||
373 | * Thaw frozen port @ap. | ||
374 | * | ||
375 | * LOCKING: | ||
376 | * None. | ||
377 | */ | ||
378 | void ata_eh_thaw_port(struct ata_port *ap) | ||
379 | { | ||
380 | unsigned long flags; | ||
381 | |||
382 | if (!ap->ops->error_handler) | ||
383 | return; | ||
384 | |||
385 | spin_lock_irqsave(&ap->host_set->lock, flags); | ||
386 | |||
387 | ap->flags &= ~ATA_FLAG_FROZEN; | ||
388 | |||
389 | if (ap->ops->thaw) | ||
390 | ap->ops->thaw(ap); | ||
391 | |||
392 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | ||
393 | |||
394 | DPRINTK("ata%u port thawed\n", ap->id); | ||
395 | } | ||
396 | |||
294 | static void ata_eh_scsidone(struct scsi_cmnd *scmd) | 397 | static void ata_eh_scsidone(struct scsi_cmnd *scmd) |
295 | { | 398 | { |
296 | /* nada */ | 399 | /* nada */ |
diff --git a/include/linux/libata.h b/include/linux/libata.h index 086e14690954..6758b4d374a0 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h | |||
@@ -665,6 +665,10 @@ extern void ata_eng_timeout(struct ata_port *ap); | |||
665 | 665 | ||
666 | extern void ata_port_schedule_eh(struct ata_port *ap); | 666 | extern void ata_port_schedule_eh(struct ata_port *ap); |
667 | extern int ata_port_abort(struct ata_port *ap); | 667 | extern int ata_port_abort(struct ata_port *ap); |
668 | extern int ata_port_freeze(struct ata_port *ap); | ||
669 | |||
670 | extern void ata_eh_freeze_port(struct ata_port *ap); | ||
671 | extern void ata_eh_thaw_port(struct ata_port *ap); | ||
668 | 672 | ||
669 | extern void ata_eh_qc_complete(struct ata_queued_cmd *qc); | 673 | extern void ata_eh_qc_complete(struct ata_queued_cmd *qc); |
670 | extern void ata_eh_qc_retry(struct ata_queued_cmd *qc); | 674 | extern void ata_eh_qc_retry(struct ata_queued_cmd *qc); |