aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Smart <James.Smart@Emulex.Com>2008-04-24 12:12:46 -0400
committerJames Bottomley <James.Bottomley@HansenPartnership.com>2008-04-27 13:19:56 -0400
commitbda232531f0c117921690ee3c060953c8f12e5a1 (patch)
treed237ba28380ff9d6fccb966463bca2bcfecbc509
parent87c4d7bc2aaa9b782aac6ab0a74cf16f87398bbc (diff)
[SCSI] scsi_transport_fc: fc_user_scan correction
Way back when, when the fc_user_scan routine was created, it kept some of its original logic that walked the rport list and kicked off a scan. Unfortunately, it didn't keep any of the locking around the rport list, nor did it consider the synchronous nature of the scan invoked. The result, there are some scan requests where the rport list changes, thus a subsequent scan is called on a bogus rport structure and the system NMI's. Signed-off-by: James Smart <james.smart@emulex.com> Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com>
-rw-r--r--drivers/scsi/scsi_transport_fc.c60
1 files changed, 53 insertions, 7 deletions
diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c
index 6b092a6c295d..5fd64e70029d 100644
--- a/drivers/scsi/scsi_transport_fc.c
+++ b/drivers/scsi/scsi_transport_fc.c
@@ -1961,12 +1961,17 @@ fc_timed_out(struct scsi_cmnd *scmd)
1961} 1961}
1962 1962
1963/* 1963/*
1964 * Must be called with shost->host_lock held 1964 * Called by fc_user_scan to locate an rport on the shost that
1965 * matches the channel and target id, and invoke scsi_scan_target()
1966 * on the rport.
1965 */ 1967 */
1966static int fc_user_scan(struct Scsi_Host *shost, uint channel, 1968static void
1967 uint id, uint lun) 1969fc_user_scan_tgt(struct Scsi_Host *shost, uint channel, uint id, uint lun)
1968{ 1970{
1969 struct fc_rport *rport; 1971 struct fc_rport *rport;
1972 unsigned long flags;
1973
1974 spin_lock_irqsave(shost->host_lock, flags);
1970 1975
1971 list_for_each_entry(rport, &fc_host_rports(shost), peers) { 1976 list_for_each_entry(rport, &fc_host_rports(shost), peers) {
1972 if (rport->scsi_target_id == -1) 1977 if (rport->scsi_target_id == -1)
@@ -1975,13 +1980,54 @@ static int fc_user_scan(struct Scsi_Host *shost, uint channel,
1975 if (rport->port_state != FC_PORTSTATE_ONLINE) 1980 if (rport->port_state != FC_PORTSTATE_ONLINE)
1976 continue; 1981 continue;
1977 1982
1978 if ((channel == SCAN_WILD_CARD || channel == rport->channel) && 1983 if ((channel == rport->channel) &&
1979 (id == SCAN_WILD_CARD || id == rport->scsi_target_id)) { 1984 (id == rport->scsi_target_id)) {
1980 scsi_scan_target(&rport->dev, rport->channel, 1985 spin_unlock_irqrestore(shost->host_lock, flags);
1981 rport->scsi_target_id, lun, 1); 1986 scsi_scan_target(&rport->dev, channel, id, lun, 1);
1987 return;
1982 } 1988 }
1983 } 1989 }
1984 1990
1991 spin_unlock_irqrestore(shost->host_lock, flags);
1992}
1993
1994/*
1995 * Called via sysfs scan routines. Necessary, as the FC transport
1996 * wants to place all target objects below the rport object. So this
1997 * routine must invoke the scsi_scan_target() routine with the rport
1998 * object as the parent.
1999 */
2000static int
2001fc_user_scan(struct Scsi_Host *shost, uint channel, uint id, uint lun)
2002{
2003 uint chlo, chhi;
2004 uint tgtlo, tgthi;
2005
2006 if (((channel != SCAN_WILD_CARD) && (channel > shost->max_channel)) ||
2007 ((id != SCAN_WILD_CARD) && (id >= shost->max_id)) ||
2008 ((lun != SCAN_WILD_CARD) && (lun > shost->max_lun)))
2009 return -EINVAL;
2010
2011 if (channel == SCAN_WILD_CARD) {
2012 chlo = 0;
2013 chhi = shost->max_channel + 1;
2014 } else {
2015 chlo = channel;
2016 chhi = channel + 1;
2017 }
2018
2019 if (id == SCAN_WILD_CARD) {
2020 tgtlo = 0;
2021 tgthi = shost->max_id;
2022 } else {
2023 tgtlo = id;
2024 tgthi = id + 1;
2025 }
2026
2027 for ( ; chlo < chhi; chlo++)
2028 for ( ; tgtlo < tgthi; tgtlo++)
2029 fc_user_scan_tgt(shost, chlo, tgtlo, lun);
2030
1985 return 0; 2031 return 0;
1986} 2032}
1987 2033