From c82dc88ddaf17112841dd3a6b08352968555ee08 Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 12 Sep 2008 16:46:51 -0500 Subject: [PATCH] [SCSI] scsi_error: fix target reset handling There's a target reset bug. This loop: for (id = 0; id <= shost->max_id; id++) { Never terminates if shost->max_id is set to ~0, like aic94xx does. It's also pretty inefficient since you mostly have compact target numbers, but the max_id can be very high. The best way would be to sort the recovery list by target id and skip them if they're equal, but even a worst case O(N^2) traversal is probably OK here, so fix it by finding the next highest target number (assuming n+1) and terminating when there isn't one. Cc: Mike Christie Signed-off-by: James Bottomley --- drivers/scsi/scsi_error.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index ad019ece2139..94ed262bdf0c 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -1065,10 +1065,10 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost, struct list_head *done_q) { struct scsi_cmnd *scmd, *tgtr_scmd, *next; - unsigned int id; + unsigned int id = 0; int rtn; - for (id = 0; id <= shost->max_id; id++) { + do { tgtr_scmd = NULL; list_for_each_entry(scmd, work_q, eh_entry) { if (id == scmd_id(scmd)) { @@ -1076,8 +1076,18 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost, break; } } + if (!tgtr_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) - continue; + /* no more commands, that's it */ + break; SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending target reset " "to target %d\n", @@ -1096,7 +1106,8 @@ static int scsi_eh_target_reset(struct Scsi_Host *shost, " failed target: " "%d\n", current->comm, id)); - } + id++; + } while(id != 0); return list_empty(work_q); }