diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c index 71ad18b7cff6e3eda05b50d586dd976dc57edef5..1968f2d140f358247cc66ac877bab426f179307b 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) repeat: /* invoke error handler */ if (ap->ops->error_handler) { - /* clear EH pending */ + /* fetch & clear EH info */ spin_lock_irqsave(hs_lock, flags); + + memset(&ap->eh_context, 0, sizeof(ap->eh_context)); + ap->eh_context.i = ap->eh_info; + memset(&ap->eh_info, 0, sizeof(ap->eh_info)); + ap->flags &= ~ATA_FLAG_EH_PENDING; + spin_unlock_irqrestore(hs_lock, flags); /* invoke EH */ @@ -255,6 +261,9 @@ void ata_scsi_error(struct Scsi_Host *host) "tries, giving up\n", ATA_EH_MAX_REPEAT); } + /* this run is complete, make sure EH info is clear */ + memset(&ap->eh_info, 0, sizeof(ap->eh_info)); + /* Clear host_eh_scheduled while holding hs_lock such * that if exception occurs after this point but * before EH completion, SCSI midlayer will diff --git a/include/linux/libata.h b/include/linux/libata.h index f5cea13599c339cdbb43a13ba3585778f6cab00f..298f9918e37528fab69b8cdce8eb65e0974c8835 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -229,6 +229,9 @@ enum { /* ering size */ ATA_ERING_SIZE = 32, + /* desc_len for ata_eh_info and context */ + ATA_EH_DESC_LEN = 80, + /* reset / recovery action types */ ATA_EH_REVALIDATE = (1 << 0), ATA_EH_SOFTRESET = (1 << 1), @@ -236,6 +239,9 @@ enum { ATA_EH_RESET_MASK = ATA_EH_SOFTRESET | ATA_EH_HARDRESET, + /* ata_eh_info->flags */ + ATA_EHI_DID_RESET = (1 << 0), /* already reset this port */ + /* max repeat if error condition is still set after ->error_handler */ ATA_EH_MAX_REPEAT = 5, @@ -420,6 +426,21 @@ struct ata_device { struct ata_ering ering; }; +struct ata_eh_info { + struct ata_device *dev; /* offending device */ + u32 serror; /* SError from LLDD */ + unsigned int err_mask; /* port-wide err_mask */ + unsigned int action; /* ATA_EH_* action mask */ + unsigned int flags; /* ATA_EHI_* flags */ + char desc[ATA_EH_DESC_LEN]; + int desc_len; +}; + +struct ata_eh_context { + struct ata_eh_info i; + int tries[ATA_MAX_DEVICES]; +}; + struct ata_port { struct Scsi_Host *host; /* our co-allocated scsi host */ const struct ata_port_operations *ops; @@ -444,6 +465,11 @@ struct ata_port { unsigned int cbl; /* cable type; ATA_CBL_xxx */ unsigned int sata_spd_limit; /* SATA PHY speed limit */ + /* record runtime error info, protected by host_set lock */ + struct ata_eh_info eh_info; + /* EH context owned by EH */ + struct ata_eh_context eh_context; + struct ata_device device[ATA_MAX_DEVICES]; struct ata_queued_cmd qcmd[ATA_MAX_QUEUE]; @@ -710,6 +736,20 @@ extern void ata_eh_qc_retry(struct ata_queued_cmd *qc); #define ata_dev_printk(dev, lv, fmt, args...) \ printk(lv"ata%u.%02u: "fmt, (dev)->ap->id, (dev)->devno , ##args) +/* + * ata_eh_info helpers + */ +#define ata_ehi_push_desc(ehi, fmt, args...) do { \ + (ehi)->desc_len += scnprintf((ehi)->desc + (ehi)->desc_len, \ + ATA_EH_DESC_LEN - (ehi)->desc_len, \ + fmt , ##args); \ +} while (0) + +#define ata_ehi_clear_desc(ehi) do { \ + (ehi)->desc[0] = '\0'; \ + (ehi)->desc_len = 0; \ +} while (0) + /* * qc helpers */