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) ...@@ -216,6 +216,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize)
spin_lock_init(&shost->default_lock); spin_lock_init(&shost->default_lock);
scsi_assign_lock(shost, &shost->default_lock); scsi_assign_lock(shost, &shost->default_lock);
INIT_LIST_HEAD(&shost->__devices); INIT_LIST_HEAD(&shost->__devices);
INIT_LIST_HEAD(&shost->__targets);
INIT_LIST_HEAD(&shost->eh_cmd_q); INIT_LIST_HEAD(&shost->eh_cmd_q);
INIT_LIST_HEAD(&shost->starved_list); INIT_LIST_HEAD(&shost->starved_list);
init_waitqueue_head(&shost->host_wait); init_waitqueue_head(&shost->host_wait);
......
...@@ -1115,6 +1115,60 @@ void starget_for_each_device(struct scsi_target *starget, void * data, ...@@ -1115,6 +1115,60 @@ void starget_for_each_device(struct scsi_target *starget, void * data,
} }
EXPORT_SYMBOL(starget_for_each_device); 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) * scsi_device_lookup - find a device given the host (UNLOCKED)
* @shost: SCSI host pointer * @shost: SCSI host pointer
......
...@@ -365,10 +365,11 @@ static void scsi_single_lun_run(struct scsi_device *current_sdev) ...@@ -365,10 +365,11 @@ static void scsi_single_lun_run(struct scsi_device *current_sdev)
{ {
struct Scsi_Host *shost = current_sdev->host; struct Scsi_Host *shost = current_sdev->host;
struct scsi_device *sdev, *tmp; struct scsi_device *sdev, *tmp;
struct scsi_target *starget = scsi_target(current_sdev);
unsigned long flags; unsigned long flags;
spin_lock_irqsave(shost->host_lock, 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); spin_unlock_irqrestore(shost->host_lock, flags);
/* /*
...@@ -380,10 +381,12 @@ static void scsi_single_lun_run(struct scsi_device *current_sdev) ...@@ -380,10 +381,12 @@ static void scsi_single_lun_run(struct scsi_device *current_sdev)
blk_run_queue(current_sdev->request_queue); blk_run_queue(current_sdev->request_queue);
spin_lock_irqsave(shost->host_lock, flags); spin_lock_irqsave(shost->host_lock, flags);
if (scsi_target(current_sdev)->starget_sdev_user) if (starget->starget_sdev_user)
goto out; 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) { same_target_siblings) {
if (sdev == current_sdev)
continue;
if (scsi_device_get(sdev)) if (scsi_device_get(sdev))
continue; continue;
......
...@@ -200,12 +200,12 @@ static void print_inquiry(unsigned char *inq_result) ...@@ -200,12 +200,12 @@ static void print_inquiry(unsigned char *inq_result)
* Return value: * Return value:
* scsi_Device pointer, or NULL on failure. * scsi_Device pointer, or NULL on failure.
**/ **/
static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, static struct scsi_device *scsi_alloc_sdev(struct scsi_target *starget,
uint channel, uint id, uint lun, void *hostdata) unsigned int lun, void *hostdata)
{ {
struct scsi_device *sdev; struct scsi_device *sdev;
unsigned long flags;
int display_failure_msg = 1, ret; int display_failure_msg = 1, ret;
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
sdev = kmalloc(sizeof(*sdev) + shost->transportt->device_size, sdev = kmalloc(sizeof(*sdev) + shost->transportt->device_size,
GFP_ATOMIC); GFP_ATOMIC);
...@@ -217,9 +217,9 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, ...@@ -217,9 +217,9 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
sdev->model = scsi_null_device_strs; sdev->model = scsi_null_device_strs;
sdev->rev = scsi_null_device_strs; sdev->rev = scsi_null_device_strs;
sdev->host = shost; sdev->host = shost;
sdev->id = id; sdev->id = starget->id;
sdev->lun = lun; sdev->lun = lun;
sdev->channel = channel; sdev->channel = starget->channel;
sdev->sdev_state = SDEV_CREATED; sdev->sdev_state = SDEV_CREATED;
INIT_LIST_HEAD(&sdev->siblings); INIT_LIST_HEAD(&sdev->siblings);
INIT_LIST_HEAD(&sdev->same_target_siblings); INIT_LIST_HEAD(&sdev->same_target_siblings);
...@@ -227,6 +227,9 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, ...@@ -227,6 +227,9 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
INIT_LIST_HEAD(&sdev->starved_entry); INIT_LIST_HEAD(&sdev->starved_entry);
spin_lock_init(&sdev->list_lock); 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 */ /* usually NULL and set by ->slave_alloc instead */
sdev->hostdata = hostdata; sdev->hostdata = hostdata;
...@@ -248,8 +251,12 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, ...@@ -248,8 +251,12 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
spin_lock_init(&sdev->sdev_lock); spin_lock_init(&sdev->sdev_lock);
sdev->request_queue = scsi_alloc_queue(sdev); sdev->request_queue = scsi_alloc_queue(sdev);
if (!sdev->request_queue) if (!sdev->request_queue) {
goto out_free_dev; /* 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; sdev->request_queue->queuedata = sdev;
scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun); 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, ...@@ -262,6 +269,8 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
/* /*
* if LLDD reports slave not present, don't clutter * if LLDD reports slave not present, don't clutter
* console with alloc failure messages * console with alloc failure messages
*/ */
if (ret == -ENXIO) if (ret == -ENXIO)
display_failure_msg = 0; display_failure_msg = 0;
...@@ -269,32 +278,128 @@ static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost, ...@@ -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; 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: out_device_destroy:
transport_destroy_device(&sdev->sdev_gendev); transport_destroy_device(&sdev->sdev_gendev);
scsi_free_queue(sdev->request_queue); scsi_free_queue(sdev->request_queue);
out_free_dev: put_device(&sdev->sdev_gendev);
kfree(sdev);
out: out:
if (display_failure_msg) if (display_failure_msg)
printk(ALLOC_FAILURE_MSG, __FUNCTION__); printk(ALLOC_FAILURE_MSG, __FUNCTION__);
return NULL; 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 * scsi_probe_lun - probe a single LUN using a SCSI INQUIRY
* @sreq: used to send the 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) ...@@ -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 * 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 * @sdevscan: probe the LUN corresponding to this Scsi_Device
* @sdevnew: store the value of any new Scsi_Device allocated * @sdevnew: store the value of any new Scsi_Device allocated
* @bflagsp: store bflags here if not NULL * @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) ...@@ -651,45 +758,48 @@ static int scsi_add_lun(struct scsi_device *sdev, char *inq_result, int *bflags)
* attached at the LUN * attached at the LUN
* SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized * SCSI_SCAN_LUN_PRESENT: a new Scsi_Device was allocated and initialized
**/ **/
static int scsi_probe_and_add_lun(struct Scsi_Host *host, static int scsi_probe_and_add_lun(struct scsi_target *starget,
uint channel, uint id, uint lun, int *bflagsp, uint lun, int *bflagsp,
struct scsi_device **sdevp, int rescan, void *hostdata) struct scsi_device **sdevp, int rescan,
void *hostdata)
{ {
struct scsi_device *sdev; struct scsi_device *sdev;
struct scsi_request *sreq; struct scsi_request *sreq;
unsigned char *result; unsigned char *result;
int bflags, res = SCSI_SCAN_NO_RESPONSE; 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 * The rescan flag is used as an optimization, the first scan of a
* host adapter calls into here with rescan == 0. * host adapter calls into here with rescan == 0.
*/ */
if (rescan) { if (rescan) {
sdev = scsi_device_lookup(host, channel, id, lun); sdev = scsi_device_lookup_by_target(starget, lun);
if (sdev) { if (sdev) {
SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO
"scsi scan: device exists on <%d:%d:%d:%d>\n", "scsi scan: device exists on %s\n",
host->host_no, channel, id, lun)); sdev->sdev_gendev.bus_id));
if (sdevp) if (sdevp)
*sdevp = sdev; *sdevp = sdev;
else
scsi_device_put(sdev);
if (bflagsp) if (bflagsp)
*bflagsp = scsi_get_device_flags(sdev, *bflagsp = scsi_get_device_flags(sdev,
sdev->vendor, sdev->vendor,
sdev->model); sdev->model);
/* XXX: bandaid until callers do refcounting */
scsi_device_put(sdev);
return SCSI_SCAN_LUN_PRESENT; return SCSI_SCAN_LUN_PRESENT;
} }
} }
sdev = scsi_alloc_sdev(host, channel, id, lun, hostdata); sdev = scsi_alloc_sdev(starget, lun, hostdata);
if (!sdev) if (!sdev)
goto out; goto out;
sreq = scsi_allocate_request(sdev, GFP_ATOMIC); sreq = scsi_allocate_request(sdev, GFP_ATOMIC);
if (!sreq) if (!sreq)
goto out_free_sdev; goto out_free_sdev;
result = kmalloc(256, GFP_ATOMIC | result = kmalloc(256, GFP_ATOMIC |
(host->unchecked_isa_dma) ? __GFP_DMA : 0); (shost->unchecked_isa_dma) ? __GFP_DMA : 0);
if (!result) if (!result)
goto out_free_sreq; goto out_free_sreq;
...@@ -734,8 +844,10 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host, ...@@ -734,8 +844,10 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
scsi_release_request(sreq); scsi_release_request(sreq);
out_free_sdev: out_free_sdev:
if (res == SCSI_SCAN_LUN_PRESENT) { if (res == SCSI_SCAN_LUN_PRESENT) {
if (sdevp) if (sdevp) {
scsi_device_get(sdev);
*sdevp = sdev; *sdevp = sdev;
}
} else { } else {
if (sdev->host->hostt->slave_destroy) if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev); sdev->host->hostt->slave_destroy(sdev);
...@@ -748,7 +860,7 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host, ...@@ -748,7 +860,7 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
/** /**
* scsi_sequential_lun_scan - sequentially scan a SCSI target * 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 * @bflags: black/white list flag for LUN 0
* @lun0_res: result of scanning LUN 0 * @lun0_res: result of scanning LUN 0
* *
...@@ -759,14 +871,15 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host, ...@@ -759,14 +871,15 @@ static int scsi_probe_and_add_lun(struct Scsi_Host *host,
* *
* Modifies sdevscan->lun. * Modifies sdevscan->lun.
**/ **/
static void scsi_sequential_lun_scan(struct Scsi_Host *shost, uint channel, static void scsi_sequential_lun_scan(struct scsi_target *starget,
uint id, int bflags, int lun0_res, int scsi_level, int rescan) int bflags, int lun0_res, int scsi_level,
int rescan)
{ {
unsigned int sparse_lun, lun, max_dev_lun; 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" SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: Sequential scan of"
" host %d channel %d id %d\n", shost->host_no, "%s\n", starget->dev.bus_id));
channel, id));
max_dev_lun = min(max_scsi_luns, shost->max_lun); 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, ...@@ -828,8 +941,8 @@ static void scsi_sequential_lun_scan(struct Scsi_Host *shost, uint channel,
* sparse_lun. * sparse_lun.
*/ */
for (lun = 1; lun < max_dev_lun; ++lun) for (lun = 1; lun < max_dev_lun; ++lun)
if ((scsi_probe_and_add_lun(shost, channel, id, lun, if ((scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan,
NULL, NULL, rescan, NULL) != SCSI_SCAN_LUN_PRESENT) && NULL) != SCSI_SCAN_LUN_PRESENT) &&
!sparse_lun) !sparse_lun)
return; return;
} }
...@@ -893,6 +1006,7 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags, ...@@ -893,6 +1006,7 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
struct scsi_request *sreq; struct scsi_request *sreq;
u8 *data; u8 *data;
struct scsi_sense_hdr sshdr; 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. * 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, ...@@ -1043,8 +1157,8 @@ static int scsi_report_lun_scan(struct scsi_device *sdev, int bflags,
} else { } else {
int res; int res;
res = scsi_probe_and_add_lun(sdev->host, sdev->channel, res = scsi_probe_and_add_lun(starget,
sdev->id, lun, NULL, NULL, rescan, NULL); lun, NULL, NULL, rescan, NULL);
if (res == SCSI_SCAN_NO_RESPONSE) { if (res == SCSI_SCAN_NO_RESPONSE) {
/* /*
* Got some results, but now none, abort. * Got some results, but now none, abort.
...@@ -1074,14 +1188,20 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, ...@@ -1074,14 +1188,20 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel,
uint id, uint lun, void *hostdata) uint id, uint lun, void *hostdata)
{ {
struct scsi_device *sdev; struct scsi_device *sdev;
struct device *parent = &shost->shost_gendev;
int res; int res;
struct scsi_target *starget = scsi_alloc_target(parent, channel, id);
if (!starget)
return ERR_PTR(-ENOMEM);
down(&shost->scan_mutex); down(&shost->scan_mutex);
res = scsi_probe_and_add_lun(shost, channel, id, lun, NULL, res = scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata);
&sdev, 1, hostdata);
if (res != SCSI_SCAN_LUN_PRESENT) if (res != SCSI_SCAN_LUN_PRESENT)
sdev = ERR_PTR(-ENODEV); sdev = ERR_PTR(-ENODEV);
up(&shost->scan_mutex); up(&shost->scan_mutex);
scsi_target_reap(starget);
put_device(&starget->dev);
return sdev; return sdev;
} }
...@@ -1122,12 +1242,14 @@ EXPORT_SYMBOL(scsi_rescan_device); ...@@ -1122,12 +1242,14 @@ EXPORT_SYMBOL(scsi_rescan_device);
* First try a REPORT LUN scan, if that does not scan the target, do a * First try a REPORT LUN scan, if that does not scan the target, do a
* sequential scan of LUNs on the target id. * 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) unsigned int id, unsigned int lun, int rescan)
{ {
struct Scsi_Host *shost = dev_to_shost(parent);
int bflags = 0; int bflags = 0;
int res; int res;
struct scsi_device *sdev; struct scsi_device *sdev = NULL;
struct scsi_target *starget;
if (shost->this_id == id) if (shost->this_id == id)
/* /*
...@@ -1135,28 +1257,33 @@ static void scsi_scan_target(struct Scsi_Host *shost, unsigned int channel, ...@@ -1135,28 +1257,33 @@ static void scsi_scan_target(struct Scsi_Host *shost, unsigned int channel,
*/ */
return; return;
starget = scsi_alloc_target(parent, channel, id);
if (!starget)
return;
get_device(&starget->dev);
if (lun != SCAN_WILD_CARD) { if (lun != SCAN_WILD_CARD) {
/* /*
* Scan for a specific host/chan/id/lun. * Scan for a specific host/chan/id/lun.
*/ */
scsi_probe_and_add_lun(shost, channel, id, lun, NULL, NULL, scsi_probe_and_add_lun(starget, lun, NULL, NULL, rescan, NULL);
rescan, NULL); goto out_reap;
return;
} }
/* /*
* Scan LUN 0, if there is some response, scan further. Ideally, we * Scan LUN 0, if there is some response, scan further. Ideally, we
* would not configure LUN 0 until all LUNs are scanned. * would not configure LUN 0 until all LUNs are scanned.
*/ */
res = scsi_probe_and_add_lun(shost, channel, id, 0, &bflags, &sdev, res = scsi_probe_and_add_lun(starget, 0, &bflags, &sdev, rescan, NULL);
rescan, NULL);
if (res == SCSI_SCAN_LUN_PRESENT) { if (res == SCSI_SCAN_LUN_PRESENT) {
if (scsi_report_lun_scan(sdev, bflags, rescan) != 0) if (scsi_report_lun_scan(sdev, bflags, rescan) != 0)
/* /*
* The REPORT LUN did not scan the target, * The REPORT LUN did not scan the target,
* do a sequential scan. * do a sequential scan.
*/ */
scsi_sequential_lun_scan(shost, channel, id, bflags, scsi_sequential_lun_scan(starget, bflags,
res, sdev->scsi_level, rescan); res, sdev->scsi_level, rescan);
} else if (res == SCSI_SCAN_TARGET_PRESENT) { } else if (res == SCSI_SCAN_TARGET_PRESENT) {
/* /*
...@@ -1165,10 +1292,20 @@ static void scsi_scan_target(struct Scsi_Host *shost, unsigned int channel, ...@@ -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 * sequential lun scan with a bflags of SPARSELUN and
* a default scsi level of SCSI_2 * 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); 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, static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel,
unsigned int id, unsigned int lun, int rescan) unsigned int id, unsigned int lun, int rescan)
...@@ -1193,10 +1330,10 @@ static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel, ...@@ -1193,10 +1330,10 @@ static void scsi_scan_channel(struct Scsi_Host *shost, unsigned int channel,
order_id = shost->max_id - id - 1; order_id = shost->max_id - id - 1;
else else
order_id = id; order_id = id;
scsi_scan_target(shost, channel, order_id, lun, rescan); scsi_scan_target(&shost->shost_gendev, channel, order_id, lun, rescan);
} }
else 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, int scsi_scan_host_selected(struct Scsi_Host *shost, unsigned int channel,
...@@ -1247,7 +1384,7 @@ EXPORT_SYMBOL(scsi_scan_single_target); ...@@ -1247,7 +1384,7 @@ EXPORT_SYMBOL(scsi_scan_single_target);
void scsi_forget_host(struct Scsi_Host *shost) void scsi_forget_host(struct Scsi_Host *shost)
{ {
struct scsi_device *sdev, *tmp; struct scsi_target *starget, *tmp;
unsigned long flags; unsigned long flags;
/* /*
...@@ -1260,9 +1397,9 @@ void scsi_forget_host(struct Scsi_Host *shost) ...@@ -1260,9 +1397,9 @@ void scsi_forget_host(struct Scsi_Host *shost)
* after that we don't look at sdev anymore. * after that we don't look at sdev anymore.
*/ */
spin_lock_irqsave(shost->host_lock, flags); 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); spin_unlock_irqrestore(shost->host_lock, flags);
scsi_remove_device(sdev); scsi_remove_target(starget);
spin_lock_irqsave(shost->host_lock, flags); spin_lock_irqsave(shost->host_lock, flags);
} }
spin_unlock_irqrestore(shost->host_lock, flags); spin_unlock_irqrestore(shost->host_lock, flags);
...@@ -1291,11 +1428,18 @@ void scsi_forget_host(struct Scsi_Host *shost) ...@@ -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 *scsi_get_host_dev(struct Scsi_Host *shost)
{ {
struct scsi_device *sdev; 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) { if (sdev) {
sdev->sdev_gendev.parent = get_device(&starget->dev);
sdev->borken = 0; sdev->borken = 0;
} }
put_device(&starget->dev);
return sdev; return sdev;
} }
EXPORT_SYMBOL(scsi_get_host_dev); EXPORT_SYMBOL(scsi_get_host_dev);
......
...@@ -154,33 +154,25 @@ void scsi_device_dev_release(struct device *dev) ...@@ -154,33 +154,25 @@ void scsi_device_dev_release(struct device *dev)
{ {
struct scsi_device *sdev; struct scsi_device *sdev;
struct device *parent; struct device *parent;
struct scsi_target *starget;
unsigned long flags; unsigned long flags;
int delete;
parent = dev->parent; parent = dev->parent;
sdev = to_scsi_device(dev); sdev = to_scsi_device(dev);
starget = to_scsi_target(parent);
spin_lock_irqsave(sdev->host->host_lock, flags); spin_lock_irqsave(sdev->host->host_lock, flags);
/* If we're the last LUN on the target, destroy the target */ starget->reap_ref++;
delete = list_empty(&sdev->same_target_siblings);
list_del(&sdev->siblings); list_del(&sdev->siblings);
list_del(&sdev->same_target_siblings); list_del(&sdev->same_target_siblings);
list_del(&sdev->starved_entry); list_del(&sdev->starved_entry);
spin_unlock_irqrestore(sdev->host->host_lock, flags); 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) if (sdev->request_queue)
scsi_free_queue(sdev->request_queue); scsi_free_queue(sdev->request_queue);
scsi_target_reap(scsi_target(sdev));
kfree(sdev->inquiry); kfree(sdev->inquiry);
kfree(sdev); kfree(sdev);
...@@ -560,14 +552,6 @@ static int attr_add(struct device *dev, struct device_attribute *attr) ...@@ -560,14 +552,6 @@ static int attr_add(struct device *dev, struct device_attribute *attr)
return device_create_file(dev, 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 * scsi_sysfs_add_sdev - add scsi device to sysfs
* @sdev: scsi_device to add * @sdev: scsi_device to add
...@@ -577,25 +561,16 @@ static void scsi_target_dev_release(struct device *dev) ...@@ -577,25 +561,16 @@ static void scsi_target_dev_release(struct device *dev)
**/ **/
int scsi_sysfs_add_sdev(struct scsi_device *sdev) int scsi_sysfs_add_sdev(struct scsi_device *sdev)
{ {
struct scsi_target *starget = sdev->sdev_target;
struct Scsi_Host *shost = sdev->host; struct Scsi_Host *shost = sdev->host;
int error, i, create; struct scsi_target *starget = scsi_target(sdev);
int error, i;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(shost->host_lock, flags); spin_lock_irqsave(shost->host_lock, flags);
create = starget->create; list_add_tail(&sdev->same_target_siblings, &starget->devices);
starget->create = 0; list_add_tail(&sdev->siblings, &shost->__devices);
spin_unlock_irqrestore(shost->host_lock, flags); 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) if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0)
return error; return error;
...@@ -643,7 +618,6 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) ...@@ -643,7 +618,6 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev)
out: out:
return error; return error;
class_device_del(&sdev->sdev_classdev);
clean_device: clean_device:
scsi_device_set_state(sdev, SDEV_CANCEL); scsi_device_set_state(sdev, SDEV_CANCEL);
...@@ -673,12 +647,40 @@ void scsi_remove_device(struct scsi_device *sdev) ...@@ -673,12 +647,40 @@ void scsi_remove_device(struct scsi_device *sdev)
sdev->host->hostt->slave_destroy(sdev); sdev->host->hostt->slave_destroy(sdev);
transport_unregister_device(&sdev->sdev_gendev); transport_unregister_device(&sdev->sdev_gendev);
put_device(&sdev->sdev_gendev); put_device(&sdev->sdev_gendev);
out: out:
up(&shost->scan_mutex); up(&shost->scan_mutex);
} }
EXPORT_SYMBOL(scsi_remove_device); 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) int scsi_register_driver(struct device_driver *drv)
{ {
drv->bus = &scsi_bus_type; drv->bus = &scsi_bus_type;
...@@ -780,7 +782,7 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev) ...@@ -780,7 +782,7 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev)
snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE, snprintf(sdev->sdev_classdev.class_id, BUS_ID_SIZE,
"%d:%d:%d:%d", sdev->host->host_no, "%d:%d:%d:%d", sdev->host->host_no,
sdev->channel, sdev->id, sdev->lun); sdev->channel, sdev->id, sdev->lun);
sdev->scsi_level = SCSI_2;
transport_setup_device(&sdev->sdev_gendev); transport_setup_device(&sdev->sdev_gendev);
} }
...@@ -790,73 +792,6 @@ int scsi_is_sdev_device(const struct device *dev) ...@@ -790,73 +792,6 @@ int scsi_is_sdev_device(const struct device *dev)
} }
EXPORT_SYMBOL(scsi_is_sdev_device); 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 /* A blank transport template that is used in drivers that don't
* yet implement Transport Attributes */ * yet implement Transport Attributes */
struct scsi_transport_template blank_transport_template = { NULL, }; struct scsi_transport_template blank_transport_template = { NULL, };
...@@ -144,7 +144,10 @@ struct scsi_device { ...@@ -144,7 +144,10 @@ struct scsi_device {
*/ */
struct scsi_target { struct scsi_target {
struct scsi_device *starget_sdev_user; struct scsi_device *starget_sdev_user;
struct list_head siblings;
struct list_head devices;
struct device dev; struct device dev;
unsigned int reap_ref; /* protected by the host lock */
unsigned int channel; unsigned int channel;
unsigned int id; /* target id ... replace unsigned int id; /* target id ... replace
* scsi_device.id eventually */ * scsi_device.id eventually */
...@@ -173,6 +176,10 @@ extern struct scsi_device *scsi_device_lookup(struct Scsi_Host *, ...@@ -173,6 +176,10 @@ extern struct scsi_device *scsi_device_lookup(struct Scsi_Host *,
uint, uint, uint); uint, uint, uint);
extern struct scsi_device *__scsi_device_lookup(struct Scsi_Host *, extern struct scsi_device *__scsi_device_lookup(struct Scsi_Host *,
uint, uint, uint); 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 *, extern void starget_for_each_device(struct scsi_target *, void *,
void (*fn)(struct scsi_device *, void *)); void (*fn)(struct scsi_device *, void *));
...@@ -226,6 +233,10 @@ extern int scsi_device_quiesce(struct scsi_device *sdev); ...@@ -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_device_resume(struct scsi_device *sdev);
extern void scsi_target_quiesce(struct scsi_target *); extern void scsi_target_quiesce(struct scsi_target *);
extern void scsi_target_resume(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 const char *scsi_device_state_name(enum scsi_device_state);
extern int scsi_is_sdev_device(const struct device *); extern int scsi_is_sdev_device(const struct device *);
extern int scsi_is_target_device(const struct device *); extern int scsi_is_target_device(const struct device *);
......
...@@ -416,6 +416,7 @@ struct Scsi_Host { ...@@ -416,6 +416,7 @@ struct Scsi_Host {
* access this list directly from a driver. * access this list directly from a driver.
*/ */
struct list_head __devices; struct list_head __devices;
struct list_head __targets;
struct scsi_host_cmd_pool *cmd_pool; struct scsi_host_cmd_pool *cmd_pool;
spinlock_t free_list_lock; spinlock_t free_list_lock;
...@@ -553,11 +554,21 @@ struct Scsi_Host { ...@@ -553,11 +554,21 @@ struct Scsi_Host {
unsigned long hostdata[0] /* Used for storage of host specific stuff */ unsigned long hostdata[0] /* Used for storage of host specific stuff */
__attribute__ ((aligned (sizeof(unsigned long)))); __attribute__ ((aligned (sizeof(unsigned long))));
}; };
#define dev_to_shost(d) \
container_of(d, struct Scsi_Host, shost_gendev)
#define class_to_shost(d) \ #define class_to_shost(d) \
container_of(d, struct Scsi_Host, shost_classdev) 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 struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *, int);
extern int __must_check scsi_add_host(struct Scsi_Host *, struct device *); extern int __must_check scsi_add_host(struct Scsi_Host *, struct device *);
...@@ -601,8 +612,6 @@ struct class_container; ...@@ -601,8 +612,6 @@ struct class_container;
*/ */
extern void scsi_free_host_dev(struct scsi_device *); extern void scsi_free_host_dev(struct scsi_device *);
extern struct scsi_device *scsi_get_host_dev(struct Scsi_Host *); extern struct scsi_device *scsi_get_host_dev(struct Scsi_Host *);
int scsi_is_host_device(const struct device *);
/* legacy interfaces */ /* legacy interfaces */
extern struct Scsi_Host *scsi_register(struct scsi_host_template *, int); 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