libata-eh.c

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

C
2,561
字号
	if (ap->pflags & ATA_PFLAG_RECOVERED)		ata_port_printk(ap, KERN_INFO, "EH complete\n");	ap->pflags &= ~(ATA_PFLAG_SCSI_HOTPLUG | ATA_PFLAG_RECOVERED);	/* tell wait_eh that we're done */	ap->pflags &= ~ATA_PFLAG_EH_IN_PROGRESS;	wake_up_all(&ap->eh_wait_q);	spin_unlock_irqrestore(ap->lock, flags);	DPRINTK("EXIT\n");}/** *	ata_port_wait_eh - Wait for the currently pending EH to complete *	@ap: Port to wait EH for * *	Wait until the currently pending EH is complete. * *	LOCKING: *	Kernel thread context (may sleep). */void ata_port_wait_eh(struct ata_port *ap){	unsigned long flags;	DEFINE_WAIT(wait); retry:	spin_lock_irqsave(ap->lock, flags);	while (ap->pflags & (ATA_PFLAG_EH_PENDING | ATA_PFLAG_EH_IN_PROGRESS)) {		prepare_to_wait(&ap->eh_wait_q, &wait, TASK_UNINTERRUPTIBLE);		spin_unlock_irqrestore(ap->lock, flags);		schedule();		spin_lock_irqsave(ap->lock, flags);	}	finish_wait(&ap->eh_wait_q, &wait);	spin_unlock_irqrestore(ap->lock, flags);	/* make sure SCSI EH is complete */	if (scsi_host_in_recovery(ap->scsi_host)) {		msleep(10);		goto retry;	}}static int ata_eh_nr_in_flight(struct ata_port *ap){	unsigned int tag;	int nr = 0;	/* count only non-internal commands */	for (tag = 0; tag < ATA_MAX_QUEUE - 1; tag++)		if (ata_qc_from_tag(ap, tag))			nr++;	return nr;}void ata_eh_fastdrain_timerfn(unsigned long arg){	struct ata_port *ap = (void *)arg;	unsigned long flags;	int cnt;	spin_lock_irqsave(ap->lock, flags);	cnt = ata_eh_nr_in_flight(ap);	/* are we done? */	if (!cnt)		goto out_unlock;	if (cnt == ap->fastdrain_cnt) {		unsigned int tag;		/* No progress during the last interval, tag all		 * in-flight qcs as timed out and freeze the port.		 */		for (tag = 0; tag < ATA_MAX_QUEUE - 1; tag++) {			struct ata_queued_cmd *qc = ata_qc_from_tag(ap, tag);			if (qc)				qc->err_mask |= AC_ERR_TIMEOUT;		}		ata_port_freeze(ap);	} else {		/* some qcs have finished, give it another chance */		ap->fastdrain_cnt = cnt;		ap->fastdrain_timer.expires =			jiffies + ATA_EH_FASTDRAIN_INTERVAL;		add_timer(&ap->fastdrain_timer);	} out_unlock:	spin_unlock_irqrestore(ap->lock, flags);}/** *	ata_eh_set_pending - set ATA_PFLAG_EH_PENDING and activate fast drain *	@ap: target ATA port *	@fastdrain: activate fast drain * *	Set ATA_PFLAG_EH_PENDING and activate fast drain if @fastdrain *	is non-zero and EH wasn't pending before.  Fast drain ensures *	that EH kicks in in timely manner. * *	LOCKING: *	spin_lock_irqsave(host lock) */static void ata_eh_set_pending(struct ata_port *ap, int fastdrain){	int cnt;	/* already scheduled? */	if (ap->pflags & ATA_PFLAG_EH_PENDING)		return;	ap->pflags |= ATA_PFLAG_EH_PENDING;	if (!fastdrain)		return;	/* do we have in-flight qcs? */	cnt = ata_eh_nr_in_flight(ap);	if (!cnt)		return;	/* activate fast drain */	ap->fastdrain_cnt = cnt;	ap->fastdrain_timer.expires = jiffies + ATA_EH_FASTDRAIN_INTERVAL;	add_timer(&ap->fastdrain_timer);}/** *	ata_qc_schedule_eh - schedule qc for error handling *	@qc: command to schedule error handling for * *	Schedule error handling for @qc.  EH will kick in as soon as *	other commands are drained. * *	LOCKING: *	spin_lock_irqsave(host lock) */void ata_qc_schedule_eh(struct ata_queued_cmd *qc){	struct ata_port *ap = qc->ap;	WARN_ON(!ap->ops->error_handler);	qc->flags |= ATA_QCFLAG_FAILED;	ata_eh_set_pending(ap, 1);	/* The following will fail if timeout has already expired.	 * ata_scsi_error() takes care of such scmds on EH entry.	 * Note that ATA_QCFLAG_FAILED is unconditionally set after	 * this function completes.	 */	scsi_req_abort_cmd(qc->scsicmd);}/** *	ata_port_schedule_eh - schedule error handling without a qc *	@ap: ATA port to schedule EH for * *	Schedule error handling for @ap.  EH will kick in as soon as *	all commands are drained. * *	LOCKING: *	spin_lock_irqsave(host lock) */void ata_port_schedule_eh(struct ata_port *ap){	WARN_ON(!ap->ops->error_handler);	if (ap->pflags & ATA_PFLAG_INITIALIZING)		return;	ata_eh_set_pending(ap, 1);	scsi_schedule_eh(ap->scsi_host);	DPRINTK("port EH scheduled\n");}static int ata_do_link_abort(struct ata_port *ap, struct ata_link *link){	int tag, nr_aborted = 0;	WARN_ON(!ap->ops->error_handler);	/* we're gonna abort all commands, no need for fast drain */	ata_eh_set_pending(ap, 0);	for (tag = 0; tag < ATA_MAX_QUEUE; tag++) {		struct ata_queued_cmd *qc = ata_qc_from_tag(ap, tag);		if (qc && (!link || qc->dev->link == link)) {			qc->flags |= ATA_QCFLAG_FAILED;			ata_qc_complete(qc);			nr_aborted++;		}	}	if (!nr_aborted)		ata_port_schedule_eh(ap);	return nr_aborted;}/** *	ata_link_abort - abort all qc's on the link *	@link: ATA link to abort qc's for * *	Abort all active qc's active on @link and schedule EH. * *	LOCKING: *	spin_lock_irqsave(host lock) * *	RETURNS: *	Number of aborted qc's. */int ata_link_abort(struct ata_link *link){	return ata_do_link_abort(link->ap, link);}/** *	ata_port_abort - abort all qc's on the port *	@ap: ATA port to abort qc's for * *	Abort all active qc's of @ap and schedule EH. * *	LOCKING: *	spin_lock_irqsave(host_set lock) * *	RETURNS: *	Number of aborted qc's. */int ata_port_abort(struct ata_port *ap){	return ata_do_link_abort(ap, NULL);}/** *	__ata_port_freeze - freeze port *	@ap: ATA port to freeze * *	This function is called when HSM violation or some other *	condition disrupts normal operation of the port.  Frozen port *	is not allowed to perform any operation until the port is *	thawed, which usually follows a successful reset. * *	ap->ops->freeze() callback can be used for freezing the port *	hardware-wise (e.g. mask interrupt and stop DMA engine).  If a *	port cannot be frozen hardware-wise, the interrupt handler *	must ack and clear interrupts unconditionally while the port *	is frozen. * *	LOCKING: *	spin_lock_irqsave(host lock) */static void __ata_port_freeze(struct ata_port *ap){	WARN_ON(!ap->ops->error_handler);	if (ap->ops->freeze)		ap->ops->freeze(ap);	ap->pflags |= ATA_PFLAG_FROZEN;	DPRINTK("ata%u port frozen\n", ap->print_id);}/** *	ata_port_freeze - abort & freeze port *	@ap: ATA port to freeze * *	Abort and freeze @ap. * *	LOCKING: *	spin_lock_irqsave(host lock) * *	RETURNS: *	Number of aborted commands. */int ata_port_freeze(struct ata_port *ap){	int nr_aborted;	WARN_ON(!ap->ops->error_handler);	nr_aborted = ata_port_abort(ap);	__ata_port_freeze(ap);	return nr_aborted;}/** *	sata_async_notification - SATA async notification handler *	@ap: ATA port where async notification is received * *	Handler to be called when async notification via SDB FIS is *	received.  This function schedules EH if necessary. * *	LOCKING: *	spin_lock_irqsave(host lock) * *	RETURNS: *	1 if EH is scheduled, 0 otherwise. */int sata_async_notification(struct ata_port *ap){	u32 sntf;	int rc;	if (!(ap->flags & ATA_FLAG_AN))		return 0;	rc = sata_scr_read(&ap->link, SCR_NOTIFICATION, &sntf);	if (rc == 0)		sata_scr_write(&ap->link, SCR_NOTIFICATION, sntf);	if (!ap->nr_pmp_links || rc) {		/* PMP is not attached or SNTF is not available */		if (!ap->nr_pmp_links) {			/* PMP is not attached.  Check whether ATAPI			 * AN is configured.  If so, notify media			 * change.			 */			struct ata_device *dev = ap->link.device;			if ((dev->class == ATA_DEV_ATAPI) &&			    (dev->flags & ATA_DFLAG_AN))				ata_scsi_media_change_notify(dev);			return 0;		} else {			/* PMP is attached but SNTF is not available.			 * ATAPI async media change notification is			 * not used.  The PMP must be reporting PHY			 * status change, schedule EH.			 */			ata_port_schedule_eh(ap);			return 1;		}	} else {		/* PMP is attached and SNTF is available */		struct ata_link *link;		/* check and notify ATAPI AN */		ata_port_for_each_link(link, ap) {			if (!(sntf & (1 << link->pmp)))				continue;			if ((link->device->class == ATA_DEV_ATAPI) &&			    (link->device->flags & ATA_DFLAG_AN))				ata_scsi_media_change_notify(link->device);		}		/* If PMP is reporting that PHY status of some		 * downstream ports has changed, schedule EH.		 */		if (sntf & (1 << SATA_PMP_CTRL_PORT)) {			ata_port_schedule_eh(ap);			return 1;		}		return 0;	}}/** *	ata_eh_freeze_port - EH helper to freeze port *	@ap: ATA port to freeze * *	Freeze @ap. * *	LOCKING: *	None. */void ata_eh_freeze_port(struct ata_port *ap){	unsigned long flags;	if (!ap->ops->error_handler)		return;	spin_lock_irqsave(ap->lock, flags);	__ata_port_freeze(ap);	spin_unlock_irqrestore(ap->lock, flags);}/** *	ata_port_thaw_port - EH helper to thaw port *	@ap: ATA port to thaw * *	Thaw frozen port @ap. * *	LOCKING: *	None. */void ata_eh_thaw_port(struct ata_port *ap){	unsigned long flags;	if (!ap->ops->error_handler)		return;	spin_lock_irqsave(ap->lock, flags);	ap->pflags &= ~ATA_PFLAG_FROZEN;	if (ap->ops->thaw)		ap->ops->thaw(ap);	spin_unlock_irqrestore(ap->lock, flags);	DPRINTK("ata%u port thawed\n", ap->print_id);}static void ata_eh_scsidone(struct scsi_cmnd *scmd){	/* nada */}static void __ata_eh_qc_complete(struct ata_queued_cmd *qc){	struct ata_port *ap = qc->ap;	struct scsi_cmnd *scmd = qc->scsicmd;	unsigned long flags;	spin_lock_irqsave(ap->lock, flags);	qc->scsidone = ata_eh_scsidone;	__ata_qc_complete(qc);	WARN_ON(ata_tag_valid(qc->tag));	spin_unlock_irqrestore(ap->lock, flags);	scsi_eh_finish_cmd(scmd, &ap->eh_done_q);}/** *	ata_eh_qc_complete - Complete an active ATA command from EH *	@qc: Command to complete * *	Indicate to the mid and upper layers that an ATA command has *	completed.  To be used from EH. */void ata_eh_qc_complete(struct ata_queued_cmd *qc){	struct scsi_cmnd *scmd = qc->scsicmd;	scmd->retries = scmd->allowed;	__ata_eh_qc_complete(qc);}/** *	ata_eh_qc_retry - Tell midlayer to retry an ATA command after EH *	@qc: Command to retry * *	Indicate to the mid and upper layers that an ATA command *	should be retried.  To be used from EH. * *	SCSI midlayer limits the number of retries to scmd->allowed. *	scmd->retries is decremented for commands which get retried *	due to unrelated failures (qc->err_mask is zero). */void ata_eh_qc_retry(struct ata_queued_cmd *qc){	struct scsi_cmnd *scmd = qc->scsicmd;	if (!qc->err_mask && scmd->retries)		scmd->retries--;	__ata_eh_qc_complete(qc);}/** *	ata_eh_detach_dev - detach ATA device *	@dev: ATA device to detach * *	Detach @dev. * *	LOCKING: *	None. */void ata_eh_detach_dev(struct ata_device *dev){	struct ata_link *link = dev->link;	struct ata_port *ap = link->ap;	unsigned long flags;	ata_dev_disable(dev);	spin_lock_irqsave(ap->lock, flags);	dev->flags &= ~ATA_DFLAG_DETACH;	if (ata_scsi_offline_dev(dev)) {		dev->flags |= ATA_DFLAG_DETACHED;		ap->pflags |= ATA_PFLAG_SCSI_HOTPLUG;	}	/* clear per-dev EH actions */	ata_eh_clear_action(link, dev, &link->eh_info, ATA_EH_PERDEV_MASK);	ata_eh_clear_action(link, dev, &link->eh_context.i, ATA_EH_PERDEV_MASK);	spin_unlock_irqrestore(ap->lock, flags);}/** *	ata_eh_about_to_do - about to perform eh_action *	@link: target ATA link *	@dev: target ATA dev for per-dev action (can be NULL) *	@action: action about to be performed *

⌨️ 快捷键说明

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