diff options
| -rw-r--r-- | drivers/scsi/libata-eh.c | 11 | ||||
| -rw-r--r-- | include/linux/libata.h | 40 |
2 files changed, 50 insertions, 1 deletions
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index 71ad18b7cff6..1968f2d140f3 100644 --- a/drivers/scsi/libata-eh.c +++ b/drivers/scsi/libata-eh.c | |||
| @@ -229,9 +229,15 @@ void ata_scsi_error(struct Scsi_Host *host) | |||
| 229 | repeat: | 229 | repeat: |
| 230 | /* invoke error handler */ | 230 | /* invoke error handler */ |
| 231 | if (ap->ops->error_handler) { | 231 | if (ap->ops->error_handler) { |
| 232 | /* clear EH pending */ | 232 | /* fetch & clear EH info */ |
| 233 | spin_lock_irqsave(hs_lock, flags); | 233 | spin_lock_irqsave(hs_lock, flags); |
| 234 | |||
| 235 | memset(&ap->eh_context, 0, sizeof(ap->eh_context)); | ||
| 236 | ap->eh_context.i = ap->eh_info; | ||
| 237 | memset(&ap->eh_info, 0, sizeof(ap->eh_info)); | ||
| 238 | |||
| 234 | ap->flags &= ~ATA_FLAG_EH_PENDING; | 239 | ap->flags &= ~ATA_FLAG_EH_PENDING; |
| 240 | |||
| 235 | spin_unlock_irqrestore(hs_lock, flags); | 241 | spin_unlock_irqrestore(hs_lock, flags); |
| 236 | 242 | ||
| 237 | /* invoke EH */ | 243 | /* invoke EH */ |
| @@ -255,6 +261,9 @@ void ata_scsi_error(struct Scsi_Host *host) | |||
| 255 | "tries, giving up\n", ATA_EH_MAX_REPEAT); | 261 | "tries, giving up\n", ATA_EH_MAX_REPEAT); |
| 256 | } | 262 | } |
| 257 | 263 | ||
| 264 | /* this run is complete, make sure EH info is clear */ | ||
| 265 | memset(&ap->eh_info, 0, sizeof(ap->eh_info)); | ||
| 266 | |||
| 258 | /* Clear host_eh_scheduled while holding hs_lock such | 267 | /* Clear host_eh_scheduled while holding hs_lock such |
| 259 | * that if exception occurs after this point but | 268 | * that if exception occurs after this point but |
| 260 | * before EH completion, SCSI midlayer will | 269 | * before EH completion, SCSI midlayer will |
diff --git a/include/linux/libata.h b/include/linux/libata.h index f5cea13599c3..298f9918e375 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h | |||
| @@ -229,6 +229,9 @@ enum { | |||
| 229 | /* ering size */ | 229 | /* ering size */ |
| 230 | ATA_ERING_SIZE = 32, | 230 | ATA_ERING_SIZE = 32, |
| 231 | 231 | ||
| 232 | /* desc_len for ata_eh_info and context */ | ||
| 233 | ATA_EH_DESC_LEN = 80, | ||
| 234 | |||
| 232 | /* reset / recovery action types */ | 235 | /* reset / recovery action types */ |
| 233 | ATA_EH_REVALIDATE = (1 << 0), | 236 | ATA_EH_REVALIDATE = (1 << 0), |
| 234 | ATA_EH_SOFTRESET = (1 << 1), | 237 | ATA_EH_SOFTRESET = (1 << 1), |
| @@ -236,6 +239,9 @@ enum { | |||
| 236 | 239 | ||
| 237 | ATA_EH_RESET_MASK = ATA_EH_SOFTRESET | ATA_EH_HARDRESET, | 240 | ATA_EH_RESET_MASK = ATA_EH_SOFTRESET | ATA_EH_HARDRESET, |
| 238 | 241 | ||
| 242 | /* ata_eh_info->flags */ | ||
| 243 | ATA_EHI_DID_RESET = (1 << 0), /* already reset this port */ | ||
| 244 | |||
| 239 | /* max repeat if error condition is still set after ->error_handler */ | 245 | /* max repeat if error condition is still set after ->error_handler */ |
| 240 | ATA_EH_MAX_REPEAT = 5, | 246 | ATA_EH_MAX_REPEAT = 5, |
| 241 | 247 | ||
| @@ -420,6 +426,21 @@ struct ata_device { | |||
| 420 | struct ata_ering ering; | 426 | struct ata_ering ering; |
| 421 | }; | 427 | }; |
| 422 | 428 | ||
| 429 | struct ata_eh_info { | ||
| 430 | struct ata_device *dev; /* offending device */ | ||
| 431 | u32 serror; /* SError from LLDD */ | ||
| 432 | unsigned int err_mask; /* port-wide err_mask */ | ||
| 433 | unsigned int action; /* ATA_EH_* action mask */ | ||
| 434 | unsigned int flags; /* ATA_EHI_* flags */ | ||
| 435 | char desc[ATA_EH_DESC_LEN]; | ||
| 436 | int desc_len; | ||
| 437 | }; | ||
| 438 | |||
| 439 | struct ata_eh_context { | ||
| 440 | struct ata_eh_info i; | ||
| 441 | int tries[ATA_MAX_DEVICES]; | ||
| 442 | }; | ||
| 443 | |||
| 423 | struct ata_port { | 444 | struct ata_port { |
| 424 | struct Scsi_Host *host; /* our co-allocated scsi host */ | 445 | struct Scsi_Host *host; /* our co-allocated scsi host */ |
| 425 | const struct ata_port_operations *ops; | 446 | const struct ata_port_operations *ops; |
| @@ -444,6 +465,11 @@ struct ata_port { | |||
| 444 | unsigned int cbl; /* cable type; ATA_CBL_xxx */ | 465 | unsigned int cbl; /* cable type; ATA_CBL_xxx */ |
| 445 | unsigned int sata_spd_limit; /* SATA PHY speed limit */ | 466 | unsigned int sata_spd_limit; /* SATA PHY speed limit */ |
| 446 | 467 | ||
| 468 | /* record runtime error info, protected by host_set lock */ | ||
| 469 | struct ata_eh_info eh_info; | ||
| 470 | /* EH context owned by EH */ | ||
| 471 | struct ata_eh_context eh_context; | ||
| 472 | |||
| 447 | struct ata_device device[ATA_MAX_DEVICES]; | 473 | struct ata_device device[ATA_MAX_DEVICES]; |
| 448 | 474 | ||
| 449 | struct ata_queued_cmd qcmd[ATA_MAX_QUEUE]; | 475 | struct ata_queued_cmd qcmd[ATA_MAX_QUEUE]; |
| @@ -711,6 +737,20 @@ extern void ata_eh_qc_retry(struct ata_queued_cmd *qc); | |||
| 711 | printk(lv"ata%u.%02u: "fmt, (dev)->ap->id, (dev)->devno , ##args) | 737 | printk(lv"ata%u.%02u: "fmt, (dev)->ap->id, (dev)->devno , ##args) |
| 712 | 738 | ||
| 713 | /* | 739 | /* |
| 740 | * ata_eh_info helpers | ||
| 741 | */ | ||
| 742 | #define ata_ehi_push_desc(ehi, fmt, args...) do { \ | ||
| 743 | (ehi)->desc_len += scnprintf((ehi)->desc + (ehi)->desc_len, \ | ||
| 744 | ATA_EH_DESC_LEN - (ehi)->desc_len, \ | ||
| 745 | fmt , ##args); \ | ||
| 746 | } while (0) | ||
| 747 | |||
| 748 | #define ata_ehi_clear_desc(ehi) do { \ | ||
| 749 | (ehi)->desc[0] = '\0'; \ | ||
| 750 | (ehi)->desc_len = 0; \ | ||
| 751 | } while (0) | ||
| 752 | |||
| 753 | /* | ||
| 714 | * qc helpers | 754 | * qc helpers |
| 715 | */ | 755 | */ |
| 716 | static inline int | 756 | static inline int |
