mirror of
https://gitee.com/openharmony/kernel_linux
synced 2025-05-17 20:26:37 +00:00
[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
@ -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)
|
||||||
|
@ -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…
x
Reference in New Issue
Block a user