2657 lines · 74.1 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only/* *  scsi_error.c Copyright (C) 1997 Eric Youngdale * *  SCSI error/timeout handling *      Initial versions: Eric Youngdale.  Based upon conversations with *                        Leonard Zubkoff and David Miller at Linux Expo, *                        ideas originating from all over the place. * *	Restructured scsi_unjam_host and associated functions. *	September 04, 2002 Mike Anderson (andmike@us.ibm.com) * *	Forward port of Russell King's (rmk@arm.linux.org.uk) changes and *	minor cleanups. *	September 30, 2002 Mike Anderson (andmike@us.ibm.com) */ #include <linux/module.h>#include <linux/sched.h>#include <linux/gfp.h>#include <linux/timer.h>#include <linux/string.h>#include <linux/kernel.h>#include <linux/freezer.h>#include <linux/kthread.h>#include <linux/interrupt.h>#include <linux/blkdev.h>#include <linux/delay.h>#include <linux/jiffies.h> #include <scsi/scsi.h>#include <scsi/scsi_cmnd.h>#include <scsi/scsi_dbg.h>#include <scsi/scsi_device.h>#include <scsi/scsi_driver.h>#include <scsi/scsi_eh.h>#include <scsi/scsi_common.h>#include <scsi/scsi_transport.h>#include <scsi/scsi_host.h>#include <scsi/scsi_ioctl.h>#include <scsi/scsi_dh.h>#include <scsi/scsi_devinfo.h>#include <scsi/sg.h> #include "scsi_priv.h"#include "scsi_logging.h"#include "scsi_transport_api.h" #include <trace/events/scsi.h> #include <linux/unaligned.h> /* * These should *probably* be handled by the host itself. * Since it is allowed to sleep, it probably should. */#define BUS_RESET_SETTLE_TIME   (10)#define HOST_RESET_SETTLE_TIME  (10) static int scsi_eh_try_stu(struct scsi_cmnd *scmd);static enum scsi_disposition scsi_try_to_abort_cmd(const struct scsi_host_template *,						   struct scsi_cmnd *); void scsi_eh_wakeup(struct Scsi_Host *shost, unsigned int busy){	lockdep_assert_held(shost->host_lock); 	if (busy == shost->host_failed) {		trace_scsi_eh_wakeup(shost);		wake_up_process(shost->ehandler);		SCSI_LOG_ERROR_RECOVERY(5, shost_printk(KERN_INFO, shost,			"Waking error handler thread\n"));	}} void scsi_rcu_eh_wakeup(struct work_struct *work){	struct Scsi_Host *shost = container_of(work, struct Scsi_Host, eh_work);	unsigned long flags;	unsigned int busy; 	/*	 * Ensure any running scsi_dec_host_busy has completed its rcu section	 * so changes to host state and host_eh_scheduled are visible to all	 * future calls of scsi_dec_host_busy	 */	synchronize_rcu(); 	busy = scsi_host_busy(shost); 	spin_lock_irqsave(shost->host_lock, flags);	scsi_eh_wakeup(shost, busy);	spin_unlock_irqrestore(shost->host_lock, flags);} /** * scsi_schedule_eh - schedule EH for SCSI host * @shost:	SCSI host to invoke error handling on. * * Schedule SCSI EH without scmd. */void scsi_schedule_eh(struct Scsi_Host *shost){	unsigned long flags; 	spin_lock_irqsave(shost->host_lock, flags); 	if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 ||	    scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) {		shost->host_eh_scheduled++;		queue_work(shost->tmf_work_q, &shost->eh_work);	} 	spin_unlock_irqrestore(shost->host_lock, flags);}EXPORT_SYMBOL_GPL(scsi_schedule_eh); static int scsi_host_eh_past_deadline(struct Scsi_Host *shost){	if (!shost->last_reset || shost->eh_deadline == -1)		return 0; 	/*	 * 32bit accesses are guaranteed to be atomic	 * (on all supported architectures), so instead	 * of using a spinlock we can as well double check	 * if eh_deadline has been set to 'off' during the	 * time_before call.	 */	if (time_before(jiffies, shost->last_reset + shost->eh_deadline) &&	    shost->eh_deadline > -1)		return 0; 	return 1;} static bool scsi_cmd_retry_allowed(struct scsi_cmnd *cmd){	if (cmd->allowed == SCSI_CMD_RETRIES_NO_LIMIT)		return true; 	return ++cmd->retries <= cmd->allowed;} static bool scsi_eh_should_retry_cmd(struct scsi_cmnd *cmd){	struct scsi_device *sdev = cmd->device;	struct Scsi_Host *host = sdev->host; 	if (host->hostt->eh_should_retry_cmd)		return  host->hostt->eh_should_retry_cmd(cmd); 	return true;} /** * scmd_eh_abort_handler - Handle command aborts * @work:	command to be aborted. * * Note: this function must be called only for a command that has timed out. * Because the block layer marks a request as complete before it calls * scsi_timeout(), a .scsi_done() call from the LLD for a command that has * timed out do not have any effect. Hence it is safe to call * scsi_finish_command() from this function. */voidscmd_eh_abort_handler(struct work_struct *work){	struct scsi_cmnd *scmd =		container_of(work, struct scsi_cmnd, abort_work.work);	struct scsi_device *sdev = scmd->device;	struct Scsi_Host *shost = sdev->host;	enum scsi_disposition rtn;	unsigned long flags; 	if (scsi_host_eh_past_deadline(shost)) {		SCSI_LOG_ERROR_RECOVERY(3,			scmd_printk(KERN_INFO, scmd,				    "eh timeout, not aborting\n"));		goto out;	} 	SCSI_LOG_ERROR_RECOVERY(3,			scmd_printk(KERN_INFO, scmd,				    "aborting command\n"));	rtn = scsi_try_to_abort_cmd(shost->hostt, scmd);	if (rtn != SUCCESS) {		SCSI_LOG_ERROR_RECOVERY(3,			scmd_printk(KERN_INFO, scmd,				    "cmd abort %s\n",				    (rtn == FAST_IO_FAIL) ?				    "not send" : "failed"));		goto out;	}	set_host_byte(scmd, DID_TIME_OUT);	if (scsi_host_eh_past_deadline(shost)) {		SCSI_LOG_ERROR_RECOVERY(3,			scmd_printk(KERN_INFO, scmd,				    "eh timeout, not retrying "				    "aborted command\n"));		goto out;	} 	spin_lock_irqsave(shost->host_lock, flags);	list_del_init(&scmd->eh_entry); 	/*	 * If the abort succeeds, and there is no further	 * EH action, clear the ->last_reset time.	 */	if (list_empty(&shost->eh_abort_list) &&	    list_empty(&shost->eh_cmd_q))		if (shost->eh_deadline != -1)			shost->last_reset = 0; 	spin_unlock_irqrestore(shost->host_lock, flags); 	if (!scsi_noretry_cmd(scmd) &&	    scsi_cmd_retry_allowed(scmd) &&	    scsi_eh_should_retry_cmd(scmd)) {		SCSI_LOG_ERROR_RECOVERY(3,			scmd_printk(KERN_WARNING, scmd,				    "retry aborted command\n"));		scsi_queue_insert(scmd, SCSI_MLQUEUE_EH_RETRY);	} else {		SCSI_LOG_ERROR_RECOVERY(3,			scmd_printk(KERN_WARNING, scmd,				    "finish aborted command\n"));		scsi_finish_command(scmd);	}	return; out:	spin_lock_irqsave(shost->host_lock, flags);	list_del_init(&scmd->eh_entry);	spin_unlock_irqrestore(shost->host_lock, flags); 	scsi_eh_scmd_add(scmd);} /** * scsi_abort_command - schedule a command abort * @scmd:	scmd to abort. * * We only need to abort commands after a command timeout */static intscsi_abort_command(struct scsi_cmnd *scmd){	struct scsi_device *sdev = scmd->device;	struct Scsi_Host *shost = sdev->host;	unsigned long flags; 	if (!shost->hostt->eh_abort_handler) {		/* No abort handler, fail command directly */		return FAILED;	} 	if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED) {		/*		 * Retry after abort failed, escalate to next level.		 */		SCSI_LOG_ERROR_RECOVERY(3,			scmd_printk(KERN_INFO, scmd,				    "previous abort failed\n"));		BUG_ON(delayed_work_pending(&scmd->abort_work));		return FAILED;	} 	spin_lock_irqsave(shost->host_lock, flags);	if (shost->eh_deadline != -1 && !shost->last_reset)		shost->last_reset = jiffies;	BUG_ON(!list_empty(&scmd->eh_entry));	list_add_tail(&scmd->eh_entry, &shost->eh_abort_list);	spin_unlock_irqrestore(shost->host_lock, flags); 	scmd->eh_eflags |= SCSI_EH_ABORT_SCHEDULED;	SCSI_LOG_ERROR_RECOVERY(3,		scmd_printk(KERN_INFO, scmd, "abort scheduled\n"));	queue_delayed_work(shost->tmf_work_q, &scmd->abort_work, HZ / 100);	return SUCCESS;} /** * scsi_eh_reset - call into ->eh_action to reset internal counters * @scmd:	scmd to run eh on. * * The scsi driver might be carrying internal state about the * devices, so we need to call into the driver to reset the * internal state once the error handler is started. */static void scsi_eh_reset(struct scsi_cmnd *scmd){	if (!blk_rq_is_passthrough(scsi_cmd_to_rq(scmd))) {		struct scsi_driver *sdrv = scsi_cmd_to_driver(scmd);		if (sdrv->eh_reset)			sdrv->eh_reset(scmd);	}} static void scsi_eh_inc_host_failed(struct rcu_head *head){	struct scsi_cmnd *scmd = container_of(head, typeof(*scmd), rcu);	struct Scsi_Host *shost = scmd->device->host;	unsigned int busy;	unsigned long flags; 	spin_lock_irqsave(shost->host_lock, flags);	shost->host_failed++;	spin_unlock_irqrestore(shost->host_lock, flags);	/*	 * The counting of busy requests needs to occur after adding to	 * host_failed or after the lock acquire for adding to host_failed	 * to prevent a race with host unbusy and missing an eh wakeup.	 */	busy = scsi_host_busy(shost); 	spin_lock_irqsave(shost->host_lock, flags);	scsi_eh_wakeup(shost, busy);	spin_unlock_irqrestore(shost->host_lock, flags);} /** * scsi_eh_scmd_add - add scsi cmd to error handling. * @scmd:	scmd to run eh on. */void scsi_eh_scmd_add(struct scsi_cmnd *scmd){	struct Scsi_Host *shost = scmd->device->host;	unsigned long flags;	int ret; 	WARN_ON_ONCE(!shost->ehandler);	WARN_ON_ONCE(!test_bit(SCMD_STATE_INFLIGHT, &scmd->state)); 	spin_lock_irqsave(shost->host_lock, flags);	if (scsi_host_set_state(shost, SHOST_RECOVERY)) {		ret = scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY);		WARN_ON_ONCE(ret);	}	if (shost->eh_deadline != -1 && !shost->last_reset)		shost->last_reset = jiffies; 	scsi_eh_reset(scmd);	list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q);	spin_unlock_irqrestore(shost->host_lock, flags);	/*	 * Ensure that all tasks observe the host state change before the	 * host_failed change.	 */	call_rcu_hurry(&scmd->rcu, scsi_eh_inc_host_failed);} /** * scsi_timeout - Timeout function for normal scsi commands. * @req:	request that is timing out. * * Notes: *     We do not need to lock this.  There is the potential for a race *     only in that the normal completion handling might run, but if the *     normal completion function determines that the timer has already *     fired, then it mustn't do anything. */enum blk_eh_timer_return scsi_timeout(struct request *req){	struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(req);	struct Scsi_Host *host = scmd->device->host; 	trace_scsi_dispatch_cmd_timeout(scmd);	scsi_log_completion(scmd, TIMEOUT_ERROR); 	atomic_inc(&scmd->device->iotmo_cnt);	if (host->eh_deadline != -1 && !host->last_reset)		host->last_reset = jiffies; 	if (host->hostt->eh_timed_out) {		switch (host->hostt->eh_timed_out(scmd)) {		case SCSI_EH_DONE:			return BLK_EH_DONE;		case SCSI_EH_RESET_TIMER:			return BLK_EH_RESET_TIMER;		case SCSI_EH_NOT_HANDLED:			break;		}	} 	/*	 * If scsi_done() has already set SCMD_STATE_COMPLETE, do not modify	 * *scmd.	 */	if (test_and_set_bit(SCMD_STATE_COMPLETE, &scmd->state))		return BLK_EH_DONE;	atomic_inc(&scmd->device->iodone_cnt);	if (scsi_abort_command(scmd) != SUCCESS) {		set_host_byte(scmd, DID_TIME_OUT);		scsi_eh_scmd_add(scmd);	} 	return BLK_EH_DONE;} /** * scsi_block_when_processing_errors - Prevent cmds from being queued. * @sdev:	Device on which we are performing recovery. * * Description: *     We block until the host is out of error recovery, and then check to *     see whether the host or the device is offline. * * Return value: *     0 when dev was taken offline by error recovery. 1 OK to proceed. */int scsi_block_when_processing_errors(struct scsi_device *sdev){	int online; 	wait_event(sdev->host->host_wait, !scsi_host_in_recovery(sdev->host)); 	online = scsi_device_online(sdev); 	return online;}EXPORT_SYMBOL(scsi_block_when_processing_errors); #ifdef CONFIG_SCSI_LOGGING/** * scsi_eh_prt_fail_stats - Log info on failures. * @shost:	scsi host being recovered. * @work_q:	Queue of scsi cmds to process. */static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,					  struct list_head *work_q){	struct scsi_cmnd *scmd;	struct scsi_device *sdev;	int total_failures = 0;	int cmd_failed = 0;	int cmd_cancel = 0;	int devices_failed = 0; 	shost_for_each_device(sdev, shost) {		list_for_each_entry(scmd, work_q, eh_entry) {			if (scmd->device == sdev) {				++total_failures;				if (scmd->eh_eflags & SCSI_EH_ABORT_SCHEDULED)					++cmd_cancel;				else					++cmd_failed;			}		} 		if (cmd_cancel || cmd_failed) {			SCSI_LOG_ERROR_RECOVERY(3,				shost_printk(KERN_INFO, shost,					    "%s: cmds failed: %d, cancel: %d\n",					    __func__, cmd_failed,					    cmd_cancel));			cmd_cancel = 0;			cmd_failed = 0;			++devices_failed;		}	} 	SCSI_LOG_ERROR_RECOVERY(2, shost_printk(KERN_INFO, shost,				   "Total of %d commands on %d"				   " devices require eh work\n",				   total_failures, devices_failed));}#endif  /** * scsi_report_lun_change - Set flag on all *other* devices on the same target *                          to indicate that a UNIT ATTENTION is expected. * @sdev:	Device reporting the UNIT ATTENTION */static void scsi_report_lun_change(struct scsi_device *sdev){	sdev->sdev_target->expecting_lun_change = 1;} /** * scsi_report_sense - Examine scsi sense information and log messages for *		       certain conditions, also issue uevents for some of them. * @sdev:	Device reporting the sense code * @sshdr:	sshdr to be examined */static void scsi_report_sense(struct scsi_device *sdev,			      struct scsi_sense_hdr *sshdr){	enum scsi_device_event evt_type = SDEV_EVT_MAXBITS;	/* i.e. none */ 	if (sshdr->sense_key == UNIT_ATTENTION) {		if (sshdr->asc == 0x3f && sshdr->ascq == 0x03) {			evt_type = SDEV_EVT_INQUIRY_CHANGE_REPORTED;			sdev_printk(KERN_WARNING, sdev,				    "Inquiry data has changed");		} else if (sshdr->asc == 0x3f && sshdr->ascq == 0x0e) {			evt_type = SDEV_EVT_LUN_CHANGE_REPORTED;			scsi_report_lun_change(sdev);			sdev_printk(KERN_WARNING, sdev,

Lines 1–500 of 2657.