[PATCH] libata-ncq: update EH to handle NCQ
Update EH to handle NCQ. ata_eh_autopsy() is updated to call ata_eh_analyze_ncq_error() which reads log page 10h on NCQ device error and updates eh_context accordingly. ata_eh_report() is updated to report SActive. Signed-off-by: Tejun Heo <htejun@gmail.com>
This commit is contained in:
parent
3dc1d88193
commit
e8ee84518c
@ -684,6 +684,98 @@ static const char * ata_err_string(unsigned int err_mask)
|
||||
return "unknown error";
|
||||
}
|
||||
|
||||
/**
|
||||
* ata_read_log_page - read a specific log page
|
||||
* @dev: target device
|
||||
* @page: page to read
|
||||
* @buf: buffer to store read page
|
||||
* @sectors: number of sectors to read
|
||||
*
|
||||
* Read log page using READ_LOG_EXT command.
|
||||
*
|
||||
* LOCKING:
|
||||
* Kernel thread context (may sleep).
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 on success, AC_ERR_* mask otherwise.
|
||||
*/
|
||||
static unsigned int ata_read_log_page(struct ata_device *dev,
|
||||
u8 page, void *buf, unsigned int sectors)
|
||||
{
|
||||
struct ata_taskfile tf;
|
||||
unsigned int err_mask;
|
||||
|
||||
DPRINTK("read log page - page %d\n", page);
|
||||
|
||||
ata_tf_init(dev, &tf);
|
||||
tf.command = ATA_CMD_READ_LOG_EXT;
|
||||
tf.lbal = page;
|
||||
tf.nsect = sectors;
|
||||
tf.hob_nsect = sectors >> 8;
|
||||
tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_LBA48 | ATA_TFLAG_DEVICE;
|
||||
tf.protocol = ATA_PROT_PIO;
|
||||
|
||||
err_mask = ata_exec_internal(dev, &tf, NULL, DMA_FROM_DEVICE,
|
||||
buf, sectors * ATA_SECT_SIZE);
|
||||
|
||||
DPRINTK("EXIT, err_mask=%x\n", err_mask);
|
||||
return err_mask;
|
||||
}
|
||||
|
||||
/**
|
||||
* ata_eh_read_log_10h - Read log page 10h for NCQ error details
|
||||
* @dev: Device to read log page 10h from
|
||||
* @tag: Resulting tag of the failed command
|
||||
* @tf: Resulting taskfile registers of the failed command
|
||||
*
|
||||
* Read log page 10h to obtain NCQ error details and clear error
|
||||
* condition.
|
||||
*
|
||||
* LOCKING:
|
||||
* Kernel thread context (may sleep).
|
||||
*
|
||||
* RETURNS:
|
||||
* 0 on success, -errno otherwise.
|
||||
*/
|
||||
static int ata_eh_read_log_10h(struct ata_device *dev,
|
||||
int *tag, struct ata_taskfile *tf)
|
||||
{
|
||||
u8 *buf = dev->ap->sector_buf;
|
||||
unsigned int err_mask;
|
||||
u8 csum;
|
||||
int i;
|
||||
|
||||
err_mask = ata_read_log_page(dev, ATA_LOG_SATA_NCQ, buf, 1);
|
||||
if (err_mask)
|
||||
return -EIO;
|
||||
|
||||
csum = 0;
|
||||
for (i = 0; i < ATA_SECT_SIZE; i++)
|
||||
csum += buf[i];
|
||||
if (csum)
|
||||
ata_dev_printk(dev, KERN_WARNING,
|
||||
"invalid checksum 0x%x on log page 10h\n", csum);
|
||||
|
||||
if (buf[0] & 0x80)
|
||||
return -ENOENT;
|
||||
|
||||
*tag = buf[0] & 0x1f;
|
||||
|
||||
tf->command = buf[2];
|
||||
tf->feature = buf[3];
|
||||
tf->lbal = buf[4];
|
||||
tf->lbam = buf[5];
|
||||
tf->lbah = buf[6];
|
||||
tf->device = buf[7];
|
||||
tf->hob_lbal = buf[8];
|
||||
tf->hob_lbam = buf[9];
|
||||
tf->hob_lbah = buf[10];
|
||||
tf->nsect = buf[12];
|
||||
tf->hob_nsect = buf[13];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* atapi_eh_request_sense - perform ATAPI REQUEST_SENSE
|
||||
* @dev: device to perform REQUEST_SENSE to
|
||||
@ -782,6 +874,66 @@ static void ata_eh_analyze_serror(struct ata_port *ap)
|
||||
ehc->i.action |= action;
|
||||
}
|
||||
|
||||
/**
|
||||
* ata_eh_analyze_ncq_error - analyze NCQ error
|
||||
* @ap: ATA port to analyze NCQ error for
|
||||
*
|
||||
* Read log page 10h, determine the offending qc and acquire
|
||||
* error status TF. For NCQ device errors, all LLDDs have to do
|
||||
* is setting AC_ERR_DEV in ehi->err_mask. This function takes
|
||||
* care of the rest.
|
||||
*
|
||||
* LOCKING:
|
||||
* Kernel thread context (may sleep).
|
||||
*/
|
||||
static void ata_eh_analyze_ncq_error(struct ata_port *ap)
|
||||
{
|
||||
struct ata_eh_context *ehc = &ap->eh_context;
|
||||
struct ata_device *dev = ap->device;
|
||||
struct ata_queued_cmd *qc;
|
||||
struct ata_taskfile tf;
|
||||
int tag, rc;
|
||||
|
||||
/* if frozen, we can't do much */
|
||||
if (ap->flags & ATA_FLAG_FROZEN)
|
||||
return;
|
||||
|
||||
/* is it NCQ device error? */
|
||||
if (!ap->sactive || !(ehc->i.err_mask & AC_ERR_DEV))
|
||||
return;
|
||||
|
||||
/* has LLDD analyzed already? */
|
||||
for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {
|
||||
qc = __ata_qc_from_tag(ap, tag);
|
||||
|
||||
if (!(qc->flags & ATA_QCFLAG_FAILED))
|
||||
continue;
|
||||
|
||||
if (qc->err_mask)
|
||||
return;
|
||||
}
|
||||
|
||||
/* okay, this error is ours */
|
||||
rc = ata_eh_read_log_10h(dev, &tag, &tf);
|
||||
if (rc) {
|
||||
ata_port_printk(ap, KERN_ERR, "failed to read log page 10h "
|
||||
"(errno=%d)\n", rc);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(ap->sactive & (1 << tag))) {
|
||||
ata_port_printk(ap, KERN_ERR, "log page 10h reported "
|
||||
"inactive tag %d\n", tag);
|
||||
return;
|
||||
}
|
||||
|
||||
/* we've got the perpetrator, condemn it */
|
||||
qc = __ata_qc_from_tag(ap, tag);
|
||||
memcpy(&qc->result_tf, &tf, sizeof(tf));
|
||||
qc->err_mask |= AC_ERR_DEV;
|
||||
ehc->i.err_mask &= ~AC_ERR_DEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* ata_eh_analyze_tf - analyze taskfile of a failed qc
|
||||
* @qc: qc to analyze
|
||||
@ -999,6 +1151,9 @@ static void ata_eh_autopsy(struct ata_port *ap)
|
||||
} else if (rc != -EOPNOTSUPP)
|
||||
action |= ATA_EH_HARDRESET;
|
||||
|
||||
/* analyze NCQ failure */
|
||||
ata_eh_analyze_ncq_error(ap);
|
||||
|
||||
/* any real error trumps AC_ERR_OTHER */
|
||||
if (ehc->i.err_mask & ~AC_ERR_OTHER)
|
||||
ehc->i.err_mask &= ~AC_ERR_OTHER;
|
||||
@ -1093,17 +1248,17 @@ static void ata_eh_report(struct ata_port *ap)
|
||||
frozen = " frozen";
|
||||
|
||||
if (ehc->i.dev) {
|
||||
ata_dev_printk(ehc->i.dev, KERN_ERR,
|
||||
"exception Emask 0x%x SErr 0x%x action 0x%x%s\n",
|
||||
ehc->i.err_mask, ehc->i.serror, ehc->i.action,
|
||||
frozen);
|
||||
ata_dev_printk(ehc->i.dev, KERN_ERR, "exception Emask 0x%x "
|
||||
"SAct 0x%x SErr 0x%x action 0x%x%s\n",
|
||||
ehc->i.err_mask, ap->sactive, ehc->i.serror,
|
||||
ehc->i.action, frozen);
|
||||
if (desc)
|
||||
ata_dev_printk(ehc->i.dev, KERN_ERR, "(%s)\n", desc);
|
||||
} else {
|
||||
ata_port_printk(ap, KERN_ERR,
|
||||
"exception Emask 0x%x SErr 0x%x action 0x%x%s\n",
|
||||
ehc->i.err_mask, ehc->i.serror, ehc->i.action,
|
||||
frozen);
|
||||
ata_port_printk(ap, KERN_ERR, "exception Emask 0x%x "
|
||||
"SAct 0x%x SErr 0x%x action 0x%x%s\n",
|
||||
ehc->i.err_mask, ap->sactive, ehc->i.serror,
|
||||
ehc->i.action, frozen);
|
||||
if (desc)
|
||||
ata_port_printk(ap, KERN_ERR, "(%s)\n", desc);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user