Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

[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>

+25 -36
+25 -36
drivers/scsi/scsi_error.c
··· 1124 1124 struct list_head *work_q, 1125 1125 struct list_head *done_q) 1126 1126 { 1127 - struct scsi_cmnd *scmd, *tgtr_scmd, *next; 1128 - unsigned int id = 0; 1129 - int rtn; 1127 + LIST_HEAD(tmp_list); 1130 1128 1131 - do { 1132 - tgtr_scmd = NULL; 1133 - list_for_each_entry(scmd, work_q, eh_entry) { 1134 - if (id == scmd_id(scmd)) { 1135 - tgtr_scmd = scmd; 1136 - break; 1137 - } 1138 - } 1139 - if (!tgtr_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; 1129 + list_splice_init(work_q, &tmp_list); 1130 + 1131 + while (!list_empty(&tmp_list)) { 1132 + struct scsi_cmnd *next, *scmd; 1133 + int rtn; 1134 + unsigned int id; 1135 + 1136 + scmd = list_entry(tmp_list.next, struct scsi_cmnd, eh_entry); 1137 + id = scmd_id(scmd); 1151 1138 1152 1139 SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending target reset " 1153 1140 "to target %d\n", 1154 1141 current->comm, id)); 1155 - rtn = scsi_try_target_reset(tgtr_scmd); 1156 - 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 1142 + rtn = scsi_try_target_reset(scmd); 1143 + if (rtn != SUCCESS && rtn != FAST_IO_FAIL) 1166 1144 SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Target reset" 1167 1145 " failed target: " 1168 1146 "%d\n", 1169 1147 current->comm, id)); 1170 - id++; 1171 - } while(id != 0); 1148 + list_for_each_entry_safe(scmd, next, &tmp_list, eh_entry) { 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 1162 return list_empty(work_q); 1174 1163 }