aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <htejun@gmail.com>2006-05-15 07:58:21 -0400
committerTejun Heo <htejun@gmail.com>2006-05-15 07:58:21 -0400
commitf3e81b19aac23c0e8c55d5961324ef7de44c23bb (patch)
tree9ea0bf3c5ecabb855a78623236d07e7bc2976ca3
parent0c247c559cd70f85ba9f0764ce13ae00e20fcad8 (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.c11
-rw-r--r--include/linux/libata.h40
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
429struct 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
439struct ata_eh_context {
440 struct ata_eh_info i;
441 int tries[ATA_MAX_DEVICES];
442};
443
423struct ata_port { 444struct 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 */
716static inline int 756static inline int