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 71ad18b7cff..1968f2d140f 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 f5cea13599c..298f9918e37 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 |