diff options
author | Tejun Heo <htejun@gmail.com> | 2006-05-15 07:58:21 -0400 |
---|---|---|
committer | Tejun Heo <htejun@gmail.com> | 2006-05-15 07:58:21 -0400 |
commit | f3e81b19aac23c0e8c55d5961324ef7de44c23bb (patch) | |
tree | 9ea0bf3c5ecabb855a78623236d07e7bc2976ca3 | |
parent | 0c247c559cd70f85ba9f0764ce13ae00e20fcad8 (diff) |
[PATCH] libata-eh: implement ata_eh_info and ata_eh_context
struct ata_eh_info serves as the communication channel between
execution path and EH. Execution path describes detected error
condition in ap->eh_info and EH recovers the port using it. To avoid
missing error conditions detected during EH, EH makes its own copy of
eh_info and clears it on entry allowing error info to accumulate
during EH.
Most EH states including EH's copy of eh_info are stored in
ap->eh_context (struct ata_eh_context) which is owned by EH and thus
doesn't require any synchronization to access and alter. This
standardized context makes it easy to integrate various parts of EH
and extend EH to handle multiple links (for PM).
Signed-off-by: Tejun Heo <htejun@gmail.com>
-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 |