Commit a7711276 authored by James Bottomley's avatar James Bottomley

Add scsi_target abstraction and place it in sysfs

For some of the transport class attributes, we need to hang them off the
SCSI target rather than the LUN (represented by scsi_device).  To do this
efficiently, we need to make the target visible in the sysfs hierarchy.

The net effect of this change is to make an extra target component appear
in all the SCSI sysfs paths:

jejb@raven> ls -l /sys/class/scsi_device/0\:0\:5\:0/device
lrwxrwxrwx  1 root root 0 Sep  7 12:10 /sys/class/scsi_device/0:0:5:0/device -> ../../../devices/parisc8/parisc8:0/pci0000:00/0000:00:13.0/host0/target0:0:5/0:0:5:0/
Signed-off-by: default avatarJames Bottomley <James.Bottomley@SteelEye.com>
parent cd31a496
......@@ -365,7 +365,7 @@ static void scsi_single_lun_run(struct scsi_device *current_sdev)
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
current_sdev->sdev_target->starget_sdev_user = NULL;
scsi_target(current_sdev)->starget_sdev_user = NULL;
spin_unlock_irqrestore(shost->host_lock, flags);
/*
......@@ -377,7 +377,7 @@ 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 (current_sdev->sdev_target->starget_sdev_user)
if (scsi_target(current_sdev)->starget_sdev_user)
goto out;
list_for_each_entry_safe(sdev, tmp, &current_sdev->same_target_siblings,
same_target_siblings) {
......@@ -1247,10 +1247,10 @@ static void scsi_request_fn(struct request_queue *q)
if (!scsi_host_queue_ready(q, shost, sdev))
goto not_ready;
if (sdev->single_lun) {
if (sdev->sdev_target->starget_sdev_user &&
sdev->sdev_target->starget_sdev_user != sdev)
if (scsi_target(sdev)->starget_sdev_user &&
scsi_target(sdev)->starget_sdev_user != sdev)
goto not_ready;
sdev->sdev_target->starget_sdev_user = sdev;
scsi_target(sdev)->starget_sdev_user = sdev;
}
shost->host_busy++;
......
......@@ -65,9 +65,15 @@ struct Scsi_Host;
*/
struct scsi_target {
struct scsi_device *starget_sdev_user;
unsigned int starget_refcnt;
struct device dev;
};
#define to_scsi_target(d) container_of(d, struct scsi_target, dev)
static inline struct scsi_target *scsi_target(struct scsi_device *sdev)
{
return to_scsi_target(sdev->sdev_gendev.parent);
}
/* hosts.c */
extern int scsi_init_hosts(void);
extern void scsi_exit_hosts(void);
......@@ -156,6 +162,8 @@ extern int scsi_sysfs_add_sdev(struct scsi_device *);
extern int scsi_sysfs_add_host(struct Scsi_Host *);
extern int scsi_sysfs_register(void);
extern void scsi_sysfs_unregister(void);
extern int scsi_sysfs_device_initialize(struct scsi_device *);
extern int scsi_sysfs_target_initialize(struct scsi_device *);
extern struct scsi_transport_template blank_transport_template;
extern struct class sdev_class;
......
......@@ -261,30 +261,8 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
goto out_cleanup_slave;
}
if (get_device(&sdev->host->shost_gendev)) {
device_initialize(&sdev->sdev_gendev);
sdev->sdev_gendev.parent = &sdev->host->shost_gendev;
sdev->sdev_gendev.bus = &scsi_bus_type;
sdev->sdev_gendev.release = scsi_device_dev_release;
sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d",
sdev->host->host_no, sdev->channel, sdev->id,
sdev->lun);
class_device_initialize(&sdev->sdev_classdev);
sdev->sdev_classdev.dev = &sdev->sdev_gendev;
sdev->sdev_classdev.class = &sdev_class;
snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE,
"%d:%d:%d:%d", sdev->host->host_no,
sdev->channel, sdev->id, sdev->lun);
class_device_initialize(&sdev->transport_classdev);
sdev->transport_classdev.dev = &sdev->sdev_gendev;
sdev->transport_classdev.class = sdev->host->transportt->class;
snprintf(sdev->transport_classdev.class_id, BUS_ID_SIZE,
"%d:%d:%d:%d", sdev->host->host_no,
sdev->channel, sdev->id, sdev->lun);
} else
if (get_device(&sdev->host->shost_gendev) == NULL ||
scsi_sysfs_device_initialize(sdev) != 0)
goto out_cleanup_transport;
/*
......@@ -500,10 +478,6 @@ static void scsi_probe_lun(struct scsi_request *sreq, char *inq_result,
**/
static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
{
struct scsi_device *sdev_sibling;
struct scsi_target *starget;
unsigned long flags;
/*
* XXX do not save the inquiry, since it can change underneath us,
* save just vendor/model/rev.
......@@ -612,40 +586,14 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
if (*bflags & BLIST_NOSTARTONADD)
sdev->no_start_on_add = 1;
/*
* If we need to allow I/O to only one of the luns attached to
* this target id at a time set single_lun, and allocate or modify
* sdev_target.
*/
if (*bflags & BLIST_SINGLELUN) {
/* NOTE: this target initialisation code depends critically on
* lun scanning being sequential. */
if(scsi_sysfs_target_initialize(sdev))
return SCSI_SCAN_NO_RESPONSE;
if (*bflags & BLIST_SINGLELUN)
sdev->single_lun = 1;
spin_lock_irqsave(sdev->host->host_lock, flags);
starget = NULL;
/*
* Search for an existing target for this sdev.
*/
list_for_each_entry(sdev_sibling, &sdev->same_target_siblings,
same_target_siblings) {
if (sdev_sibling->sdev_target != NULL) {
starget = sdev_sibling->sdev_target;
break;
}
}
if (!starget) {
starget = kmalloc(sizeof(*starget), GFP_ATOMIC);
if (!starget) {
printk(ALLOC_FAILURE_MSG, __FUNCTION__);
spin_unlock_irqrestore(sdev->host->host_lock,
flags);
return SCSI_SCAN_NO_RESPONSE;
}
starget->starget_refcnt = 0;
starget->starget_sdev_user = NULL;
}
starget->starget_refcnt++;
sdev->sdev_target = starget;
spin_unlock_irqrestore(sdev->host->host_lock, flags);
}
sdev->use_10_for_rw = 1;
......
......@@ -153,25 +153,31 @@ void scsi_device_dev_release(struct device *dev)
struct scsi_device *sdev;
struct device *parent;
unsigned long flags;
int delete;
parent = dev->parent;
sdev = to_scsi_device(dev);
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) && parent);
list_del(&sdev->siblings);
list_del(&sdev->same_target_siblings);
list_del(&sdev->starved_entry);
if (sdev->single_lun && --sdev->sdev_target->starget_refcnt == 0)
kfree(sdev->sdev_target);
spin_unlock_irqrestore(sdev->host->host_lock, flags);
if (delete) {
device_del(parent);
put_device(parent);
}
if (sdev->request_queue)
scsi_free_queue(sdev->request_queue);
kfree(sdev->inquiry);
kfree(sdev);
put_device(parent);
if (parent)
put_device(parent);
}
struct class sdev_class = {
......@@ -430,6 +436,14 @@ 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
......@@ -447,6 +461,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
error = device_add(&sdev->sdev_gendev);
if (error) {
put_device(sdev->sdev_gendev.parent);
printk(KERN_INFO "error 1\n");
return error;
}
......@@ -629,6 +644,76 @@ int scsi_sysfs_add_host(struct Scsi_Host *shost)
return 0;
}
int scsi_sysfs_device_initialize(struct scsi_device *sdev)
{
device_initialize(&sdev->sdev_gendev);
sdev->sdev_gendev.bus = &scsi_bus_type;
sdev->sdev_gendev.release = scsi_device_dev_release;
sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d",
sdev->host->host_no, sdev->channel, sdev->id,
sdev->lun);
class_device_initialize(&sdev->sdev_classdev);
sdev->sdev_classdev.dev = &sdev->sdev_gendev;
sdev->sdev_classdev.class = &sdev_class;
snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE,
"%d:%d:%d:%d", sdev->host->host_no,
sdev->channel, sdev->id, sdev->lun);
class_device_initialize(&sdev->transport_classdev);
sdev->transport_classdev.dev = &sdev->sdev_gendev;
sdev->transport_classdev.class = sdev->host->transportt->class;
snprintf(sdev->transport_classdev.class_id, BUS_ID_SIZE,
"%d:%d:%d:%d", sdev->host->host_no,
sdev->channel, sdev->id, sdev->lun);
return 0;
}
int scsi_sysfs_target_initialize(struct scsi_device *sdev)
{
struct scsi_target *starget = NULL;
struct scsi_device *sdev_sibling;
struct device *dev = NULL;
int error;
unsigned long flags;
spin_lock_irqsave(sdev->host->host_lock, flags);
/*
* Search for an existing target for this sdev.
*/
list_for_each_entry(sdev_sibling, &sdev->same_target_siblings,
same_target_siblings) {
if (sdev_sibling->sdev_gendev.parent != NULL) {
starget = scsi_target(sdev_sibling);
break;
}
}
if (!starget) {
starget = kmalloc(sizeof(*starget), GFP_ATOMIC);
if (!starget) {
printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__);
spin_unlock_irqrestore(sdev->host->host_lock,
flags);
return -ENOMEM;
}
memset(starget, 0, sizeof(*starget));
dev = &starget->dev;
device_initialize(dev);
dev->parent = &sdev->host->shost_gendev;
dev->release = scsi_target_dev_release;
sprintf(dev->bus_id, "target%d:%d:%d",
sdev->host->host_no, sdev->channel, sdev->id);
}
get_device(&starget->dev);
sdev->sdev_gendev.parent = &starget->dev;
spin_unlock_irqrestore(sdev->host->host_lock, flags);
if (dev && (error = device_add(dev))) {
printk(KERN_ERR "Target device_add failed\n");
return error;
}
return 0;
}
/* A blank transport template that is used in drivers that don't
* yet implement Transport Attributes */
struct scsi_transport_template blank_transport_template = { NULL, };
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