libata-eh.c

来自「linux 内核源代码」· C语言 代码 · 共 2,561 行 · 第 1/5 页

C
2,561
字号
	}	/* Determine which reset to use and record in ehc->i.action.	 * prereset() may examine and modify it.	 */	if (softreset && (!hardreset || (!(lflags & ATA_LFLAG_NO_SRST) &&					 !sata_set_spd_needed(link) &&					 !(ehc->i.action & ATA_EH_HARDRESET))))		tmp_action = ATA_EH_SOFTRESET;	else		tmp_action = ATA_EH_HARDRESET;	ehc->i.action = (ehc->i.action & ~ATA_EH_RESET_MASK) | tmp_action;	if (prereset) {		rc = prereset(link, jiffies + ATA_EH_PRERESET_TIMEOUT);		if (rc) {			if (rc == -ENOENT) {				ata_link_printk(link, KERN_DEBUG,						"port disabled. ignoring.\n");				ehc->i.action &= ~ATA_EH_RESET_MASK;				ata_link_for_each_dev(dev, link)					classes[dev->devno] = ATA_DEV_NONE;				rc = 0;			} else				ata_link_printk(link, KERN_ERR,					"prereset failed (errno=%d)\n", rc);			goto out;		}	}	/* prereset() might have modified ehc->i.action */	if (ehc->i.action & ATA_EH_HARDRESET)		reset = hardreset;	else if (ehc->i.action & ATA_EH_SOFTRESET)		reset = softreset;	else {		/* prereset told us not to reset, bang classes and return */		ata_link_for_each_dev(dev, link)			classes[dev->devno] = ATA_DEV_NONE;		rc = 0;		goto out;	}	/* did prereset() screw up?  if so, fix up to avoid oopsing */	if (!reset) {		if (softreset)			reset = softreset;		else			reset = hardreset;	} retry:	deadline = jiffies + ata_eh_reset_timeouts[try++];	/* shut up during boot probing */	if (verbose)		ata_link_printk(link, KERN_INFO, "%s resetting link\n",				reset == softreset ? "soft" : "hard");	/* mark that this EH session started with reset */	if (reset == hardreset)		ehc->i.flags |= ATA_EHI_DID_HARDRESET;	else		ehc->i.flags |= ATA_EHI_DID_SOFTRESET;	rc = ata_do_reset(link, reset, classes, deadline);	if (reset == hardreset &&	    ata_eh_followup_srst_needed(link, rc, classify, classes)) {		/* okay, let's do follow-up softreset */		reset = softreset;		if (!reset) {			ata_link_printk(link, KERN_ERR,					"follow-up softreset required "					"but no softreset avaliable\n");			rc = -EINVAL;			goto fail;		}		ata_eh_about_to_do(link, NULL, ATA_EH_RESET_MASK);		rc = ata_do_reset(link, reset, classes, deadline);	}	/* -EAGAIN can happen if we skipped followup SRST */	if (rc && rc != -EAGAIN)		goto fail;	/* was classification successful? */	if (classify && classes[0] == ATA_DEV_UNKNOWN &&	    !(lflags & ATA_LFLAG_ASSUME_CLASS)) {		if (try < max_tries) {			ata_link_printk(link, KERN_WARNING,					"classification failed\n");			rc = -EINVAL;			goto fail;		}		ata_link_printk(link, KERN_WARNING,				"classfication failed, assuming ATA\n");		lflags |= ATA_LFLAG_ASSUME_ATA;	}	ata_link_for_each_dev(dev, link) {		/* After the reset, the device state is PIO 0 and the		 * controller state is undefined.  Reset also wakes up		 * drives from sleeping mode.		 */		dev->pio_mode = XFER_PIO_0;		dev->flags &= ~ATA_DFLAG_SLEEPING;		if (ata_link_offline(link))			continue;		/* apply class override */		if (lflags & ATA_LFLAG_ASSUME_ATA)			classes[dev->devno] = ATA_DEV_ATA;		else if (lflags & ATA_LFLAG_ASSUME_SEMB)			classes[dev->devno] = ATA_DEV_SEMB_UNSUP; /* not yet */	}	/* record current link speed */	if (sata_scr_read(link, SCR_STATUS, &sstatus) == 0)		link->sata_spd = (sstatus >> 4) & 0xf;	if (postreset)		postreset(link, classes);	/* reset successful, schedule revalidation */	ata_eh_done(link, NULL, ehc->i.action & ATA_EH_RESET_MASK);	ehc->i.action |= ATA_EH_REVALIDATE;	rc = 0; out:	/* clear hotplug flag */	ehc->i.flags &= ~ATA_EHI_HOTPLUGGED;	spin_lock_irqsave(ap->lock, flags);	ap->pflags &= ~ATA_PFLAG_RESETTING;	spin_unlock_irqrestore(ap->lock, flags);	return rc; fail:	if (rc == -ERESTART || try >= max_tries)		goto out;	now = jiffies;	if (time_before(now, deadline)) {		unsigned long delta = deadline - now;		ata_link_printk(link, KERN_WARNING, "reset failed "				"(errno=%d), retrying in %u secs\n",				rc, (jiffies_to_msecs(delta) + 999) / 1000);		while (delta)			delta = schedule_timeout_uninterruptible(delta);	}	if (rc == -EPIPE || try == max_tries - 1)		sata_down_spd_limit(link);	if (hardreset)		reset = hardreset;	goto retry;}static int ata_eh_revalidate_and_attach(struct ata_link *link,					struct ata_device **r_failed_dev){	struct ata_port *ap = link->ap;	struct ata_eh_context *ehc = &link->eh_context;	struct ata_device *dev;	unsigned int new_mask = 0;	unsigned long flags;	int rc = 0;	DPRINTK("ENTER\n");	/* For PATA drive side cable detection to work, IDENTIFY must	 * be done backwards such that PDIAG- is released by the slave	 * device before the master device is identified.	 */	ata_link_for_each_dev_reverse(dev, link) {		unsigned int action = ata_eh_dev_action(dev);		unsigned int readid_flags = 0;		if (ehc->i.flags & ATA_EHI_DID_RESET)			readid_flags |= ATA_READID_POSTRESET;		if ((action & ATA_EH_REVALIDATE) && ata_dev_enabled(dev)) {			WARN_ON(dev->class == ATA_DEV_PMP);			if (ata_link_offline(link)) {				rc = -EIO;				goto err;			}			ata_eh_about_to_do(link, dev, ATA_EH_REVALIDATE);			rc = ata_dev_revalidate(dev, ehc->classes[dev->devno],						readid_flags);			if (rc)				goto err;			ata_eh_done(link, dev, ATA_EH_REVALIDATE);			/* Configuration may have changed, reconfigure			 * transfer mode.			 */			ehc->i.flags |= ATA_EHI_SETMODE;			/* schedule the scsi_rescan_device() here */			queue_work(ata_aux_wq, &(ap->scsi_rescan_task));		} else if (dev->class == ATA_DEV_UNKNOWN &&			   ehc->tries[dev->devno] &&			   ata_class_enabled(ehc->classes[dev->devno])) {			dev->class = ehc->classes[dev->devno];			if (dev->class == ATA_DEV_PMP)				rc = sata_pmp_attach(dev);			else				rc = ata_dev_read_id(dev, &dev->class,						     readid_flags, dev->id);			switch (rc) {			case 0:				new_mask |= 1 << dev->devno;				break;			case -ENOENT:				/* IDENTIFY was issued to non-existent				 * device.  No need to reset.  Just				 * thaw and kill the device.				 */				ata_eh_thaw_port(ap);				dev->class = ATA_DEV_UNKNOWN;				break;			default:				dev->class = ATA_DEV_UNKNOWN;				goto err;			}		}	}	/* PDIAG- should have been released, ask cable type if post-reset */	if (ata_is_host_link(link) && ap->ops->cable_detect &&	    (ehc->i.flags & ATA_EHI_DID_RESET))		ap->cbl = ap->ops->cable_detect(ap);	/* Configure new devices forward such that user doesn't see	 * device detection messages backwards.	 */	ata_link_for_each_dev(dev, link) {		if (!(new_mask & (1 << dev->devno)) ||		    dev->class == ATA_DEV_PMP)			continue;		ehc->i.flags |= ATA_EHI_PRINTINFO;		rc = ata_dev_configure(dev);		ehc->i.flags &= ~ATA_EHI_PRINTINFO;		if (rc)			goto err;		spin_lock_irqsave(ap->lock, flags);		ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG;		spin_unlock_irqrestore(ap->lock, flags);		/* new device discovered, configure xfermode */		ehc->i.flags |= ATA_EHI_SETMODE;	}	return 0; err:	*r_failed_dev = dev;	DPRINTK("EXIT rc=%d\n", rc);	return rc;}static int ata_link_nr_enabled(struct ata_link *link){	struct ata_device *dev;	int cnt = 0;	ata_link_for_each_dev(dev, link)		if (ata_dev_enabled(dev))			cnt++;	return cnt;}static int ata_link_nr_vacant(struct ata_link *link){	struct ata_device *dev;	int cnt = 0;	ata_link_for_each_dev(dev, link)		if (dev->class == ATA_DEV_UNKNOWN)			cnt++;	return cnt;}static int ata_eh_skip_recovery(struct ata_link *link){	struct ata_eh_context *ehc = &link->eh_context;	struct ata_device *dev;	/* skip disabled links */	if (link->flags & ATA_LFLAG_DISABLED)		return 1;	/* thaw frozen port, resume link and recover failed devices */	if ((link->ap->pflags & ATA_PFLAG_FROZEN) ||	    (ehc->i.flags & ATA_EHI_RESUME_LINK) || ata_link_nr_enabled(link))		return 0;	/* skip if class codes for all vacant slots are ATA_DEV_NONE */	ata_link_for_each_dev(dev, link) {		if (dev->class == ATA_DEV_UNKNOWN &&		    ehc->classes[dev->devno] != ATA_DEV_NONE)			return 0;	}	return 1;}static int ata_eh_handle_dev_fail(struct ata_device *dev, int err){	struct ata_eh_context *ehc = &dev->link->eh_context;	ehc->tries[dev->devno]--;	switch (err) {	case -ENODEV:		/* device missing or wrong IDENTIFY data, schedule probing */		ehc->i.probe_mask |= (1 << dev->devno);	case -EINVAL:		/* give it just one more chance */		ehc->tries[dev->devno] = min(ehc->tries[dev->devno], 1);	case -EIO:		if (ehc->tries[dev->devno] == 1 && dev->pio_mode > XFER_PIO_0) {			/* This is the last chance, better to slow			 * down than lose it.			 */			sata_down_spd_limit(dev->link);			ata_down_xfermask_limit(dev, ATA_DNXFER_PIO);		}	}	if (ata_dev_enabled(dev) && !ehc->tries[dev->devno]) {		/* disable device if it has used up all its chances */		ata_dev_disable(dev);		/* detach if offline */		if (ata_link_offline(dev->link))			ata_eh_detach_dev(dev);		/* probe if requested */		if ((ehc->i.probe_mask & (1 << dev->devno)) &&		    !(ehc->did_probe_mask & (1 << dev->devno))) {			ata_eh_detach_dev(dev);			ata_dev_init(dev);			ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;			ehc->did_probe_mask |= (1 << dev->devno);			ehc->i.action |= ATA_EH_SOFTRESET;		}		return 1;	} else {		/* soft didn't work?  be haaaaard */		if (ehc->i.flags & ATA_EHI_DID_RESET)			ehc->i.action |= ATA_EH_HARDRESET;		else			ehc->i.action |= ATA_EH_SOFTRESET;		return 0;	}}/** *	ata_eh_recover - recover host port after error *	@ap: host port to recover *	@prereset: prereset method (can be NULL) *	@softreset: softreset method (can be NULL) *	@hardreset: hardreset method (can be NULL) *	@postreset: postreset method (can be NULL) *	@r_failed_link: out parameter for failed link * *	This is the alpha and omega, eum and yang, heart and soul of *	libata exception handling.  On entry, actions required to *	recover each link and hotplug requests are recorded in the *	link's eh_context.  This function executes all the operations *	with appropriate retrials and fallbacks to resurrect failed *	devices, detach goners and greet newcomers. * *	LOCKING: *	Kernel thread context (may sleep). * *	RETURNS: *	0 on success, -errno on failure. */int ata_eh_recover(struct ata_port *ap, ata_prereset_fn_t prereset,		   ata_reset_fn_t softreset, ata_reset_fn_t hardreset,		   ata_postreset_fn_t postreset,		   struct ata_link **r_failed_link){	struct ata_link *link;	struct ata_device *dev;	int nr_failed_devs, nr_disabled_devs;	int reset, rc;	unsigned long flags;	DPRINTK("ENTER\n");	/* prep for recovery */	ata_port_for_each_link(link, ap) {		struct ata_eh_context *ehc = &link->eh_context;		/* re-enable link? */		if (ehc->i.action & ATA_EH_ENABLE_LINK) {			ata_eh_about_to_do(link, NULL, ATA_EH_ENABLE_LINK);			spin_lock_irqsave(ap->lock, flags);			link->flags &= ~ATA_LFLAG_DISABLED;			spin_unlock_irqrestore(ap->lock, flags);			ata_eh_done(link, NULL, ATA_EH_ENABLE_LINK);		}		ata_link_for_each_dev(dev, link) {			if (link->flags & ATA_LFLAG_NO_RETRY)				ehc->tries[dev->devno] = 1;			else				ehc->tries[dev->devno] = ATA_EH_DEV_TRIES;			/* collect port action mask recorded in dev actions */			ehc->i.action |= ehc->i.dev_action[dev->devno] &					 ~ATA_EH_PERDEV_MASK;			ehc->i.dev_action[dev->devno] &= ATA_EH_PERDEV_MASK;			/* process hotplug request */			if (dev->flags & ATA_DFLAG_DETACH)				ata_eh_detach_dev(dev);			if (!ata_dev_enabled(dev) &&			    ((ehc->i.probe_mask & (1 << dev->devno)) &&			     !(ehc->did_probe_mask & (1 << dev->devno)))) {				ata_eh_detach_dev(dev);				ata_dev_init(dev);				ehc->did_probe_mask |= (1 << dev->devno);				ehc->i.action |= ATA_EH_SOFTRESET;			}		}	} retry:	rc = 0;	nr_failed_devs = 0;	nr_disabled_devs = 0;	reset = 0;	/* if UNLOADING, finish immediately */	if (ap->pflags & ATA_PFLAG_UNLOADING)		goto out;	/* prep for EH */	ata_port_for_each_link(link, ap) {		struct ata_eh_context *ehc = &link->eh_context;		/* skip EH if possible. */		if (ata_eh_skip_recovery(link))			ehc->i.action = 0;		/* do we need to reset? */		if (ehc->i.action & ATA_EH_RESET_MASK)			reset = 1;		ata_link_for_each_dev(dev, link)			ehc->classes[dev->devno] = ATA_DEV_UNKNOWN;	}	/* reset */	if (reset) {		/* if PMP is attached, this function only deals with		 * downstream links, port should stay thawed.		 */		if (!ap->nr_pmp_links)			ata_eh_freeze_port(ap);		ata_port_for_each_link(link, ap) {			struct ata_eh_context *ehc = &link->eh_context;			if (!(ehc->i.action & ATA_EH_RESET_MASK))				continue;			rc = ata_eh_reset(link, ata_link_nr_vacant(link),					  prereset, softreset, hardreset,					  postreset);			if (rc) {				ata_link_printk(link, KERN_ERR,						"reset failed, giving up\n");				goto out;			}		}		if (!ap->nr_pmp_links)			ata_eh_thaw_port(ap);	}	/* the rest */	ata_port_for_each_

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?