forked from mirrors/linux
		
	[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: Mike Christie <michaelc@cs.wisc.edu> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
This commit is contained in:
		
							parent
							
								
									f7441a791a
								
							
						
					
					
						commit
						30bd7df8ce
					
				
					 3 changed files with 106 additions and 18 deletions
				
			
		| 
						 | 
					@ -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,8 +616,9 @@ 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_bus_reset(scmd) != SUCCESS)
 | 
								if (scsi_try_target_reset(scmd) != SUCCESS)
 | 
				
			||||||
				scsi_try_host_reset(scmd);
 | 
									if (scsi_try_bus_reset(scmd) != SUCCESS)
 | 
				
			||||||
 | 
										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_bus_reset(shost, work_q, done_q))
 | 
								if (!scsi_eh_target_reset(shost, work_q, done_q))
 | 
				
			||||||
				if (!scsi_eh_host_reset(work_q, done_q))
 | 
									if (!scsi_eh_bus_reset(shost, work_q, done_q))
 | 
				
			||||||
					scsi_eh_offline_sdevs(work_q, done_q);
 | 
										if (!scsi_eh_host_reset(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)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -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 *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue