#include <scsi/scsi_host.h>
#include <scsi/scsi_request.h>
#include <scsi/scsi_transport.h>
+#include <scsi/scsi_eh.h>
#include "scsi_priv.h"
#include "scsi_logging.h"
static struct scsi_device *scsi_alloc_sdev(struct Scsi_Host *shost,
uint channel, uint id, uint lun, void *hostdata)
{
- struct scsi_device *sdev, *device;
+ struct scsi_device *sdev;
unsigned long flags;
+ int display_failure_msg = 1, ret;
- sdev = kmalloc(sizeof(*sdev) + shost->transportt->size, GFP_ATOMIC);
+ sdev = kmalloc(sizeof(*sdev) + shost->transportt->device_size,
+ GFP_ATOMIC);
if (!sdev)
goto out;
sdev->request_queue->queuedata = sdev;
scsi_adjust_queue_depth(sdev, 0, sdev->host->cmd_per_lun);
+ scsi_sysfs_device_initialize(sdev);
+
if (shost->hostt->slave_alloc) {
- if (shost->hostt->slave_alloc(sdev))
- goto out_free_queue;
+ ret = shost->hostt->slave_alloc(sdev);
+ if (ret) {
+ /*
+ * if LLDD reports slave not present, don't clutter
+ * console with alloc failure messages
+ */
+ if (ret == -ENXIO)
+ display_failure_msg = 0;
+ goto out_device_destroy;
+ }
}
- if (shost->transportt->setup) {
- if (shost->transportt->setup(sdev))
- goto out_cleanup_slave;
- }
+ /* NOTE: this target initialisation code depends critically on
+ * lun scanning being sequential. */
+ if (scsi_sysfs_target_initialize(sdev))
+ goto out_remove_siblings;
- 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
- goto out_cleanup_transport;
+ return sdev;
- /*
- * If there are any same target siblings, add this to the
- * sibling list
- */
+out_remove_siblings:
spin_lock_irqsave(shost->host_lock, flags);
- 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;
- break;
- }
- }
-
- /*
- * If there wasn't another lun already configured at this
- * target, then default this device to SCSI_2 until we
- * know better
- */
- if (!sdev->scsi_level)
- sdev->scsi_level = SCSI_2;
-
- list_add_tail(&sdev->siblings, &shost->__devices);
+ list_del(&sdev->siblings);
+ list_del(&sdev->same_target_siblings);
spin_unlock_irqrestore(shost->host_lock, flags);
- return sdev;
-out_cleanup_transport:
- if (shost->transportt->cleanup)
- shost->transportt->cleanup(sdev);
-out_cleanup_slave:
if (shost->hostt->slave_destroy)
shost->hostt->slave_destroy(sdev);
-out_free_queue:
+out_device_destroy:
+ transport_destroy_device(&sdev->sdev_gendev);
scsi_free_queue(sdev->request_queue);
out_free_dev:
kfree(sdev);
out:
- printk(ALLOC_FAILURE_MSG, __FUNCTION__);
+ if (display_failure_msg)
+ printk(ALLOC_FAILURE_MSG, __FUNCTION__);
return NULL;
}
{
struct scsi_device *sdev = sreq->sr_device; /* a bit ugly */
unsigned char scsi_cmd[MAX_COMMAND_SIZE];
- int possible_inq_resp_len;
- int count = 0;
+ int first_inquiry_len, try_inquiry_len, next_inquiry_len;
+ int response_len = 0;
+ int pass, count;
+ struct scsi_sense_hdr sshdr;
*bflags = 0;
- repeat_inquiry:
- SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY to host %d"
- " channel %d id %d lun %d\n", sdev->host->host_no,
- sdev->channel, sdev->id, sdev->lun));
-
- memset(scsi_cmd, 0, 6);
- scsi_cmd[0] = INQUIRY;
- scsi_cmd[4] = 36; /* issue conservative alloc_length */
- sreq->sr_cmd_len = 0;
- sreq->sr_data_direction = DMA_FROM_DEVICE;
- memset(inq_result, 0, 36);
- scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result, 36,
- HZ/2 + HZ*scsi_inq_timeout, 3);
+ /* Perform up to 3 passes. The first pass uses a conservative
+ * transfer length of 36 unless sdev->inquiry_len specifies a
+ * different value. */
+ first_inquiry_len = sdev->inquiry_len ? sdev->inquiry_len : 36;
+ try_inquiry_len = first_inquiry_len;
+ pass = 1;
+
+ next_pass:
+ SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY pass %d "
+ "to host %d channel %d id %d lun %d, length %d\n",
+ pass, sdev->host->host_no, sdev->channel,
+ sdev->id, sdev->lun, try_inquiry_len));
+
+ /* Each pass gets up to three chances to ignore Unit Attention */
+ for (count = 0; count < 3; ++count) {
+ memset(scsi_cmd, 0, 6);
+ scsi_cmd[0] = INQUIRY;
+ scsi_cmd[4] = (unsigned char) try_inquiry_len;
+ sreq->sr_cmd_len = 0;
+ sreq->sr_data_direction = DMA_FROM_DEVICE;
- SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 1st INQUIRY %s with"
- " code 0x%x\n", sreq->sr_result ?
- "failed" : "successful", sreq->sr_result));
- ++count;
+ memset(inq_result, 0, try_inquiry_len);
+ scsi_wait_req(sreq, (void *) scsi_cmd, (void *) inq_result,
+ try_inquiry_len,
+ HZ/2 + HZ*scsi_inq_timeout, 3);
- if (sreq->sr_result) {
- if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) != 0 &&
- (sreq->sr_sense_buffer[2] & 0xf) == UNIT_ATTENTION &&
- (sreq->sr_sense_buffer[12] == 0x28 ||
- sreq->sr_sense_buffer[12] == 0x29) &&
- sreq->sr_sense_buffer[13] == 0) {
- /* not-ready to ready transition or power-on - good */
- /* dpg: bogus? INQUIRY never returns UNIT_ATTENTION */
- /* Supposedly, but many buggy devices do so anyway */
- if (count < 3)
- goto repeat_inquiry;
+ SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: INQUIRY %s "
+ "with code 0x%x\n",
+ sreq->sr_result ? "failed" : "successful",
+ sreq->sr_result));
+
+ if (sreq->sr_result) {
+ /*
+ * not-ready to ready transition [asc/ascq=0x28/0x0]
+ * or power-on, reset [asc/ascq=0x29/0x0], continue.
+ * INQUIRY should not yield UNIT_ATTENTION
+ * but many buggy devices do so anyway.
+ */
+ if ((driver_byte(sreq->sr_result) & DRIVER_SENSE) &&
+ scsi_request_normalize_sense(sreq, &sshdr)) {
+ if ((sshdr.sense_key == UNIT_ATTENTION) &&
+ ((sshdr.asc == 0x28) ||
+ (sshdr.asc == 0x29)) &&
+ (sshdr.ascq == 0))
+ continue;
+ }
}
- /*
- * assume no peripheral if any other sort of error
- */
- return;
+ break;
}
- /*
- * Get any flags for this device.
- *
- * XXX add a bflags to Scsi_Device, and replace the corresponding
- * bit fields in Scsi_Device, so bflags need not be passed as an
- * argument.
- */
- *bflags |= scsi_get_device_flags(sdev, &inq_result[8], &inq_result[16]);
+ if (sreq->sr_result == 0) {
+ response_len = (unsigned char) inq_result[4] + 5;
+ if (response_len > 255)
+ response_len = first_inquiry_len; /* sanity */
- possible_inq_resp_len = (unsigned char) inq_result[4] + 5;
- if (BLIST_INQUIRY_36 & *bflags)
- possible_inq_resp_len = 36;
- else if (BLIST_INQUIRY_58 & *bflags)
- possible_inq_resp_len = 58;
- else if (possible_inq_resp_len > 255)
- possible_inq_resp_len = 36; /* sanity */
-
- if (possible_inq_resp_len > 36) { /* do additional INQUIRY */
- memset(scsi_cmd, 0, 6);
- scsi_cmd[0] = INQUIRY;
- scsi_cmd[4] = (unsigned char) possible_inq_resp_len;
- sreq->sr_cmd_len = 0;
- sreq->sr_data_direction = DMA_FROM_DEVICE;
/*
- * re-zero inq_result just to be safe.
+ * Get any flags for this device.
+ *
+ * XXX add a bflags to Scsi_Device, and replace the
+ * corresponding bit fields in Scsi_Device, so bflags
+ * need not be passed as an argument.
*/
- memset(inq_result, 0, possible_inq_resp_len);
- scsi_wait_req(sreq, (void *) scsi_cmd,
- (void *) inq_result,
- possible_inq_resp_len, (1+scsi_inq_timeout)*(HZ/2), 3);
- SCSI_LOG_SCAN_BUS(3, printk(KERN_INFO "scsi scan: 2nd INQUIRY"
- " %s with code 0x%x\n", sreq->sr_result ?
- "failed" : "successful", sreq->sr_result));
- if (sreq->sr_result) {
- /* if the longer inquiry has failed, flag the device
- * as only accepting 36 byte inquiries and retry the
- * 36 byte inquiry */
- printk(KERN_INFO "scsi scan: %d byte inquiry failed"
- " with code %d. Consider BLIST_INQUIRY_36 for"
- " this device\n", possible_inq_resp_len,
- sreq->sr_result);
- *bflags = BLIST_INQUIRY_36;
- goto repeat_inquiry;
+ *bflags = scsi_get_device_flags(sdev, &inq_result[8],
+ &inq_result[16]);
+
+ /* When the first pass succeeds we gain information about
+ * what larger transfer lengths might work. */
+ if (pass == 1) {
+ if (BLIST_INQUIRY_36 & *bflags)
+ next_inquiry_len = 36;
+ else if (BLIST_INQUIRY_58 & *bflags)
+ next_inquiry_len = 58;
+ else if (sdev->inquiry_len)
+ next_inquiry_len = sdev->inquiry_len;
+ else
+ next_inquiry_len = response_len;
+
+ /* If more data is available perform the second pass */
+ if (next_inquiry_len > try_inquiry_len) {
+ try_inquiry_len = next_inquiry_len;
+ pass = 2;
+ goto next_pass;
+ }
}
- /*
- * The INQUIRY can change, this means the length can change.
- */
- possible_inq_resp_len = (unsigned char) inq_result[4] + 5;
- if (BLIST_INQUIRY_58 & *bflags)
- possible_inq_resp_len = 58;
- else if (possible_inq_resp_len > 255)
- possible_inq_resp_len = 36; /* sanity */
+ } else if (pass == 2) {
+ printk(KERN_INFO "scsi scan: %d byte inquiry failed. "
+ "Consider BLIST_INQUIRY_36 for this device\n",
+ try_inquiry_len);
+
+ /* If this pass failed, the third pass goes back and transfers
+ * the same amount as we successfully got in the first pass. */
+ try_inquiry_len = first_inquiry_len;
+ pass = 3;
+ goto next_pass;
}
- sdev->inquiry_len = possible_inq_resp_len;
+ /* If the last transfer attempt got an error, assume the
+ * peripheral doesn't exist or is dead. */
+ if (sreq->sr_result)
+ return;
+
+ /* Don't report any more data than the device says is valid */
+ sdev->inquiry_len = min(try_inquiry_len, response_len);
/*
* XXX Abort if the response length is less than 36? If less than
**/
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.
*/
inq_result[0] = TYPE_ROM;
inq_result[1] |= 0x80; /* removable */
- }
+ } else if (*bflags & BLIST_NO_ULD_ATTACH)
+ sdev->no_uld_attach = 1;
switch (sdev->type = (inq_result[0] & 0x1f)) {
case TYPE_TAPE:
if ((*bflags & BLIST_BORKEN) == 0)
sdev->borken = 0;
+ /*
+ * Apparently some really broken devices (contrary to the SCSI
+ * standards) need to be selected without asserting ATN
+ */
+ if (*bflags & BLIST_SELECT_NO_ATN)
+ sdev->select_no_atn = 1;
+
/*
* Some devices may not want to have a start command automatically
* issued when a device is added.
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) {
+ 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;
if (*bflags & BLIST_NOT_LOCKABLE)
sdev->lockable = 0;
- if(sdev->host->hostt->slave_configure)
+ transport_configure_device(&sdev->sdev_gendev);
+
+ if (sdev->host->hostt->slave_configure)
sdev->host->hostt->slave_configure(sdev);
/*
} else {
if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev);
- if (sdev->host->transportt->cleanup)
- sdev->host->transportt->cleanup(sdev);
+ transport_destroy_device(&sdev->sdev_gendev);
put_device(&sdev->sdev_gendev);
}
out:
struct scsi_lun *lunp, *lun_data;
struct scsi_request *sreq;
u8 *data;
+ struct scsi_sense_hdr sshdr;
/*
* Only support SCSI-3 and up devices if BLIST_NOREPORTLUN is not set.
" %s (try %d) result 0x%x\n", sreq->sr_result
? "failed" : "successful", retries,
sreq->sr_result));
- if (sreq->sr_result == 0 ||
- sreq->sr_sense_buffer[2] != UNIT_ATTENTION)
+ if (sreq->sr_result == 0)
break;
+ else if (scsi_request_normalize_sense(sreq, &sshdr)) {
+ if (sshdr.sense_key != UNIT_ATTENTION)
+ break;
+ }
}
if (sreq->sr_result) {
return sdev;
}
+EXPORT_SYMBOL(__scsi_add_device);
void scsi_rescan_device(struct device *dev)
{
module_put(drv->owner);
}
}
+EXPORT_SYMBOL(scsi_rescan_device);
/**
* scsi_scan_target - scan a target id, possibly including all LUNs on the
scsi_scan_host_selected(shost, SCAN_WILD_CARD, SCAN_WILD_CARD,
SCAN_WILD_CARD, 0);
}
+EXPORT_SYMBOL(scsi_scan_host);
+
+/**
+ * scsi_scan_single_target - scan the given SCSI target
+ * @shost: adapter to scan
+ * @chan: channel to scan
+ * @id: target id to scan
+ **/
+void scsi_scan_single_target(struct Scsi_Host *shost,
+ unsigned int chan, unsigned int id)
+{
+ scsi_scan_host_selected(shost, chan, id, SCAN_WILD_CARD, 1);
+}
+EXPORT_SYMBOL(scsi_scan_single_target);
void scsi_forget_host(struct Scsi_Host *shost)
{
}
return sdev;
}
+EXPORT_SYMBOL(scsi_get_host_dev);
/*
* Function: scsi_free_host_dev()
if (sdev->host->hostt->slave_destroy)
sdev->host->hostt->slave_destroy(sdev);
- if (sdev->host->transportt->cleanup)
- sdev->host->transportt->cleanup(sdev);
+ transport_destroy_device(&sdev->sdev_gendev);
put_device(&sdev->sdev_gendev);
}
+EXPORT_SYMBOL(scsi_free_host_dev);
+