From f95d882d81ee731be2a4a3b34f86810e29b68836 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 10 Feb 2007 14:41:56 -0800 Subject: PCI/sysfs/kobject kernel-doc fixes Fix kernel-doc warnings in PCI, sysfs, and kobject files. Signed-off-by: Randy Dunlap Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'fs/sysfs/file.c') diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index c0e117649a4d..98b0910ad80c 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -54,7 +54,7 @@ static struct sysfs_ops subsys_sysfs_ops = { /** * add_to_collection - add buffer to a collection * @buffer: buffer to be added - * @node inode of set to add to + * @node: inode of set to add to */ static inline void -- cgit From dfa87c824a9a5430008acd1ed2e8111ed164fcbe Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 20 Feb 2007 15:02:44 -0500 Subject: sysfs: allow attributes to be added to groups This patch (as860) adds two new sysfs routines: sysfs_add_file_to_group() and sysfs_remove_file_from_group(). A later patch adds code that uses the new routines. Signed-off-by: Alan Stern Cc: Maneesh Soni Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/sysfs.h | 17 +++++++++++++++++ 2 files changed, 61 insertions(+) (limited to 'fs/sysfs/file.c') diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 98b0910ad80c..8d4d839a9d88 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -501,6 +501,30 @@ int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) } +/** + * sysfs_add_file_to_group - add an attribute file to a pre-existing group. + * @kobj: object we're acting for. + * @attr: attribute descriptor. + * @group: group name. + */ +int sysfs_add_file_to_group(struct kobject *kobj, + const struct attribute *attr, const char *group) +{ + struct dentry *dir; + int error; + + dir = lookup_one_len(group, kobj->dentry, strlen(group)); + if (IS_ERR(dir)) + error = PTR_ERR(dir); + else { + error = sysfs_add_file(dir, attr, SYSFS_KOBJ_ATTR); + dput(dir); + } + return error; +} +EXPORT_SYMBOL_GPL(sysfs_add_file_to_group); + + /** * sysfs_update_file - update the modified timestamp on an object attribute. * @kobj: object we're acting for. @@ -586,6 +610,26 @@ void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) } +/** + * sysfs_remove_file_from_group - remove an attribute file from a group. + * @kobj: object we're acting for. + * @attr: attribute descriptor. + * @group: group name. + */ +void sysfs_remove_file_from_group(struct kobject *kobj, + const struct attribute *attr, const char *group) +{ + struct dentry *dir; + + dir = lookup_one_len(group, kobj->dentry, strlen(group)); + if (!IS_ERR(dir)) { + sysfs_hash_and_remove(dir, attr->name); + dput(dir); + } +} +EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); + + EXPORT_SYMBOL_GPL(sysfs_create_file); EXPORT_SYMBOL_GPL(sysfs_remove_file); EXPORT_SYMBOL_GPL(sysfs_update_file); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 192de3afa96b..f45450b295c0 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -126,6 +126,11 @@ void sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr); int __must_check sysfs_create_group(struct kobject *, const struct attribute_group *); void sysfs_remove_group(struct kobject *, const struct attribute_group *); +int sysfs_add_file_to_group(struct kobject *kobj, + const struct attribute *attr, const char *group); +void sysfs_remove_file_from_group(struct kobject *kobj, + const struct attribute *attr, const char *group); + void sysfs_notify(struct kobject * k, char *dir, char *attr); @@ -210,6 +215,18 @@ static inline void sysfs_remove_group(struct kobject * k, const struct attribute ; } +static inline int sysfs_add_file_to_group(struct kobject *kobj, + const struct attribute *attr, const char *group) +{ + return 0; +} + +static inline void sysfs_remove_file_from_group(struct kobject *kobj, + const struct attribute *attr, const char *group); +{ + ; +} + static inline void sysfs_notify(struct kobject * k, char *dir, char *attr) { } -- cgit From d9a9cdfb078d755e648d53ec25b7370f84ee5729 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 15 Mar 2007 15:50:34 -0400 Subject: [PATCH] sysfs and driver core: add callback helper, used by SCSI and S390 This patch (as868) adds a helper routine for device drivers that need to set up a callback to perform some action in a different process's context. This is intended for use by attribute methods that want to unregister themselves or their parent device. Attribute method calls are mutually exclusive with unregistration, so such actions cannot be taken directly. Two attribute methods are converted to use the new helper routine: one for SCSI device deletion and one for System/390 ccwgroup devices. Signed-off-by: Alan Stern Cc: Hugh Dickins Cc: Cornelia Huck Cc: Oliver Neukum Signed-off-by: Linus Torvalds --- drivers/base/core.c | 29 ++++++++++++++++++++++++ drivers/s390/cio/ccwgroup.c | 18 ++++++++++++--- drivers/scsi/scsi_sysfs.c | 14 +++++++++++- fs/sysfs/file.c | 54 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 2 ++ include/linux/sysfs.h | 9 ++++++++ 6 files changed, 122 insertions(+), 4 deletions(-) (limited to 'fs/sysfs/file.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index f191afe62b4d..ad0f4a2f25c4 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -407,6 +407,35 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr) } EXPORT_SYMBOL_GPL(device_remove_bin_file); +/** + * device_schedule_callback - helper to schedule a callback for a device + * @dev: device. + * @func: callback function to invoke later. + * + * Attribute methods must not unregister themselves or their parent device + * (which would amount to the same thing). Attempts to do so will deadlock, + * since unregistration is mutually exclusive with driver callbacks. + * + * Instead methods can call this routine, which will attempt to allocate + * and schedule a workqueue request to call back @func with @dev as its + * argument in the workqueue's process context. @dev will be pinned until + * @func returns. + * + * Returns 0 if the request was submitted, -ENOMEM if storage could not + * be allocated. + * + * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an + * underlying sysfs routine (since it is intended for use by attribute + * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. + */ +int device_schedule_callback(struct device *dev, + void (*func)(struct device *)) +{ + return sysfs_schedule_callback(&dev->kobj, + (void (*)(void *)) func, dev); +} +EXPORT_SYMBOL_GPL(device_schedule_callback); + static void klist_children_get(struct klist_node *n) { struct device *dev = container_of(n, struct device, knode_parent); diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index d48e3ca4752c..5aeb68e732b0 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -71,19 +71,31 @@ __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev) * Provide an 'ungroup' attribute so the user can remove group devices no * longer needed or accidentially created. Saves memory :) */ +static void ccwgroup_ungroup_callback(struct device *dev) +{ + struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + + __ccwgroup_remove_symlinks(gdev); + device_unregister(dev); +} + static ssize_t ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct ccwgroup_device *gdev; + int rc; gdev = to_ccwgroupdev(dev); if (gdev->state != CCWGROUP_OFFLINE) return -EINVAL; - __ccwgroup_remove_symlinks(gdev); - device_unregister(dev); - + /* Note that we cannot unregister the device from one of its + * attribute methods, so we have to use this roundabout approach. + */ + rc = device_schedule_callback(dev, ccwgroup_ungroup_callback); + if (rc) + count = rc; return count; } diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index c275dcac3f18..939de0de18bc 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -452,10 +452,22 @@ store_rescan_field (struct device *dev, struct device_attribute *attr, const cha } static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field); +static void sdev_store_delete_callback(struct device *dev) +{ + scsi_remove_device(to_scsi_device(dev)); +} + 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)); + int rc; + + /* An attribute cannot be unregistered by one of its own methods, + * so we have to use this roundabout approach. + */ + rc = device_schedule_callback(dev, sdev_store_delete_callback); + if (rc) + count = rc; return count; }; static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 8d4d839a9d88..1bafdf6e171c 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -629,6 +629,60 @@ void sysfs_remove_file_from_group(struct kobject *kobj, } EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); +struct sysfs_schedule_callback_struct { + struct kobject *kobj; + void (*func)(void *); + void *data; + struct work_struct work; +}; + +static void sysfs_schedule_callback_work(struct work_struct *work) +{ + struct sysfs_schedule_callback_struct *ss = container_of(work, + struct sysfs_schedule_callback_struct, work); + + (ss->func)(ss->data); + kobject_put(ss->kobj); + kfree(ss); +} + +/** + * sysfs_schedule_callback - helper to schedule a callback for a kobject + * @kobj: object we're acting for. + * @func: callback function to invoke later. + * @data: argument to pass to @func. + * + * sysfs attribute methods must not unregister themselves or their parent + * kobject (which would amount to the same thing). Attempts to do so will + * deadlock, since unregistration is mutually exclusive with driver + * callbacks. + * + * Instead methods can call this routine, which will attempt to allocate + * and schedule a workqueue request to call back @func with @data as its + * argument in the workqueue's process context. @kobj will be pinned + * until @func returns. + * + * Returns 0 if the request was submitted, -ENOMEM if storage could not + * be allocated. + */ +int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), + void *data) +{ + struct sysfs_schedule_callback_struct *ss; + + ss = kmalloc(sizeof(*ss), GFP_KERNEL); + if (!ss) + return -ENOMEM; + kobject_get(kobj); + ss->kobj = kobj; + ss->func = func; + ss->data = data; + INIT_WORK(&ss->work, sysfs_schedule_callback_work); + schedule_work(&ss->work); + return 0; +} +EXPORT_SYMBOL_GPL(sysfs_schedule_callback); + EXPORT_SYMBOL_GPL(sysfs_create_file); EXPORT_SYMBOL_GPL(sysfs_remove_file); diff --git a/include/linux/device.h b/include/linux/device.h index 39a3199a826d..caad9bba9652 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -353,6 +353,8 @@ extern int __must_check device_create_bin_file(struct device *dev, struct bin_attribute *attr); extern void device_remove_bin_file(struct device *dev, struct bin_attribute *attr); +extern int device_schedule_callback(struct device *dev, + void (*func)(struct device *)); /* device resource management */ typedef void (*dr_release_t)(struct device *dev, void *res); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 523405e1e1f6..0544edda7168 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -78,6 +78,9 @@ struct sysfs_ops { #ifdef CONFIG_SYSFS +extern int sysfs_schedule_callback(struct kobject *kobj, + void (*func)(void *), void *data); + extern int __must_check sysfs_create_dir(struct kobject *, struct dentry *); @@ -132,6 +135,12 @@ extern int __must_check sysfs_init(void); #else /* CONFIG_SYSFS */ +static inline int sysfs_schedule_callback(struct kobject *kobj, + void (*func)(void *), void *data) +{ + return -ENOSYS; +} + static inline int sysfs_create_dir(struct kobject * k, struct dentry *shadow) { return 0; -- cgit From e7b0d26a86943370c04d6833c6edba2a72a6e240 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 15 Mar 2007 15:51:28 -0400 Subject: [PATCH] sysfs: reinstate exclusion between method calls and attribute unregistration This patch (as869) reinstates the mutual exclusion between sysfs attribute method calls and attribute unregistration. The previously-reported deadlocks have been fixed, and this exclusion is by far the simplest way to avoid races during driver unbinding. The check for orphaned read-buffers has been moved down slightly, so that the remainder of a partially-read buffer will still be available to userspace even after the attribute has been unregistered. Signed-off-by: Alan Stern Cc: Hugh Dickins Cc: Cornelia Huck Cc: Oliver Neukum Signed-off-by: Linus Torvalds --- fs/sysfs/file.c | 10 +++++----- fs/sysfs/inode.c | 10 +++++++--- 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'fs/sysfs/file.c') diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 1bafdf6e171c..fc4633378dc0 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -168,12 +168,12 @@ sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) ssize_t retval = 0; down(&buffer->sem); - if (buffer->orphaned) { - retval = -ENODEV; - goto out; - } if (buffer->needs_read_fill) { - if ((retval = fill_read_buffer(file->f_path.dentry,buffer))) + if (buffer->orphaned) + retval = -ENODEV; + else + retval = fill_read_buffer(file->f_path.dentry,buffer); + if (retval) goto out; } pr_debug("%s: count = %zd, ppos = %lld, buf = %s\n", diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index ccb7d722c558..4de5c6b89918 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -222,13 +222,17 @@ const unsigned char * sysfs_get_name(struct sysfs_dirent *sd) static inline void orphan_all_buffers(struct inode *node) { - struct sysfs_buffer_collection *set = node->i_private; + struct sysfs_buffer_collection *set; struct sysfs_buffer *buf; mutex_lock_nested(&node->i_mutex, I_MUTEX_CHILD); - if (node->i_private) { - list_for_each_entry(buf, &set->associates, associates) + set = node->i_private; + if (set) { + list_for_each_entry(buf, &set->associates, associates) { + down(&buf->sem); buf->orphaned = 1; + up(&buf->sem); + } } mutex_unlock(&node->i_mutex); } -- cgit From 523ded71de0c5e66973335bf99a80edfda9f401b Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Thu, 26 Apr 2007 00:12:04 -0700 Subject: device_schedule_callback() needs a module reference This patch (as896b) fixes an oversight in the design of device_schedule_callback(). It is necessary to acquire a reference to the module owning the callback routine, to prevent the module from being unloaded before the callback can run. Signed-off-by: Alan Stern Cc: Satyam Sharma Cc: Neil Brown Cc: Cornelia Huck Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 16 ++++++++++------ fs/sysfs/file.c | 14 +++++++++++--- include/linux/device.h | 8 ++++++-- include/linux/sysfs.h | 4 ++-- 4 files changed, 29 insertions(+), 13 deletions(-) (limited to 'fs/sysfs/file.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index f69305c7269d..8aa090da1cd7 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -480,9 +480,10 @@ void device_remove_bin_file(struct device *dev, struct bin_attribute *attr) EXPORT_SYMBOL_GPL(device_remove_bin_file); /** - * device_schedule_callback - helper to schedule a callback for a device + * device_schedule_callback_owner - helper to schedule a callback for a device * @dev: device. * @func: callback function to invoke later. + * @owner: module owning the callback routine * * Attribute methods must not unregister themselves or their parent device * (which would amount to the same thing). Attempts to do so will deadlock, @@ -493,20 +494,23 @@ EXPORT_SYMBOL_GPL(device_remove_bin_file); * argument in the workqueue's process context. @dev will be pinned until * @func returns. * + * This routine is usually called via the inline device_schedule_callback(), + * which automatically sets @owner to THIS_MODULE. + * * Returns 0 if the request was submitted, -ENOMEM if storage could not - * be allocated. + * be allocated, -ENODEV if a reference to @owner isn't available. * * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an * underlying sysfs routine (since it is intended for use by attribute * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. */ -int device_schedule_callback(struct device *dev, - void (*func)(struct device *)) +int device_schedule_callback_owner(struct device *dev, + void (*func)(struct device *), struct module *owner) { return sysfs_schedule_callback(&dev->kobj, - (void (*)(void *)) func, dev); + (void (*)(void *)) func, dev, owner); } -EXPORT_SYMBOL_GPL(device_schedule_callback); +EXPORT_SYMBOL_GPL(device_schedule_callback_owner); static void klist_children_get(struct klist_node *n) { diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index fc4633378dc0..db0413a411d6 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -633,6 +633,7 @@ struct sysfs_schedule_callback_struct { struct kobject *kobj; void (*func)(void *); void *data; + struct module *owner; struct work_struct work; }; @@ -643,6 +644,7 @@ static void sysfs_schedule_callback_work(struct work_struct *work) (ss->func)(ss->data); kobject_put(ss->kobj); + module_put(ss->owner); kfree(ss); } @@ -651,6 +653,7 @@ static void sysfs_schedule_callback_work(struct work_struct *work) * @kobj: object we're acting for. * @func: callback function to invoke later. * @data: argument to pass to @func. + * @owner: module owning the callback code * * sysfs attribute methods must not unregister themselves or their parent * kobject (which would amount to the same thing). Attempts to do so will @@ -663,20 +666,25 @@ static void sysfs_schedule_callback_work(struct work_struct *work) * until @func returns. * * Returns 0 if the request was submitted, -ENOMEM if storage could not - * be allocated. + * be allocated, -ENODEV if a reference to @owner isn't available. */ int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), - void *data) + void *data, struct module *owner) { struct sysfs_schedule_callback_struct *ss; + if (!try_module_get(owner)) + return -ENODEV; ss = kmalloc(sizeof(*ss), GFP_KERNEL); - if (!ss) + if (!ss) { + module_put(owner); return -ENOMEM; + } kobject_get(kobj); ss->kobj = kobj; ss->func = func; ss->data = data; + ss->owner = owner; INIT_WORK(&ss->work, sysfs_schedule_callback_work); schedule_work(&ss->work); return 0; diff --git a/include/linux/device.h b/include/linux/device.h index af603a137690..8511d14071b3 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -367,8 +367,12 @@ extern int __must_check device_create_bin_file(struct device *dev, struct bin_attribute *attr); extern void device_remove_bin_file(struct device *dev, struct bin_attribute *attr); -extern int device_schedule_callback(struct device *dev, - void (*func)(struct device *)); +extern int device_schedule_callback_owner(struct device *dev, + void (*func)(struct device *), struct module *owner); + +/* This is a macro to avoid include problems with THIS_MODULE */ +#define device_schedule_callback(dev, func) \ + device_schedule_callback_owner(dev, func, THIS_MODULE) /* device resource management */ typedef void (*dr_release_t)(struct device *dev, void *res); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index fea9a6b3fb7b..7d5d1ec95c2e 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -80,7 +80,7 @@ struct sysfs_ops { #ifdef CONFIG_SYSFS extern int sysfs_schedule_callback(struct kobject *kobj, - void (*func)(void *), void *data); + void (*func)(void *), void *data, struct module *owner); extern int __must_check sysfs_create_dir(struct kobject *, struct dentry *); @@ -137,7 +137,7 @@ extern int __must_check sysfs_init(void); #else /* CONFIG_SYSFS */ static inline int sysfs_schedule_callback(struct kobject *kobj, - void (*func)(void *), void *data) + void (*func)(void *), void *data, struct module *owner) { return -ENOSYS; } -- cgit