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;
......
......@@ -200,12 +200,12 @@ static void print_inquiry(unsigned char *inq_result)
* Return value:
* scsi_Device pointer, or NULL on failure.
**/
static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
uint channel, uint id, uint lun, void *hostdata)
static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
unsigned int lun, void *hostdata)
{
struct scsi_device *sdev;
unsigned long flags;
int display_failure_msg = 1, ret;
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
sdev = kmalloc(sizeof(*sdev) + shost->transportt->device_size,
GFP_ATOMIC);
......@@ -217,9 +217,9 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
sdev->model = scsi_null_device_strs;
sdev->rev = scsi_null_device_strs;
sdev->host = shost;
sdev->id = id;
sdev->id = starget->id;
sdev->lun = lun;
sdev->channel = channel;
sdev->channel = starget->channel;
sdev->sdev_state = SDEV_CREATED;
INIT_LIST_HEAD(&sdev->siblings);
INIT_LIST_HEAD(&sdev->same_target_siblings);
......@@ -227,6 +227,9 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
INIT_LIST_HEAD(&sdev->starved_entry);
spin_lock_init(&sdev->list_lock);
sdev->sdev_gendev.parent = get_device(&starget->dev);
sdev->sdev_target = starget;
/* usually NULL and set by ->slave_alloc instead */
sdev->hostdata = hostdata;
......@@ -248,8 +251,12 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
spin_lock_init(&sdev->sdev_lock);
sdev->request_queue = scsi_alloc_queue(sdev);
if (!sdev->request_queue)
goto out_free_dev;
if (!sdev->request_queue) {
/* release fn is set up in scsi_sysfs_device_initialise, so
* have to free and put manually here */
put_device(&starget->dev);
goto out;
}
sdev->request_queue->queuedata = sdev;
scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
......@@ -262,6 +269,8 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
/*
* if LLDD reports slave not present, don't clutter
* console with alloc failure messages
*/
if (ret == -ENXIO)
display_failure_msg = 0;
......@@ -269,32 +278,128 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
}
}
/* NOTE: this target initialisation code depends critically on
* lun scanning being sequential. */
if (scsi_sysfs_target_initialize(sdev))
goto out_remove_siblings;
return sdev;
out_remove_siblings:
spin_lock_irqsave(shost->host_lock, flags);
list_del(&sdev->siblings);
list_del(&sdev->same_target_siblings);
spin_unlock_irqrestore(shost->host_lock, flags);
if (shost->hostt->slave_destroy)
shost->hostt->slave_destroy(sdev);
out_device_destroy:
transport_destroy_device(&sdev->sdev_gendev);
scsi_free_queue(sdev->request_queue);
out_free_dev:
kfree(sdev);
put_device(&sdev->sdev_gendev);
out:
if (display_failure_msg)
printk(ALLOC_FAILURE_MSG, __FUNCTION__);
return NULL;
}
static void scsi_target_dev_release(struct device *dev)
{
struct device *parent = dev->parent;
struct scsi_target *starget = to_scsi_target(dev);
kfree(starget);
put_device(parent);
}
int scsi_is_target_device(const struct device *dev)
{
return dev->release == scsi_target_dev_release;
}
EXPORT_SYMBOL(scsi_is_target_device);
static struct scsi_target *__scsi_find_target(struct device *parent,
int channel, uint id)
{
struct scsi_target *starget, *found_starget = NULL;
struct Scsi_Host *shost = dev_to_shost(parent);
/*
* Search for an existing target for this sdev.
*/
list_for_each_entry(starget, &shost->__targets, siblings) {
if (starget->id == id &&
starget->channel == channel) {
found_starget = starget;
break;
}
}
if (found_starget)
get_device(&found_starget->dev);
return found_starget;
}
static struct scsi_target *scsi_alloc_target(struct device *parent,
int channel, uint id)
{
struct Scsi_Host *shost = dev_to_shost(parent);
struct device *dev = NULL;
unsigned long flags;
const int size = sizeof(struct scsi_target)
+ shost->transportt->target_size;
struct scsi_target *starget = kmalloc(size, GFP_ATOMIC);
struct scsi_target *found_target;
if (!starget) {
printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__);
return NULL;
}
memset(starget, 0, size);
dev = &starget->dev;
device_initialize(dev);
starget->reap_ref = 1;
dev->parent = get_device(parent);
dev->release = scsi_target_dev_release;
sprintf(dev->bus_id, "target%d:%d:%d",
shost->host_no, channel, id);
starget->id = id;
starget->channel = channel;
INIT_LIST_HEAD(&starget->siblings);
INIT_LIST_HEAD(&starget->devices);
spin_lock_irqsave(shost->host_lock, flags);
found_target = __scsi_find_target(parent, channel, id);
if (found_target)
goto found;
list_add_tail(&starget->siblings, &shost->__targets);
spin_unlock_irqrestore(shost->host_lock, flags);
/* allocate and add */
transport_setup_device(&starget->dev);
device_add(&starget->dev);
transport_add_device(&starget->dev);
return starget;
found:
found_target->reap_ref++;
spin_unlock_irqrestore(shost->host_lock, flags);
put_device(parent);
kfree(starget);
return found_target;
}
/**
* scsi_target_reap - check to see if target is in use and destroy if not
*
* @starget: target to be checked
*
* This is used after removing a LUN or doing a last put of the target
* it checks atomically that nothing is using the target and removes
* it if so.
*/
void scsi_target_reap(struct scsi_target *starget)
{
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags);
if (--starget->reap_ref == 0 && list_empty(&starget->devices)) {
list_del_init(&starget->siblings);
spin_unlock_irqrestore(shost->host_lock, flags);
device_del(&starget->dev);
transport_unregister_device(&starget->dev);
put_device(&starget->dev);
return;
}
spin_unlock_irqrestore(shost->host_lock, flags);
}
/**
* scsi_probe_lun - probe a single LUN using a SCSI INQUIRY
* @sreq: used to send the INQUIRY
......@@ -637,6 +742,8 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
/**
* scsi_probe_and_add_lun - probe a LUN, if a LUN is found add it
* @starget: pointer to target device structure
* @lun: LUN of target device
* @sdevscan: probe the LUN corresponding to this Scsi_Device
* @sdevnew: store the value of any new Scsi_Device allocated
* @bflagsp: store bflags here if not NULL
......@@ -651,45 +758,48 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
* attached at the LUN
* SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized
**/
static int scsi_probe_and_add_lun(struct Scsi_Host *host,
uint channel, uint id, uint lun, int *bflagsp,
struct scsi_device **sdevp, int rescan, void *hostdata)
static int scsi_probe_and_add_lun(struct scsi_target *starget,
uint lun, int *bflagsp,
struct scsi_device **sdevp, int rescan,
void *hostdata)
{
struct scsi_device *sdev;
struct scsi_request *sreq;
unsigned char *result;
int bflags, res = SCSI_SCAN_NO_RESPONSE;
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
/*
* The rescan flag is used as an optimization, the first scan of a
* host adapter calls into here with rescan == 0.
*/
if (rescan) {
sdev = scsi_device_lookup(host, channel, id, lun);
sdev = scsi_device_lookup_by_target(starget, lun);
if (sdev) {
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO
"scsi scan: device exists on <%d:%d:%d:%d>\n",
host->host_no, channel, id, lun));
"scsi scan: device exists on %s\n",
sdev->sdev_gendev.bus_id));
if (sdevp)
*sdevp = sdev;
else
scsi_device_put(sdev);
if (bflagsp)
*bflagsp = scsi_get_device_flags(sdev,
sdev->vendor,
sdev->model);
/* XXX: bandaid until callers do refcounting */
scsi_device_put(sdev);
return SCSI_SCAN_LUN_PRESENT;
}
}
sdev = scsi_alloc_sdev(host, channel, id, lun, hostdata);
sdev = scsi_alloc_sdev(starget, lun, hostdata);
if (!sdev)
goto out;
sreq = scsi_allocate_request(sdev, GFP_ATOMIC);
if (!sreq)
goto out_free_sdev;
result = kmalloc(256, GFP_ATOMIC |
(host->unchecked_isa_dma) ? __GFP_DMA : 0);
(shost->unchecked_isa_dma) ? __GFP_DMA : 0);
if (!result)
goto out_free_sreq;
......@@ -734,8 +844,10 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
scsi_release_request(sreq);
out_free_sdev:
if (res == SCSI_SCAN_LUN_PRESENT) {
if (sdevp)
if (sdevp) {
scsi_device_get(sdev);
*sdevp = sdev;
}
} else {
if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev);
......@@ -748,7 +860,7 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
/**
* scsi_sequential_lun_scan - sequentially scan a SCSI target
* @sdevscan: scan the host, channel, and id of this Scsi_Device
* @starget: pointer to target structure to scan
* @bflags: black/white list flag for LUN 0
* @lun0_res: result of scanning LUN 0
*
......@@ -759,14 +871,15 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
*
* Modifies sdevscan->lun.
**/
static void scsi_sequential_lun_scan(struct Scsi_Host *shost, uint channel,
uint id, int bflags, int lun0_res, int scsi_level, int rescan)
static void scsi_sequential_lun_scan(struct scsi_target *starget,
int bflags, int lun0_res, int scsi_level,
int rescan)
{
unsigned int sparse_lun, lun, max_dev_lun;
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: Sequential scan of"
" host %d channel %d id %d\n", shost->host_no,
channel, id));
"%s\n", starget->dev.bus_id));
max_dev_lun = min(max_scsi_luns, shost->max_lun);
/*
......@@ -828,8 +941,8 @@ static void scsi_sequential_lun_scan(struct Scsi_Host *shost, uint channel,
* sparse_lun.
*/
for (lun = 1; lun < max_dev_lun; ++lun)
if ((scsi_probe_and_add_lun(shost, channel, id, lun,
NULL, NULL, rescan, NULL) != SCSI_SCAN_LUN_PRESENT) &&
if ((scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan,
NULL) != SCSI_SCAN_LUN_PRESENT) &&
!sparse_lun)
return;
}
......@@ -893,6 +1006,7 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
struct scsi_request *sreq;
u8 *data;
struct scsi_sense_hdr sshdr;
struct scsi_target *starget = scsi_target(sdev);
/*
* Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set.
......@@ -1043,8 +1157,8 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
} else {
int res;
res = scsi_probe_and_add_lun(sdev->host, sdev->channel,
sdev->id, lun, NULL, NULL, rescan, NULL);
res = scsi_probe_and_add_lun(starget,
lun, NULL, NULL, rescan, NULL);
if (res == SCSI_SCAN_NO_RESPONSE) {
/*
* Got some results, but now none, abort.
......@@ -1074,14 +1188,20 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
uint id, uint lun, void *hostdata)
{
struct scsi_device *sdev;
struct device *parent = &shost->shost_gendev;
int res;
struct scsi_target *starget = scsi_alloc_target(parent, channel, id);
if (!starget)
return ERR_PTR(-ENOMEM);
down(&shost->scan_mutex);
res = scsi_probe_and_add_lun(shost, channel, id, lun, NULL,
&sdev, 1, hostdata);
res = scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata);
if (res != SCSI_SCAN_LUN_PRESENT)
sdev = ERR_PTR(-ENODEV);
up(&shost->scan_mutex);
scsi_target_reap(starget);
put_device(&starget->dev);
return sdev;
}
......@@ -1122,12 +1242,14 @@ EXPORT_SYMBOL(scsi_rescan_device);
* First try a REPORT LUN scan, if that does not scan the target, do a
* sequential scan of LUNs on the target id.
**/
static void scsi_scan_target(struct Scsi_Host *shost, unsigned int channel,
void scsi_scan_target(struct device *parent, unsigned int channel,
unsigned int id, unsigned int lun, int rescan)
{
struct Scsi_Host *shost = dev_to_shost(parent);
int bflags = 0;
int res;
struct scsi_device *sdev;
struct scsi_device *sdev = NULL;
struct scsi_target *starget;
if (shost->this_id == id)
/*
......@@ -1135,28 +1257,33 @@ static void scsi_scan_target(struct Scsi_Host *shost, unsigned int channel,
*/
return;
starget = scsi_alloc_target(parent, channel, id);
if (!starget)
return;
get_device(&starget->dev);
if (lun != SCAN_WILD_CARD) {
/*
* Scan for a specific host/chan/id/lun.
*/
scsi_probe_and_add_lun(shost, channel, id, lun, NULL, NULL,
rescan, NULL);
return;
scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan, NULL);
goto out_reap;
}
/*
* Scan LUN 0, if there is some response, scan further. Ideally, we
* would not configure LUN 0 until all LUNs are scanned.
*/
res = scsi_probe_and_add_lun(shost, channel, id, 0, &bflags, &sdev,
rescan, NULL);
res = scsi_probe_and_add_lun(starget, 0, &bflags, &sdev, rescan, NULL);
if (res == SCSI_SCAN_LUN_PRESENT) {
if (scsi_report_lun_scan(sdev, bflags, rescan) != 0)
/*
* The REPORT LUN did not scan the target,
* do a sequential scan.
*/
scsi_sequential_lun_scan(shost, channel, id, bflags,
scsi_sequential_lun_scan(starget, bflags,
res, sdev->scsi_level, rescan);
} else if (res == SCSI_SCAN_TARGET_PRESENT) {
/*
......@@ -1165,10 +1292,20 @@ static void scsi_scan_target(struct Scsi_Host *shost, unsigned int channel,
* sequential lun scan with a bflags of SPARSELUN and
* a default scsi level of SCSI_2
*/
scsi_sequential_lun_scan(shost, channel, id, BLIST_SPARSELUN,
scsi_sequential_lun_scan(starget, BLIST_SPARSELUN,
SCSI_SCAN_TARGET_PRESENT, SCSI_2, rescan);
}
if (sdev)
scsi_device_put(sdev);
out_reap:
/* now determine if the target has any children at all
* and if not, nuke it */
scsi_target_reap(starget);
put_device(&starget->dev);
}
EXPORT_SYMBOL(scsi_scan_target);
static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel,
unsigned int id, unsigned int lun, int rescan)
......@@ -1193,10 +1330,10 @@ static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel,
order_id = shost->max_id - id - 1;
else
order_id = id;
scsi_scan_target(shost, channel, order_id, lun, rescan);
scsi_scan_target(&shost->shost_gendev, channel, order_id, lun, rescan);
}
else
scsi_scan_target(shost, channel, id, lun, rescan);
scsi_scan_target(&shost->shost_gendev, channel, id, lun, rescan);
}
int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel,
......@@ -1247,7 +1384,7 @@ EXPORT_SYMBOL(scsi_scan_single_target);
void scsi_forget_host(struct Scsi_Host *shost)
{
struct scsi_device *sdev, *tmp;
struct scsi_target *starget, *tmp;
unsigned long flags;
/*
......@@ -1260,9 +1397,9 @@ void scsi_forget_host(struct Scsi_Host *shost)
* after that we don't look at sdev anymore.
*/
spin_lock_irqsave(shost->host_lock, flags);
list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) {
list_for_each_entry_safe(starget, tmp, &shost->__targets, siblings) {
spin_unlock_irqrestore(shost->host_lock, flags);
scsi_remove_device(sdev);
scsi_remove_target(starget);
spin_lock_irqsave(shost->host_lock, flags);
}
spin_unlock_irqrestore(shost->host_lock, flags);
......@@ -1291,11 +1428,18 @@ void scsi_forget_host(struct Scsi_Host *shost)
struct scsi_device *scsi_get_host_dev(struct Scsi_Host *shost)
{
struct scsi_device *sdev;
struct scsi_target *starget;
starget = scsi_alloc_target(&shost->shost_gendev, 0, shost->this_id);
if (!starget)
return NULL;
sdev = scsi_alloc_sdev(shost, 0, shost->this_id, 0, NULL);
sdev = scsi_alloc_sdev(starget, 0, NULL);
if (sdev) {
sdev->sdev_gendev.parent = get_device(&starget->dev);
sdev->borken = 0;
}
put_device(&starget->dev);
return sdev;
}
EXPORT_SYMBOL(scsi_get_host_dev);
......
......@@ -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