* Created to pull SCSI mid layer sysfs routines into one file.
*/
-#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/blkdev.h>
#include <scsi/scsi.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
+#include <scsi/scsi_tcq.h>
#include <scsi/scsi_transport.h>
#include "scsi_priv.h"
#include "scsi_logging.h"
-static struct {
+static const struct {
enum scsi_device_state value;
char *name;
} sdev_states[] = {
int i;
char *name = NULL;
- for (i = 0; i < sizeof(sdev_states)/sizeof(sdev_states[0]); i++) {
+ for (i = 0; i < ARRAY_SIZE(sdev_states); i++) {
if (sdev_states[i].value == state) {
name = sdev_states[i].name;
break;
return name;
}
+static const struct {
+ enum scsi_host_state value;
+ char *name;
+} shost_states[] = {
+ { SHOST_CREATED, "created" },
+ { SHOST_RUNNING, "running" },
+ { SHOST_CANCEL, "cancel" },
+ { SHOST_DEL, "deleted" },
+ { SHOST_RECOVERY, "recovery" },
+ { SHOST_CANCEL_RECOVERY, "cancel/recovery" },
+ { SHOST_DEL_RECOVERY, "deleted/recovery", },
+};
+const char *scsi_host_state_name(enum scsi_host_state state)
+{
+ int i;
+ char *name = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(shost_states); i++) {
+ if (shost_states[i].value == state) {
+ name = shost_states[i].name;
+ break;
+ }
+ }
+ return name;
+}
+
static int check_set(unsigned int *val, char *src)
{
char *last;
return -EINVAL;
if (check_set(&lun, s3))
return -EINVAL;
- res = scsi_scan_host_selected(shost, channel, id, lun, 1);
+ if (shost->transportt->user_scan)
+ res = shost->transportt->user_scan(shost, channel, id, lun);
+ else
+ res = scsi_scan_host_selected(shost, channel, id, lun, 1);
return res;
}
};
static CLASS_DEVICE_ATTR(scan, S_IWUSR, NULL, store_scan);
+static ssize_t
+store_shost_state(struct class_device *class_dev, const char *buf, size_t count)
+{
+ int i;
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ enum scsi_host_state state = 0;
+
+ for (i = 0; i < ARRAY_SIZE(shost_states); i++) {
+ const int len = strlen(shost_states[i].name);
+ if (strncmp(shost_states[i].name, buf, len) == 0 &&
+ buf[len] == '\n') {
+ state = shost_states[i].value;
+ break;
+ }
+ }
+ if (!state)
+ return -EINVAL;
+
+ if (scsi_host_set_state(shost, state))
+ return -EINVAL;
+ return count;
+}
+
+static ssize_t
+show_shost_state(struct class_device *class_dev, char *buf)
+{
+ struct Scsi_Host *shost = class_to_shost(class_dev);
+ const char *name = scsi_host_state_name(shost->shost_state);
+
+ if (!name)
+ return -EINVAL;
+
+ return snprintf(buf, 20, "%s\n", name);
+}
+
+static CLASS_DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_shost_state, store_shost_state);
+
shost_rd_attr(unique_id, "%u\n");
shost_rd_attr(host_busy, "%hu\n");
shost_rd_attr(cmd_per_lun, "%hd\n");
+shost_rd_attr(can_queue, "%hd\n");
shost_rd_attr(sg_tablesize, "%hu\n");
shost_rd_attr(unchecked_isa_dma, "%d\n");
shost_rd_attr2(proc_name, hostt->proc_name, "%s\n");
&class_device_attr_unique_id,
&class_device_attr_host_busy,
&class_device_attr_cmd_per_lun,
+ &class_device_attr_can_queue,
&class_device_attr_sg_tablesize,
&class_device_attr_unchecked_isa_dma,
&class_device_attr_proc_name,
&class_device_attr_scan,
+ &class_device_attr_state,
NULL
};
put_device(&sdev->sdev_gendev);
}
-void scsi_device_dev_release(struct device *dev)
+static void scsi_device_dev_release_usercontext(struct work_struct *work)
{
struct scsi_device *sdev;
struct device *parent;
+ struct scsi_target *starget;
unsigned long flags;
- int delete;
- parent = dev->parent;
- sdev = to_scsi_device(dev);
+ sdev = container_of(work, struct scsi_device, ew.work);
+
+ parent = sdev->sdev_gendev.parent;
+ 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);
- struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
- if (!starget->create) {
- if (shost->transportt->target_destroy)
- shost->transportt->target_destroy(starget);
- device_del(parent);
- if (starget->transport_classdev.class)
- class_device_unregister(&starget->transport_classdev);
- }
- put_device(parent);
- }
- if (sdev->request_queue)
+ if (sdev->request_queue) {
+ sdev->request_queue->queuedata = NULL;
+ /* user context needed to free queue */
scsi_free_queue(sdev->request_queue);
+ /* temporary expedient, try to catch use of queue lock
+ * after free of sdev */
+ sdev->request_queue = NULL;
+ }
+
+ scsi_target_reap(scsi_target(sdev));
kfree(sdev->inquiry);
kfree(sdev);
put_device(parent);
}
-struct class sdev_class = {
+static void scsi_device_dev_release(struct device *dev)
+{
+ struct scsi_device *sdp = to_scsi_device(dev);
+ execute_in_process_context(scsi_device_dev_release_usercontext,
+ &sdp->ew);
+}
+
+static struct class sdev_class = {
.name = "scsi_device",
.release = scsi_device_cls_release,
};
static int scsi_bus_match(struct device *dev, struct device_driver *gendrv)
{
struct scsi_device *sdp = to_scsi_device(dev);
+ if (sdp->no_uld_attach)
+ return 0;
return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0;
}
+static int scsi_bus_suspend(struct device * dev, pm_message_t state)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_host_template *sht = sdev->host->hostt;
+ int err;
+
+ err = scsi_device_quiesce(sdev);
+ if (err)
+ return err;
+
+ if (sht->suspend)
+ err = sht->suspend(sdev, state);
+
+ return err;
+}
+
+static int scsi_bus_resume(struct device * dev)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_host_template *sht = sdev->host->hostt;
+ int err = 0;
+
+ if (sht->resume)
+ err = sht->resume(sdev);
+
+ scsi_device_resume(sdev);
+ return err;
+}
+
struct bus_type scsi_bus_type = {
.name = "scsi",
.match = scsi_bus_match,
+ .suspend = scsi_bus_suspend,
+ .resume = scsi_bus_resume,
};
int scsi_sysfs_register(void)
*/
#define sdev_show_function(field, format_string) \
static ssize_t \
-sdev_show_##field (struct device *dev, char *buf) \
+sdev_show_##field (struct device *dev, struct device_attribute *attr, char *buf) \
{ \
struct scsi_device *sdev; \
sdev = to_scsi_device(dev); \
sdev_show_function(field, format_string) \
\
static ssize_t \
-sdev_store_##field (struct device *dev, const char *buf, size_t count) \
+sdev_store_##field (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
{ \
struct scsi_device *sdev; \
sdev = to_scsi_device(dev); \
sdev_show_function(field, "%d\n") \
\
static ssize_t \
-sdev_store_##field (struct device *dev, const char *buf, size_t count) \
+sdev_store_##field (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
{ \
int ret; \
struct scsi_device *sdev; \
sdev_rd_attr (rev, "%.4s\n");
static ssize_t
-sdev_show_timeout (struct device *dev, char *buf)
+sdev_show_timeout (struct device *dev, struct device_attribute *attr, char *buf)
{
struct scsi_device *sdev;
sdev = to_scsi_device(dev);
}
static ssize_t
-sdev_store_timeout (struct device *dev, const char *buf, size_t count)
+sdev_store_timeout (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
struct scsi_device *sdev;
int timeout;
static DEVICE_ATTR(timeout, S_IRUGO | S_IWUSR, sdev_show_timeout, sdev_store_timeout);
static ssize_t
-store_rescan_field (struct device *dev, const char *buf, size_t count)
+store_rescan_field (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
scsi_rescan_device(dev);
return count;
}
static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field);
-static ssize_t sdev_store_delete(struct device *dev, const char *buf,
+static ssize_t sdev_store_delete(struct device *dev, struct device_attribute *attr, const char *buf,
size_t count)
{
scsi_remove_device(to_scsi_device(dev));
static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete);
static ssize_t
-store_state_field(struct device *dev, const char *buf, size_t count)
+store_state_field(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
int i;
struct scsi_device *sdev = to_scsi_device(dev);
enum scsi_device_state state = 0;
- for (i = 0; i < sizeof(sdev_states)/sizeof(sdev_states[0]); i++) {
+ for (i = 0; i < ARRAY_SIZE(sdev_states); i++) {
const int len = strlen(sdev_states[i].name);
if (strncmp(sdev_states[i].name, buf, len) == 0 &&
buf[len] == '\n') {
}
static ssize_t
-show_state_field(struct device *dev, char *buf)
+show_state_field(struct device *dev, struct device_attribute *attr, char *buf)
{
struct scsi_device *sdev = to_scsi_device(dev);
const char *name = scsi_device_state_name(sdev->sdev_state);
return snprintf(buf, 20, "%s\n", name);
}
-DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_state_field, store_state_field);
+static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_state_field, store_state_field);
+
+static ssize_t
+show_queue_type_field(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ const char *name = "none";
+
+ if (sdev->ordered_tags)
+ name = "ordered";
+ else if (sdev->simple_tags)
+ name = "simple";
+
+ return snprintf(buf, 20, "%s\n", name);
+}
+
+static DEVICE_ATTR(queue_type, S_IRUGO, show_queue_type_field, NULL);
+
+static ssize_t
+show_iostat_counterbits(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, 20, "%d\n", (int)sizeof(atomic_t) * 8);
+}
+
+static DEVICE_ATTR(iocounterbits, S_IRUGO, show_iostat_counterbits, NULL);
+
+#define show_sdev_iostat(field) \
+static ssize_t \
+show_iostat_##field(struct device *dev, struct device_attribute *attr, char *buf) \
+{ \
+ struct scsi_device *sdev = to_scsi_device(dev); \
+ unsigned long long count = atomic_read(&sdev->field); \
+ return snprintf(buf, 20, "0x%llx\n", count); \
+} \
+static DEVICE_ATTR(field, S_IRUGO, show_iostat_##field, NULL)
+
+show_sdev_iostat(iorequest_cnt);
+show_sdev_iostat(iodone_cnt);
+show_sdev_iostat(ioerr_cnt);
/* Default template for device attributes. May NOT be modified */
static struct device_attribute *scsi_sysfs_sdev_attrs[] = {
&dev_attr_device_blocked,
&dev_attr_queue_depth,
+ &dev_attr_queue_type,
&dev_attr_type,
&dev_attr_scsi_level,
&dev_attr_vendor,
&dev_attr_delete,
&dev_attr_state,
&dev_attr_timeout,
+ &dev_attr_iocounterbits,
+ &dev_attr_iorequest_cnt,
+ &dev_attr_iodone_cnt,
+ &dev_attr_ioerr_cnt,
NULL
};
+static ssize_t sdev_store_queue_depth_rw(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ int depth, retval;
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_host_template *sht = sdev->host->hostt;
+
+ if (!sht->change_queue_depth)
+ return -EINVAL;
+
+ depth = simple_strtoul(buf, NULL, 0);
+
+ if (depth < 1)
+ return -EINVAL;
+
+ retval = sht->change_queue_depth(sdev, depth);
+ if (retval < 0)
+ return retval;
+
+ return count;
+}
+
+static struct device_attribute sdev_attr_queue_depth_rw =
+ __ATTR(queue_depth, S_IRUGO | S_IWUSR, sdev_show_queue_depth,
+ sdev_store_queue_depth_rw);
+
+static ssize_t sdev_store_queue_type_rw(struct device *dev, struct device_attribute *attr, const char *buf,
+ size_t count)
+{
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct scsi_host_template *sht = sdev->host->hostt;
+ int tag_type = 0, retval;
+ int prev_tag_type = scsi_get_tag_type(sdev);
+
+ if (!sdev->tagged_supported || !sht->change_queue_type)
+ return -EINVAL;
+
+ if (strncmp(buf, "ordered", 7) == 0)
+ tag_type = MSG_ORDERED_TAG;
+ else if (strncmp(buf, "simple", 6) == 0)
+ tag_type = MSG_SIMPLE_TAG;
+ else if (strncmp(buf, "none", 4) != 0)
+ return -EINVAL;
+
+ if (tag_type == prev_tag_type)
+ return count;
+
+ retval = sht->change_queue_type(sdev, tag_type);
+ if (retval < 0)
+ return retval;
+
+ return count;
+}
+
+static struct device_attribute sdev_attr_queue_type_rw =
+ __ATTR(queue_type, S_IRUGO | S_IWUSR, show_queue_type_field,
+ sdev_store_queue_type_rw);
+
+static struct device_attribute *attr_changed_internally(
+ struct Scsi_Host *shost,
+ struct device_attribute * attr)
+{
+ if (!strcmp("queue_depth", attr->attr.name)
+ && shost->hostt->change_queue_depth)
+ return &sdev_attr_queue_depth_rw;
+ else if (!strcmp("queue_type", attr->attr.name)
+ && shost->hostt->change_queue_type)
+ return &sdev_attr_queue_type_rw;
+ return attr;
+}
+
static struct device_attribute *attr_overridden(
struct device_attribute **attrs,
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
**/
int scsi_sysfs_add_sdev(struct scsi_device *sdev)
{
- struct class_device_attribute **attrs;
- struct scsi_target *starget = sdev->sdev_target;
- struct Scsi_Host *shost = sdev->host;
- int error, i, create;
- unsigned long flags;
-
- spin_lock_irqsave(shost->host_lock, flags);
- create = starget->create;
- starget->create = 0;
- 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;
- }
- if (starget->transport_classdev.class) {
- int i;
- struct class_device_attribute **attrs =
- sdev->host->transportt->target_attrs;
-
- error = class_device_add(&starget->transport_classdev);
- if (error) {
- dev_printk(KERN_ERR, &starget->dev,
- "Target transport add failed\n");
- return error;
- }
-
- /* take a reference for the transport_classdev; this
- * is released by the transport_class .release */
- get_device(&starget->dev);
- for (i = 0; attrs[i]; i++) {
- error = class_device_create_file(&starget->transport_classdev,
- attrs[i]);
- if (error) {
- dev_printk(KERN_ERR, &starget->dev,
- "Target transport attr add failed\n");
- return error;
- }
- }
- }
- }
+ int error, i;
if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0)
return error;
printk(KERN_INFO "error 1\n");
return error;
}
-
error = class_device_add(&sdev->sdev_classdev);
if (error) {
printk(KERN_INFO "error 2\n");
goto clean_device;
}
+
/* take a reference for the sdev_classdev; this is
* released by the sdev_class .release */
get_device(&sdev->sdev_gendev);
- if (sdev->transport_classdev.class) {
- error = class_device_add(&sdev->transport_classdev);
- if (error)
- goto clean_device2;
- /* take a reference for the transport_classdev; this
- * is released by the transport_class .release */
- get_device(&sdev->sdev_gendev);
-
- }
-
if (sdev->host->hostt->sdev_attrs) {
for (i = 0; sdev->host->hostt->sdev_attrs[i]; i++) {
error = attr_add(&sdev->sdev_gendev,
sdev->host->hostt->sdev_attrs[i]);
if (error) {
- scsi_remove_device(sdev);
+ __scsi_remove_device(sdev);
goto out;
}
}
for (i = 0; scsi_sysfs_sdev_attrs[i]; i++) {
if (!attr_overridden(sdev->host->hostt->sdev_attrs,
scsi_sysfs_sdev_attrs[i])) {
- error = device_create_file(&sdev->sdev_gendev,
- scsi_sysfs_sdev_attrs[i]);
+ struct device_attribute * attr =
+ attr_changed_internally(sdev->host,
+ scsi_sysfs_sdev_attrs[i]);
+ error = device_create_file(&sdev->sdev_gendev, attr);
if (error) {
- scsi_remove_device(sdev);
+ __scsi_remove_device(sdev);
goto out;
}
}
}
- if (sdev->transport_classdev.class) {
- attrs = sdev->host->transportt->device_attrs;
- for (i = 0; attrs[i]; i++) {
- error = class_device_create_file(&sdev->transport_classdev,
- attrs[i]);
- if (error) {
- scsi_remove_device(sdev);
- goto out;
- }
- }
- }
-
+ transport_add_device(&sdev->sdev_gendev);
out:
return error;
- clean_device2:
- class_device_del(&sdev->sdev_classdev);
clean_device:
scsi_device_set_state(sdev, SDEV_CANCEL);
device_del(&sdev->sdev_gendev);
+ transport_destroy_device(&sdev->sdev_gendev);
put_device(&sdev->sdev_gendev);
return error;
}
+void __scsi_remove_device(struct scsi_device *sdev)
+{
+ struct device *dev = &sdev->sdev_gendev;
+
+ if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0)
+ return;
+
+ class_device_unregister(&sdev->sdev_classdev);
+ transport_remove_device(dev);
+ device_del(dev);
+ scsi_device_set_state(sdev, SDEV_DEL);
+ if (sdev->host->hostt->slave_destroy)
+ sdev->host->hostt->slave_destroy(sdev);
+ transport_destroy_device(dev);
+ put_device(dev);
+}
+
/**
* scsi_remove_device - unregister a device from the scsi bus
* @sdev: scsi_device to unregister
{
struct Scsi_Host *shost = sdev->host;
- down(&shost->scan_mutex);
- if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0)
- goto out;
+ mutex_lock(&shost->scan_mutex);
+ __scsi_remove_device(sdev);
+ mutex_unlock(&shost->scan_mutex);
+}
+EXPORT_SYMBOL(scsi_remove_device);
- class_device_unregister(&sdev->sdev_classdev);
- if (sdev->transport_classdev.class)
- class_device_unregister(&sdev->transport_classdev);
- device_del(&sdev->sdev_gendev);
- scsi_device_set_state(sdev, SDEV_DEL);
- if (sdev->host->hostt->slave_destroy)
- sdev->host->hostt->slave_destroy(sdev);
- if (sdev->host->transportt->device_destroy)
- sdev->host->transportt->device_destroy(sdev);
- put_device(&sdev->sdev_gendev);
+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;
+
+ spin_lock_irqsave(shost->host_lock, flags);
+ starget->reap_ref++;
+ restart:
+ list_for_each_entry(sdev, &shost->__devices, siblings) {
+ if (sdev->channel != starget->channel ||
+ sdev->id != starget->id ||
+ sdev->sdev_state == SDEV_DEL)
+ continue;
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ scsi_remove_device(sdev);
+ spin_lock_irqsave(shost->host_lock, flags);
+ goto restart;
+ }
+ spin_unlock_irqrestore(shost->host_lock, flags);
+ scsi_target_reap(starget);
+}
+
+static int __remove_child (struct device * dev, void * data)
+{
+ if (scsi_is_target_device(dev))
+ __scsi_remove_target(to_scsi_target(dev));
+ return 0;
+}
-out:
- up(&shost->scan_mutex);
+/**
+ * scsi_remove_target - try to remove a target and all its devices
+ * @dev: generic starget or parent of generic stargets to be removed
+ *
+ * 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 device *dev)
+{
+ struct device *rdev;
+
+ if (scsi_is_target_device(dev)) {
+ __scsi_remove_target(to_scsi_target(dev));
+ return;
+ }
+
+ rdev = get_device(dev);
+ device_for_each_child(dev, NULL, __remove_child);
+ put_device(rdev);
}
+EXPORT_SYMBOL(scsi_remove_target);
int scsi_register_driver(struct device_driver *drv)
{
return driver_register(drv);
}
+EXPORT_SYMBOL(scsi_register_driver);
int scsi_register_interface(struct class_interface *intf)
{
return class_interface_register(intf);
}
+EXPORT_SYMBOL(scsi_register_interface);
static struct class_device_attribute *class_attr_overridden(
}
}
- class_device_initialize(&shost->transport_classdev);
- shost->transport_classdev.class = shost->transportt->host_class;
- shost->transport_classdev.dev = &shost->shost_gendev;
- snprintf(shost->transport_classdev.class_id, BUS_ID_SIZE,
- "host%d", shost->host_no);
-
- if (shost->transport_classdev.class) {
- struct class_device_attribute **attrs =
- shost->transportt->host_attrs;
- error = class_device_add(&shost->transport_classdev);
- if (error)
- return error;
- /* take a reference for the transport_classdev; this
- * is released by the transport_class .release */
- get_device(&shost->shost_gendev);
- for (i = 0; attrs[i]; i++) {
- error = class_device_create_file(&shost->transport_classdev,
- attrs[i]);
- if (error)
- return error;
- }
- }
-
+ transport_register_device(&shost->shost_gendev);
return 0;
}
-int scsi_sysfs_device_initialize(struct scsi_device *sdev)
+void scsi_sysfs_device_initialize(struct scsi_device *sdev)
{
+ unsigned long flags;
+ struct Scsi_Host *shost = sdev->host;
+ struct scsi_target *starget = sdev->sdev_target;
+
device_initialize(&sdev->sdev_gendev);
sdev->sdev_gendev.bus = &scsi_bus_type;
sdev->sdev_gendev.release = scsi_device_dev_release;
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->device_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_Host *shost = sdev->host;
- struct scsi_device *device;
- struct device *dev = NULL;
- unsigned long flags;
- int create = 0;
-
+ sdev->scsi_level = SCSI_2;
+ transport_setup_device(&sdev->sdev_gendev);
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);
- class_device_initialize(&starget->transport_classdev);
- starget->transport_classdev.dev = &starget->dev;
- starget->transport_classdev.class = shost->transportt->target_class;
- snprintf(starget->transport_classdev.class_id, BUS_ID_SIZE,
- "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->same_target_siblings, &starget->devices);
list_add_tail(&sdev->siblings, &shost->__devices);
spin_unlock_irqrestore(shost->host_lock, flags);
- if (create && shost->transportt->target_setup)
- shost->transportt->target_setup(starget);
- return 0;
}
+int scsi_is_sdev_device(const struct device *dev)
+{
+ return dev->release == scsi_device_dev_release;
+}
+EXPORT_SYMBOL(scsi_is_sdev_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, };
+struct scsi_transport_template blank_transport_template = { { { {NULL, }, }, }, };