X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=drivers%2Fscsi%2Fscsi_sysfs.c;h=ed83cdb6e67dfbdb5c829238c91ed861b124a426;hb=2419505acc479d2f1feed94d195b0554a64269a4;hp=34cdce6738a6c3fe4fab28a739b7ea926788babd;hpb=39dca558a5b52b63e49bc234a7e887be092aa690;p=~andy%2Flinux diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 34cdce6738a..ed83cdb6e67 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -190,6 +190,48 @@ show_shost_state(struct class_device *class_dev, char *buf) static CLASS_DEVICE_ATTR(state, S_IRUGO | S_IWUSR, show_shost_state, store_shost_state); +static ssize_t +show_shost_mode(unsigned int mode, char *buf) +{ + ssize_t len = 0; + + if (mode & MODE_INITIATOR) + len = sprintf(buf, "%s", "Initiator"); + + if (mode & MODE_TARGET) + len += sprintf(buf + len, "%s%s", len ? ", " : "", "Target"); + + len += sprintf(buf + len, "\n"); + + return len; +} + +static ssize_t show_shost_supported_mode(struct class_device *class_dev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + unsigned int supported_mode = shost->hostt->supported_mode; + + if (supported_mode == MODE_UNKNOWN) + /* by default this should be initiator */ + supported_mode = MODE_INITIATOR; + + return show_shost_mode(supported_mode, buf); +} + +static CLASS_DEVICE_ATTR(supported_mode, S_IRUGO | S_IWUSR, show_shost_supported_mode, NULL); + +static ssize_t show_shost_active_mode(struct class_device *class_dev, char *buf) +{ + struct Scsi_Host *shost = class_to_shost(class_dev); + + if (shost->active_mode == MODE_UNKNOWN) + return snprintf(buf, 20, "unknown\n"); + else + return show_shost_mode(shost->active_mode, buf); +} + +static CLASS_DEVICE_ATTR(active_mode, S_IRUGO | S_IWUSR, show_shost_active_mode, NULL); + shost_rd_attr(unique_id, "%u\n"); shost_rd_attr(host_busy, "%hu\n"); shost_rd_attr(cmd_per_lun, "%hd\n"); @@ -208,6 +250,8 @@ static struct class_device_attribute *scsi_sysfs_shost_attrs[] = { &class_device_attr_proc_name, &class_device_attr_scan, &class_device_attr_state, + &class_device_attr_supported_mode, + &class_device_attr_active_mode, NULL }; @@ -224,6 +268,7 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work) struct scsi_device *sdev; struct device *parent; struct scsi_target *starget; + struct list_head *this, *tmp; unsigned long flags; sdev = container_of(work, struct scsi_device, ew.work); @@ -238,6 +283,16 @@ static void scsi_device_dev_release_usercontext(struct work_struct *work) list_del(&sdev->starved_entry); spin_unlock_irqrestore(sdev->host->host_lock, flags); + cancel_work_sync(&sdev->event_work); + + list_for_each_safe(this, tmp, &sdev->event_list) { + struct scsi_event *evt; + + evt = list_entry(this, struct scsi_event, node); + list_del(&evt->node); + kfree(evt); + } + if (sdev->request_queue) { sdev->request_queue->queuedata = NULL; /* user context needed to free queue */ @@ -277,16 +332,11 @@ static int scsi_bus_match(struct device *dev, struct device_driver *gendrv) return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0; } -static int scsi_bus_uevent(struct device *dev, char **envp, int num_envp, - char *buffer, int buffer_size) +static int scsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env) { struct scsi_device *sdev = to_scsi_device(dev); - int i = 0; - int length = 0; - add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length, - "MODALIAS=" SCSI_DEVICE_MODALIAS_FMT, sdev->type); - envp[i] = NULL; + add_uevent_var(env, "MODALIAS=" SCSI_DEVICE_MODALIAS_FMT, sdev->type); return 0; } @@ -323,12 +373,29 @@ static int scsi_bus_resume(struct device * dev) return err; } +static int scsi_bus_remove(struct device *dev) +{ + struct device_driver *drv = dev->driver; + struct scsi_device *sdev = to_scsi_device(dev); + int err = 0; + + /* reset the prep_fn back to the default since the + * driver may have altered it and it's being removed */ + blk_queue_prep_rq(sdev->request_queue, scsi_prep_fn); + + if (drv && drv->remove) + err = drv->remove(dev); + + return 0; +} + struct bus_type scsi_bus_type = { .name = "scsi", .match = scsi_bus_match, .uevent = scsi_bus_uevent, .suspend = scsi_bus_suspend, .resume = scsi_bus_resume, + .remove = scsi_bus_remove, }; int scsi_sysfs_register(void) @@ -575,25 +642,68 @@ sdev_show_modalias(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR(modalias, S_IRUGO, sdev_show_modalias, NULL); +#define DECLARE_EVT_SHOW(name, Cap_name) \ +static ssize_t \ +sdev_show_evt_##name(struct device *dev, struct device_attribute *attr, \ + char *buf) \ +{ \ + struct scsi_device *sdev = to_scsi_device(dev); \ + int val = test_bit(SDEV_EVT_##Cap_name, sdev->supported_events);\ + return snprintf(buf, 20, "%d\n", val); \ +} + +#define DECLARE_EVT_STORE(name, Cap_name) \ +static ssize_t \ +sdev_store_evt_##name(struct device *dev, struct device_attribute *attr, \ + const char *buf, size_t count) \ +{ \ + struct scsi_device *sdev = to_scsi_device(dev); \ + int val = simple_strtoul(buf, NULL, 0); \ + if (val == 0) \ + clear_bit(SDEV_EVT_##Cap_name, sdev->supported_events); \ + else if (val == 1) \ + set_bit(SDEV_EVT_##Cap_name, sdev->supported_events); \ + else \ + return -EINVAL; \ + return count; \ +} + +#define DECLARE_EVT(name, Cap_name) \ + DECLARE_EVT_SHOW(name, Cap_name) \ + DECLARE_EVT_STORE(name, Cap_name) \ + static DEVICE_ATTR(evt_##name, S_IRUGO, sdev_show_evt_##name, \ + sdev_store_evt_##name); +#define REF_EVT(name) &dev_attr_evt_##name.attr + +DECLARE_EVT(media_change, MEDIA_CHANGE) + /* 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_model, - &dev_attr_rev, - &dev_attr_rescan, - &dev_attr_delete, - &dev_attr_state, - &dev_attr_timeout, - &dev_attr_iocounterbits, - &dev_attr_iorequest_cnt, - &dev_attr_iodone_cnt, - &dev_attr_ioerr_cnt, - &dev_attr_modalias, +static struct attribute *scsi_sdev_attrs[] = { + &dev_attr_device_blocked.attr, + &dev_attr_type.attr, + &dev_attr_scsi_level.attr, + &dev_attr_vendor.attr, + &dev_attr_model.attr, + &dev_attr_rev.attr, + &dev_attr_rescan.attr, + &dev_attr_delete.attr, + &dev_attr_state.attr, + &dev_attr_timeout.attr, + &dev_attr_iocounterbits.attr, + &dev_attr_iorequest_cnt.attr, + &dev_attr_iodone_cnt.attr, + &dev_attr_ioerr_cnt.attr, + &dev_attr_modalias.attr, + REF_EVT(media_change), + NULL +}; + +static struct attribute_group scsi_sdev_attr_group = { + .attrs = scsi_sdev_attrs, +}; + +static struct attribute_group *scsi_sdev_attr_groups[] = { + &scsi_sdev_attr_group, NULL }; @@ -655,56 +765,6 @@ 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, - struct device_attribute *attr) -{ - int i; - - if (!attrs) - return NULL; - for (i = 0; attrs[i]; i++) - if (!strcmp(attrs[i]->attr.name, attr->attr.name)) - return attrs[i]; - return NULL; -} - -static int attr_add(struct device *dev, struct device_attribute *attr) -{ - struct device_attribute *base_attr; - - /* - * Spare the caller from having to copy things it's not interested in. - */ - base_attr = attr_overridden(scsi_sysfs_sdev_attrs, attr); - if (base_attr) { - /* extend permissions */ - attr->attr.mode |= base_attr->attr.mode; - - /* override null show/store with default */ - if (!attr->show) - attr->show = base_attr->show; - if (!attr->store) - attr->store = base_attr->store; - } - - return device_create_file(dev, attr); -} - /** * scsi_sysfs_add_sdev - add scsi device to sysfs * @sdev: scsi_device to add @@ -736,6 +796,24 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) * released by the sdev_class .release */ get_device(&sdev->sdev_gendev); + /* create queue files, which may be writable, depending on the host */ + if (sdev->host->hostt->change_queue_depth) + error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_depth_rw); + else + error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_depth); + if (error) { + __scsi_remove_device(sdev); + goto out; + } + if (sdev->host->hostt->change_queue_type) + error = device_create_file(&sdev->sdev_gendev, &sdev_attr_queue_type_rw); + else + error = device_create_file(&sdev->sdev_gendev, &dev_attr_queue_type); + if (error) { + __scsi_remove_device(sdev); + goto out; + } + error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL); if (error) @@ -746,9 +824,10 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) * nothing went wrong */ error = 0; + /* add additional host specific attributes */ if (sdev->host->hostt->sdev_attrs) { for (i = 0; sdev->host->hostt->sdev_attrs[i]; i++) { - error = attr_add(&sdev->sdev_gendev, + error = device_create_file(&sdev->sdev_gendev, sdev->host->hostt->sdev_attrs[i]); if (error) { __scsi_remove_device(sdev); @@ -756,20 +835,6 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) } } } - - for (i = 0; scsi_sysfs_sdev_attrs[i]; i++) { - if (!attr_overridden(sdev->host->hostt->sdev_attrs, - 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); - goto out; - } - } - } transport_add_device(&sdev->sdev_gendev); out: @@ -953,9 +1018,16 @@ int scsi_sysfs_add_host(struct Scsi_Host *shost) } transport_register_device(&shost->shost_gendev); + transport_configure_device(&shost->shost_gendev); return 0; } +static struct device_type scsi_dev_type = { + .name = "scsi_device", + .release = scsi_device_dev_release, + .groups = scsi_sdev_attr_groups, +}; + void scsi_sysfs_device_initialize(struct scsi_device *sdev) { unsigned long flags; @@ -964,7 +1036,7 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev) device_initialize(&sdev->sdev_gendev); sdev->sdev_gendev.bus = &scsi_bus_type; - sdev->sdev_gendev.release = scsi_device_dev_release; + sdev->sdev_gendev.type = &scsi_dev_type; sprintf(sdev->sdev_gendev.bus_id,"%d:%d:%d:%d", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); @@ -985,7 +1057,7 @@ void scsi_sysfs_device_initialize(struct scsi_device *sdev) int scsi_is_sdev_device(const struct device *dev) { - return dev->release == scsi_device_dev_release; + return dev->type == &scsi_dev_type; } EXPORT_SYMBOL(scsi_is_sdev_device);