Commit e02f3f59 authored by Christoph Hellwig's avatar Christoph Hellwig Committed by James Bottomley

[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: default avatarChristoph Hellwig <hch@lst.de>
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 6d5b0c31
...@@ -26,12 +26,6 @@ struct Scsi_Host; ...@@ -26,12 +26,6 @@ struct Scsi_Host;
#define SCSI_SENSE_VALID(scmd) \ #define SCSI_SENSE_VALID(scmd) \
(((scmd)->sense_buffer[0] & 0x70) == 0x70) (((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 */ /* hosts.c */
extern int scsi_init_hosts(void); extern int scsi_init_hosts(void);
extern void scsi_exit_hosts(void); extern void scsi_exit_hosts(void);
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
#include <scsi/scsi.h> #include <scsi/scsi.h>
#include <scsi/scsi_device.h> #include <scsi/scsi_device.h>
#include <scsi/scsi_host.h> #include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
#include "scsi_priv.h" #include "scsi_priv.h"
#include "scsi_logging.h" #include "scsi_logging.h"
...@@ -200,6 +201,9 @@ static int scsi_add_single_device(uint host, uint channel, uint id, uint lun) ...@@ -200,6 +201,9 @@ static int scsi_add_single_device(uint host, uint channel, uint id, uint lun)
if (IS_ERR(shost)) if (IS_ERR(shost))
return PTR_ERR(shost); return PTR_ERR(shost);
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); error = scsi_scan_host_selected(shost, channel, id, lun, 1);
scsi_host_put(shost); scsi_host_put(shost);
return error; return error;
......
...@@ -334,19 +334,6 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, ...@@ -334,19 +334,6 @@ static struct scsi_target *scsi_alloc_target(struct device *parent,
struct scsi_target *starget; struct scsi_target *starget;
struct scsi_target *found_target; 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); starget = kmalloc(size, GFP_KERNEL);
if (!starget) { if (!starget) {
printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__); printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__);
...@@ -1283,8 +1270,9 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, ...@@ -1283,8 +1270,9 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
struct scsi_device *sdev; struct scsi_device *sdev;
struct device *parent = &shost->shost_gendev; struct device *parent = &shost->shost_gendev;
int res; 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) if (!starget)
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
......
...@@ -106,6 +106,9 @@ static int scsi_scan(struct Scsi_Host *shost, const char *str) ...@@ -106,6 +106,9 @@ static int scsi_scan(struct Scsi_Host *shost, const char *str)
return -EINVAL; return -EINVAL;
if (check_set(&lun, s3)) if (check_set(&lun, s3))
return -EINVAL; return -EINVAL;
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); res = scsi_scan_host_selected(shost, channel, id, lun, 1);
return res; return res;
} }
......
...@@ -1093,17 +1093,23 @@ static int fc_rport_match(struct attribute_container *cont, ...@@ -1093,17 +1093,23 @@ static int fc_rport_match(struct attribute_container *cont,
/* /*
* Must be called with shost->host_lock held * Must be called with shost->host_lock held
*/ */
static struct device *fc_target_parent(struct Scsi_Host *shost, static int fc_user_scan(struct Scsi_Host *shost, uint channel,
int channel, uint id) uint id, uint lun)
{ {
struct fc_rport *rport; struct fc_rport *rport;
list_for_each_entry(rport, &fc_host_rports(shost), peers) list_for_each_entry(rport, &fc_host_rports(shost), peers) {
if ((rport->channel == channel) && if (rport->scsi_target_id == -1)
(rport->scsi_target_id == id)) continue;
return &rport->dev;
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 * struct scsi_transport_template *
...@@ -1142,7 +1148,7 @@ fc_attach_transport(struct fc_function_template *ft) ...@@ -1142,7 +1148,7 @@ fc_attach_transport(struct fc_function_template *ft)
/* Transport uses the shost workq for scsi scanning */ /* Transport uses the shost workq for scsi scanning */
i->t.create_work_queue = 1; i->t.create_work_queue = 1;
i->t.target_parent = fc_target_parent; i->t.user_scan = fc_user_scan;
/* /*
* Setup SCSI Target Attributes. * Setup SCSI Target Attributes.
......
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/string.h> #include <linux/string.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h> #include <scsi/scsi_device.h>
#include <scsi/scsi_host.h> #include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h> #include <scsi/scsi_transport.h>
...@@ -62,7 +63,7 @@ struct sas_internal { ...@@ -62,7 +63,7 @@ struct sas_internal {
struct sas_host_attrs { struct sas_host_attrs {
struct list_head rphy_list; struct list_head rphy_list;
spinlock_t lock; struct mutex lock;
u32 next_target_id; u32 next_target_id;
}; };
#define to_sas_host_attrs(host) ((struct sas_host_attrs *)(host)->shost_data) #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, ...@@ -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); struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
INIT_LIST_HEAD(&sas_host->rphy_list); INIT_LIST_HEAD(&sas_host->rphy_list);
spin_lock_init(&sas_host->lock); mutex_init(&sas_host->lock);
sas_host->next_target_id = 0; sas_host->next_target_id = 0;
return 0; return 0;
} }
...@@ -626,7 +627,7 @@ int sas_rphy_add(struct sas_rphy *rphy) ...@@ -626,7 +627,7 @@ int sas_rphy_add(struct sas_rphy *rphy)
transport_add_device(&rphy->dev); transport_add_device(&rphy->dev);
transport_configure_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); list_add_tail(&rphy->list, &sas_host->rphy_list);
if (identify->device_type == SAS_END_DEVICE && if (identify->device_type == SAS_END_DEVICE &&
(identify->target_port_protocols & (identify->target_port_protocols &
...@@ -634,7 +635,7 @@ int sas_rphy_add(struct sas_rphy *rphy) ...@@ -634,7 +635,7 @@ int sas_rphy_add(struct sas_rphy *rphy)
rphy->scsi_target_id = sas_host->next_target_id++; rphy->scsi_target_id = sas_host->next_target_id++;
else else
rphy->scsi_target_id = -1; rphy->scsi_target_id = -1;
spin_unlock(&sas_host->lock); mutex_unlock(&sas_host->lock);
if (rphy->scsi_target_id != -1) { if (rphy->scsi_target_id != -1) {
scsi_scan_target(&rphy->dev, parent->number, scsi_scan_target(&rphy->dev, parent->number,
...@@ -661,9 +662,9 @@ void sas_rphy_free(struct sas_rphy *rphy) ...@@ -661,9 +662,9 @@ void sas_rphy_free(struct sas_rphy *rphy)
struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent); struct Scsi_Host *shost = dev_to_shost(rphy->dev.parent->parent);
struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); 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); list_del(&rphy->list);
spin_unlock(&sas_host->lock); mutex_unlock(&sas_host->lock);
transport_destroy_device(&rphy->dev); transport_destroy_device(&rphy->dev);
put_device(rphy->dev.parent); put_device(rphy->dev.parent);
...@@ -703,9 +704,9 @@ sas_rphy_delete(struct sas_rphy *rphy) ...@@ -703,9 +704,9 @@ sas_rphy_delete(struct sas_rphy *rphy)
device_del(dev); device_del(dev);
transport_destroy_device(dev); transport_destroy_device(dev);
spin_lock(&sas_host->lock); mutex_lock(&sas_host->lock);
list_del(&rphy->list); list_del(&rphy->list);
spin_unlock(&sas_host->lock); mutex_unlock(&sas_host->lock);
parent->rphy = NULL; parent->rphy = NULL;
...@@ -731,23 +732,28 @@ EXPORT_SYMBOL(scsi_is_sas_rphy); ...@@ -731,23 +732,28 @@ EXPORT_SYMBOL(scsi_is_sas_rphy);
* SCSI scan helper * SCSI scan helper
*/ */
static struct device *sas_target_parent(struct Scsi_Host *shost, static int sas_user_scan(struct Scsi_Host *shost, uint channel,
int channel, uint id) uint id, uint lun)
{ {
struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); struct sas_host_attrs *sas_host = to_sas_host_attrs(shost);
struct sas_rphy *rphy; 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) { list_for_each_entry(rphy, &sas_host->rphy_list, list) {
struct sas_phy *parent = dev_to_phy(rphy->dev.parent); struct sas_phy *parent = dev_to_phy(rphy->dev.parent);
if (parent->number == channel &&
rphy->scsi_target_id == id) if (rphy->scsi_target_id == -1)
dev = &rphy->dev; 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);
} }
spin_unlock(&sas_host->lock); }
mutex_unlock(&sas_host->lock);
return dev; return 0;
} }
...@@ -792,7 +798,7 @@ sas_attach_transport(struct sas_function_template *ft) ...@@ -792,7 +798,7 @@ sas_attach_transport(struct sas_function_template *ft)
return NULL; return NULL;
memset(i, 0, sizeof(struct sas_internal)); 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.attrs = &i->host_attrs[0];
i->t.host_attrs.ac.class = &sas_host_class.class; i->t.host_attrs.ac.class = &sas_host_class.class;
......
...@@ -31,6 +31,12 @@ extern const unsigned char scsi_command_size[8]; ...@@ -31,6 +31,12 @@ extern const unsigned char scsi_command_size[8];
#define MAX_SCSI_DEVICE_CODE 15 #define MAX_SCSI_DEVICE_CODE 15
extern const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE]; 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 * SCSI opcodes
*/ */
......
...@@ -30,12 +30,9 @@ struct scsi_transport_template { ...@@ -30,12 +30,9 @@ struct scsi_transport_template {
struct transport_container device_attrs; struct transport_container device_attrs;
/* /*
* If set, call target_parent prior to allocating a scsi_target, * If set, called from sysfs and legacy procfs rescanning code.
* 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.
*/ */
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 /* The size of the specific transport attribute structure (a
* space of this size will be left at the end of the * space of this size will be left at the end of the
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment