[SCSI] fix id computation in scsi_eh_target_reset()
The current code in scsi_eh_target_reset() has an off by one error that actually sends spurious extra resets. Since there's no real need to reset the targets in numerical order, simply chunk up the command recovery list doing target resets and pulling matching targets out of the list (that also makes the loop O(N) instead of O(N^2). [mike christie found and fixed a list_splice -> list_splice_init problem] Reported-by: Hillf Danton<dhillf@gmail.com> Signed-off-by: James Bottomley <James.Bottomley@suse.de>
This commit is contained in:
parent
110def851f
commit
98db519573
1 changed files with 25 additions and 36 deletions
|
@ -1124,51 +1124,40 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost,
|
||||||
struct list_head *work_q,
|
struct list_head *work_q,
|
||||||
struct list_head *done_q)
|
struct list_head *done_q)
|
||||||
{
|
{
|
||||||
struct scsi_cmnd *scmd, *tgtr_scmd, *next;
|
LIST_HEAD(tmp_list);
|
||||||
unsigned int id = 0;
|
|
||||||
int rtn;
|
|
||||||
|
|
||||||
do {
|
list_splice_init(work_q, &tmp_list);
|
||||||
tgtr_scmd = NULL;
|
|
||||||
list_for_each_entry(scmd, work_q, eh_entry) {
|
while (!list_empty(&tmp_list)) {
|
||||||
if (id == scmd_id(scmd)) {
|
struct scsi_cmnd *next, *scmd;
|
||||||
tgtr_scmd = scmd;
|
int rtn;
|
||||||
break;
|
unsigned int id;
|
||||||
}
|
|
||||||
}
|
scmd = list_entry(tmp_list.next, struct scsi_cmnd, eh_entry);
|
||||||
if (!tgtr_scmd) {
|
id = scmd_id(scmd);
|
||||||
/* not one exactly equal; find the next highest */
|
|
||||||
list_for_each_entry(scmd, work_q, eh_entry) {
|
|
||||||
if (scmd_id(scmd) > id &&
|
|
||||||
(!tgtr_scmd ||
|
|
||||||
scmd_id(tgtr_scmd) > scmd_id(scmd)))
|
|
||||||
tgtr_scmd = scmd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!tgtr_scmd)
|
|
||||||
/* no more commands, that's it */
|
|
||||||
break;
|
|
||||||
|
|
||||||
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending target reset "
|
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending target reset "
|
||||||
"to target %d\n",
|
"to target %d\n",
|
||||||
current->comm, id));
|
current->comm, id));
|
||||||
rtn = scsi_try_target_reset(tgtr_scmd);
|
rtn = scsi_try_target_reset(scmd);
|
||||||
if (rtn == SUCCESS || rtn == FAST_IO_FAIL) {
|
if (rtn != SUCCESS && rtn != FAST_IO_FAIL)
|
||||||
list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
|
|
||||||
if (id == scmd_id(scmd))
|
|
||||||
if (!scsi_device_online(scmd->device) ||
|
|
||||||
rtn == FAST_IO_FAIL ||
|
|
||||||
!scsi_eh_tur(tgtr_scmd))
|
|
||||||
scsi_eh_finish_cmd(scmd,
|
|
||||||
done_q);
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Target reset"
|
SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Target reset"
|
||||||
" failed target: "
|
" failed target: "
|
||||||
"%d\n",
|
"%d\n",
|
||||||
current->comm, id));
|
current->comm, id));
|
||||||
id++;
|
list_for_each_entry_safe(scmd, next, &tmp_list, eh_entry) {
|
||||||
} while(id != 0);
|
if (scmd_id(scmd) != id)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((rtn == SUCCESS || rtn == FAST_IO_FAIL)
|
||||||
|
&& (!scsi_device_online(scmd->device) ||
|
||||||
|
rtn == FAST_IO_FAIL || !scsi_eh_tur(scmd)))
|
||||||
|
scsi_eh_finish_cmd(scmd, done_q);
|
||||||
|
else
|
||||||
|
/* push back on work queue for further processing */
|
||||||
|
list_move(&scmd->eh_entry, work_q);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return list_empty(work_q);
|
return list_empty(work_q);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue