diff options
author | Tejun Heo <htejun@gmail.com> | 2006-05-15 07:58:09 -0400 |
---|---|---|
committer | Tejun Heo <htejun@gmail.com> | 2006-05-15 07:58:09 -0400 |
commit | e318049949b07152d851dbfebbd93e560af45ebe (patch) | |
tree | cbc322c94717c9ac5d7e2c48a9f690a2e0145b81 | |
parent | 7b70fc039824bc7303e4007a5f758f832de56611 (diff) |
[PATCH] libata-eh-fw: implement freeze/thaw
Freezing is performed atomic w.r.t. host_set->lock and once frozen
LLDD is not allowed to access the port or any qc on it. Also, libata
makes sure that no new qc gets issued to a frozen port.
A frozen port is thawed after a reset operation completes
successfully, so reset methods must do its job while the port is
frozen. During initialization all ports get frozen before requesting
IRQ, so reset methods are always invoked on a frozen port.
Optional ->freeze and ->thaw operations notify LLDD that the port is
being frozen and thawed, respectively. LLDD can disable/enable
hardware interrupt in these callbacks if the controller's IRQ mask can
be changed dynamically. If the controller doesn't allow such
operation, LLDD can check for frozen state in the interrupt handler
and ack/clear interrupts unconditionally while frozen.
Signed-off-by: Tejun Heo <htejun@gmail.com>
-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); |