From e02f3f59225d8c3b2a0ad0dc941a09865e27da61 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Fri, 13 Jan 2006 19:04:00 +0100 Subject: [PATCH] [SCSI] remove target parent limitiation When James Smart fixed the issue of the userspace scan atributes crashing the system with the FC transport class he added a patch to let the transport class check if the parent is valid for a given transport class. When adding support for the integrated raid of fusion sas devices we ran into a problem with that, as it didn't allow adding virtual raid volumes without the transport class knowing about it. So this patch adds a user_scan attribute instead, that takes over from scsi_scan_host_selected if the transport class sets it and thus lets the transport class control the user-initiated scanning. As this plugs the hole about user-initiated scanning the target_parent hook goes away and we rely on callers of the scanning routines to do something sensible. For SAS this meant I had to switch from a spinlock to a mutex to synchronize the topology linked lists, in FC they were completely unsynchronized which seems wrong. Signed-off-by: Christoph Hellwig Signed-off-by: James Bottomley --- drivers/scsi/scsi_priv.h | 6 ----- drivers/scsi/scsi_proc.c | 6 ++++- drivers/scsi/scsi_scan.c | 16 ++--------- drivers/scsi/scsi_sysfs.c | 5 +++- drivers/scsi/scsi_transport_fc.c | 22 ++++++++++------ drivers/scsi/scsi_transport_sas.c | 44 ++++++++++++++++++------------- include/scsi/scsi.h | 6 +++++ include/scsi/scsi_transport.h | 7 ++--- 8 files changed, 58 insertions(+), 54 deletions(-) diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 14a6198cb8d2..27c48274e8cb 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -26,12 +26,6 @@ struct Scsi_Host; #define SCSI_SENSE_VALID(scmd) \ (((scmd)->sense_buffer[0] & 0x70) == 0x70) -/* - * Special value for scanning to specify scanning or rescanning of all - * possible channels, (target) ids, or luns on a given shost. - */ -#define SCAN_WILD_CARD ~0 - /* hosts.c */ extern int scsi_init_hosts(void); extern void scsi_exit_hosts(void); diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c index 1b5711e714a5..07be62bbaaea 100644 --- a/drivers/scsi/scsi_proc.c +++ b/drivers/scsi/scsi_proc.c @@ -31,6 +31,7 @@ #include #include #include +#include #include "scsi_priv.h" #include "scsi_logging.h" @@ -200,7 +201,10 @@ static int scsi_add_single_device(uint host, uint channel, uint id, uint lun) if (IS_ERR(shost)) return PTR_ERR(shost); - error = scsi_scan_host_selected(shost, channel, id, lun, 1); + if (shost->transportt->user_scan) + error = shost->transportt->user_scan(shost, channel, id, lun); + else + error = scsi_scan_host_selected(shost, channel, id, lun, 1); scsi_host_put(shost); return error; } diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index edcd80b749c1..752fb5da3de4 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -334,19 +334,6 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, struct scsi_target *starget; struct scsi_target *found_target; - /* - * Obtain the real parent from the transport. The transport - * is allowed to fail (no error) if there is nothing at that - * target id. - */ - if (shost->transportt->target_parent) { - spin_lock_irqsave(shost->host_lock, flags); - parent = shost->transportt->target_parent(shost, channel, id); - spin_unlock_irqrestore(shost->host_lock, flags); - if (!parent) - return NULL; - } - starget = kmalloc(size, GFP_KERNEL); if (!starget) { printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__); @@ -1283,8 +1270,9 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, struct scsi_device *sdev; struct device *parent = &shost->shost_gendev; int res; - struct scsi_target *starget = scsi_alloc_target(parent, channel, id); + struct scsi_target *starget; + starget = scsi_alloc_target(parent, channel, id); if (!starget) return ERR_PTR(-ENOMEM); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 2cb962751a7e..a77b32deaf8f 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -106,7 +106,10 @@ static int scsi_scan(struct Scsi_Host *shost, const char *str) return -EINVAL; if (check_set(&lun, s3)) return -EINVAL; - res = scsi_scan_host_selected(shost, channel, id, lun, 1); + if (shost->transportt->user_scan) + res = shost->transportt->user_scan(shost, channel, id, lun); + else + res = scsi_scan_host_selected(shost, channel, id, lun, 1); return res; } diff --git a/drivers/scsi/scsi_transport_fc.c b/drivers/scsi/scsi_transport_fc.c index 625f4a664d06..f2c9acf11bd0 100644 --- a/drivers/scsi/scsi_transport_fc.c +++ b/drivers/scsi/scsi_transport_fc.c @@ -1093,17 +1093,23 @@ static int fc_rport_match(struct attribute_container *cont, /* * Must be called with shost->host_lock held */ -static struct device *fc_target_parent(struct Scsi_Host *shost, - int channel, uint id) +static int fc_user_scan(struct Scsi_Host *shost, uint channel, + uint id, uint lun) { struct fc_rport *rport; - list_for_each_entry(rport, &fc_host_rports(shost), peers) - if ((rport->channel == channel) && - (rport->scsi_target_id == id)) - return &rport->dev; + list_for_each_entry(rport, &fc_host_rports(shost), peers) { + if (rport->scsi_target_id == -1) + continue; - return NULL; + if ((channel == SCAN_WILD_CARD || channel == rport->channel) && + (id == SCAN_WILD_CARD || id == rport->scsi_target_id)) { + scsi_scan_target(&rport->dev, rport->channel, + rport->scsi_target_id, lun, 1); + } + } + + return 0; } struct scsi_transport_template * @@ -1142,7 +1148,7 @@ fc_attach_transport(struct fc_function_template *ft) /* Transport uses the shost workq for scsi scanning */ i->t.create_work_queue = 1; - i->t.target_parent = fc_target_parent; + i->t.user_scan = fc_user_scan; /* * Setup SCSI Target Attributes. diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index e950435a11d8..fb6641b42dfa 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include @@ -62,7 +63,7 @@ struct sas_internal { struct sas_host_attrs { struct list_head rphy_list; - spinlock_t lock; + struct mutex lock; u32 next_target_id; }; #define to_sas_host_attrs(host) ((struct sas_host_attrs *)(host)->shost_data) @@ -165,7 +166,7 @@ static int sas_host_setup(struct transport_container *tc, struct device *dev, struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); INIT_LIST_HEAD(&sas_host->rphy_list); - spin_lock_init(&sas_host->lock); + mutex_init(&sas_host->lock); sas_host->next_target_id = 0; return 0; } @@ -626,7 +627,7 @@ int sas_rphy_add(struct sas_rphy *rphy) transport_add_device(&rphy->dev); transport_configure_device(&rphy->dev); - spin_lock(&sas_host->lock); + mutex_lock(&sas_host->lock); list_add_tail(&rphy->list, &sas_host->rphy_list); if (identify->device_type == SAS_END_DEVICE && (identify->target_port_protocols & @@ -634,7 +635,7 @@ int sas_rphy_add(struct sas_rphy *rphy) rphy->scsi_target_id = sas_host->next_target_id++; else rphy->scsi_target_id = -1; - spin_unlock(&sas_host->lock); + mutex_unlock(&sas_host->lock); if (rphy->scsi_target_id != -1) { scsi_scan_target(&rphy->dev, parent->number, @@ -661,9 +662,9 @@ void sas_rphy_free(struct sas_rphy *rphy) struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent); struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); - spin_lock(&sas_host->lock); + mutex_lock(&sas_host->lock); list_del(&rphy->list); - spin_unlock(&sas_host->lock); + mutex_unlock(&sas_host->lock); transport_destroy_device(&rphy->dev); put_device(rphy->dev.parent); @@ -703,9 +704,9 @@ sas_rphy_delete(struct sas_rphy *rphy) device_del(dev); transport_destroy_device(dev); - spin_lock(&sas_host->lock); + mutex_lock(&sas_host->lock); list_del(&rphy->list); - spin_unlock(&sas_host->lock); + mutex_unlock(&sas_host->lock); parent->rphy = NULL; @@ -731,23 +732,28 @@ EXPORT_SYMBOL(scsi_is_sas_rphy); * SCSI scan helper */ -static struct device *sas_target_parent(struct Scsi_Host *shost, - int channel, uint id) +static int sas_user_scan(struct Scsi_Host *shost, uint channel, + uint id, uint lun) { struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); struct sas_rphy *rphy; - struct device *dev = NULL; - spin_lock(&sas_host->lock); + mutex_lock(&sas_host->lock); list_for_each_entry(rphy, &sas_host->rphy_list, list) { struct sas_phy *parent = dev_to_phy(rphy->dev.parent); - if (parent->number == channel && - rphy->scsi_target_id == id) - dev = &rphy->dev; - } - spin_unlock(&sas_host->lock); - return dev; + if (rphy->scsi_target_id == -1) + continue; + + if ((channel == SCAN_WILD_CARD || channel == parent->number) && + (id == SCAN_WILD_CARD || id == rphy->scsi_target_id)) { + scsi_scan_target(&rphy->dev, parent->number, + rphy->scsi_target_id, lun, 1); + } + } + mutex_unlock(&sas_host->lock); + + return 0; } @@ -792,7 +798,7 @@ sas_attach_transport(struct sas_function_template *ft) return NULL; memset(i, 0, sizeof(struct sas_internal)); - i->t.target_parent = sas_target_parent; + i->t.user_scan = sas_user_scan; i->t.host_attrs.ac.attrs = &i->host_attrs[0]; i->t.host_attrs.ac.class = &sas_host_class.class; diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h index 6cb1e2788d8b..c60b8ff2f5e4 100644 --- a/include/scsi/scsi.h +++ b/include/scsi/scsi.h @@ -31,6 +31,12 @@ extern const unsigned char scsi_command_size[8]; #define MAX_SCSI_DEVICE_CODE 15 extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE]; +/* + * Special value for scanning to specify scanning or rescanning of all + * possible channels, (target) ids, or luns on a given shost. + */ +#define SCAN_WILD_CARD ~0 + /* * SCSI opcodes */ diff --git a/include/scsi/scsi_transport.h b/include/scsi/scsi_transport.h index f6e0bb484c63..e7b1054adf86 100644 --- a/include/scsi/scsi_transport.h +++ b/include/scsi/scsi_transport.h @@ -30,12 +30,9 @@ struct scsi_transport_template { struct transport_container device_attrs; /* - * If set, call target_parent prior to allocating a scsi_target, - * so we get the appropriate parent for the target. This function - * is required for transports like FC and iSCSI that do not put the - * scsi_target under scsi_host. + * If set, called from sysfs and legacy procfs rescanning code. */ - struct device *(*target_parent)(struct Scsi_Host *, int, uint); + int (*user_scan)(struct Scsi_Host *, uint, uint, uint); /* The size of the specific transport attribute structure (a * space of this size will be left at the end of the