Commit 9b21fa11 authored by James Bottomley's avatar James Bottomley

SCSI: revamp target scanning routines

The basic change is to allow target scans to be done
by generic device (which need only have a scsi host
as a parent).  Also changes most target functions to
take a struct scsi_target instead of the H/C/T numbers.
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent 6ea4fe3d
......@@ -216,6 +216,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
spin_lock_init(&shost->default_lock);
scsi_assign_lock(shost, &shost->default_lock);
INIT_LIST_HEAD(&shost->__devices);
INIT_LIST_HEAD(&shost->__targets);
INIT_LIST_HEAD(&shost->eh_cmd_q);
INIT_LIST_HEAD(&shost->starved_list);
init_waitqueue_head(&shost->host_wait);
......
......@@ -1115,6 +1115,60 @@ void starget_for_each_device(struct scsi_target *starget, void * data,
}
EXPORT_SYMBOL(starget_for_each_device);
/**
* __scsi_device_lookup_by_target - find a device given the target (UNLOCKED)
* @starget: SCSI target pointer
* @lun: SCSI Logical Unit Number
*
* Looks up the scsi_device with the specified @lun for a give
* @starget. The returned scsi_device does not have an additional
* reference. You must hold the host's host_lock over this call and
* any access to the returned scsi_device.
*
* Note: The only reason why drivers would want to use this is because
* they're need to access the device list in irq context. Otherwise you
* really want to use scsi_device_lookup_by_target instead.
**/
struct scsi_device *__scsi_device_lookup_by_target(struct scsi_target *starget,
uint lun)
{
struct scsi_device *sdev;
list_for_each_entry(sdev, &starget->devices, same_target_siblings) {
if (sdev->lun ==lun)
return sdev;
}
return NULL;
}
EXPORT_SYMBOL(__scsi_device_lookup_by_target);
/**
* scsi_device_lookup_by_target - find a device given the target
* @starget: SCSI target pointer
* @lun: SCSI Logical Unit Number
*
* Looks up the scsi_device with the specified @channel, @id, @lun for a
* give host. The returned scsi_device has an additional reference that
* needs to be release with scsi_host_put once you're done with it.
**/
struct scsi_device *scsi_device_lookup_by_target(struct scsi_target *starget,
uint lun)
{
struct scsi_device *sdev;
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
sdev = __scsi_device_lookup_by_target(starget, lun);
if (sdev && scsi_device_get(sdev))
sdev = NULL;
spin_unlock_irqrestore(shost->host_lock, flags);
return sdev;
}
EXPORT_SYMBOL(scsi_device_lookup_by_target);
/**
* scsi_device_lookup - find a device given the host (UNLOCKED)
* @shost: SCSI host pointer
......
......@@ -365,10 +365,11 @@ static void scsi_single_lun_run(struct scsi_device *current_sdev)
{
struct Scsi_Host *shost = current_sdev->host;
struct scsi_device *sdev, *tmp;
struct scsi_target *starget = scsi_target(current_sdev);
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
scsi_target(current_sdev)->starget_sdev_user = NULL;
starget->starget_sdev_user = NULL;
spin_unlock_irqrestore(shost->host_lock, flags);
/*
......@@ -380,10 +381,12 @@ static void scsi_single_lun_run(struct scsi_device *current_sdev)
blk_run_queue(current_sdev->request_queue);
spin_lock_irqsave(shost->host_lock, flags);
if (scsi_target(current_sdev)->starget_sdev_user)
if (starget->starget_sdev_user)
goto out;
list_for_each_entry_safe(sdev, tmp, &current_sdev->same_target_siblings,
list_for_each_entry_safe(sdev, tmp, &starget->devices,
same_target_siblings) {
if (sdev == current_sdev)
continue;
if (scsi_device_get(sdev))
continue;
......
This diff is collapsed.
......@@ -154,33 +154,25 @@ void scsi_device_dev_release(struct device *dev)
{
struct scsi_device *sdev;
struct device *parent;
struct scsi_target *starget;
unsigned long flags;
int delete;
parent = dev->parent;
sdev = to_scsi_device(dev);
starget = to_scsi_target(parent);
spin_lock_irqsave(sdev->host->host_lock, flags);
/* If we're the last LUN on the target, destroy the target */
delete = list_empty(&sdev->same_target_siblings);
starget->reap_ref++;
list_del(&sdev->siblings);
list_del(&sdev->same_target_siblings);
list_del(&sdev->starved_entry);
spin_unlock_irqrestore(sdev->host->host_lock, flags);
if (delete) {
struct scsi_target *starget = to_scsi_target(parent);
if (!starget->create) {
transport_remove_device(&starget->dev);
device_del(parent);
}
transport_destroy_device(&starget->dev);
put_device(parent);
}
if (sdev->request_queue)
scsi_free_queue(sdev->request_queue);
scsi_target_reap(scsi_target(sdev));
kfree(sdev->inquiry);
kfree(sdev);
......@@ -560,14 +552,6 @@ static int attr_add(struct device *dev, struct device_attribute *attr)
return device_create_file(dev, attr);
}
static void scsi_target_dev_release(struct device *dev)
{
struct scsi_target *starget = to_scsi_target(dev);
struct device *parent = dev->parent;
kfree(starget);
put_device(parent);
}
/**
* scsi_sysfs_add_sdev - add scsi device to sysfs
* @sdev: scsi_device to add
......@@ -577,25 +561,16 @@ static void scsi_target_dev_release(struct device *dev)
**/
int scsi_sysfs_add_sdev(struct scsi_device *sdev)
{
struct scsi_target *starget = sdev->sdev_target;
struct Scsi_Host *shost = sdev->host;
int error, i, create;
struct scsi_target *starget = scsi_target(sdev);
int error, i;
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
create = starget->create;
starget->create = 0;
list_add_tail(&sdev->same_target_siblings, &starget->devices);
list_add_tail(&sdev->siblings, &shost->__devices);
spin_unlock_irqrestore(shost->host_lock, flags);
if (create) {
error = device_add(&starget->dev);
if (error) {
printk(KERN_ERR "Target device_add failed\n");
return error;
}
transport_add_device(&starget->dev);
}
if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0)
return error;
......@@ -643,7 +618,6 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
out:
return error;
class_device_del(&sdev->sdev_classdev);
clean_device:
scsi_device_set_state(sdev, SDEV_CANCEL);
......@@ -673,12 +647,40 @@ void scsi_remove_device(struct scsi_device *sdev)
sdev->host->hostt->slave_destroy(sdev);
transport_unregister_device(&sdev->sdev_gendev);
put_device(&sdev->sdev_gendev);
out:
up(&shost->scan_mutex);
}
EXPORT_SYMBOL(scsi_remove_device);
/**
* scsi_remove_target - try to remove a target and all its devices
* @starget: the target to remove
*
* Note: This is slightly racy. It is possible that if the user
* requests the addition of another device then the target won't be
* removed.
*/
void scsi_remove_target(struct scsi_target *starget)
{
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
unsigned long flags;
struct scsi_device *sdev, *tmp;
spin_lock_irqsave(shost->host_lock, flags);
starget->reap_ref++;
list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) {
if (sdev->channel != starget->channel ||
sdev->id != starget->id)
continue;
spin_unlock_irqrestore(shost->host_lock, flags);
scsi_remove_device(sdev);
spin_lock_irqsave(shost->host_lock, flags);
}
spin_unlock_irqrestore(shost->host_lock, flags);
scsi_target_reap(starget);
}
EXPORT_SYMBOL(scsi_remove_target);
int scsi_register_driver(struct device_driver *drv)
{
drv->bus = &scsi_bus_type;
......@@ -780,7 +782,7 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev)
snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE,
"%d:%d:%d:%d", sdev->host->host_no,
sdev->channel, sdev->id, sdev->lun);
sdev->scsi_level = SCSI_2;
transport_setup_device(&sdev->sdev_gendev);
}
......@@ -790,73 +792,6 @@ int scsi_is_sdev_device(const struct device *dev)
}
EXPORT_SYMBOL(scsi_is_sdev_device);
int scsi_sysfs_target_initialize(struct scsi_device *sdev)
{
struct scsi_target *starget = NULL;
struct Scsi_Host *shost = sdev->host;
struct scsi_device *device;
struct device *dev = NULL;
unsigned long flags;
int create = 0;
spin_lock_irqsave(shost->host_lock, flags);
/*
* Search for an existing target for this sdev.
*/
list_for_each_entry(device, &shost->__devices, siblings) {
if (device->id == sdev->id &&
device->channel == sdev->channel) {
list_add_tail(&sdev->same_target_siblings,
&device->same_target_siblings);
sdev->scsi_level = device->scsi_level;
starget = device->sdev_target;
break;
}
}
if (!starget) {
const int size = sizeof(*starget) +
shost->transportt->target_size;
starget = kmalloc(size, GFP_ATOMIC);
if (!starget) {
printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__);
spin_unlock_irqrestore(shost->host_lock,
flags);
return -ENOMEM;
}
memset(starget, 0, size);
dev = &starget->dev;
device_initialize(dev);
dev->parent = get_device(&shost->shost_gendev);
dev->release = scsi_target_dev_release;
sprintf(dev->bus_id, "target%d:%d:%d",
shost->host_no, sdev->channel, sdev->id);
starget->id = sdev->id;
starget->channel = sdev->channel;
create = starget->create = 1;
/*
* If there wasn't another lun already configured at
* this target, then default this device to SCSI_2
* until we know better
*/
sdev->scsi_level = SCSI_2;
}
get_device(&starget->dev);
sdev->sdev_gendev.parent = &starget->dev;
sdev->sdev_target = starget;
list_add_tail(&sdev->siblings, &shost->__devices);
spin_unlock_irqrestore(shost->host_lock, flags);
if (create)
transport_setup_device(&starget->dev);
return 0;
}
int scsi_is_target_device(const struct device *dev)
{
return dev->release == scsi_target_dev_release;
}
EXPORT_SYMBOL(scsi_is_target_device);
/* A blank transport template that is used in drivers that don't
* yet implement Transport Attributes */
struct scsi_transport_template blank_transport_template = { NULL, };
......@@ -144,7 +144,10 @@ struct scsi_device {
*/
struct scsi_target {
struct scsi_device *starget_sdev_user;
struct list_head siblings;
struct list_head devices;
struct device dev;
unsigned int reap_ref; /* protected by the host lock */
unsigned int channel;
unsigned int id; /* target id ... replace
* scsi_device.id eventually */
......@@ -173,6 +176,10 @@ extern struct scsi_device *scsi_device_lookup(struct Scsi_Host *,
uint, uint, uint);
extern struct scsi_device *__scsi_device_lookup(struct Scsi_Host *,
uint, uint, uint);
extern struct scsi_device *scsi_device_lookup_by_target(struct scsi_target *,
uint);
extern struct scsi_device *__scsi_device_lookup_by_target(struct scsi_target *,
uint);
extern void starget_for_each_device(struct scsi_target *, void *,
void (*fn)(struct scsi_device *, void *));
......@@ -226,6 +233,10 @@ extern int scsi_device_quiesce(struct scsi_device *sdev);
extern void scsi_device_resume(struct scsi_device *sdev);
extern void scsi_target_quiesce(struct scsi_target *);
extern void scsi_target_resume(struct scsi_target *);
extern void scsi_scan_target(struct device *parent, unsigned int channel,
unsigned int id, unsigned int lun, int rescan);
extern void scsi_target_reap(struct scsi_target *);
extern void scsi_remove_target(struct scsi_target *);
extern const char *scsi_device_state_name(enum scsi_device_state);
extern int scsi_is_sdev_device(const struct device *);
extern int scsi_is_target_device(const struct device *);
......
......@@ -416,6 +416,7 @@ struct Scsi_Host {
* access this list directly from a driver.
*/
struct list_head __devices;
struct list_head __targets;
struct scsi_host_cmd_pool *cmd_pool;
spinlock_t free_list_lock;
......@@ -553,11 +554,21 @@ struct Scsi_Host {
unsigned long hostdata[0] /* Used for storage of host specific stuff */
__attribute__ ((aligned (sizeof(unsigned long))));
};
#define dev_to_shost(d) \
container_of(d, struct Scsi_Host, shost_gendev)
#define class_to_shost(d) \
container_of(d, struct Scsi_Host, shost_classdev)
int scsi_is_host_device(const struct device *);
static inline struct Scsi_Host *dev_to_shost(struct device *dev)
{
while (!scsi_is_host_device(dev)) {
if (!dev->parent)
return NULL;
dev = dev->parent;
}
return container_of(dev, struct Scsi_Host, shost_gendev);
}
extern struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *, int);
extern int __must_check scsi_add_host(struct Scsi_Host *, struct device *);
......@@ -601,8 +612,6 @@ struct class_container;
*/
extern void scsi_free_host_dev(struct scsi_device *);
extern struct scsi_device *scsi_get_host_dev(struct Scsi_Host *);
int scsi_is_host_device(const struct device *);
/* legacy interfaces */
extern struct Scsi_Host *scsi_register(struct scsi_host_template *, int);
......
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