libata-eh.c
来自「linux 内核源代码」· C语言 代码 · 共 2,561 行 · 第 1/5 页
C
2,561 行
arg.since = j64 - min(j64, j5mins); ata_ering_map(&dev->ering, speed_down_verdict_cb, &arg); if (arg.nr_errors[1] + arg.nr_errors[2] + arg.nr_errors[3] > 10) verdict |= ATA_EH_SPDN_FALLBACK_TO_PIO; return verdict;}/** * ata_eh_speed_down - record error and speed down if necessary * @dev: Failed device * @is_io: Did the device fail during normal IO? * @err_mask: err_mask of the error * * Record error and examine error history to determine whether * adjusting transmission speed is necessary. It also sets * transmission limits appropriately if such adjustment is * necessary. * * LOCKING: * Kernel thread context (may sleep). * * RETURNS: * Determined recovery action. */static unsigned int ata_eh_speed_down(struct ata_device *dev, int is_io, unsigned int err_mask){ unsigned int verdict; unsigned int action = 0; /* don't bother if Cat-0 error */ if (ata_eh_categorize_error(is_io, err_mask) == 0) return 0; /* record error and determine whether speed down is necessary */ ata_ering_record(&dev->ering, is_io, err_mask); verdict = ata_eh_speed_down_verdict(dev); /* turn off NCQ? */ if ((verdict & ATA_EH_SPDN_NCQ_OFF) && (dev->flags & (ATA_DFLAG_PIO | ATA_DFLAG_NCQ | ATA_DFLAG_NCQ_OFF)) == ATA_DFLAG_NCQ) { dev->flags |= ATA_DFLAG_NCQ_OFF; ata_dev_printk(dev, KERN_WARNING, "NCQ disabled due to excessive errors\n"); goto done; } /* speed down? */ if (verdict & ATA_EH_SPDN_SPEED_DOWN) { /* speed down SATA link speed if possible */ if (sata_down_spd_limit(dev->link) == 0) { action |= ATA_EH_HARDRESET; goto done; } /* lower transfer mode */ if (dev->spdn_cnt < 2) { static const int dma_dnxfer_sel[] = { ATA_DNXFER_DMA, ATA_DNXFER_40C }; static const int pio_dnxfer_sel[] = { ATA_DNXFER_PIO, ATA_DNXFER_FORCE_PIO0 }; int sel; if (dev->xfer_shift != ATA_SHIFT_PIO) sel = dma_dnxfer_sel[dev->spdn_cnt]; else sel = pio_dnxfer_sel[dev->spdn_cnt]; dev->spdn_cnt++; if (ata_down_xfermask_limit(dev, sel) == 0) { action |= ATA_EH_SOFTRESET; goto done; } } } /* Fall back to PIO? Slowing down to PIO is meaningless for * SATA. Consider it only for PATA. */ if ((verdict & ATA_EH_SPDN_FALLBACK_TO_PIO) && (dev->spdn_cnt >= 2) && (dev->link->ap->cbl != ATA_CBL_SATA) && (dev->xfer_shift != ATA_SHIFT_PIO)) { if (ata_down_xfermask_limit(dev, ATA_DNXFER_FORCE_PIO) == 0) { dev->spdn_cnt = 0; action |= ATA_EH_SOFTRESET; goto done; } } return 0; done: /* device has been slowed down, blow error history */ ata_ering_clear(&dev->ering); return action;}/** * ata_eh_link_autopsy - analyze error and determine recovery action * @link: host link to perform autopsy on * * Analyze why @link failed and determine which recovery actions * are needed. This function also sets more detailed AC_ERR_* * values and fills sense data for ATAPI CHECK SENSE. * * LOCKING: * Kernel thread context (may sleep). */static void ata_eh_link_autopsy(struct ata_link *link){ struct ata_port *ap = link->ap; struct ata_eh_context *ehc = &link->eh_context; struct ata_device *dev; unsigned int all_err_mask = 0; int tag, is_io = 0; u32 serror; int rc; DPRINTK("ENTER\n"); if (ehc->i.flags & ATA_EHI_NO_AUTOPSY) return; /* obtain and analyze SError */ rc = sata_scr_read(link, SCR_ERROR, &serror); if (rc == 0) { ehc->i.serror |= serror; ata_eh_analyze_serror(link); } else if (rc != -EOPNOTSUPP) { /* SError read failed, force hardreset and probing */ ata_ehi_schedule_probe(&ehc->i); ehc->i.action |= ATA_EH_HARDRESET; ehc->i.err_mask |= AC_ERR_OTHER; } /* analyze NCQ failure */ ata_eh_analyze_ncq_error(link); /* any real error trumps AC_ERR_OTHER */ if (ehc->i.err_mask & ~AC_ERR_OTHER) ehc->i.err_mask &= ~AC_ERR_OTHER; all_err_mask |= ehc->i.err_mask; for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag); if (!(qc->flags & ATA_QCFLAG_FAILED) || qc->dev->link != link) continue; /* inherit upper level err_mask */ qc->err_mask |= ehc->i.err_mask; /* analyze TF */ ehc->i.action |= ata_eh_analyze_tf(qc, &qc->result_tf); /* DEV errors are probably spurious in case of ATA_BUS error */ if (qc->err_mask & AC_ERR_ATA_BUS) qc->err_mask &= ~(AC_ERR_DEV | AC_ERR_MEDIA | AC_ERR_INVALID); /* any real error trumps unknown error */ if (qc->err_mask & ~AC_ERR_OTHER) qc->err_mask &= ~AC_ERR_OTHER; /* SENSE_VALID trumps dev/unknown error and revalidation */ if (qc->flags & ATA_QCFLAG_SENSE_VALID) qc->err_mask &= ~(AC_ERR_DEV | AC_ERR_OTHER); /* accumulate error info */ ehc->i.dev = qc->dev; all_err_mask |= qc->err_mask; if (qc->flags & ATA_QCFLAG_IO) is_io = 1; } /* enforce default EH actions */ if (ap->pflags & ATA_PFLAG_FROZEN || all_err_mask & (AC_ERR_HSM | AC_ERR_TIMEOUT)) ehc->i.action |= ATA_EH_SOFTRESET; else if ((is_io && all_err_mask) || (!is_io && (all_err_mask & ~AC_ERR_DEV))) ehc->i.action |= ATA_EH_REVALIDATE; /* If we have offending qcs and the associated failed device, * perform per-dev EH action only on the offending device. */ if (ehc->i.dev) { ehc->i.dev_action[ehc->i.dev->devno] |= ehc->i.action & ATA_EH_PERDEV_MASK; ehc->i.action &= ~ATA_EH_PERDEV_MASK; } /* propagate timeout to host link */ if ((all_err_mask & AC_ERR_TIMEOUT) && !ata_is_host_link(link)) ap->link.eh_context.i.err_mask |= AC_ERR_TIMEOUT; /* record error and consider speeding down */ dev = ehc->i.dev; if (!dev && ((ata_link_max_devices(link) == 1 && ata_dev_enabled(link->device)))) dev = link->device; if (dev) ehc->i.action |= ata_eh_speed_down(dev, is_io, all_err_mask); DPRINTK("EXIT\n");}/** * ata_eh_autopsy - analyze error and determine recovery action * @ap: host port to perform autopsy on * * Analyze all links of @ap and determine why they failed and * which recovery actions are needed. * * LOCKING: * Kernel thread context (may sleep). */void ata_eh_autopsy(struct ata_port *ap){ struct ata_link *link; ata_port_for_each_link(link, ap) ata_eh_link_autopsy(link); /* Autopsy of fanout ports can affect host link autopsy. * Perform host link autopsy last. */ if (ap->nr_pmp_links) ata_eh_link_autopsy(&ap->link);}/** * ata_eh_link_report - report error handling to user * @link: ATA link EH is going on * * Report EH to user. * * LOCKING: * None. */static void ata_eh_link_report(struct ata_link *link){ struct ata_port *ap = link->ap; struct ata_eh_context *ehc = &link->eh_context; const char *frozen, *desc; char tries_buf[6]; int tag, nr_failed = 0; if (ehc->i.flags & ATA_EHI_QUIET) return; desc = NULL; if (ehc->i.desc[0] != '\0') desc = ehc->i.desc; for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag); if (!(qc->flags & ATA_QCFLAG_FAILED) || qc->dev->link != link || ((qc->flags & ATA_QCFLAG_QUIET) && qc->err_mask == AC_ERR_DEV)) continue; if (qc->flags & ATA_QCFLAG_SENSE_VALID && !qc->err_mask) continue; nr_failed++; } if (!nr_failed && !ehc->i.err_mask) return; frozen = ""; if (ap->pflags & ATA_PFLAG_FROZEN) frozen = " frozen"; memset(tries_buf, 0, sizeof(tries_buf)); if (ap->eh_tries < ATA_EH_MAX_TRIES) snprintf(tries_buf, sizeof(tries_buf) - 1, " t%d", ap->eh_tries); if (ehc->i.dev) { ata_dev_printk(ehc->i.dev, KERN_ERR, "exception Emask 0x%x " "SAct 0x%x SErr 0x%x action 0x%x%s%s\n", ehc->i.err_mask, link->sactive, ehc->i.serror, ehc->i.action, frozen, tries_buf); if (desc) ata_dev_printk(ehc->i.dev, KERN_ERR, "%s\n", desc); } else { ata_link_printk(link, KERN_ERR, "exception Emask 0x%x " "SAct 0x%x SErr 0x%x action 0x%x%s%s\n", ehc->i.err_mask, link->sactive, ehc->i.serror, ehc->i.action, frozen, tries_buf); if (desc) ata_link_printk(link, KERN_ERR, "%s\n", desc); } if (ehc->i.serror) ata_port_printk(ap, KERN_ERR, "SError: { %s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s}\n", ehc->i.serror & SERR_DATA_RECOVERED ? "RecovData " : "", ehc->i.serror & SERR_COMM_RECOVERED ? "RecovComm " : "", ehc->i.serror & SERR_DATA ? "UnrecovData " : "", ehc->i.serror & SERR_PERSISTENT ? "Persist " : "", ehc->i.serror & SERR_PROTOCOL ? "Proto " : "", ehc->i.serror & SERR_INTERNAL ? "HostInt " : "", ehc->i.serror & SERR_PHYRDY_CHG ? "PHYRdyChg " : "", ehc->i.serror & SERR_PHY_INT_ERR ? "PHYInt " : "", ehc->i.serror & SERR_COMM_WAKE ? "CommWake " : "", ehc->i.serror & SERR_10B_8B_ERR ? "10B8B " : "", ehc->i.serror & SERR_DISPARITY ? "Dispar " : "", ehc->i.serror & SERR_CRC ? "BadCRC " : "", ehc->i.serror & SERR_HANDSHAKE ? "Handshk " : "", ehc->i.serror & SERR_LINK_SEQ_ERR ? "LinkSeq " : "", ehc->i.serror & SERR_TRANS_ST_ERROR ? "TrStaTrns " : "", ehc->i.serror & SERR_UNRECOG_FIS ? "UnrecFIS " : "", ehc->i.serror & SERR_DEV_XCHG ? "DevExch " : ""); for (tag = 0; tag < ATA_MAX_QUEUE; tag++) { struct ata_queued_cmd *qc = __ata_qc_from_tag(ap, tag); struct ata_taskfile *cmd = &qc->tf, *res = &qc->result_tf; const u8 *cdb = qc->cdb; char data_buf[20] = ""; char cdb_buf[70] = ""; if (!(qc->flags & ATA_QCFLAG_FAILED) || qc->dev->link != link || !qc->err_mask) continue; if (qc->dma_dir != DMA_NONE) { static const char *dma_str[] = { [DMA_BIDIRECTIONAL] = "bidi", [DMA_TO_DEVICE] = "out", [DMA_FROM_DEVICE] = "in", }; static const char *prot_str[] = { [ATA_PROT_PIO] = "pio", [ATA_PROT_DMA] = "dma", [ATA_PROT_NCQ] = "ncq", [ATA_PROT_ATAPI] = "pio", [ATA_PROT_ATAPI_DMA] = "dma", }; snprintf(data_buf, sizeof(data_buf), " %s %u %s", prot_str[qc->tf.protocol], qc->nbytes, dma_str[qc->dma_dir]); } if (is_atapi_taskfile(&qc->tf)) snprintf(cdb_buf, sizeof(cdb_buf), "cdb %02x %02x %02x %02x %02x %02x %02x %02x " "%02x %02x %02x %02x %02x %02x %02x %02x\n ", cdb[0], cdb[1], cdb[2], cdb[3], cdb[4], cdb[5], cdb[6], cdb[7], cdb[8], cdb[9], cdb[10], cdb[11], cdb[12], cdb[13], cdb[14], cdb[15]); ata_dev_printk(qc->dev, KERN_ERR, "cmd %02x/%02x:%02x:%02x:%02x:%02x/%02x:%02x:%02x:%02x:%02x/%02x " "tag %d%s\n %s" "res %02x/%02x:%02x:%02x:%02x:%02x/%02x:%02x:%02x:%02x:%02x/%02x " "Emask 0x%x (%s)%s\n", cmd->command, cmd->feature, cmd->nsect, cmd->lbal, cmd->lbam, cmd->lbah, cmd->hob_feature, cmd->hob_nsect, cmd->hob_lbal, cmd->hob_lbam, cmd->hob_lbah, cmd->device, qc->tag, data_buf, cdb_buf, res->command, res->feature, res->nsect, res->lbal, res->lbam, res->lbah, res->hob_feature, res->hob_nsect, res->hob_lbal, res->hob_lbam, res->hob_lbah, res->device, qc->err_mask, ata_err_string(qc->err_mask), qc->err_mask & AC_ERR_NCQ ? " <F>" : ""); if (res->command & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ | ATA_ERR)) { if (res->command & ATA_BUSY) ata_dev_printk(qc->dev, KERN_ERR, "status: { Busy }\n"); else ata_dev_printk(qc->dev, KERN_ERR, "status: { %s%s%s%s}\n", res->command & ATA_DRDY ? "DRDY " : "", res->command & ATA_DF ? "DF " : "", res->command & ATA_DRQ ? "DRQ " : "", res->command & ATA_ERR ? "ERR " : ""); } if (cmd->command != ATA_CMD_PACKET && (res->feature & (ATA_ICRC | ATA_UNC | ATA_IDNF | ATA_ABORTED))) ata_dev_printk(qc->dev, KERN_ERR, "error: { %s%s%s%s}\n", res->feature & ATA_ICRC ? "ICRC " : "", res->feature & ATA_UNC ? "UNC " : "", res->feature & ATA_IDNF ? "IDNF " : "", res->feature & ATA_ABORTED ? "ABRT " : ""); }}/** * ata_eh_report - report error handling to user * @ap: ATA port to report EH about * * Report EH to user. * * LOCKING: * None. */void ata_eh_report(struct ata_port *ap){ struct ata_link *link; __ata_port_for_each_link(link, ap) ata_eh_link_report(link);}static int ata_do_reset(struct ata_link *link, ata_reset_fn_t reset, unsigned int *classes, unsigned long deadline){ struct ata_device *dev; int rc; ata_link_for_each_dev(dev, link) classes[dev->devno] = ATA_DEV_UNKNOWN; rc = reset(link, classes, deadline); if (rc) return rc; /* If any class isn't ATA_DEV_UNKNOWN, consider classification * is complete and convert all ATA_DEV_UNKNOWN to * ATA_DEV_NONE. */ ata_link_for_each_dev(dev, link) if (classes[dev->devno] != ATA_DEV_UNKNOWN) break; if (dev) { ata_link_for_each_dev(dev, link) { if (classes[dev->devno] == ATA_DEV_UNKNOWN) classes[dev->devno] = ATA_DEV_NONE; } } return 0;}static int ata_eh_followup_srst_needed(struct ata_link *link, int rc, int classify, const unsigned int *classes){ if (link->flags & ATA_LFLAG_NO_SRST) return 0; if (rc == -EAGAIN) return 1; if (rc != 0) return 0; if ((link->ap->flags & ATA_FLAG_PMP) && ata_is_host_link(link)) return 1; if (classify && !(link->flags & ATA_LFLAG_ASSUME_CLASS) && classes[0] == ATA_DEV_UNKNOWN) return 1; return 0;}int ata_eh_reset(struct ata_link *link, int classify, ata_prereset_fn_t prereset, ata_reset_fn_t softreset, ata_reset_fn_t hardreset, ata_postreset_fn_t postreset){ const int max_tries = ARRAY_SIZE(ata_eh_reset_timeouts); struct ata_port *ap = link->ap; struct ata_eh_context *ehc = &link->eh_context; unsigned int *classes = ehc->classes; unsigned int lflags = link->flags; int verbose = !(ehc->i.flags & ATA_EHI_QUIET); int try = 0; struct ata_device *dev; unsigned long deadline, now; unsigned int tmp_action; ata_reset_fn_t reset; unsigned long flags; u32 sstatus; int rc; /* about to reset */ spin_lock_irqsave(ap->lock, flags); ap->pflags |= ATA_PFLAG_RESETTING; spin_unlock_irqrestore(ap->lock, flags); ata_eh_about_to_do(link, NULL, ehc->i.action & ATA_EH_RESET_MASK); ata_link_for_each_dev(dev, link) { /* If we issue an SRST then an ATA drive (not ATAPI) * may change configuration and be in PIO0 timing. If * we do a hard reset (or are coming from power on) * this is true for ATA or ATAPI. Until we've set a * suitable controller mode we should not touch the * bus as we may be talking too fast. */ dev->pio_mode = XFER_PIO_0; /* If the controller has a pio mode setup function * then use it to set the chipset to rights. Don't * touch the DMA setup as that will be dealt with when * configuring devices. */ if (ap->ops->set_piomode) ap->ops->set_piomode(ap, dev);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?