#include "scsi_priv.h"
#include "scsi_logging.h"
-static struct {
+static const struct {
enum scsi_device_state value;
char *name;
} sdev_states[] = {
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 < sizeof(shost_states)/sizeof(shost_states[0]); 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 < sizeof(shost_states)/sizeof(shost_states[0]); 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");
&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(void *data)
{
+ struct device *dev = data;
struct scsi_device *sdev;
struct device *parent;
struct scsi_target *starget;
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 */
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, dev,
+ &sdp->ew);
+}
+
+static struct class sdev_class = {
.name = "scsi_device",
.release = scsi_device_cls_release,
};
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);
}
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);
static DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_state_field, store_state_field);
static ssize_t
-show_queue_type_field(struct device *dev, char *buf)
+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";
static DEVICE_ATTR(queue_type, S_IRUGO, show_queue_type_field, NULL);
static ssize_t
-show_iostat_counterbits(struct device *dev, char *buf)
+show_iostat_counterbits(struct device *dev, struct device_attribute *attr, char *buf)
{
return snprintf(buf, 20, "%d\n", (int)sizeof(atomic_t) * 8);
}
#define show_sdev_iostat(field) \
static ssize_t \
-show_iostat_##field(struct device *dev, char *buf) \
+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); \
NULL
};
-static ssize_t sdev_store_queue_depth_rw(struct device *dev, const char *buf,
+static ssize_t sdev_store_queue_depth_rw(struct device *dev, struct device_attribute *attr, const char *buf,
size_t count)
{
int depth, retval;
__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, const char *buf,
+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);
error = attr_add(&sdev->sdev_gendev,
sdev->host->hostt->sdev_attrs[i]);
if (error) {
- scsi_remove_device(sdev);
+ __scsi_remove_device(sdev);
goto out;
}
}
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;
}
}
return error;
}
-/**
- * scsi_remove_device - unregister a device from the scsi bus
- * @sdev: scsi_device to unregister
- **/
-void scsi_remove_device(struct scsi_device *sdev)
+void __scsi_remove_device(struct scsi_device *sdev)
{
- struct Scsi_Host *shost = sdev->host;
+ struct device *dev = &sdev->sdev_gendev;
- down(&shost->scan_mutex);
if (scsi_device_set_state(sdev, SDEV_CANCEL) != 0)
- goto out;
+ return;
class_device_unregister(&sdev->sdev_classdev);
- device_del(&sdev->sdev_gendev);
+ 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_unregister_device(&sdev->sdev_gendev);
- put_device(&sdev->sdev_gendev);
-out:
- up(&shost->scan_mutex);
+ transport_destroy_device(dev);
+ put_device(dev);
+}
+
+/**
+ * scsi_remove_device - unregister a device from the scsi bus
+ * @sdev: scsi_device to unregister
+ **/
+void scsi_remove_device(struct scsi_device *sdev)
+{
+ struct Scsi_Host *shost = sdev->host;
+
+ mutex_lock(&shost->scan_mutex);
+ __scsi_remove_device(sdev);
+ mutex_unlock(&shost->scan_mutex);
}
EXPORT_SYMBOL(scsi_remove_device);
{
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
unsigned long flags;
- struct scsi_device *sdev, *tmp;
+ struct scsi_device *sdev;
spin_lock_irqsave(shost->host_lock, flags);
starget->reap_ref++;
- list_for_each_entry_safe(sdev, tmp, &shost->__devices, siblings) {
+ restart:
+ list_for_each_entry(sdev, &shost->__devices, siblings) {
if (sdev->channel != starget->channel ||
- sdev->id != starget->id)
+ 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;
+}
+
/**
* scsi_remove_target - try to remove a target and all its devices
* @dev: generic starget or parent of generic stargets to be removed
*/
void scsi_remove_target(struct device *dev)
{
- struct device *rdev, *idev, *next;
+ struct device *rdev;
if (scsi_is_target_device(dev)) {
__scsi_remove_target(to_scsi_target(dev));
}
rdev = get_device(dev);
- list_for_each_entry_safe(idev, next, &dev->children, node) {
- if (scsi_is_target_device(idev))
- __scsi_remove_target(to_scsi_target(idev));
- }
+ device_for_each_child(dev, NULL, __remove_child);
put_device(rdev);
}
EXPORT_SYMBOL(scsi_remove_target);