Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 30bd7df8 authored by Mike Christie's avatar Mike Christie Committed by James Bottomley
Browse files

[SCSI] scsi_error: add target reset handler



The problem is that serveral drivers are sending a target reset from the
device reset handler, and if we have multiple devices a target reset gets
sent for each device when only one would be sufficient. And if we do a target
reset it affects all the commands on the target so the device reset handler
code only cleaning up one devices's commands makes programming the driver a
little more difficult than it should be.

This patch adds a target reset handler, which drivers can use to send
a target reset. If successful it cleans up the commands for a devices
accessed through that starget.

Signed-off-by: default avatarMike Christie <michaelc@cs.wisc.edu>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@HansenPartnership.com>
parent f7441a79
Loading
Loading
Loading
Loading
+104 −18
Original line number Original line Diff line number Diff line
@@ -524,6 +524,41 @@ static int scsi_try_bus_reset(struct scsi_cmnd *scmd)
	return rtn;
	return rtn;
}
}


static void __scsi_report_device_reset(struct scsi_device *sdev, void *data)
{
	sdev->was_reset = 1;
	sdev->expecting_cc_ua = 1;
}

/**
 * scsi_try_target_reset - Ask host to perform a target reset
 * @scmd:	SCSI cmd used to send a target reset
 *
 * Notes:
 *    There is no timeout for this operation.  if this operation is
 *    unreliable for a given host, then the host itself needs to put a
 *    timer on it, and set the host back to a consistent state prior to
 *    returning.
 */
static int scsi_try_target_reset(struct scsi_cmnd *scmd)
{
	unsigned long flags;
	int rtn;

	if (!scmd->device->host->hostt->eh_target_reset_handler)
		return FAILED;

	rtn = scmd->device->host->hostt->eh_target_reset_handler(scmd);
	if (rtn == SUCCESS) {
		spin_lock_irqsave(scmd->device->host->host_lock, flags);
		__starget_for_each_device(scsi_target(scmd->device), NULL,
					  __scsi_report_device_reset);
		spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
	}

	return rtn;
}

/**
/**
 * scsi_try_bus_device_reset - Ask host to perform a BDR on a dev
 * scsi_try_bus_device_reset - Ask host to perform a BDR on a dev
 * @scmd:	SCSI cmd used to send BDR
 * @scmd:	SCSI cmd used to send BDR
@@ -542,11 +577,8 @@ static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd)
		return FAILED;
		return FAILED;


	rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd);
	rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd);
	if (rtn == SUCCESS) {
	if (rtn == SUCCESS)
		scmd->device->was_reset = 1;
		__scsi_report_device_reset(scmd->device, NULL);
		scmd->device->expecting_cc_ua = 1;
	}

	return rtn;
	return rtn;
}
}


@@ -584,6 +616,7 @@ static void scsi_abort_eh_cmnd(struct scsi_cmnd *scmd)
{
{
	if (__scsi_try_to_abort_cmd(scmd) != SUCCESS)
	if (__scsi_try_to_abort_cmd(scmd) != SUCCESS)
		if (scsi_try_bus_device_reset(scmd) != SUCCESS)
		if (scsi_try_bus_device_reset(scmd) != SUCCESS)
			if (scsi_try_target_reset(scmd) != SUCCESS)
				if (scsi_try_bus_reset(scmd) != SUCCESS)
				if (scsi_try_bus_reset(scmd) != SUCCESS)
					scsi_try_host_reset(scmd);
					scsi_try_host_reset(scmd);
}
}
@@ -1059,6 +1092,56 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost,
	return list_empty(work_q);
	return list_empty(work_q);
}
}


/**
 * scsi_eh_target_reset - send target reset if needed
 * @shost:	scsi host being recovered.
 * @work_q:     &list_head for pending commands.
 * @done_q:	&list_head for processed commands.
 *
 * Notes:
 *    Try a target reset.
 */
static int scsi_eh_target_reset(struct Scsi_Host *shost,
				struct list_head *work_q,
				struct list_head *done_q)
{
	struct scsi_cmnd *scmd, *tgtr_scmd, *next;
	unsigned int id;
	int rtn;

	for (id = 0; id <= shost->max_id; id++) {
		tgtr_scmd = NULL;
		list_for_each_entry(scmd, work_q, eh_entry) {
			if (id == scmd_id(scmd)) {
				tgtr_scmd = scmd;
				break;
			}
		}
		if (!tgtr_scmd)
			continue;

		SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending target reset "
						  "to target %d\n",
						  current->comm, id));
		rtn = scsi_try_target_reset(tgtr_scmd);
		if (rtn == SUCCESS) {
			list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
				if (id == scmd_id(scmd))
					if (!scsi_device_online(scmd->device) ||
					    !scsi_eh_tur(tgtr_scmd))
						scsi_eh_finish_cmd(scmd,
								   done_q);
			}
		} else
			SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Target reset"
							  " failed target: "
							  "%d\n",
							  current->comm, id));
	}

	return list_empty(work_q);
}

/**
/**
 * scsi_eh_bus_reset - send a bus reset 
 * scsi_eh_bus_reset - send a bus reset 
 * @shost:	&scsi host being recovered.
 * @shost:	&scsi host being recovered.
@@ -1447,9 +1530,11 @@ void scsi_eh_ready_devs(struct Scsi_Host *shost,
{
{
	if (!scsi_eh_stu(shost, work_q, done_q))
	if (!scsi_eh_stu(shost, work_q, done_q))
		if (!scsi_eh_bus_device_reset(shost, work_q, done_q))
		if (!scsi_eh_bus_device_reset(shost, work_q, done_q))
			if (!scsi_eh_target_reset(shost, work_q, done_q))
				if (!scsi_eh_bus_reset(shost, work_q, done_q))
				if (!scsi_eh_bus_reset(shost, work_q, done_q))
					if (!scsi_eh_host_reset(work_q, done_q))
					if (!scsi_eh_host_reset(work_q, done_q))
					scsi_eh_offline_sdevs(work_q, done_q);
						scsi_eh_offline_sdevs(work_q,
								      done_q);
}
}
EXPORT_SYMBOL_GPL(scsi_eh_ready_devs);
EXPORT_SYMBOL_GPL(scsi_eh_ready_devs);


@@ -1619,10 +1704,8 @@ void scsi_report_bus_reset(struct Scsi_Host *shost, int channel)
	struct scsi_device *sdev;
	struct scsi_device *sdev;


	__shost_for_each_device(sdev, shost) {
	__shost_for_each_device(sdev, shost) {
		if (channel == sdev_channel(sdev)) {
		if (channel == sdev_channel(sdev))
			sdev->was_reset = 1;
			__scsi_report_device_reset(sdev, NULL);
			sdev->expecting_cc_ua = 1;
		}
	}
	}
}
}
EXPORT_SYMBOL(scsi_report_bus_reset);
EXPORT_SYMBOL(scsi_report_bus_reset);
@@ -1655,10 +1738,8 @@ void scsi_report_device_reset(struct Scsi_Host *shost, int channel, int target)


	__shost_for_each_device(sdev, shost) {
	__shost_for_each_device(sdev, shost) {
		if (channel == sdev_channel(sdev) &&
		if (channel == sdev_channel(sdev) &&
		    target == sdev_id(sdev)) {
		    target == sdev_id(sdev))
			sdev->was_reset = 1;
			__scsi_report_device_reset(sdev, NULL);
			sdev->expecting_cc_ua = 1;
		}
	}
	}
}
}
EXPORT_SYMBOL(scsi_report_device_reset);
EXPORT_SYMBOL(scsi_report_device_reset);
@@ -1714,6 +1795,11 @@ scsi_reset_provider(struct scsi_device *dev, int flag)
		if (rtn == SUCCESS)
		if (rtn == SUCCESS)
			break;
			break;
		/* FALLTHROUGH */
		/* FALLTHROUGH */
	case SCSI_TRY_RESET_TARGET:
		rtn = scsi_try_target_reset(scmd);
		if (rtn == SUCCESS)
			break;
		/* FALLTHROUGH */
	case SCSI_TRY_RESET_BUS:
	case SCSI_TRY_RESET_BUS:
		rtn = scsi_try_bus_reset(scmd);
		rtn = scsi_try_bus_reset(scmd);
		if (rtn == SUCCESS)
		if (rtn == SUCCESS)
+1 −0
Original line number Original line Diff line number Diff line
@@ -64,6 +64,7 @@ extern int scsi_get_sense_info_fld(const u8 * sense_buffer, int sb_len,
#define SCSI_TRY_RESET_DEVICE	1
#define SCSI_TRY_RESET_DEVICE	1
#define SCSI_TRY_RESET_BUS	2
#define SCSI_TRY_RESET_BUS	2
#define SCSI_TRY_RESET_HOST	3
#define SCSI_TRY_RESET_HOST	3
#define SCSI_TRY_RESET_TARGET	4


extern int scsi_reset_provider(struct scsi_device *, int);
extern int scsi_reset_provider(struct scsi_device *, int);


+1 −0
Original line number Original line Diff line number Diff line
@@ -172,6 +172,7 @@ struct scsi_host_template {
	 */
	 */
	int (* eh_abort_handler)(struct scsi_cmnd *);
	int (* eh_abort_handler)(struct scsi_cmnd *);
	int (* eh_device_reset_handler)(struct scsi_cmnd *);
	int (* eh_device_reset_handler)(struct scsi_cmnd *);
	int (* eh_target_reset_handler)(struct scsi_cmnd *);
	int (* eh_bus_reset_handler)(struct scsi_cmnd *);
	int (* eh_bus_reset_handler)(struct scsi_cmnd *);
	int (* eh_host_reset_handler)(struct scsi_cmnd *);
	int (* eh_host_reset_handler)(struct scsi_cmnd *);