diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c index f4a5fe623da9c25a66211725ff9ad7004e5264bd..a8d46b1243b77e7cd39aa851956b59859d0e3385 100644 --- a/drivers/scsi/53c700.c +++ b/drivers/scsi/53c700.c @@ -124,6 +124,7 @@ #include <linux/spinlock.h> #include <linux/completion.h> #include <linux/sched.h> +#include <linux/init.h> #include <linux/proc_fs.h> #include <asm/dma.h> #include <asm/system.h> @@ -173,6 +174,8 @@ STATIC void NCR_700_chip_reset(struct Scsi_Host *host); STATIC int NCR_700_slave_configure(Scsi_Device *SDpnt); STATIC void NCR_700_slave_destroy(Scsi_Device *SDpnt); +static struct device_attribute **NCR_700_dev_attrs = NULL; + static char *NCR_700_phase[] = { "", "after selection", @@ -247,6 +250,9 @@ NCR_700_detect(Scsi_Host_Template *tpnt, static int banner = 0; int j; + if(tpnt->sdev_attrs == NULL) + tpnt->sdev_attrs = NCR_700_dev_attrs; + memory = dma_alloc_noncoherent(hostdata->dev, TOTAL_MEM_SIZE, &pScript, GFP_KERNEL); if(memory == NULL) { @@ -2015,6 +2021,56 @@ NCR_700_slave_destroy(Scsi_Device *SDp) /* to do here: deallocate memory */ } +static ssize_t +NCR_700_store_queue_depth(struct device *dev, const char *buf, size_t count) +{ + int depth; + + struct scsi_device *SDp = to_scsi_device(dev); + depth = simple_strtoul(buf, NULL, 0); + if(depth > NCR_700_MAX_TAGS) + return -EINVAL; + scsi_adjust_queue_depth(SDp, MSG_ORDERED_TAG, depth); + + return count; +} + +static ssize_t +NCR_700_show_active_tags(struct device *dev, char *buf) +{ + struct scsi_device *SDp = to_scsi_device(dev); + + return snprintf(buf, 20, "%d\n", NCR_700_get_depth(SDp)); +} + +static struct device_attribute NCR_700_queue_depth_attr = { + .attr = { + .name = "queue_depth", + .mode = S_IWUSR, + }, + .store = NCR_700_store_queue_depth, +}; + +static struct device_attribute NCR_700_active_tags_attr = { + .attr = { + .name = "active_tags", + .mode = S_IRUGO, + }, + .show = NCR_700_show_active_tags, +}; + +STATIC int __init +NCR_700_init(void) +{ + scsi_sysfs_modify_sdev_attribute(&NCR_700_dev_attrs, + &NCR_700_queue_depth_attr); + scsi_sysfs_modify_sdev_attribute(&NCR_700_dev_attrs, + &NCR_700_active_tags_attr); + return 0; +} + EXPORT_SYMBOL(NCR_700_detect); EXPORT_SYMBOL(NCR_700_release); EXPORT_SYMBOL(NCR_700_intr); + +module_init(NCR_700_init); diff --git a/drivers/scsi/NCR_D700.c b/drivers/scsi/NCR_D700.c index 687e62e184221877dab9266a619bbac58551f0bd..efe023a5f5f5ae5b8bd4eab3a9ef625ba95bf0ef 100644 --- a/drivers/scsi/NCR_D700.c +++ b/drivers/scsi/NCR_D700.c @@ -385,6 +385,7 @@ static int __init NCR_D700_init(void) static void __exit NCR_D700_exit(void) { mca_unregister_driver(&NCR_D700_driver); + scsi_sysfs_release_attributes(); } module_init(NCR_D700_init); diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 07e18b0b1ddad4849200e6c9b5f8e5badf2374f1..f4969be0edc0da0d9e08909934889984b7bf725e 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -321,7 +321,12 @@ struct Scsi_Host * scsi_register(Scsi_Host_Template *shost_tp, int xtr_bytes) shost_tp->eh_host_reset_handler == NULL) { printk(KERN_ERR "ERROR: SCSI host `%s' has no error handling\nERROR: This is not a safe way to run your SCSI host\nERROR: The error handling must be added to this driver\n", shost_tp->proc_name); dump_stack(); - } + } + if(shost_tp->shost_attrs == NULL) + /* if its not set in the template, use the default */ + shost_tp->shost_attrs = scsi_sysfs_shost_attrs; + if(shost_tp->sdev_attrs == NULL) + shost_tp->sdev_attrs = scsi_sysfs_sdev_attrs; gfp_mask = GFP_KERNEL; if (shost_tp->unchecked_isa_dma && xtr_bytes) gfp_mask |= __GFP_DMA; diff --git a/drivers/scsi/hosts.h b/drivers/scsi/hosts.h index 3d85a6f80012363a4d196c33f2c43e4b718ba930..0468c3c45fb0fc6beb7711c80403006832b944be 100644 --- a/drivers/scsi/hosts.h +++ b/drivers/scsi/hosts.h @@ -356,6 +356,16 @@ typedef struct SHT * FIXME: This should probably be a value in the template */ #define SCSI_DEFAULT_HOST_BLOCKED 7 + /* + * pointer to the sysfs class properties for this host + */ + struct class_device_attribute **shost_attrs; + + /* + * Pointer to the SCSI device properties for this host + */ + struct device_attribute **sdev_attrs; + } Scsi_Host_Template; /* @@ -588,4 +598,6 @@ static inline Scsi_Device *scsi_find_device(struct Scsi_Host *shost, return NULL; } +extern void scsi_sysfs_release_attributes(struct SHT *hostt); + #endif diff --git a/drivers/scsi/scsi.h b/drivers/scsi/scsi.h index 5a8eefb32f24a8c179123d3b3fb2e1aa1b2db6e7..448fadd040554f0ec0bb34ca4dd3913634f89754 100644 --- a/drivers/scsi/scsi.h +++ b/drivers/scsi/scsi.h @@ -682,4 +682,9 @@ static inline Scsi_Cmnd *scsi_find_tag(Scsi_Device *SDpnt, int tag) { int scsi_set_medium_removal(Scsi_Device *dev, char state); +extern int scsi_sysfs_modify_sdev_attribute(struct device_attribute ***dev_attrs, + struct device_attribute *attr); +extern int scsi_sysfs_modify_shost_attribute(struct class_device_attribute ***class_attrs, + struct class_device_attribute *attr); + #endif /* _SCSI_H */ diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 950c8f8861931c2e07ca3b44491ef1fc872d0673..dda78cf7c068be323aabdcacfd04cdfd1676caf0 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -131,4 +131,9 @@ extern void scsi_sysfs_remove_host(struct Scsi_Host *); extern int scsi_sysfs_register(void); extern void scsi_sysfs_unregister(void); +/* definitions for the linker default sections covering the host + * class and device attributes */ +extern struct class_device_attribute *scsi_sysfs_shost_attrs[]; +extern struct device_attribute *scsi_sysfs_sdev_attrs[]; + #endif /* _SCSI_PRIV_H */ diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 253440c915d78204e6ed768e03c53181b9273c7b..ddc82627983ccbaa3094fb201a982e67029d6348 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -45,12 +45,13 @@ shost_rd_attr(cmd_per_lun, "%hd\n"); shost_rd_attr(sg_tablesize, "%hu\n"); shost_rd_attr(unchecked_isa_dma, "%d\n"); -static struct class_device_attribute *const shost_attrs[] = { +struct class_device_attribute *scsi_sysfs_shost_attrs[] = { &class_device_attr_unique_id, &class_device_attr_host_busy, &class_device_attr_cmd_per_lun, &class_device_attr_sg_tablesize, &class_device_attr_unchecked_isa_dma, + NULL }; static struct class shost_class = { @@ -243,7 +244,8 @@ store_rescan_field (struct device *dev, const char *buf, size_t count) static DEVICE_ATTR(rescan, S_IRUGO | S_IWUSR, show_rescan_field, store_rescan_field) -static struct device_attribute * const sdev_attrs[] = { +/* Default template for device attributes. May NOT be modified */ +struct device_attribute *scsi_sysfs_sdev_attrs[] = { &dev_attr_device_blocked, &dev_attr_queue_depth, &dev_attr_type, @@ -254,6 +256,7 @@ static struct device_attribute * const sdev_attrs[] = { &dev_attr_rev, &dev_attr_online, &dev_attr_rescan, + NULL }; static void scsi_device_release(struct device *dev) @@ -287,9 +290,9 @@ int scsi_device_register(struct scsi_device *sdev) if (error) return error; - for (i = 0; !error && i < ARRAY_SIZE(sdev_attrs); i++) + for (i = 0; !error && sdev->host->hostt->sdev_attrs[i] != NULL; i++) error = device_create_file(&sdev->sdev_driverfs_dev, - sdev_attrs[i]); + sdev->host->hostt->sdev_attrs[i]); if (error) scsi_device_unregister(sdev); @@ -305,8 +308,8 @@ void scsi_device_unregister(struct scsi_device *sdev) { int i; - for (i = 0; i < ARRAY_SIZE(sdev_attrs); i++) - device_remove_file(&sdev->sdev_driverfs_dev, sdev_attrs[i]); + for (i = 0; sdev->host->hostt->sdev_attrs[i] != NULL; i++) + device_remove_file(&sdev->sdev_driverfs_dev, sdev->host->hostt->sdev_attrs[i]); device_unregister(&sdev->sdev_driverfs_dev); } @@ -357,9 +360,9 @@ int scsi_sysfs_add_host(struct Scsi_Host *shost, struct device *dev) if (error) goto clean_device; - for (i = 0; !error && i < ARRAY_SIZE(shost_attrs); i++) + for (i = 0; !error && shost->hostt->shost_attrs[i] != NULL; i++) error = class_device_create_file(&shost->class_dev, - shost_attrs[i]); + shost->hostt->shost_attrs[i]); if (error) goto clean_class; @@ -383,3 +386,118 @@ void scsi_sysfs_remove_host(struct Scsi_Host *shost) device_del(&shost->host_gendev); } +/** scsi_sysfs_modify_shost_attribute - modify or add a host class attribute + * + * @class_attrs:host class attribute list to be added to or modified + * @attr: individual attribute to change or added + * + * returns zero if successful or error if not + **/ +int scsi_sysfs_modify_shost_attribute(struct class_device_attribute ***class_attrs, + struct class_device_attribute *attr) +{ + int modify = 0; + int num_attrs; + + if(*class_attrs == NULL) + *class_attrs = scsi_sysfs_shost_attrs; + + for(num_attrs=0; (*class_attrs)[num_attrs] != NULL; num_attrs++) + if(strcmp((*class_attrs)[num_attrs]->attr.name, attr->attr.name) == 0) + modify = num_attrs; + + if(*class_attrs == scsi_sysfs_shost_attrs || !modify) { + /* note: need space for null at the end as well */ + struct class_device_attribute **tmp_attrs = kmalloc(sizeof(struct class_device_attribute)*(num_attrs + (modify ? 1 : 2)), GFP_KERNEL); + if(tmp_attrs == NULL) + return -ENOMEM; + memcpy(tmp_attrs, *class_attrs, sizeof(struct class_device_attribute)*num_attrs); + if(*class_attrs != scsi_sysfs_shost_attrs) + kfree(*class_attrs); + *class_attrs = tmp_attrs; + } + if(modify) { + /* spare the caller from having to copy things it's + * not interested in */ + struct class_device_attribute *old_attr = + (*class_attrs)[modify]; + /* extend permissions */ + attr->attr.mode |= old_attr->attr.mode; + + /* override null show/store with default */ + if(attr->show == NULL) + attr->show = old_attr->show; + if(attr->store == NULL) + attr->store = old_attr->store; + (*class_attrs)[modify] = attr; + } else { + (*class_attrs)[num_attrs++] = attr; + (*class_attrs)[num_attrs] = NULL; + } + + return 0; +} +EXPORT_SYMBOL(scsi_sysfs_modify_shost_attribute); + +/** scsi_sysfs_modify_sdev_attribute - modify or add a host device attribute + * + * @dev_attrs: pointer to the attribute list to be added to or modified + * @attr: individual attribute to change or added + * + * returns zero if successful or error if not + **/ +int scsi_sysfs_modify_sdev_attribute(struct device_attribute ***dev_attrs, + struct device_attribute *attr) +{ + int modify = 0; + int num_attrs; + + if(*dev_attrs == NULL) + *dev_attrs = scsi_sysfs_sdev_attrs; + + for(num_attrs=0; (*dev_attrs)[num_attrs] != NULL; num_attrs++) + if(strcmp((*dev_attrs)[num_attrs]->attr.name, attr->attr.name) == 0) + modify = num_attrs; + + if(*dev_attrs == scsi_sysfs_sdev_attrs || !modify) { + /* note: need space for null at the end as well */ + struct device_attribute **tmp_attrs = kmalloc(sizeof(struct device_attribute)*(num_attrs + (modify ? 1 : 2)), GFP_KERNEL); + if(tmp_attrs == NULL) + return -ENOMEM; + memcpy(tmp_attrs, *dev_attrs, sizeof(struct device_attribute)*num_attrs); + if(*dev_attrs != scsi_sysfs_sdev_attrs) + kfree(*dev_attrs); + *dev_attrs = tmp_attrs; + } + if(modify) { + /* spare the caller from having to copy things it's + * not interested in */ + struct device_attribute *old_attr = + (*dev_attrs)[modify]; + /* extend permissions */ + attr->attr.mode |= old_attr->attr.mode; + + /* override null show/store with default */ + if(attr->show == NULL) + attr->show = old_attr->show; + if(attr->store == NULL) + attr->store = old_attr->store; + (*dev_attrs)[modify] = attr; + } else { + (*dev_attrs)[num_attrs++] = attr; + (*dev_attrs)[num_attrs] = NULL; + } + + return 0; +} +EXPORT_SYMBOL(scsi_sysfs_modify_sdev_attribute); + +void scsi_sysfs_release_attributes(struct SHT *hostt) +{ + if(hostt->sdev_attrs != scsi_sysfs_sdev_attrs) + kfree(hostt->sdev_attrs); + + if(hostt->shost_attrs != scsi_sysfs_shost_attrs) + kfree(hostt->shost_attrs); +} +EXPORT_SYMBOL(scsi_sysfs_release_attributes);