diff options
author | Tejun Heo <htejun@gmail.com> | 2006-06-19 05:27:23 -0400 |
---|---|---|
committer | Jeff Garzik <jeff@garzik.org> | 2006-06-22 23:36:58 -0400 |
commit | 47005f255ed126a4b48a1a2f63164fb1d83bcb0a (patch) | |
tree | bac4a73716af3d9cdb201f1bd83d65952fa2c988 /drivers | |
parent | 3a778275626c0eb97674e92875efeba01189ce0e (diff) |
[PATCH] libata: implement per-dev EH action mask eh_info->dev_action[]
Currently, the only per-dev EH action is REVALIDATE. EH used to
exploit ehi->dev to do selective revalidation on a ATA bus. However,
this is a bit hacky and makes it impossible to request selective
revalidation from outside of EH or add another per-dev EH action.
This patch adds per-dev EH action mask eh_info->dev_action[] and
update EH to use this field for REVALIDATE. Note that per-dev actions
can still be specified at port-level and it has the same effect of
specifying the action for all devices on the port.
Signed-off-by: Tejun Heo <htejun@gmail.com>
Signed-off-by: Jeff Garzik <jeff@garzik.org>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/libata-eh.c | 85 |
1 files changed, 69 insertions, 16 deletions
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index 531a4e11c078..70b623988a9f 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c | |||
@@ -706,9 +706,35 @@ static void ata_eh_detach_dev(struct ata_device *dev) | |||
706 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | 706 | spin_unlock_irqrestore(&ap->host_set->lock, flags); |
707 | } | 707 | } |
708 | 708 | ||
709 | static void ata_eh_clear_action(struct ata_device *dev, | ||
710 | struct ata_eh_info *ehi, unsigned int action) | ||
711 | { | ||
712 | int i; | ||
713 | |||
714 | if (!dev) { | ||
715 | ehi->action &= ~action; | ||
716 | for (i = 0; i < ATA_MAX_DEVICES; i++) | ||
717 | ehi->dev_action[i] &= ~action; | ||
718 | } else { | ||
719 | /* doesn't make sense for port-wide EH actions */ | ||
720 | WARN_ON(!(action & ATA_EH_PERDEV_MASK)); | ||
721 | |||
722 | /* break ehi->action into ehi->dev_action */ | ||
723 | if (ehi->action & action) { | ||
724 | for (i = 0; i < ATA_MAX_DEVICES; i++) | ||
725 | ehi->dev_action[i] |= ehi->action & action; | ||
726 | ehi->action &= ~action; | ||
727 | } | ||
728 | |||
729 | /* turn off the specified per-dev action */ | ||
730 | ehi->dev_action[dev->devno] &= ~action; | ||
731 | } | ||
732 | } | ||
733 | |||
709 | /** | 734 | /** |
710 | * ata_eh_about_to_do - about to perform eh_action | 735 | * ata_eh_about_to_do - about to perform eh_action |
711 | * @ap: target ATA port | 736 | * @ap: target ATA port |
737 | * @dev: target ATA dev for per-dev action (can be NULL) | ||
712 | * @action: action about to be performed | 738 | * @action: action about to be performed |
713 | * | 739 | * |
714 | * Called just before performing EH actions to clear related bits | 740 | * Called just before performing EH actions to clear related bits |
@@ -718,17 +744,36 @@ static void ata_eh_detach_dev(struct ata_device *dev) | |||
718 | * LOCKING: | 744 | * LOCKING: |
719 | * None. | 745 | * None. |
720 | */ | 746 | */ |
721 | static void ata_eh_about_to_do(struct ata_port *ap, unsigned int action) | 747 | static void ata_eh_about_to_do(struct ata_port *ap, struct ata_device *dev, |
748 | unsigned int action) | ||
722 | { | 749 | { |
723 | unsigned long flags; | 750 | unsigned long flags; |
724 | 751 | ||
725 | spin_lock_irqsave(&ap->host_set->lock, flags); | 752 | spin_lock_irqsave(&ap->host_set->lock, flags); |
726 | ap->eh_info.action &= ~action; | 753 | ata_eh_clear_action(dev, &ap->eh_info, action); |
727 | ap->flags |= ATA_FLAG_RECOVERED; | 754 | ap->flags |= ATA_FLAG_RECOVERED; |
728 | spin_unlock_irqrestore(&ap->host_set->lock, flags); | 755 | spin_unlock_irqrestore(&ap->host_set->lock, flags); |
729 | } | 756 | } |
730 | 757 | ||
731 | /** | 758 | /** |
759 | * ata_eh_done - EH action complete | ||
760 | * @ap: target ATA port | ||
761 | * @dev: target ATA dev for per-dev action (can be NULL) | ||
762 | * @action: action just completed | ||
763 | * | ||
764 | * Called right after performing EH actions to clear related bits | ||
765 | * in @ap->eh_context. | ||
766 | * | ||
767 | * LOCKING: | ||
768 | * None. | ||
769 | */ | ||
770 | static void ata_eh_done(struct ata_port *ap, struct ata_device *dev, | ||
771 | unsigned int action) | ||
772 | { | ||
773 | ata_eh_clear_action(dev, &ap->eh_context.i, action); | ||
774 | } | ||
775 | |||
776 | /** | ||
732 | * ata_err_string - convert err_mask to descriptive string | 777 | * ata_err_string - convert err_mask to descriptive string |
733 | * @err_mask: error mask to convert to string | 778 | * @err_mask: error mask to convert to string |
734 | * | 779 | * |
@@ -1271,10 +1316,6 @@ static void ata_eh_autopsy(struct ata_port *ap) | |||
1271 | is_io = 1; | 1316 | is_io = 1; |
1272 | } | 1317 | } |
1273 | 1318 | ||
1274 | /* speed down iff command was in progress */ | ||
1275 | if (failed_dev) | ||
1276 | action |= ata_eh_speed_down(failed_dev, is_io, all_err_mask); | ||
1277 | |||
1278 | /* enforce default EH actions */ | 1319 | /* enforce default EH actions */ |
1279 | if (ap->flags & ATA_FLAG_FROZEN || | 1320 | if (ap->flags & ATA_FLAG_FROZEN || |
1280 | all_err_mask & (AC_ERR_HSM | AC_ERR_TIMEOUT)) | 1321 | all_err_mask & (AC_ERR_HSM | AC_ERR_TIMEOUT)) |
@@ -1282,6 +1323,17 @@ static void ata_eh_autopsy(struct ata_port *ap) | |||
1282 | else if (all_err_mask) | 1323 | else if (all_err_mask) |
1283 | action |= ATA_EH_REVALIDATE; | 1324 | action |= ATA_EH_REVALIDATE; |
1284 | 1325 | ||
1326 | /* if we have offending qcs and the associated failed device */ | ||
1327 | if (failed_dev) { | ||
1328 | /* speed down */ | ||
1329 | action |= ata_eh_speed_down(failed_dev, is_io, all_err_mask); | ||
1330 | |||
1331 | /* perform per-dev EH action only on the offending device */ | ||
1332 | ehc->i.dev_action[failed_dev->devno] |= | ||
1333 | action & ATA_EH_PERDEV_MASK; | ||
1334 | action &= ~ATA_EH_PERDEV_MASK; | ||
1335 | } | ||
1336 | |||
1285 | /* record autopsy result */ | 1337 | /* record autopsy result */ |
1286 | ehc->i.dev = failed_dev; | 1338 | ehc->i.dev = failed_dev; |
1287 | ehc->i.action = action; | 1339 | ehc->i.action = action; |
@@ -1457,7 +1509,7 @@ static int ata_eh_reset(struct ata_port *ap, int classify, | |||
1457 | reset == softreset ? "soft" : "hard"); | 1509 | reset == softreset ? "soft" : "hard"); |
1458 | 1510 | ||
1459 | /* reset */ | 1511 | /* reset */ |
1460 | ata_eh_about_to_do(ap, ATA_EH_RESET_MASK); | 1512 | ata_eh_about_to_do(ap, NULL, ATA_EH_RESET_MASK); |
1461 | ehc->i.flags |= ATA_EHI_DID_RESET; | 1513 | ehc->i.flags |= ATA_EHI_DID_RESET; |
1462 | 1514 | ||
1463 | rc = ata_do_reset(ap, reset, classes); | 1515 | rc = ata_do_reset(ap, reset, classes); |
@@ -1476,7 +1528,7 @@ static int ata_eh_reset(struct ata_port *ap, int classify, | |||
1476 | return -EINVAL; | 1528 | return -EINVAL; |
1477 | } | 1529 | } |
1478 | 1530 | ||
1479 | ata_eh_about_to_do(ap, ATA_EH_RESET_MASK); | 1531 | ata_eh_about_to_do(ap, NULL, ATA_EH_RESET_MASK); |
1480 | rc = ata_do_reset(ap, reset, classes); | 1532 | rc = ata_do_reset(ap, reset, classes); |
1481 | 1533 | ||
1482 | if (rc == 0 && classify && | 1534 | if (rc == 0 && classify && |
@@ -1520,8 +1572,7 @@ static int ata_eh_reset(struct ata_port *ap, int classify, | |||
1520 | postreset(ap, classes); | 1572 | postreset(ap, classes); |
1521 | 1573 | ||
1522 | /* reset successful, schedule revalidation */ | 1574 | /* reset successful, schedule revalidation */ |
1523 | ehc->i.dev = NULL; | 1575 | ata_eh_done(ap, NULL, ATA_EH_RESET_MASK); |
1524 | ehc->i.action &= ~ATA_EH_RESET_MASK; | ||
1525 | ehc->i.action |= ATA_EH_REVALIDATE; | 1576 | ehc->i.action |= ATA_EH_REVALIDATE; |
1526 | } | 1577 | } |
1527 | 1578 | ||
@@ -1539,21 +1590,25 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap, | |||
1539 | DPRINTK("ENTER\n"); | 1590 | DPRINTK("ENTER\n"); |
1540 | 1591 | ||
1541 | for (i = 0; i < ATA_MAX_DEVICES; i++) { | 1592 | for (i = 0; i < ATA_MAX_DEVICES; i++) { |
1593 | unsigned int action; | ||
1594 | |||
1542 | dev = &ap->device[i]; | 1595 | dev = &ap->device[i]; |
1596 | action = ehc->i.action | ehc->i.dev_action[dev->devno]; | ||
1543 | 1597 | ||
1544 | if (ehc->i.action & ATA_EH_REVALIDATE && ata_dev_enabled(dev) && | 1598 | if (action & ATA_EH_REVALIDATE && ata_dev_enabled(dev)) { |
1545 | (!ehc->i.dev || ehc->i.dev == dev)) { | ||
1546 | if (ata_port_offline(ap)) { | 1599 | if (ata_port_offline(ap)) { |
1547 | rc = -EIO; | 1600 | rc = -EIO; |
1548 | break; | 1601 | break; |
1549 | } | 1602 | } |
1550 | 1603 | ||
1551 | ata_eh_about_to_do(ap, ATA_EH_REVALIDATE); | 1604 | ata_eh_about_to_do(ap, dev, ATA_EH_REVALIDATE); |
1552 | rc = ata_dev_revalidate(dev, | 1605 | rc = ata_dev_revalidate(dev, |
1553 | ehc->i.flags & ATA_EHI_DID_RESET); | 1606 | ehc->i.flags & ATA_EHI_DID_RESET); |
1554 | if (rc) | 1607 | if (rc) |
1555 | break; | 1608 | break; |
1556 | 1609 | ||
1610 | ata_eh_done(ap, dev, ATA_EH_REVALIDATE); | ||
1611 | |||
1557 | /* schedule the scsi_rescan_device() here */ | 1612 | /* schedule the scsi_rescan_device() here */ |
1558 | queue_work(ata_aux_wq, &(ap->scsi_rescan_task)); | 1613 | queue_work(ata_aux_wq, &(ap->scsi_rescan_task)); |
1559 | } else if (dev->class == ATA_DEV_UNKNOWN && | 1614 | } else if (dev->class == ATA_DEV_UNKNOWN && |
@@ -1576,9 +1631,7 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap, | |||
1576 | } | 1631 | } |
1577 | } | 1632 | } |
1578 | 1633 | ||
1579 | if (rc == 0) | 1634 | if (rc) |
1580 | ehc->i.action &= ~ATA_EH_REVALIDATE; | ||
1581 | else | ||
1582 | *r_failed_dev = dev; | 1635 | *r_failed_dev = dev; |
1583 | 1636 | ||
1584 | DPRINTK("EXIT\n"); | 1637 | DPRINTK("EXIT\n"); |