aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@suse.de>2010-10-25 16:53:41 -0400
committerJames Bottomley <James.Bottomley@suse.de>2010-12-21 13:23:56 -0500
commit98db519573e805f9f7e988fb5661da951fcb16b1 (patch)
tree8186c195ae339439f45109110afea40fbf833363
parent110def851fc823bb1a4584cb6308e30e5ffb3e05 (diff)
[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>
-rw-r--r--drivers/scsi/scsi_error.c61
1 files changed, 25 insertions, 36 deletions
diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 30ac116186f5..45c75649b9e0 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -1124,51 +1124,40 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost,
1124 struct list_head *work_q, 1124 struct list_head *work_q,
1125 struct list_head *done_q) 1125 struct list_head *done_q)
1126{ 1126{
1127 struct scsi_cmnd *scmd, *tgtr_scmd, *next; 1127 LIST_HEAD(tmp_list);
1128 unsigned int id = 0;
1129 int rtn;
1130 1128
1131 do { 1129 list_splice_init(work_q, &tmp_list);
1132 tgtr_scmd = NULL; 1130
1133 list_for_each_entry(scmd, work_q, eh_entry) { 1131 while (!list_empty(&tmp_list)) {
1134 if (id == scmd_id(scmd)) { 1132 struct scsi_cmnd *next, *scmd;
1135 tgtr_scmd = scmd; 1133 int rtn;
1136 break; 1134 unsigned int id;
1137 } 1135
1138 } 1136 scmd = list_entry(tmp_list.next, struct scsi_cmnd, eh_entry);
1139 if (!tgtr_scmd) { 1137 id = scmd_id(scmd);
1140 /* not one exactly equal; find the next highest */
1141 list_for_each_entry(scmd, work_q, eh_entry) {
1142 if (scmd_id(scmd) > id &&
1143 (!tgtr_scmd ||
1144 scmd_id(tgtr_scmd) > scmd_id(scmd)))
1145 tgtr_scmd = scmd;
1146 }
1147 }
1148 if (!tgtr_scmd)
1149 /* no more commands, that's it */
1150 break;
1151 1138
1152 SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending target reset " 1139 SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending target reset "
1153 "to target %d\n", 1140 "to target %d\n",
1154 current->comm, id)); 1141 current->comm, id));
1155 rtn = scsi_try_target_reset(tgtr_scmd); 1142 rtn = scsi_try_target_reset(scmd);
1156 if (rtn == SUCCESS || rtn == FAST_IO_FAIL) { 1143 if (rtn != SUCCESS && rtn != FAST_IO_FAIL)
1157 list_for_each_entry_safe(scmd, next, work_q, eh_entry) {
1158 if (id == scmd_id(scmd))
1159 if (!scsi_device_online(scmd->device) ||
1160 rtn == FAST_IO_FAIL ||
1161 !scsi_eh_tur(tgtr_scmd))
1162 scsi_eh_finish_cmd(scmd,
1163 done_q);
1164 }
1165 } else
1166 SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Target reset" 1144 SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Target reset"
1167 " failed target: " 1145 " failed target: "
1168 "%d\n", 1146 "%d\n",
1169 current->comm, id)); 1147 current->comm, id));
1170 id++; 1148 list_for_each_entry_safe(scmd, next, &tmp_list, eh_entry) {
1171 } while(id != 0); 1149 if (scmd_id(scmd) != id)
1150 continue;
1151
1152 if ((rtn == SUCCESS || rtn == FAST_IO_FAIL)
1153 && (!scsi_device_online(scmd->device) ||
1154 rtn == FAST_IO_FAIL || !scsi_eh_tur(scmd)))
1155 scsi_eh_finish_cmd(scmd, done_q);
1156 else
1157 /* push back on work queue for further processing */
1158 list_move(&scmd->eh_entry, work_q);
1159 }
1160 }
1172 1161
1173 return list_empty(work_q); 1162 return list_empty(work_q);
1174} 1163}