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 + -
显示快捷键?