From 7d15d6a4dc08dfd456d834e33ef6c1d798fb2edc Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Fri, 14 Mar 2008 14:12:43 -0700 Subject: [SCSI] st: fix up after class_device removal There's a change in the SCSI tree that adds another class_device, so change it to an ordinary device [jejb: this one got rebased until it's basically cosmetic only] Cc: Kai Makisara Signed-off-by: James Bottomley --- drivers/scsi/st.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c index a860c3a9ae99..e8db66ad0bde 100644 --- a/drivers/scsi/st.c +++ b/drivers/scsi/st.c @@ -4322,7 +4322,7 @@ static void do_remove_sysfs_files(void) static ssize_t st_defined_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct st_modedef *STm = (struct st_modedef *)dev_get_drvdata(dev); + struct st_modedef *STm = dev_get_drvdata(dev); ssize_t l = 0; l = snprintf(buf, PAGE_SIZE, "%d\n", STm->defined); @@ -4334,7 +4334,7 @@ DEVICE_ATTR(defined, S_IRUGO, st_defined_show, NULL); static ssize_t st_defblk_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct st_modedef *STm = (struct st_modedef *)dev_get_drvdata(dev); + struct st_modedef *STm = dev_get_drvdata(dev); ssize_t l = 0; l = snprintf(buf, PAGE_SIZE, "%d\n", STm->default_blksize); @@ -4346,7 +4346,7 @@ DEVICE_ATTR(default_blksize, S_IRUGO, st_defblk_show, NULL); static ssize_t st_defdensity_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct st_modedef *STm = (struct st_modedef *)dev_get_drvdata(dev); + struct st_modedef *STm = dev_get_drvdata(dev); ssize_t l = 0; char *fmt; @@ -4361,7 +4361,7 @@ static ssize_t st_defcompression_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct st_modedef *STm = (struct st_modedef *)dev_get_drvdata(dev); + struct st_modedef *STm = dev_get_drvdata(dev); ssize_t l = 0; l = snprintf(buf, PAGE_SIZE, "%d\n", STm->default_compression - 1); @@ -4373,7 +4373,7 @@ DEVICE_ATTR(default_compression, S_IRUGO, st_defcompression_show, NULL); static ssize_t st_options_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct st_modedef *STm = (struct st_modedef *)dev_get_drvdata(dev); + struct st_modedef *STm = dev_get_drvdata(dev); struct scsi_tape *STp; int i, j, options; ssize_t l = 0; -- cgit v1.2.3 From cb6b7f40630f94126233194847a86bf5501fb63c Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sat, 15 Mar 2008 13:01:40 -0500 Subject: [SCSI] ses: fix up functionality after class_device->device conversion ses uses an unusual two level class hierarchy which broke in this conversion. Fix it up still with a two level hierarchy, but this time let the ses device manage the links to and from the real device in the enclosure. Signed-off-by: James Bottomley --- drivers/misc/enclosure.c | 100 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 71 insertions(+), 29 deletions(-) diff --git a/drivers/misc/enclosure.c b/drivers/misc/enclosure.c index fafb57fed761..0736cff9d97a 100644 --- a/drivers/misc/enclosure.c +++ b/drivers/misc/enclosure.c @@ -31,7 +31,6 @@ static LIST_HEAD(container_list); static DEFINE_MUTEX(container_list_lock); static struct class enclosure_class; -static struct class enclosure_component_class; /** * enclosure_find - find an enclosure given a device @@ -166,6 +165,40 @@ void enclosure_unregister(struct enclosure_device *edev) } EXPORT_SYMBOL_GPL(enclosure_unregister); +#define ENCLOSURE_NAME_SIZE 64 + +static void enclosure_link_name(struct enclosure_component *cdev, char *name) +{ + strcpy(name, "enclosure_device:"); + strcat(name, cdev->cdev.bus_id); +} + +static void enclosure_remove_links(struct enclosure_component *cdev) +{ + char name[ENCLOSURE_NAME_SIZE]; + + enclosure_link_name(cdev, name); + sysfs_remove_link(&cdev->dev->kobj, name); + sysfs_remove_link(&cdev->cdev.kobj, "device"); +} + +static int enclosure_add_links(struct enclosure_component *cdev) +{ + int error; + char name[ENCLOSURE_NAME_SIZE]; + + error = sysfs_create_link(&cdev->cdev.kobj, &cdev->dev->kobj, "device"); + if (error) + return error; + + enclosure_link_name(cdev, name); + error = sysfs_create_link(&cdev->dev->kobj, &cdev->cdev.kobj, name); + if (error) + sysfs_remove_link(&cdev->cdev.kobj, "device"); + + return error; +} + static void enclosure_release(struct device *cdev) { struct enclosure_device *edev = to_enclosure_device(cdev); @@ -178,10 +211,15 @@ static void enclosure_component_release(struct device *dev) { struct enclosure_component *cdev = to_enclosure_component(dev); - put_device(cdev->dev); + if (cdev->dev) { + enclosure_remove_links(cdev); + put_device(cdev->dev); + } put_device(dev->parent); } +static struct attribute_group *enclosure_groups[]; + /** * enclosure_component_register - add a particular component to an enclosure * @edev: the enclosure to add the component @@ -217,12 +255,14 @@ enclosure_component_register(struct enclosure_device *edev, ecomp->number = number; cdev = &ecomp->cdev; cdev->parent = get_device(&edev->edev); - cdev->class = &enclosure_component_class; if (name) snprintf(cdev->bus_id, BUS_ID_SIZE, "%s", name); else snprintf(cdev->bus_id, BUS_ID_SIZE, "%u", number); + cdev->release = enclosure_component_release; + cdev->groups = enclosure_groups; + err = device_register(cdev); if (err) ERR_PTR(err); @@ -255,10 +295,12 @@ int enclosure_add_device(struct enclosure_device *edev, int component, cdev = &edev->component[component]; - device_del(&cdev->cdev); + if (cdev->dev) + enclosure_remove_links(cdev); + put_device(cdev->dev); cdev->dev = get_device(dev); - return device_add(&cdev->cdev); + return enclosure_add_links(cdev); } EXPORT_SYMBOL_GPL(enclosure_add_device); @@ -442,24 +484,32 @@ static ssize_t get_component_type(struct device *cdev, } -static struct device_attribute enclosure_component_attrs[] = { - __ATTR(fault, S_IRUGO | S_IWUSR, get_component_fault, - set_component_fault), - __ATTR(status, S_IRUGO | S_IWUSR, get_component_status, - set_component_status), - __ATTR(active, S_IRUGO | S_IWUSR, get_component_active, - set_component_active), - __ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate, - set_component_locate), - __ATTR(type, S_IRUGO, get_component_type, NULL), - __ATTR_NULL +static DEVICE_ATTR(fault, S_IRUGO | S_IWUSR, get_component_fault, + set_component_fault); +static DEVICE_ATTR(status, S_IRUGO | S_IWUSR, get_component_status, + set_component_status); +static DEVICE_ATTR(active, S_IRUGO | S_IWUSR, get_component_active, + set_component_active); +static DEVICE_ATTR(locate, S_IRUGO | S_IWUSR, get_component_locate, + set_component_locate); +static DEVICE_ATTR(type, S_IRUGO, get_component_type, NULL); + +static struct attribute *enclosure_component_attrs[] = { + &dev_attr_fault.attr, + &dev_attr_status.attr, + &dev_attr_active.attr, + &dev_attr_locate.attr, + &dev_attr_type.attr, + NULL }; -static struct class enclosure_component_class = { - .name = "enclosure_component", - .owner = THIS_MODULE, - .dev_attrs = enclosure_component_attrs, - .dev_release = enclosure_component_release, +static struct attribute_group enclosure_group = { + .attrs = enclosure_component_attrs, +}; + +static struct attribute_group *enclosure_groups[] = { + &enclosure_group, + NULL }; static int __init enclosure_init(void) @@ -469,20 +519,12 @@ static int __init enclosure_init(void) err = class_register(&enclosure_class); if (err) return err; - err = class_register(&enclosure_component_class); - if (err) - goto err_out; return 0; - err_out: - class_unregister(&enclosure_class); - - return err; } static void __exit enclosure_exit(void) { - class_unregister(&enclosure_component_class); class_unregister(&enclosure_class); } -- cgit v1.2.3 From b0ed43360fdca227048d88a08290365cb681c1a8 Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Tue, 18 Mar 2008 14:32:28 +0100 Subject: [SCSI] add scsi_host and scsi_target to scsi_bus This patch implements scsi_host and scsi_target device types and adds both to the scsi_bus. Signed-off-by: Hannes Reinecke Signed-off-by: Kay Sievers Signed-off-by: James Bottomley --- drivers/scsi/hosts.c | 12 ++++++++++-- drivers/scsi/scsi_proc.c | 7 ++++++- drivers/scsi/scsi_scan.c | 12 ++++++++++-- drivers/scsi/scsi_sysfs.c | 29 ++++++++++++++++++++++++----- 4 files changed, 50 insertions(+), 10 deletions(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index c264a8c5f01e..63bed62f270e 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -284,6 +284,11 @@ static void scsi_host_dev_release(struct device *dev) kfree(shost); } +struct device_type scsi_host_type = { + .name = "scsi_host", + .release = scsi_host_dev_release, +}; + /** * scsi_host_alloc - register a scsi host adapter instance. * @sht: pointer to scsi host template @@ -383,7 +388,10 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) device_initialize(&shost->shost_gendev); snprintf(shost->shost_gendev.bus_id, BUS_ID_SIZE, "host%d", shost->host_no); - shost->shost_gendev.release = scsi_host_dev_release; +#ifndef CONFIG_SYSFS_DEPRECATED + shost->shost_gendev.bus = &scsi_bus_type; +#endif + shost->shost_gendev.type = &scsi_host_type; device_initialize(&shost->shost_dev); shost->shost_dev.parent = &shost->shost_gendev; @@ -496,7 +504,7 @@ void scsi_exit_hosts(void) int scsi_is_host_device(const struct device *dev) { - return dev->release == scsi_host_dev_release; + return dev->type == &scsi_host_type; } EXPORT_SYMBOL(scsi_is_host_device); diff --git a/drivers/scsi/scsi_proc.c b/drivers/scsi/scsi_proc.c index ed395154a5b1..3a1c99d5c775 100644 --- a/drivers/scsi/scsi_proc.c +++ b/drivers/scsi/scsi_proc.c @@ -190,10 +190,14 @@ void scsi_proc_host_rm(struct Scsi_Host *shost) */ static int proc_print_scsidevice(struct device *dev, void *data) { - struct scsi_device *sdev = to_scsi_device(dev); + struct scsi_device *sdev; struct seq_file *s = data; int i; + if (!scsi_is_sdev_device(dev)) + goto out; + + sdev = to_scsi_device(dev); seq_printf(s, "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n Vendor: ", sdev->host->host_no, sdev->channel, sdev->id, sdev->lun); @@ -230,6 +234,7 @@ static int proc_print_scsidevice(struct device *dev, void *data) else seq_printf(s, "\n"); +out: return 0; } diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index e67c14e31bab..e1644b270cdc 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -331,9 +331,14 @@ static void scsi_target_dev_release(struct device *dev) put_device(parent); } +struct device_type scsi_target_type = { + .name = "scsi_target", + .release = scsi_target_dev_release, +}; + int scsi_is_target_device(const struct device *dev) { - return dev->release == scsi_target_dev_release; + return dev->type == &scsi_target_type; } EXPORT_SYMBOL(scsi_is_target_device); @@ -391,9 +396,12 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, device_initialize(dev); starget->reap_ref = 1; dev->parent = get_device(parent); - dev->release = scsi_target_dev_release; sprintf(dev->bus_id, "target%d:%d:%d", shost->host_no, channel, id); +#ifndef CONFIG_SYSFS_DEPRECATED + dev->bus = &scsi_bus_type; +#endif + dev->type = &scsi_target_type; starget->id = id; starget->channel = channel; INIT_LIST_HEAD(&starget->siblings); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 67bb20ed45d2..fbd7f9ed1251 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -21,6 +21,8 @@ #include "scsi_priv.h" #include "scsi_logging.h" +static struct device_type scsi_dev_type; + static const struct { enum scsi_device_state value; char *name; @@ -335,7 +337,12 @@ static struct class sdev_class = { /* all probing is done in the individual ->probe routines */ static int scsi_bus_match(struct device *dev, struct device_driver *gendrv) { - struct scsi_device *sdp = to_scsi_device(dev); + struct scsi_device *sdp; + + if (dev->type != &scsi_dev_type) + return 0; + + sdp = to_scsi_device(dev); if (sdp->no_uld_attach) return 0; return (sdp->inq_periph_qual == SCSI_INQ_PQ_CON)? 1: 0; @@ -351,10 +358,16 @@ static int scsi_bus_uevent(struct device *dev, struct kobj_uevent_env *env) static int scsi_bus_suspend(struct device * dev, pm_message_t state) { - struct device_driver *drv = dev->driver; - struct scsi_device *sdev = to_scsi_device(dev); + struct device_driver *drv; + struct scsi_device *sdev; int err; + if (dev->type != &scsi_dev_type) + return 0; + + drv = dev->driver; + sdev = to_scsi_device(dev); + err = scsi_device_quiesce(sdev); if (err) return err; @@ -370,10 +383,16 @@ static int scsi_bus_suspend(struct device * dev, pm_message_t state) static int scsi_bus_resume(struct device * dev) { - struct device_driver *drv = dev->driver; - struct scsi_device *sdev = to_scsi_device(dev); + struct device_driver *drv; + struct scsi_device *sdev; int err = 0; + if (dev->type != &scsi_dev_type) + return 0; + + drv = dev->driver; + sdev = to_scsi_device(dev); + if (drv && drv->resume) err = drv->resume(dev); -- cgit v1.2.3 From bbd1ae412c9eb09ae7bb11cfaf7018a2367d493f Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Tue, 18 Mar 2008 14:32:28 +0100 Subject: [SCSI] qla2xxx, lfpc: Rename 'state' attribute to 'link_state' lpfc and qla2xxx overwrite the standard 'state' attribute with custom callbacks. So rename the custom attributes to 'link_state' and retain the original meaning of the 'state' attribute. Signed-off-by: Hannes Reinecke Acked-by: Andrew Vasquez Acked-by: James Smart Signed-off-by: James Bottomley --- drivers/scsi/lpfc/lpfc_attr.c | 10 +++++----- drivers/scsi/qla2xxx/qla_attr.c | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index a9fbb3f88659..960baaf11fb1 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -182,8 +182,8 @@ lpfc_option_rom_version_show(struct device *dev, struct device_attribute *attr, return snprintf(buf, PAGE_SIZE, "%s\n", phba->OptionROMVersion); } static ssize_t -lpfc_state_show(struct device *dev, struct device_attribute *attr, - char *buf) +lpfc_link_state_show(struct device *dev, struct device_attribute *attr, + char *buf) { struct Scsi_Host *shost = class_to_shost(dev); struct lpfc_vport *vport = (struct lpfc_vport *) shost->hostdata; @@ -936,7 +936,7 @@ static DEVICE_ATTR(programtype, S_IRUGO, lpfc_programtype_show, NULL); static DEVICE_ATTR(portnum, S_IRUGO, lpfc_vportnum_show, NULL); static DEVICE_ATTR(fwrev, S_IRUGO, lpfc_fwrev_show, NULL); static DEVICE_ATTR(hdw, S_IRUGO, lpfc_hdw_show, NULL); -static DEVICE_ATTR(state, S_IRUGO, lpfc_state_show, NULL); +static DEVICE_ATTR(link_state, S_IRUGO, lpfc_link_state_show, NULL); static DEVICE_ATTR(option_rom_version, S_IRUGO, lpfc_option_rom_version_show, NULL); static DEVICE_ATTR(num_discovered_ports, S_IRUGO, @@ -1666,7 +1666,7 @@ struct device_attribute *lpfc_hba_attrs[] = { &dev_attr_fwrev, &dev_attr_hdw, &dev_attr_option_rom_version, - &dev_attr_state, + &dev_attr_link_state, &dev_attr_num_discovered_ports, &dev_attr_lpfc_drvr_version, &dev_attr_lpfc_temp_sensor, @@ -1714,7 +1714,7 @@ struct device_attribute *lpfc_hba_attrs[] = { struct device_attribute *lpfc_vport_attrs[] = { &dev_attr_info, - &dev_attr_state, + &dev_attr_link_state, &dev_attr_num_discovered_ports, &dev_attr_lpfc_drvr_version, &dev_attr_lpfc_log_verbose, diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index d61df036910c..287690853caf 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -609,8 +609,8 @@ qla2x00_pci_info_show(struct device *dev, struct device_attribute *attr, } static ssize_t -qla2x00_state_show(struct device *dev, struct device_attribute *attr, - char *buf) +qla2x00_link_state_show(struct device *dev, struct device_attribute *attr, + char *buf) { scsi_qla_host_t *ha = shost_priv(class_to_shost(dev)); int len = 0; @@ -814,7 +814,7 @@ static DEVICE_ATTR(isp_id, S_IRUGO, qla2x00_isp_id_show, NULL); static DEVICE_ATTR(model_name, S_IRUGO, qla2x00_model_name_show, NULL); static DEVICE_ATTR(model_desc, S_IRUGO, qla2x00_model_desc_show, NULL); static DEVICE_ATTR(pci_info, S_IRUGO, qla2x00_pci_info_show, NULL); -static DEVICE_ATTR(state, S_IRUGO, qla2x00_state_show, NULL); +static DEVICE_ATTR(link_state, S_IRUGO, qla2x00_link_state_show, NULL); static DEVICE_ATTR(zio, S_IRUGO | S_IWUSR, qla2x00_zio_show, qla2x00_zio_store); static DEVICE_ATTR(zio_timer, S_IRUGO | S_IWUSR, qla2x00_zio_timer_show, qla2x00_zio_timer_store); @@ -838,7 +838,7 @@ struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_model_name, &dev_attr_model_desc, &dev_attr_pci_info, - &dev_attr_state, + &dev_attr_link_state, &dev_attr_zio, &dev_attr_zio_timer, &dev_attr_beacon, -- cgit v1.2.3 From 0f4238958d28044b335644b69df6071cdb04b5ce Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Thu, 20 Mar 2008 20:47:52 -0500 Subject: [SCSI] sysfs: make group is_valid return a mode_t We have a problem in scsi_transport_spi in that we need to customise not only the visibility of the attributes, but also their mode. Fix this by making the is_visible() callback return a mode, with 0 indicating is not visible. Also add a sysfs_update_group() API to allow us to change either the visibility or mode of the files at any time on the fly. Acked-by: Kay Sievers Signed-off-by: James Bottomley --- fs/sysfs/file.c | 14 ++++++--- fs/sysfs/group.c | 83 +++++++++++++++++++++++++++++++++++++++++++-------- fs/sysfs/sysfs.h | 2 ++ include/linux/sysfs.h | 4 ++- 4 files changed, 85 insertions(+), 18 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index ade9a7e6a757..dbdfabbfd609 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -477,11 +477,10 @@ const struct file_operations sysfs_file_operations = { .poll = sysfs_poll, }; - -int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, - int type) +int sysfs_add_file_mode(struct sysfs_dirent *dir_sd, + const struct attribute *attr, int type, mode_t amode) { - umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG; + umode_t mode = (amode & S_IALLUGO) | S_IFREG; struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; int rc; @@ -502,6 +501,13 @@ int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, } +int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, + int type) +{ + return sysfs_add_file_mode(dir_sd, attr, type, attr->mode); +} + + /** * sysfs_create_file - create an attribute file for an object. * @kobj: object we're creating for. diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 477904915032..eeba38417b1d 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -23,35 +23,50 @@ static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, int i; for (i = 0, attr = grp->attrs; *attr; i++, attr++) - if (!grp->is_visible || - grp->is_visible(kobj, *attr, i)) - sysfs_hash_and_remove(dir_sd, (*attr)->name); + sysfs_hash_and_remove(dir_sd, (*attr)->name); } static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, - const struct attribute_group *grp) + const struct attribute_group *grp, int update) { struct attribute *const* attr; int error = 0, i; - for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) - if (!grp->is_visible || - grp->is_visible(kobj, *attr, i)) - error |= - sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR); + for (i = 0, attr = grp->attrs; *attr && !error; i++, attr++) { + mode_t mode = 0; + + /* in update mode, we're changing the permissions or + * visibility. Do this by first removing then + * re-adding (if required) the file */ + if (update) + sysfs_hash_and_remove(dir_sd, (*attr)->name); + if (grp->is_visible) { + mode = grp->is_visible(kobj, *attr, i); + if (!mode) + continue; + } + error = sysfs_add_file_mode(dir_sd, *attr, SYSFS_KOBJ_ATTR, + (*attr)->mode | mode); + if (unlikely(error)) + break; + } if (error) remove_files(dir_sd, kobj, grp); return error; } -int sysfs_create_group(struct kobject * kobj, - const struct attribute_group * grp) +static int internal_create_group(struct kobject *kobj, int update, + const struct attribute_group *grp) { struct sysfs_dirent *sd; int error; - BUG_ON(!kobj || !kobj->sd); + BUG_ON(!kobj || (!update && !kobj->sd)); + + /* Updates may happen before the object has been instantiated */ + if (unlikely(update && !kobj->sd)) + return -EINVAL; if (grp->name) { error = sysfs_create_subdir(kobj, grp->name, &sd); @@ -60,7 +75,7 @@ int sysfs_create_group(struct kobject * kobj, } else sd = kobj->sd; sysfs_get(sd); - error = create_files(sd, kobj, grp); + error = create_files(sd, kobj, grp, update); if (error) { if (grp->name) sysfs_remove_subdir(sd); @@ -69,6 +84,47 @@ int sysfs_create_group(struct kobject * kobj, return error; } +/** + * sysfs_create_group - given a directory kobject, create an attribute group + * @kobj: The kobject to create the group on + * @grp: The attribute group to create + * + * This function creates a group for the first time. It will explicitly + * warn and error if any of the attribute files being created already exist. + * + * Returns 0 on success or error. + */ +int sysfs_create_group(struct kobject *kobj, + const struct attribute_group *grp) +{ + return internal_create_group(kobj, 0, grp); +} + +/** + * sysfs_update_group - given a directory kobject, create an attribute group + * @kobj: The kobject to create the group on + * @grp: The attribute group to create + * + * This function updates an attribute group. Unlike + * sysfs_create_group(), it will explicitly not warn or error if any + * of the attribute files being created already exist. Furthermore, + * if the visibility of the files has changed through the is_visible() + * callback, it will update the permissions and add or remove the + * relevant files. + * + * The primary use for this function is to call it after making a change + * that affects group visibility. + * + * Returns 0 on success or error. + */ +int sysfs_update_group(struct kobject *kobj, + const struct attribute_group *grp) +{ + return internal_create_group(kobj, 1, grp); +} + + + void sysfs_remove_group(struct kobject * kobj, const struct attribute_group * grp) { @@ -95,4 +151,5 @@ void sysfs_remove_group(struct kobject * kobj, EXPORT_SYMBOL_GPL(sysfs_create_group); +EXPORT_SYMBOL_GPL(sysfs_update_group); EXPORT_SYMBOL_GPL(sysfs_remove_group); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index ff17f8da9b43..ce4e15f8aaeb 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -154,6 +154,8 @@ extern const struct file_operations sysfs_file_operations; int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, int type); +int sysfs_add_file_mode(struct sysfs_dirent *dir_sd, + const struct attribute *attr, int type, mode_t amode); /* * bin.c */ diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 03378e3515b3..add3c5a40827 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -32,7 +32,7 @@ struct attribute { struct attribute_group { const char *name; - int (*is_visible)(struct kobject *, + mode_t (*is_visible)(struct kobject *, struct attribute *, int); struct attribute **attrs; }; @@ -105,6 +105,8 @@ void sysfs_remove_link(struct kobject *kobj, const char *name); int __must_check sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp); +int sysfs_update_group(struct kobject *kobj, + const struct attribute_group *grp); void sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp); int sysfs_add_file_to_group(struct kobject *kobj, -- cgit v1.2.3 From 352f6bb422bd31a80b4a0f1c3f19b6993df2508c Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Thu, 20 Mar 2008 20:57:02 -0500 Subject: [SCSI] scsi_transport_spi: fix the attribute settings We now take advantage of the mode_t return of is_valid, and also update the attributes when the target is configured. Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_spi.c | 32 ++++++++------------------------ 1 file changed, 8 insertions(+), 24 deletions(-) diff --git a/drivers/scsi/scsi_transport_spi.c b/drivers/scsi/scsi_transport_spi.c index bc12b5d5d676..3a26f7489b48 100644 --- a/drivers/scsi/scsi_transport_spi.c +++ b/drivers/scsi/scsi_transport_spi.c @@ -1374,11 +1374,11 @@ static int spi_host_configure(struct transport_container *tc, * overloads the return by setting 1<<1 if the attribute should * be writeable */ #define TARGET_ATTRIBUTE_HELPER(name) \ - (si->f->show_##name ? 1 : 0) + \ - (si->f->set_##name ? 2 : 0) + (si->f->show_##name ? S_IRUGO : 0) | \ + (si->f->set_##name ? S_IWUSR : 0) -static int target_attribute_is_visible(struct kobject *kobj, - struct attribute *attr, int i) +static mode_t target_attribute_is_visible(struct kobject *kobj, + struct attribute *attr, int i) { struct device *cdev = container_of(kobj, struct device, kobj); struct scsi_target *starget = transport_class_to_starget(cdev); @@ -1428,7 +1428,7 @@ static int target_attribute_is_visible(struct kobject *kobj, spi_support_ius(starget)) return TARGET_ATTRIBUTE_HELPER(hold_mcs); else if (attr == &dev_attr_revalidate.attr) - return 1; + return S_IWUSR; return 0; } @@ -1462,25 +1462,9 @@ static int spi_target_configure(struct transport_container *tc, struct device *cdev) { struct kobject *kobj = &cdev->kobj; - int i; - struct attribute *attr; - int rc; - - for (i = 0; (attr = target_attributes[i]) != NULL; i++) { - int j = target_attribute_group.is_visible(kobj, attr, i); - - /* FIXME: as well as returning -EEXIST, which we'd like - * to ignore, sysfs also does a WARN_ON and dumps a trace, - * which is bad, so temporarily, skip attributes that are - * already visible (the revalidate one) */ - if (j && attr != &dev_attr_revalidate.attr) - rc = sysfs_add_file_to_group(kobj, attr, - target_attribute_group.name); - /* and make the attribute writeable if we have a set - * function */ - if ((j & 1)) - rc = sysfs_chmod_file(kobj, attr, attr->mode | S_IWUSR); - } + + /* force an update based on parameters read from the device */ + sysfs_update_group(kobj, &target_attribute_group); return 0; } -- cgit v1.2.3 From f7120a4f75168df3c02efacd10403a4ba0bcb29d Mon Sep 17 00:00:00 2001 From: Hannes Reinecke Date: Tue, 18 Mar 2008 14:32:28 +0100 Subject: [SCSI] use default attributes for scsi_host This patch removes the unused sysfs attibute overwriting logic for the scsi host attibutes, and plugs them into the driver core default attribute creation. Signed-off-by: Hannes Reinecke Signed-off-by: Kay Sievers Signed-off-by: James Bottomley --- drivers/scsi/hosts.c | 1 + drivers/scsi/scsi_priv.h | 1 + drivers/scsi/scsi_sysfs.c | 84 +++++++++++++---------------------------------- 3 files changed, 25 insertions(+), 61 deletions(-) diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 63bed62f270e..4e811ca3270e 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -398,6 +398,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) shost->shost_dev.class = &shost_class; snprintf(shost->shost_dev.bus_id, BUS_ID_SIZE, "host%d", shost->host_no); + shost->shost_dev.groups = scsi_sysfs_shost_attr_groups; shost->ehandler = kthread_run(scsi_error_handler, shost, "scsi_eh_%d", shost->host_no); diff --git a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h index 3f34e9376b0a..b33e72516ef8 100644 --- a/drivers/scsi/scsi_priv.h +++ b/drivers/scsi/scsi_priv.h @@ -121,6 +121,7 @@ extern struct scsi_transport_template blank_transport_template; extern void __scsi_remove_device(struct scsi_device *); extern struct bus_type scsi_bus_type; +extern struct attribute_group *scsi_sysfs_shost_attr_groups[]; /* scsi_netlink.c */ #ifdef CONFIG_SCSI_NETLINK diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index fbd7f9ed1251..84e2a8ad83c9 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -251,18 +251,27 @@ 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"); -static struct device_attribute *scsi_sysfs_shost_attrs[] = { - &dev_attr_unique_id, - &dev_attr_host_busy, - &dev_attr_cmd_per_lun, - &dev_attr_can_queue, - &dev_attr_sg_tablesize, - &dev_attr_unchecked_isa_dma, - &dev_attr_proc_name, - &dev_attr_scan, - &dev_attr_hstate, - &dev_attr_supported_mode, - &dev_attr_active_mode, +static struct attribute *scsi_sysfs_shost_attrs[] = { + &dev_attr_unique_id.attr, + &dev_attr_host_busy.attr, + &dev_attr_cmd_per_lun.attr, + &dev_attr_can_queue.attr, + &dev_attr_sg_tablesize.attr, + &dev_attr_unchecked_isa_dma.attr, + &dev_attr_proc_name.attr, + &dev_attr_scan.attr, + &dev_attr_hstate.attr, + &dev_attr_supported_mode.attr, + &dev_attr_active_mode.attr, + NULL +}; + +struct attribute_group scsi_shost_attr_group = { + .attrs = scsi_sysfs_shost_attrs, +}; + +struct attribute_group *scsi_sysfs_shost_attr_groups[] = { + &scsi_shost_attr_group, NULL }; @@ -990,44 +999,6 @@ int scsi_register_interface(struct class_interface *intf) } EXPORT_SYMBOL(scsi_register_interface); - -static struct device_attribute *class_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 class_attr_add(struct device *classdev, - struct device_attribute *attr) -{ - struct device_attribute *base_attr; - - /* - * Spare the caller from having to copy things it's not interested in. - */ - base_attr = class_attr_overridden(scsi_sysfs_shost_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(classdev, attr); -} - /** * scsi_sysfs_add_host - add scsi host to subsystem * @shost: scsi host struct to add to subsystem @@ -1037,20 +1008,11 @@ int scsi_sysfs_add_host(struct Scsi_Host *shost) { int error, i; + /* add host specific attributes */ if (shost->hostt->shost_attrs) { for (i = 0; shost->hostt->shost_attrs[i]; i++) { - error = class_attr_add(&shost->shost_dev, - shost->hostt->shost_attrs[i]); - if (error) - return error; - } - } - - for (i = 0; scsi_sysfs_shost_attrs[i]; i++) { - if (!class_attr_overridden(shost->hostt->shost_attrs, - scsi_sysfs_shost_attrs[i])) { error = device_create_file(&shost->shost_dev, - scsi_sysfs_shost_attrs[i]); + shost->hostt->shost_attrs[i]); if (error) return error; } -- cgit v1.2.3 From 643eb2d932c97a0583381629d632d486934cf7ee Mon Sep 17 00:00:00 2001 From: James Bottomley Date: Sat, 22 Mar 2008 22:42:27 -0500 Subject: [SCSI] rework scsi_target allocation The current target allocation code registeres each possible target with sysfs; it will be deleted again if no useable LUN on this target was found. This results in a string of 'target add/target remove' uevents. Based on a patch by Hannes Reinecke this patch reworks the target allocation code so that only uevents for existing targets are sent. The sysfs registration is split off from the existing scsi_target_alloc() into a in a new scsi_add_target() function, which should be called whenever an existing target is found. Only then a uevent is sent, so we'll be generating events for existing targets only. Signed-off-by: James Bottomley --- drivers/scsi/scsi_scan.c | 72 ++++++++++++++++++++++------------------------ drivers/scsi/scsi_sysfs.c | 27 +++++++++++++++++ include/scsi/scsi_device.h | 3 +- 3 files changed, 63 insertions(+), 39 deletions(-) diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c index e1644b270cdc..fcd7455ffc39 100644 --- a/drivers/scsi/scsi_scan.c +++ b/drivers/scsi/scsi_scan.c @@ -322,6 +322,21 @@ out: return NULL; } +static void scsi_target_destroy(struct scsi_target *starget) +{ + struct device *dev = &starget->dev; + struct Scsi_Host *shost = dev_to_shost(dev->parent); + unsigned long flags; + + transport_destroy_device(dev); + spin_lock_irqsave(shost->host_lock, flags); + if (shost->hostt->target_destroy) + shost->hostt->target_destroy(starget); + list_del_init(&starget->siblings); + spin_unlock_irqrestore(shost->host_lock, flags); + put_device(dev); +} + static void scsi_target_dev_release(struct device *dev) { struct device *parent = dev->parent; @@ -406,7 +421,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, starget->channel = channel; INIT_LIST_HEAD(&starget->siblings); INIT_LIST_HEAD(&starget->devices); - starget->state = STARGET_RUNNING; + starget->state = STARGET_CREATED; starget->scsi_level = SCSI_2; retry: spin_lock_irqsave(shost->host_lock, flags); @@ -419,18 +434,6 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, spin_unlock_irqrestore(shost->host_lock, flags); /* allocate and add */ transport_setup_device(dev); - error = device_add(dev); - if (error) { - dev_err(dev, "target device_add failed, error %d\n", error); - spin_lock_irqsave(shost->host_lock, flags); - list_del_init(&starget->siblings); - spin_unlock_irqrestore(shost->host_lock, flags); - transport_destroy_device(dev); - put_device(parent); - kfree(starget); - return NULL; - } - transport_add_device(dev); if (shost->hostt->target_alloc) { error = shost->hostt->target_alloc(starget); @@ -438,9 +441,7 @@ static struct scsi_target *scsi_alloc_target(struct device *parent, dev_printk(KERN_ERR, dev, "target allocation failed, error %d\n", error); /* don't want scsi_target_reap to do the final * put because it will be under the host lock */ - get_device(dev); - scsi_target_reap(starget); - put_device(dev); + scsi_target_destroy(starget); return NULL; } } @@ -467,18 +468,10 @@ static void scsi_target_reap_usercontext(struct work_struct *work) { struct scsi_target *starget = container_of(work, struct scsi_target, ew.work); - struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); - unsigned long flags; transport_remove_device(&starget->dev); device_del(&starget->dev); - transport_destroy_device(&starget->dev); - spin_lock_irqsave(shost->host_lock, flags); - if (shost->hostt->target_destroy) - shost->hostt->target_destroy(starget); - list_del_init(&starget->siblings); - spin_unlock_irqrestore(shost->host_lock, flags); - put_device(&starget->dev); + scsi_target_destroy(starget); } /** @@ -493,21 +486,25 @@ void scsi_target_reap(struct scsi_target *starget) { struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); unsigned long flags; + enum scsi_target_state state; + int empty; spin_lock_irqsave(shost->host_lock, flags); + state = starget->state; + empty = --starget->reap_ref == 0 && + list_empty(&starget->devices) ? 1 : 0; + spin_unlock_irqrestore(shost->host_lock, flags); - if (--starget->reap_ref == 0 && list_empty(&starget->devices)) { - BUG_ON(starget->state == STARGET_DEL); - starget->state = STARGET_DEL; - spin_unlock_irqrestore(shost->host_lock, flags); - execute_in_process_context(scsi_target_reap_usercontext, - &starget->ew); + if (!empty) return; - } - spin_unlock_irqrestore(shost->host_lock, flags); - - return; + BUG_ON(state == STARGET_DEL); + starget->state = STARGET_DEL; + if (state == STARGET_CREATED) + scsi_target_destroy(starget); + else + execute_in_process_context(scsi_target_reap_usercontext, + &starget->ew); } /** @@ -1056,8 +1053,9 @@ static int scsi_probe_and_add_lun(struct scsi_target *starget, scsi_inq_str(vend, result, 8, 16), scsi_inq_str(mod, result, 16, 32)); }); + } - + res = SCSI_SCAN_TARGET_PRESENT; goto out_free_result; } @@ -1497,7 +1495,6 @@ struct scsi_device *__scsi_add_device(struct Scsi_Host *shost, uint channel, if (scsi_host_scan_allowed(shost)) scsi_probe_and_add_lun(starget, lun, NULL, &sdev, 1, hostdata); mutex_unlock(&shost->scan_mutex); - transport_configure_device(&starget->dev); scsi_target_reap(starget); put_device(&starget->dev); @@ -1578,7 +1575,6 @@ static void __scsi_scan_target(struct device *parent, unsigned int channel, out_reap: /* now determine if the target has any children at all * and if not, nuke it */ - transport_configure_device(&starget->dev); scsi_target_reap(starget); put_device(&starget->dev); diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 84e2a8ad83c9..198aa4571e35 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -809,6 +809,27 @@ sdev_store_queue_type_rw(struct device *dev, struct device_attribute *attr, return count; } +static int scsi_target_add(struct scsi_target *starget) +{ + int error; + + if (starget->state != STARGET_CREATED) + return 0; + + error = device_add(&starget->dev); + if (error) { + dev_err(&starget->dev, "target device_add failed, error %d\n", error); + get_device(&starget->dev); + scsi_target_reap(starget); + put_device(&starget->dev); + return error; + } + transport_add_device(&starget->dev); + starget->state = STARGET_RUNNING; + + return 0; +} + 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); @@ -824,10 +845,16 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) { int error, i; struct request_queue *rq = sdev->request_queue; + struct scsi_target *starget = sdev->sdev_target; if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0) return error; + error = scsi_target_add(starget); + if (error) + return error; + + transport_configure_device(&starget->dev); error = device_add(&sdev->sdev_gendev); if (error) { put_device(sdev->sdev_gendev.parent); diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h index b8b19e2f57bb..f6a9fe0ef09c 100644 --- a/include/scsi/scsi_device.h +++ b/include/scsi/scsi_device.h @@ -181,7 +181,8 @@ struct scsi_device { sdev_printk(prefix, (scmd)->device, fmt, ##a) enum scsi_target_state { - STARGET_RUNNING = 1, + STARGET_CREATED = 1, + STARGET_RUNNING, STARGET_DEL, }; -- cgit v1.2.3 From 97f46ae45c70857e459b7f8df1fc2807e7bd90a9 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 19 Apr 2008 00:43:14 +0900 Subject: [SCSI] bsg: add release callback support This patch adds release callback support, which is called when a bsg device goes away. bsg_register_queue() takes a pointer to a callback function. This feature is useful for stuff like sas_host that can't use the release callback in struct device. If a caller doesn't need bsg's release callback, it can call bsg_register_queue() with NULL pointer (e.g. scsi devices can use release callback in struct device so they don't need bsg's callback). With this patch, bsg uses kref for refcounts on bsg devices instead of get/put_device in fops->open/release. bsg calls put_device and the caller's release callback (if it was registered) in kref_put's release. Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- block/bsg.c | 43 ++++++++++++++++++++++++++------------- drivers/scsi/scsi_sysfs.c | 2 +- drivers/scsi/scsi_transport_sas.c | 2 +- include/linux/bsg.h | 14 +++++++++---- 4 files changed, 41 insertions(+), 20 deletions(-) diff --git a/block/bsg.c b/block/bsg.c index f51172ed27c2..23ea4fd1a66d 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -699,14 +699,26 @@ static struct bsg_device *bsg_alloc_device(void) return bd; } +static void bsg_kref_release_function(struct kref *kref) +{ + struct bsg_class_device *bcd = + container_of(kref, struct bsg_class_device, ref); + + if (bcd->release) + bcd->release(bcd->parent); + + put_device(bcd->parent); +} + static int bsg_put_device(struct bsg_device *bd) { - int ret = 0; - struct device *dev = bd->queue->bsg_dev.dev; + int ret = 0, do_free; + struct request_queue *q = bd->queue; mutex_lock(&bsg_mutex); - if (!atomic_dec_and_test(&bd->ref_count)) + do_free = atomic_dec_and_test(&bd->ref_count); + if (!do_free) goto out; dprintk("%s: tearing down\n", bd->name); @@ -723,12 +735,13 @@ static int bsg_put_device(struct bsg_device *bd) */ ret = bsg_complete_all_commands(bd); - blk_put_queue(bd->queue); hlist_del(&bd->dev_list); kfree(bd); out: mutex_unlock(&bsg_mutex); - put_device(dev); + kref_put(&q->bsg_dev.ref, bsg_kref_release_function); + if (do_free) + blk_put_queue(q); return ret; } @@ -796,7 +809,7 @@ static struct bsg_device *bsg_get_device(struct inode *inode, struct file *file) mutex_lock(&bsg_mutex); bcd = idr_find(&bsg_minor_idr, iminor(inode)); if (bcd) - get_device(bcd->dev); + kref_get(&bcd->ref); mutex_unlock(&bsg_mutex); if (!bcd) @@ -808,7 +821,7 @@ static struct bsg_device *bsg_get_device(struct inode *inode, struct file *file) bd = bsg_add_device(inode, bcd->queue, file); if (IS_ERR(bd)) - put_device(bcd->dev); + kref_put(&bcd->ref, bsg_kref_release_function); return bd; } @@ -947,14 +960,14 @@ void bsg_unregister_queue(struct request_queue *q) idr_remove(&bsg_minor_idr, bcd->minor); sysfs_remove_link(&q->kobj, "bsg"); device_unregister(bcd->class_dev); - put_device(bcd->dev); bcd->class_dev = NULL; + kref_put(&bcd->ref, bsg_kref_release_function); mutex_unlock(&bsg_mutex); } EXPORT_SYMBOL_GPL(bsg_unregister_queue); -int bsg_register_queue(struct request_queue *q, struct device *gdev, - const char *name) +int bsg_register_queue(struct request_queue *q, struct device *parent, + const char *name, void (*release)(struct device *)) { struct bsg_class_device *bcd; dev_t dev; @@ -965,7 +978,7 @@ int bsg_register_queue(struct request_queue *q, struct device *gdev, if (name) devname = name; else - devname = gdev->bus_id; + devname = parent->bus_id; /* * we need a proper transport to send commands, not a stacked device @@ -996,9 +1009,11 @@ int bsg_register_queue(struct request_queue *q, struct device *gdev, bcd->minor = minor; bcd->queue = q; - bcd->dev = get_device(gdev); + bcd->parent = get_device(parent); + bcd->release = release; + kref_init(&bcd->ref); dev = MKDEV(bsg_major, bcd->minor); - class_dev = device_create(bsg_class, gdev, dev, "%s", devname); + class_dev = device_create(bsg_class, parent, dev, "%s", devname); if (IS_ERR(class_dev)) { ret = PTR_ERR(class_dev); goto put_dev; @@ -1017,7 +1032,7 @@ int bsg_register_queue(struct request_queue *q, struct device *gdev, unregister_class_dev: device_unregister(class_dev); put_dev: - put_device(gdev); + put_device(parent); remove_idr: idr_remove(&bsg_minor_idr, minor); unlock: diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 198aa4571e35..049103f1d16f 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -889,7 +889,7 @@ int scsi_sysfs_add_sdev(struct scsi_device *sdev) goto out; } - error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL); + error = bsg_register_queue(rq, &sdev->sdev_gendev, NULL, NULL); if (error) sdev_printk(KERN_INFO, sdev, diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 27ec625ab771..94ff29f7c34b 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -219,7 +219,7 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) if (!q) return -ENOMEM; - error = bsg_register_queue(q, dev, name); + error = bsg_register_queue(q, dev, name, NULL); if (error) { blk_cleanup_queue(q); return -ENOMEM; diff --git a/include/linux/bsg.h b/include/linux/bsg.h index e8406c55c6d3..cf0303a60611 100644 --- a/include/linux/bsg.h +++ b/include/linux/bsg.h @@ -56,19 +56,25 @@ struct sg_io_v4 { #if defined(CONFIG_BLK_DEV_BSG) struct bsg_class_device { struct device *class_dev; - struct device *dev; + struct device *parent; int minor; struct request_queue *queue; + struct kref ref; + void (*release)(struct device *); }; -extern int bsg_register_queue(struct request_queue *, struct device *, const char *); +extern int bsg_register_queue(struct request_queue *q, + struct device *parent, const char *name, + void (*release)(struct device *)); extern void bsg_unregister_queue(struct request_queue *); #else -static inline int bsg_register_queue(struct request_queue * rq, struct device *dev, const char *name) +static inline int bsg_register_queue(struct request_queue *q, + struct device *parent, const char *name, + void (*release)(struct device *)) { return 0; } -static inline void bsg_unregister_queue(struct request_queue *rq) +static inline void bsg_unregister_queue(struct request_queue *q) { } #endif -- cgit v1.2.3 From 93c20a59af4624aedf53f8320606b355aa951bc1 Mon Sep 17 00:00:00 2001 From: FUJITA Tomonori Date: Sat, 19 Apr 2008 00:43:15 +0900 Subject: [SCSI] scsi_transport_sas: fix the lifetime of sas bsg objects scsi_transport_sas calls blk_cleanup_queue too early for bsg queues. If a user holds a sas_host, end_device, or expander device open, remove the device, then send a request to it, we get a kernel crash. We need to call blk_cleanup_queue in the release callback as we do with scsi devices. This patch moves blk_cleanup_queue to sas_expander_release and sas_end_device_release from sas_bsg_remove. sas_host can't use the release callback in struct device so use bsg's release callback. Signed-off-by: FUJITA Tomonori Signed-off-by: James Bottomley --- drivers/scsi/scsi_transport_sas.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_transport_sas.c b/drivers/scsi/scsi_transport_sas.c index 94ff29f7c34b..7899e3dda9bf 100644 --- a/drivers/scsi/scsi_transport_sas.c +++ b/drivers/scsi/scsi_transport_sas.c @@ -192,6 +192,16 @@ static void sas_non_host_smp_request(struct request_queue *q) sas_smp_request(q, rphy_to_shost(rphy), rphy); } +static void sas_host_release(struct device *dev) +{ + struct Scsi_Host *shost = dev_to_shost(dev); + struct sas_host_attrs *sas_host = to_sas_host_attrs(shost); + struct request_queue *q = sas_host->q; + + if (q) + blk_cleanup_queue(q); +} + static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) { struct request_queue *q; @@ -199,6 +209,7 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) struct device *dev; char namebuf[BUS_ID_SIZE]; const char *name; + void (*release)(struct device *); if (!to_sas_internal(shost->transportt)->f->smp_handler) { printk("%s can't handle SMP requests\n", shost->hostt->name); @@ -209,17 +220,19 @@ static int sas_bsg_initialize(struct Scsi_Host *shost, struct sas_rphy *rphy) q = blk_init_queue(sas_non_host_smp_request, NULL); dev = &rphy->dev; name = dev->bus_id; + release = NULL; } else { q = blk_init_queue(sas_host_smp_request, NULL); dev = &shost->shost_gendev; snprintf(namebuf, sizeof(namebuf), "sas_host%d", shost->host_no); name = namebuf; + release = sas_host_release; } if (!q) return -ENOMEM; - error = bsg_register_queue(q, dev, name, NULL); + error = bsg_register_queue(q, dev, name, release); if (error) { blk_cleanup_queue(q); return -ENOMEM; @@ -253,7 +266,6 @@ static void sas_bsg_remove(struct Scsi_Host *shost, struct sas_rphy *rphy) return; bsg_unregister_queue(q); - blk_cleanup_queue(q); } /* @@ -1301,6 +1313,9 @@ static void sas_expander_release(struct device *dev) struct sas_rphy *rphy = dev_to_rphy(dev); struct sas_expander_device *edev = rphy_to_expander_device(rphy); + if (rphy->q) + blk_cleanup_queue(rphy->q); + put_device(dev->parent); kfree(edev); } @@ -1310,6 +1325,9 @@ static void sas_end_device_release(struct device *dev) struct sas_rphy *rphy = dev_to_rphy(dev); struct sas_end_device *edev = rphy_to_end_device(rphy); + if (rphy->q) + blk_cleanup_queue(rphy->q); + put_device(dev->parent); kfree(edev); } -- cgit v1.2.3 From be0d67680d524981dd65c661efe3c9cbd52a684f Mon Sep 17 00:00:00 2001 From: Denys Vlasenko Date: Sun, 23 Mar 2008 04:41:22 +0100 Subject: [SCSI] aic7xxx, aic79xx: deinline functions Deinlines and moves big functions from .h to .c files. Adds prototypes for ahc_lookup_scb and ahd_lookup_scb to .h files. Signed-off-by: Denys Vlasenko Signed-off-by: James Bottomley --- drivers/scsi/aic7xxx/aic79xx.reg | 2 +- drivers/scsi/aic7xxx/aic79xx_core.c | 771 +++++++++++++++++++++++++++- drivers/scsi/aic7xxx/aic79xx_inline.h | 903 ++++----------------------------- drivers/scsi/aic7xxx/aic79xx_osm.c | 162 +++++- drivers/scsi/aic7xxx/aic79xx_osm.h | 176 +------ drivers/scsi/aic7xxx/aic7xxx.reg | 2 +- drivers/scsi/aic7xxx/aic7xxx_core.c | 508 ++++++++++++++++++- drivers/scsi/aic7xxx/aic7xxx_inline.h | 609 ++-------------------- drivers/scsi/aic7xxx/aic7xxx_osm.c | 77 ++- drivers/scsi/aic7xxx/aic7xxx_osm.h | 137 +---- drivers/scsi/aic7xxx/aic7xxx_osm_pci.c | 51 ++ 11 files changed, 1719 insertions(+), 1679 deletions(-) diff --git a/drivers/scsi/aic7xxx/aic79xx.reg b/drivers/scsi/aic7xxx/aic79xx.reg index be14e2ecb8f7..6ab514d7f980 100644 --- a/drivers/scsi/aic7xxx/aic79xx.reg +++ b/drivers/scsi/aic7xxx/aic79xx.reg @@ -3649,7 +3649,7 @@ scratch_ram { KERNEL_TQINPOS { size 1 } - TQINPOS { + TQINPOS { size 1 } /* diff --git a/drivers/scsi/aic7xxx/aic79xx_core.c b/drivers/scsi/aic7xxx/aic79xx_core.c index ade0fb8fbdb2..336f4bea251b 100644 --- a/drivers/scsi/aic7xxx/aic79xx_core.c +++ b/drivers/scsi/aic7xxx/aic79xx_core.c @@ -266,8 +266,752 @@ static int ahd_match_scb(struct ahd_softc *ahd, struct scb *scb, int target, char channel, int lun, u_int tag, role_t role); -/******************************** Private Inlines *****************************/ +/************************ Sequencer Execution Control *************************/ +void +ahd_set_modes(struct ahd_softc *ahd, ahd_mode src, ahd_mode dst) +{ + if (ahd->src_mode == src && ahd->dst_mode == dst) + return; +#ifdef AHD_DEBUG + if (ahd->src_mode == AHD_MODE_UNKNOWN + || ahd->dst_mode == AHD_MODE_UNKNOWN) + panic("Setting mode prior to saving it.\n"); + if ((ahd_debug & AHD_SHOW_MODEPTR) != 0) + printf("%s: Setting mode 0x%x\n", ahd_name(ahd), + ahd_build_mode_state(ahd, src, dst)); +#endif + ahd_outb(ahd, MODE_PTR, ahd_build_mode_state(ahd, src, dst)); + ahd->src_mode = src; + ahd->dst_mode = dst; +} + +void +ahd_update_modes(struct ahd_softc *ahd) +{ + ahd_mode_state mode_ptr; + ahd_mode src; + ahd_mode dst; + + mode_ptr = ahd_inb(ahd, MODE_PTR); +#ifdef AHD_DEBUG + if ((ahd_debug & AHD_SHOW_MODEPTR) != 0) + printf("Reading mode 0x%x\n", mode_ptr); +#endif + ahd_extract_mode_state(ahd, mode_ptr, &src, &dst); + ahd_known_modes(ahd, src, dst); +} + +void +ahd_assert_modes(struct ahd_softc *ahd, ahd_mode srcmode, + ahd_mode dstmode, const char *file, int line) +{ +#ifdef AHD_DEBUG + if ((srcmode & AHD_MK_MSK(ahd->src_mode)) == 0 + || (dstmode & AHD_MK_MSK(ahd->dst_mode)) == 0) { + panic("%s:%s:%d: Mode assertion failed.\n", + ahd_name(ahd), file, line); + } +#endif +} + +#define AHD_ASSERT_MODES(ahd, source, dest) \ + ahd_assert_modes(ahd, source, dest, __FILE__, __LINE__); + +ahd_mode_state +ahd_save_modes(struct ahd_softc *ahd) +{ + if (ahd->src_mode == AHD_MODE_UNKNOWN + || ahd->dst_mode == AHD_MODE_UNKNOWN) + ahd_update_modes(ahd); + + return (ahd_build_mode_state(ahd, ahd->src_mode, ahd->dst_mode)); +} + +void +ahd_restore_modes(struct ahd_softc *ahd, ahd_mode_state state) +{ + ahd_mode src; + ahd_mode dst; + + ahd_extract_mode_state(ahd, state, &src, &dst); + ahd_set_modes(ahd, src, dst); +} + +/* + * Determine whether the sequencer has halted code execution. + * Returns non-zero status if the sequencer is stopped. + */ +int +ahd_is_paused(struct ahd_softc *ahd) +{ + return ((ahd_inb(ahd, HCNTRL) & PAUSE) != 0); +} + +/* + * Request that the sequencer stop and wait, indefinitely, for it + * to stop. The sequencer will only acknowledge that it is paused + * once it has reached an instruction boundary and PAUSEDIS is + * cleared in the SEQCTL register. The sequencer may use PAUSEDIS + * for critical sections. + */ +void +ahd_pause(struct ahd_softc *ahd) +{ + ahd_outb(ahd, HCNTRL, ahd->pause); + + /* + * Since the sequencer can disable pausing in a critical section, we + * must loop until it actually stops. + */ + while (ahd_is_paused(ahd) == 0) + ; +} + +/* + * Allow the sequencer to continue program execution. + * We check here to ensure that no additional interrupt + * sources that would cause the sequencer to halt have been + * asserted. If, for example, a SCSI bus reset is detected + * while we are fielding a different, pausing, interrupt type, + * we don't want to release the sequencer before going back + * into our interrupt handler and dealing with this new + * condition. + */ +void +ahd_unpause(struct ahd_softc *ahd) +{ + /* + * Automatically restore our modes to those saved + * prior to the first change of the mode. + */ + if (ahd->saved_src_mode != AHD_MODE_UNKNOWN + && ahd->saved_dst_mode != AHD_MODE_UNKNOWN) { + if ((ahd->flags & AHD_UPDATE_PEND_CMDS) != 0) + ahd_reset_cmds_pending(ahd); + ahd_set_modes(ahd, ahd->saved_src_mode, ahd->saved_dst_mode); + } + + if ((ahd_inb(ahd, INTSTAT) & ~CMDCMPLT) == 0) + ahd_outb(ahd, HCNTRL, ahd->unpause); + + ahd_known_modes(ahd, AHD_MODE_UNKNOWN, AHD_MODE_UNKNOWN); +} + +/*********************** Scatter Gather List Handling *************************/ +void * +ahd_sg_setup(struct ahd_softc *ahd, struct scb *scb, + void *sgptr, dma_addr_t addr, bus_size_t len, int last) +{ + scb->sg_count++; + if (sizeof(dma_addr_t) > 4 + && (ahd->flags & AHD_64BIT_ADDRESSING) != 0) { + struct ahd_dma64_seg *sg; + + sg = (struct ahd_dma64_seg *)sgptr; + sg->addr = ahd_htole64(addr); + sg->len = ahd_htole32(len | (last ? AHD_DMA_LAST_SEG : 0)); + return (sg + 1); + } else { + struct ahd_dma_seg *sg; + + sg = (struct ahd_dma_seg *)sgptr; + sg->addr = ahd_htole32(addr & 0xFFFFFFFF); + sg->len = ahd_htole32(len | ((addr >> 8) & 0x7F000000) + | (last ? AHD_DMA_LAST_SEG : 0)); + return (sg + 1); + } +} + +void +ahd_setup_scb_common(struct ahd_softc *ahd, struct scb *scb) +{ + /* XXX Handle target mode SCBs. */ + scb->crc_retry_count = 0; + if ((scb->flags & SCB_PACKETIZED) != 0) { + /* XXX what about ACA?? It is type 4, but TAG_TYPE == 0x3. */ + scb->hscb->task_attribute = scb->hscb->control & SCB_TAG_TYPE; + } else { + if (ahd_get_transfer_length(scb) & 0x01) + scb->hscb->task_attribute = SCB_XFERLEN_ODD; + else + scb->hscb->task_attribute = 0; + } + + if (scb->hscb->cdb_len <= MAX_CDB_LEN_WITH_SENSE_ADDR + || (scb->hscb->cdb_len & SCB_CDB_LEN_PTR) != 0) + scb->hscb->shared_data.idata.cdb_plus_saddr.sense_addr = + ahd_htole32(scb->sense_busaddr); +} + +void +ahd_setup_data_scb(struct ahd_softc *ahd, struct scb *scb) +{ + /* + * Copy the first SG into the "current" data ponter area. + */ + if ((ahd->flags & AHD_64BIT_ADDRESSING) != 0) { + struct ahd_dma64_seg *sg; + + sg = (struct ahd_dma64_seg *)scb->sg_list; + scb->hscb->dataptr = sg->addr; + scb->hscb->datacnt = sg->len; + } else { + struct ahd_dma_seg *sg; + uint32_t *dataptr_words; + + sg = (struct ahd_dma_seg *)scb->sg_list; + dataptr_words = (uint32_t*)&scb->hscb->dataptr; + dataptr_words[0] = sg->addr; + dataptr_words[1] = 0; + if ((ahd->flags & AHD_39BIT_ADDRESSING) != 0) { + uint64_t high_addr; + + high_addr = ahd_le32toh(sg->len) & 0x7F000000; + scb->hscb->dataptr |= ahd_htole64(high_addr << 8); + } + scb->hscb->datacnt = sg->len; + } + /* + * Note where to find the SG entries in bus space. + * We also set the full residual flag which the + * sequencer will clear as soon as a data transfer + * occurs. + */ + scb->hscb->sgptr = ahd_htole32(scb->sg_list_busaddr|SG_FULL_RESID); +} + +void +ahd_setup_noxfer_scb(struct ahd_softc *ahd, struct scb *scb) +{ + scb->hscb->sgptr = ahd_htole32(SG_LIST_NULL); + scb->hscb->dataptr = 0; + scb->hscb->datacnt = 0; +} + +/************************** Memory mapping routines ***************************/ +void * +ahd_sg_bus_to_virt(struct ahd_softc *ahd, struct scb *scb, uint32_t sg_busaddr) +{ + dma_addr_t sg_offset; + + /* sg_list_phys points to entry 1, not 0 */ + sg_offset = sg_busaddr - (scb->sg_list_busaddr - ahd_sg_size(ahd)); + return ((uint8_t *)scb->sg_list + sg_offset); +} + +uint32_t +ahd_sg_virt_to_bus(struct ahd_softc *ahd, struct scb *scb, void *sg) +{ + dma_addr_t sg_offset; + + /* sg_list_phys points to entry 1, not 0 */ + sg_offset = ((uint8_t *)sg - (uint8_t *)scb->sg_list) + - ahd_sg_size(ahd); + + return (scb->sg_list_busaddr + sg_offset); +} + +void +ahd_sync_scb(struct ahd_softc *ahd, struct scb *scb, int op) +{ + ahd_dmamap_sync(ahd, ahd->scb_data.hscb_dmat, + scb->hscb_map->dmamap, + /*offset*/(uint8_t*)scb->hscb - scb->hscb_map->vaddr, + /*len*/sizeof(*scb->hscb), op); +} + +void +ahd_sync_sglist(struct ahd_softc *ahd, struct scb *scb, int op) +{ + if (scb->sg_count == 0) + return; + + ahd_dmamap_sync(ahd, ahd->scb_data.sg_dmat, + scb->sg_map->dmamap, + /*offset*/scb->sg_list_busaddr - ahd_sg_size(ahd), + /*len*/ahd_sg_size(ahd) * scb->sg_count, op); +} + +void +ahd_sync_sense(struct ahd_softc *ahd, struct scb *scb, int op) +{ + ahd_dmamap_sync(ahd, ahd->scb_data.sense_dmat, + scb->sense_map->dmamap, + /*offset*/scb->sense_busaddr, + /*len*/AHD_SENSE_BUFSIZE, op); +} + +uint32_t +ahd_targetcmd_offset(struct ahd_softc *ahd, u_int index) +{ + return (((uint8_t *)&ahd->targetcmds[index]) + - (uint8_t *)ahd->qoutfifo); +} + +/*********************** Miscelaneous Support Functions ***********************/ +/* + * Return pointers to the transfer negotiation information + * for the specified our_id/remote_id pair. + */ +struct ahd_initiator_tinfo * +ahd_fetch_transinfo(struct ahd_softc *ahd, char channel, u_int our_id, + u_int remote_id, struct ahd_tmode_tstate **tstate) +{ + /* + * Transfer data structures are stored from the perspective + * of the target role. Since the parameters for a connection + * in the initiator role to a given target are the same as + * when the roles are reversed, we pretend we are the target. + */ + if (channel == 'B') + our_id += 8; + *tstate = ahd->enabled_targets[our_id]; + return (&(*tstate)->transinfo[remote_id]); +} + +uint16_t +ahd_inw(struct ahd_softc *ahd, u_int port) +{ + /* + * Read high byte first as some registers increment + * or have other side effects when the low byte is + * read. + */ + uint16_t r = ahd_inb(ahd, port+1) << 8; + return r | ahd_inb(ahd, port); +} + +void +ahd_outw(struct ahd_softc *ahd, u_int port, u_int value) +{ + /* + * Write low byte first to accomodate registers + * such as PRGMCNT where the order maters. + */ + ahd_outb(ahd, port, value & 0xFF); + ahd_outb(ahd, port+1, (value >> 8) & 0xFF); +} + +uint32_t +ahd_inl(struct ahd_softc *ahd, u_int port) +{ + return ((ahd_inb(ahd, port)) + | (ahd_inb(ahd, port+1) << 8) + | (ahd_inb(ahd, port+2) << 16) + | (ahd_inb(ahd, port+3) << 24)); +} + +void +ahd_outl(struct ahd_softc *ahd, u_int port, uint32_t value) +{ + ahd_outb(ahd, port, (value) & 0xFF); + ahd_outb(ahd, port+1, ((value) >> 8) & 0xFF); + ahd_outb(ahd, port+2, ((value) >> 16) & 0xFF); + ahd_outb(ahd, port+3, ((value) >> 24) & 0xFF); +} + +uint64_t +ahd_inq(struct ahd_softc *ahd, u_int port) +{ + return ((ahd_inb(ahd, port)) + | (ahd_inb(ahd, port+1) << 8) + | (ahd_inb(ahd, port+2) << 16) + | (ahd_inb(ahd, port+3) << 24) + | (((uint64_t)ahd_inb(ahd, port+4)) << 32) + | (((uint64_t)ahd_inb(ahd, port+5)) << 40) + | (((uint64_t)ahd_inb(ahd, port+6)) << 48) + | (((uint64_t)ahd_inb(ahd, port+7)) << 56)); +} + +void +ahd_outq(struct ahd_softc *ahd, u_int port, uint64_t value) +{ + ahd_outb(ahd, port, value & 0xFF); + ahd_outb(ahd, port+1, (value >> 8) & 0xFF); + ahd_outb(ahd, port+2, (value >> 16) & 0xFF); + ahd_outb(ahd, port+3, (value >> 24) & 0xFF); + ahd_outb(ahd, port+4, (value >> 32) & 0xFF); + ahd_outb(ahd, port+5, (value >> 40) & 0xFF); + ahd_outb(ahd, port+6, (value >> 48) & 0xFF); + ahd_outb(ahd, port+7, (value >> 56) & 0xFF); +} + +u_int +ahd_get_scbptr(struct ahd_softc *ahd) +{ + AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK), + ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK)); + return (ahd_inb(ahd, SCBPTR) | (ahd_inb(ahd, SCBPTR + 1) << 8)); +} + +void +ahd_set_scbptr(struct ahd_softc *ahd, u_int scbptr) +{ + AHD_ASSERT_MODES(ahd, ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK), + ~(AHD_MODE_UNKNOWN_MSK|AHD_MODE_CFG_MSK)); + ahd_outb(ahd, SCBPTR, scbptr & 0xFF); + ahd_outb(ahd, SCBPTR+1, (scbptr >> 8) & 0xFF); +} + +u_int +ahd_get_hnscb_qoff(struct ahd_softc *ahd) +{ + return (ahd_inw_atomic(ahd, HNSCB_QOFF)); +} +void +ahd_set_hnscb_qoff(struct ahd_softc *ahd, u_int value) +{ + ahd_outw_atomic(ahd, HNSCB_QOFF, value); +} + +u_int +ahd_get_hescb_qoff(struct ahd_softc *ahd) +{ + return (ahd_inb(ahd, HESCB_QOFF)); +} + +void +ahd_set_hescb_qoff(struct ahd_softc *ahd, u_int value) +{ + ahd_outb(ahd, HESCB_QOFF, value); +} + +u_int +ahd_get_snscb_qoff(struct ahd_softc *ahd) +{ + u_int oldvalue; + + AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK); + oldvalue = ahd_inw(ahd, SNSCB_QOFF); + ahd_outw(ahd, SNSCB_QOFF, oldvalue); + return (oldvalue); +} + +void +ahd_set_snscb_qoff(struct ahd_softc *ahd, u_int value) +{ + AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK); + ahd_outw(ahd, SNSCB_QOFF, value); +} + +u_int +ahd_get_sescb_qoff(struct ahd_softc *ahd) +{ + AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK); + return (ahd_inb(ahd, SESCB_QOFF)); +} + +void +ahd_set_sescb_qoff(struct ahd_softc *ahd, u_int value) +{ + AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK); + ahd_outb(ahd, SESCB_QOFF, value); +} + +u_int +ahd_get_sdscb_qoff(struct ahd_softc *ahd) +{ + AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK); + return (ahd_inb(ahd, SDSCB_QOFF) | (ahd_inb(ahd, SDSCB_QOFF + 1) << 8)); +} + +void +ahd_set_sdscb_qoff(struct ahd_softc *ahd, u_int value) +{ + AHD_ASSERT_MODES(ahd, AHD_MODE_CCHAN_MSK, AHD_MODE_CCHAN_MSK); + ahd_outb(ahd, SDSCB_QOFF, value & 0xFF); + ahd_outb(ahd, SDSCB_QOFF+1, (value >> 8) & 0xFF); +} + +u_int +ahd_inb_scbram(struct ahd_softc *ahd, u_int offset) +{ + u_int value; + + /* + * Workaround PCI-X Rev A. hardware bug. + * After a host read of SCB memory, the chip + * may become confused into thinking prefetch + * was required. This starts the discard timer + * running and can cause an unexpected discard + * timer interrupt. The work around is to read + * a normal register prior to the exhaustion of + * the discard timer. The mode pointer register + * has no side effects and so serves well for + * this purpose. + * + * Razor #528 + */ + value = ahd_inb(ahd, of