From 46336009b5009e9fab3bd623a3beb9c7421545ac Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Fri, 8 Jun 2007 13:36:37 -0700 Subject: [PATCH 01/61] Rules on how to use sysfs in userspace programs Here's a document to help clear things up. Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- Documentation/sysfs-rules.txt | 166 ++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100644 Documentation/sysfs-rules.txt diff --git a/Documentation/sysfs-rules.txt b/Documentation/sysfs-rules.txt new file mode 100644 index 000000000000..42861bb0bc9b --- /dev/null +++ b/Documentation/sysfs-rules.txt @@ -0,0 +1,166 @@ +Rules on how to access information in the Linux kernel sysfs + +The kernel exported sysfs exports internal kernel implementation-details +and depends on internal kernel structures and layout. It is agreed upon +by the kernel developers that the Linux kernel does not provide a stable +internal API. As sysfs is a direct export of kernel internal +structures, the sysfs interface can not provide a stable interface eighter, +it may always change along with internal kernel changes. + +To minimize the risk of breaking users of sysfs, which are in most cases +low-level userspace applications, with a new kernel release, the users +of sysfs must follow some rules to use an as abstract-as-possible way to +access this filesystem. The current udev and HAL programs already +implement this and users are encouraged to plug, if possible, into the +abstractions these programs provide instead of accessing sysfs +directly. + +But if you really do want or need to access sysfs directly, please follow +the following rules and then your programs should work with future +versions of the sysfs interface. + +- Do not use libsysfs + It makes assumptions about sysfs which are not true. Its API does not + offer any abstraction, it exposes all the kernel driver-core + implementation details in its own API. Therefore it is not better than + reading directories and opening the files yourself. + Also, it is not actively maintained, in the sense of reflecting the + current kernel-development. The goal of providing a stable interface + to sysfs has failed, it causes more problems, than it solves. It + violates many of the rules in this document. + +- sysfs is always at /sys + Parsing /proc/mounts is a waste of time. Other mount points are a + system configuration bug you should not try to solve. For test cases, + possibly support a SYSFS_PATH environment variable to overwrite the + applications behavior, but never try to search for sysfs. Never try + to mount it, if you are not an early boot script. + +- devices are only "devices" + There is no such thing like class-, bus-, physical devices, + interfaces, and such that you can rely on in userspace. Everything is + just simply a "device". Class-, bus-, physical, ... types are just + kernel implementation details, which should not be expected by + applications that look for devices in sysfs. + + The properties of a device are: + o devpath (/devices/pci0000:00/0000:00:1d.1/usb2/2-2/2-2:1.0) + - identical to the DEVPATH value in the event sent from the kernel + at device creation and removal + - the unique key to the device at that point in time + - the kernels path to the device-directory without the leading + /sys, and always starting with with a slash + - all elements of a devpath must be real directories. Symlinks + pointing to /sys/devices must always be resolved to their real + target, and the target path must be used to access the device. + That way the devpath to the device matches the devpath of the + kernel used at event time. + - using or exposing symlink values as elements in a devpath string + is a bug in the application + + o kernel name (sda, tty, 0000:00:1f.2, ...) + - a directory name, identical to the last element of the devpath + - applications need to handle spaces and characters like '!' in + the name + + o subsystem (block, tty, pci, ...) + - simple string, never a path or a link + - retrieved by reading the "subsystem"-link and using only the + last element of the target path + + o driver (tg3, ata_piix, uhci_hcd) + - a simple string, which may contain spaces, never a path or a + link + - it is retrieved by reading the "driver"-link and using only the + last element of the target path + - devices which do not have "driver"-link, just do not have a + driver; copying the driver value in a child device context, is a + bug in the application + + o attributes + - the files in the device directory or files below a subdirectories + of the same device directory + - accessing attributes reached by a symlink pointing to another device, + like the "device"-link, is a bug in the application + + Everything else is just a kernel driver-core implementation detail, + that should not be assumed to be stable across kernel releases. + +- Properties of parent devices never belong into a child device. + Always look at the parent devices themselves for determining device + context properties. If the device 'eth0' or 'sda' does not have a + "driver"-link, then this device does not have a driver. Its value is empty. + Never copy any property of the parent-device into a child-device. Parent + device-properties may change dynamically without any notice to the + child device. + +- Hierarchy in a single device-tree + There is only one valid place in sysfs where hierarchy can be examined + and this is below: /sys/devices. + It is planned, that all device directories will end up in the tree + below this directory. + +- Classification by subsystem + There are currently three places for classification of devices: + /sys/block, /sys/class and /sys/bus. It is planned that these will + not contain any device-directories themselves, but only flat lists of + symlinks pointing to the unified /sys/devices tree. + All three places have completely different rules on how to access + device information. It is planned to merge all three + classification-directories into one place at /sys/subsystem, + following the layout of the bus-directories. All buses and + classes, including the converted block-subsystem, will show up + there. + The devices belonging to a subsystem will create a symlink in the + "devices" directory at /sys/subsystem//devices. + + If /sys/subsystem exists, /sys/bus, /sys/class and /sys/block can be + ignored. If it does not exist, you have always to scan all three + places, as the kernel is free to move a subsystem from one place to + the other, as long as the devices are still reachable by the same + subsystem name. + + Assuming /sys/class/ and /sys/bus/, or + /sys/block and /sys/class/block are not interchangeable, is a bug in + the application. + +- Block + The converted block-subsystem at /sys/class/block, or + /sys/subsystem/block will contain the links for disks and partitions + at the same level, never in a hierarchy. Assuming the block-subsytem to + contain only disks and not partition-devices in the same flat list is + a bug in the application. + +- "device"-link and :-links + Never depend on the "device"-link. The "device"-link is a workaround + for the old layout, where class-devices are not created in + /sys/devices/ like the bus-devices. If the link-resolving of a + device-directory does not end in /sys/devices/, you can use the + "device"-link to find the parent devices in /sys/devices/. That is the + single valid use of the "device"-link, it must never appear in any + path as an element. Assuming the existence of the "device"-link for + a device in /sys/devices/ is a bug in the application. + Accessing /sys/class/net/eth0/device is a bug in the application. + + Never depend on the class-specific links back to the /sys/class + directory. These links are also a workaround for the design mistake + that class-devices are not created in /sys/devices. If a device + directory does not contain directories for child devices, these links + may be used to find the child devices in /sys/class. That is the single + valid use of these links, they must never appear in any path as an + element. Assuming the existence of these links for devices which are + real child device directories in the /sys/devices tree, is a bug in + the application. + + It is planned to remove all these links when when all class-device + directories live in /sys/devices. + +- Position of devices along device chain can change. + Never depend on a specific parent device position in the devpath, + or the chain of parent devices. The kernel is free to insert devices into + the chain. You must always request the parent device you are looking for + by its subsystem value. You need to walk up the chain until you find + the device that matches the expected subsystem. Depending on a specific + position of a parent device, or exposing relative paths, using "../" to + access the chain of parents, is a bug in the application. + From cfc94cdf8e0f14e692a5a40ef3cc10f464b2511b Mon Sep 17 00:00:00 2001 From: Jan Kara Date: Wed, 9 May 2007 13:19:52 +0200 Subject: [PATCH 02/61] debugfs: add rename for debugfs files Implement debugfs_rename() to allow renaming files/directories in debugfs. Signed-off-by: Jan Kara Signed-off-by: Greg Kroah-Hartman --- fs/debugfs/inode.c | 63 +++++++++++++++++++++++++++++++++++++++++ include/linux/debugfs.h | 9 ++++++ 2 files changed, 72 insertions(+) diff --git a/fs/debugfs/inode.c b/fs/debugfs/inode.c index ec8896b264de..1d533a2ec3a6 100644 --- a/fs/debugfs/inode.c +++ b/fs/debugfs/inode.c @@ -368,6 +368,69 @@ void debugfs_remove(struct dentry *dentry) } EXPORT_SYMBOL_GPL(debugfs_remove); +/** + * debugfs_rename - rename a file/directory in the debugfs filesystem + * @old_dir: a pointer to the parent dentry for the renamed object. This + * should be a directory dentry. + * @old_dentry: dentry of an object to be renamed. + * @new_dir: a pointer to the parent dentry where the object should be + * moved. This should be a directory dentry. + * @new_name: a pointer to a string containing the target name. + * + * This function renames a file/directory in debugfs. The target must not + * exist for rename to succeed. + * + * This function will return a pointer to old_dentry (which is updated to + * reflect renaming) if it succeeds. If an error occurs, %NULL will be + * returned. + * + * If debugfs is not enabled in the kernel, the value -%ENODEV will be + * returned. + */ +struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, + struct dentry *new_dir, const char *new_name) +{ + int error; + struct dentry *dentry = NULL, *trap; + const char *old_name; + + trap = lock_rename(new_dir, old_dir); + /* Source or destination directories don't exist? */ + if (!old_dir->d_inode || !new_dir->d_inode) + goto exit; + /* Source does not exist, cyclic rename, or mountpoint? */ + if (!old_dentry->d_inode || old_dentry == trap || + d_mountpoint(old_dentry)) + goto exit; + dentry = lookup_one_len(new_name, new_dir, strlen(new_name)); + /* Lookup failed, cyclic rename or target exists? */ + if (IS_ERR(dentry) || dentry == trap || dentry->d_inode) + goto exit; + + old_name = fsnotify_oldname_init(old_dentry->d_name.name); + + error = simple_rename(old_dir->d_inode, old_dentry, new_dir->d_inode, + dentry); + if (error) { + fsnotify_oldname_free(old_name); + goto exit; + } + d_move(old_dentry, dentry); + fsnotify_move(old_dir->d_inode, new_dir->d_inode, old_name, + old_dentry->d_name.name, S_ISDIR(old_dentry->d_inode->i_mode), + NULL, old_dentry->d_inode); + fsnotify_oldname_free(old_name); + unlock_rename(new_dir, old_dir); + dput(dentry); + return old_dentry; +exit: + if (dentry && !IS_ERR(dentry)) + dput(dentry); + unlock_rename(new_dir, old_dir); + return NULL; +} +EXPORT_SYMBOL_GPL(debugfs_rename); + static decl_subsys(debug, NULL, NULL); static int __init debugfs_init(void) diff --git a/include/linux/debugfs.h b/include/linux/debugfs.h index 5a9c49534d08..104e51e20e14 100644 --- a/include/linux/debugfs.h +++ b/include/linux/debugfs.h @@ -38,6 +38,9 @@ struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent, void debugfs_remove(struct dentry *dentry); +struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, + struct dentry *new_dir, const char *new_name); + struct dentry *debugfs_create_u8(const char *name, mode_t mode, struct dentry *parent, u8 *value); struct dentry *debugfs_create_u16(const char *name, mode_t mode, @@ -85,6 +88,12 @@ static inline struct dentry *debugfs_create_symlink(const char *name, static inline void debugfs_remove(struct dentry *dentry) { } +static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry, + struct dentry *new_dir, char *new_name) +{ + return ERR_PTR(-ENODEV); +} + static inline struct dentry *debugfs_create_u8(const char *name, mode_t mode, struct dentry *parent, u8 *value) From 4f5c791a850e5305a5b1b48d0e4b4de248dc96f9 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 8 May 2007 22:07:02 +0200 Subject: [PATCH 03/61] DMI-based module autoloading The patch below adds DMI/SMBIOS based module autoloading to the Linux kernel. The idea is to load laptop drivers automatically (and other drivers which cannot be autoloaded otherwise), based on the DMI system identification information of the BIOS. Right now most distros manually try to load all available laptop drivers on bootup in the hope that at least one of them loads successfully. This patch does away with all that, and uses udev to automatically load matching drivers on the right machines. Basically the patch just exports the DMI information that has been parsed by the kernel anyway to userspace via a sysfs device /sys/class/dmi/id and makes sure that proper modalias attributes are available. Besides adding the "modalias" attribute it also adds attributes for a few other DMI fields which might be useful for writing udev rules. This patch is not an attempt to export the entire DMI/SMBIOS data to userspace. We already have "dmidecode" which parses the complete DMI info from userspace. The purpose of this patch is machine model identification and good udev integration. To take advantage of DMI based module autoloading, a driver should export one or more MODULE_ALIAS fields similar to these: MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*"); MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); These lines are specific to my msi-laptop.c driver. They are basically just a concatenation of a few carefully selected DMI fields with all potentially bad characters stripped. Besides laptop drivers, modules like "hdaps", the i2c modules and the hwmon modules are good candidates for "dmi:" MODULE_ALIAS lines. Besides merely exporting the DMI data via sysfs the patch adds support for a few more DMI fields. Especially the CHASSIS fields are very useful to identify different laptop modules. The patch also adds working MODULE_ALIAS lines to my msi-laptop.c driver. I'd like to thank Kay Sievers for helping me to clean up this patch for posting it on lkml. Patch is against Linus' current GIT HEAD. Should probably apply to older kernels as well without modification. Signed-off-by: Lennart Poettering Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/Kconfig | 9 ++ drivers/firmware/Makefile | 1 + drivers/firmware/dmi-id.c | 222 ++++++++++++++++++++++++++++++++++++ drivers/firmware/dmi_scan.c | 73 +++++++++++- drivers/misc/msi-laptop.c | 44 ++++++- include/linux/dmi.h | 8 ++ 6 files changed, 348 insertions(+), 9 deletions(-) create mode 100644 drivers/firmware/dmi-id.c diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig index 88f462122a30..05f02a326f1c 100644 --- a/drivers/firmware/Kconfig +++ b/drivers/firmware/Kconfig @@ -84,4 +84,13 @@ config DCDBAS Say Y or M here to enable the driver for use by Dell systems management software such as Dell OpenManage. +config DMIID + bool "Export DMI identification via sysfs to userspace" + depends on DMI + default y + help + Say Y here if you want to query SMBIOS/DMI system identification + information from userspace through /sys/class/dmi/id/ or if you want + DMI-based module auto-loading. + endmenu diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile index 98e395f4bb29..8d4ebc805a50 100644 --- a/drivers/firmware/Makefile +++ b/drivers/firmware/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_EFI_VARS) += efivars.o obj-$(CONFIG_EFI_PCDP) += pcdp.o obj-$(CONFIG_DELL_RBU) += dell_rbu.o obj-$(CONFIG_DCDBAS) += dcdbas.o +obj-$(CONFIG_DMIID) += dmi-id.o diff --git a/drivers/firmware/dmi-id.c b/drivers/firmware/dmi-id.c new file mode 100644 index 000000000000..59c3b5aa89f4 --- /dev/null +++ b/drivers/firmware/dmi-id.c @@ -0,0 +1,222 @@ +/* + * Export SMBIOS/DMI info via sysfs to userspace + * + * Copyright 2007, Lennart Poettering + * + * Licensed under GPLv2 + */ + +#include +#include +#include +#include +#include +#include + +#define DEFINE_DMI_ATTR(_name, _mode, _show) \ +static struct device_attribute sys_dmi_##_name##_attr = \ + __ATTR(_name, _mode, _show, NULL); + +#define DEFINE_DMI_ATTR_WITH_SHOW(_name, _mode, _field) \ +static ssize_t sys_dmi_##_name##_show(struct device *dev, \ + struct device_attribute *attr, \ + char *page) \ +{ \ + ssize_t len; \ + len = scnprintf(page, PAGE_SIZE, "%s\n", dmi_get_system_info(_field)); \ + page[len-1] = '\n'; \ + return len; \ +} \ +DEFINE_DMI_ATTR(_name, _mode, sys_dmi_##_name##_show); + +DEFINE_DMI_ATTR_WITH_SHOW(bios_vendor, 0444, DMI_BIOS_VENDOR); +DEFINE_DMI_ATTR_WITH_SHOW(bios_version, 0444, DMI_BIOS_VERSION); +DEFINE_DMI_ATTR_WITH_SHOW(bios_date, 0444, DMI_BIOS_DATE); +DEFINE_DMI_ATTR_WITH_SHOW(sys_vendor, 0444, DMI_SYS_VENDOR); +DEFINE_DMI_ATTR_WITH_SHOW(product_name, 0444, DMI_PRODUCT_NAME); +DEFINE_DMI_ATTR_WITH_SHOW(product_version, 0444, DMI_PRODUCT_VERSION); +DEFINE_DMI_ATTR_WITH_SHOW(product_serial, 0400, DMI_PRODUCT_SERIAL); +DEFINE_DMI_ATTR_WITH_SHOW(product_uuid, 0400, DMI_PRODUCT_UUID); +DEFINE_DMI_ATTR_WITH_SHOW(board_vendor, 0444, DMI_BOARD_VENDOR); +DEFINE_DMI_ATTR_WITH_SHOW(board_name, 0444, DMI_BOARD_NAME); +DEFINE_DMI_ATTR_WITH_SHOW(board_version, 0444, DMI_BOARD_VERSION); +DEFINE_DMI_ATTR_WITH_SHOW(board_serial, 0400, DMI_BOARD_SERIAL); +DEFINE_DMI_ATTR_WITH_SHOW(board_asset_tag, 0444, DMI_BOARD_ASSET_TAG); +DEFINE_DMI_ATTR_WITH_SHOW(chassis_vendor, 0444, DMI_CHASSIS_VENDOR); +DEFINE_DMI_ATTR_WITH_SHOW(chassis_type, 0444, DMI_CHASSIS_TYPE); +DEFINE_DMI_ATTR_WITH_SHOW(chassis_version, 0444, DMI_CHASSIS_VERSION); +DEFINE_DMI_ATTR_WITH_SHOW(chassis_serial, 0400, DMI_CHASSIS_SERIAL); +DEFINE_DMI_ATTR_WITH_SHOW(chassis_asset_tag, 0444, DMI_CHASSIS_ASSET_TAG); + +static void ascii_filter(char *d, const char *s) +{ + /* Filter out characters we don't want to see in the modalias string */ + for (; *s; s++) + if (*s > ' ' && *s < 127 && *s != ':') + *(d++) = *s; + + *d = 0; +} + +static ssize_t get_modalias(char *buffer, size_t buffer_size) +{ + static const struct mafield { + const char *prefix; + int field; + } fields[] = { + { "bvn", DMI_BIOS_VENDOR }, + { "bvr", DMI_BIOS_VERSION }, + { "bd", DMI_BIOS_DATE }, + { "svn", DMI_SYS_VENDOR }, + { "pn", DMI_PRODUCT_NAME }, + { "pvr", DMI_PRODUCT_VERSION }, + { "rvn", DMI_BOARD_VENDOR }, + { "rn", DMI_BOARD_NAME }, + { "rvr", DMI_BOARD_VERSION }, + { "cvn", DMI_CHASSIS_VENDOR }, + { "ct", DMI_CHASSIS_TYPE }, + { "cvr", DMI_CHASSIS_VERSION }, + { NULL, DMI_NONE } + }; + + ssize_t l, left; + char *p; + const struct mafield *f; + + strcpy(buffer, "dmi"); + p = buffer + 3; left = buffer_size - 4; + + for (f = fields; f->prefix && left > 0; f++) { + const char *c; + char *t; + + c = dmi_get_system_info(f->field); + if (!c) + continue; + + t = kmalloc(strlen(c) + 1, GFP_KERNEL); + if (!t) + break; + ascii_filter(t, c); + l = scnprintf(p, left, ":%s%s", f->prefix, t); + kfree(t); + + p += l; + left -= l; + } + + p[0] = ':'; + p[1] = 0; + + return p - buffer + 1; +} + +static ssize_t sys_dmi_modalias_show(struct device *dev, + struct device_attribute *attr, char *page) +{ + ssize_t r; + r = get_modalias(page, PAGE_SIZE-1); + page[r] = '\n'; + page[r+1] = 0; + return r+1; +} + +DEFINE_DMI_ATTR(modalias, 0444, sys_dmi_modalias_show); + +static struct attribute *sys_dmi_attributes[DMI_STRING_MAX+2]; + +static struct attribute_group sys_dmi_attribute_group = { + .attrs = sys_dmi_attributes, +}; + +static struct attribute_group* sys_dmi_attribute_groups[] = { + &sys_dmi_attribute_group, + NULL +}; + +static int dmi_dev_uevent(struct device *dev, char **envp, + int num_envp, char *buffer, int buffer_size) +{ + strcpy(buffer, "MODALIAS="); + get_modalias(buffer+9, buffer_size-9); + envp[0] = buffer; + envp[1] = NULL; + + return 0; +} + +static struct class dmi_class = { + .name = "dmi", + .dev_release = (void(*)(struct device *)) kfree, + .dev_uevent = dmi_dev_uevent, +}; + +static struct device *dmi_dev; + +/* Initialization */ + +#define ADD_DMI_ATTR(_name, _field) \ + if (dmi_get_system_info(_field)) \ + sys_dmi_attributes[i++] = & sys_dmi_##_name##_attr.attr; + +extern int dmi_available; + +static int __init dmi_id_init(void) +{ + int ret, i; + + if (!dmi_available) + return -ENODEV; + + /* Not necessarily all DMI fields are available on all + * systems, hence let's built an attribute table of just + * what's available */ + i = 0; + ADD_DMI_ATTR(bios_vendor, DMI_BIOS_VENDOR); + ADD_DMI_ATTR(bios_version, DMI_BIOS_VERSION); + ADD_DMI_ATTR(bios_date, DMI_BIOS_DATE); + ADD_DMI_ATTR(sys_vendor, DMI_SYS_VENDOR); + ADD_DMI_ATTR(product_name, DMI_PRODUCT_NAME); + ADD_DMI_ATTR(product_version, DMI_PRODUCT_VERSION); + ADD_DMI_ATTR(product_serial, DMI_PRODUCT_SERIAL); + ADD_DMI_ATTR(product_uuid, DMI_PRODUCT_UUID); + ADD_DMI_ATTR(board_vendor, DMI_BOARD_VENDOR); + ADD_DMI_ATTR(board_name, DMI_BOARD_NAME); + ADD_DMI_ATTR(board_version, DMI_BOARD_VERSION); + ADD_DMI_ATTR(board_serial, DMI_BOARD_SERIAL); + ADD_DMI_ATTR(board_asset_tag, DMI_BOARD_ASSET_TAG); + ADD_DMI_ATTR(chassis_vendor, DMI_CHASSIS_VENDOR); + ADD_DMI_ATTR(chassis_type, DMI_CHASSIS_TYPE); + ADD_DMI_ATTR(chassis_version, DMI_CHASSIS_VERSION); + ADD_DMI_ATTR(chassis_serial, DMI_CHASSIS_SERIAL); + ADD_DMI_ATTR(chassis_asset_tag, DMI_CHASSIS_ASSET_TAG); + sys_dmi_attributes[i++] = &sys_dmi_modalias_attr.attr; + + ret = class_register(&dmi_class); + if (ret) + return ret; + + dmi_dev = kzalloc(sizeof(*dmi_dev), GFP_KERNEL); + if (!dmi_dev) { + ret = -ENOMEM; + goto fail_class_unregister; + } + + dmi_dev->class = &dmi_class; + strcpy(dmi_dev->bus_id, "id"); + dmi_dev->groups = sys_dmi_attribute_groups; + + ret = device_register(dmi_dev); + if (ret) + goto fail_class_unregister; + + return 0; + +fail_class_unregister: + + class_unregister(&dmi_class); + + return ret; +} + +arch_initcall(dmi_id_init); diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c index 37deee6c0c1c..f7318b3b51f2 100644 --- a/drivers/firmware/dmi_scan.c +++ b/drivers/firmware/dmi_scan.c @@ -84,6 +84,7 @@ static int __init dmi_checksum(u8 *buf) static char *dmi_ident[DMI_STRING_MAX]; static LIST_HEAD(dmi_devices); +int dmi_available; /* * Save a DMI string @@ -102,6 +103,51 @@ static void __init dmi_save_ident(struct dmi_header *dm, int slot, int string) dmi_ident[slot] = p; } +static void __init dmi_save_uuid(struct dmi_header *dm, int slot, int index) +{ + u8 *d = (u8*) dm + index; + char *s; + int is_ff = 1, is_00 = 1, i; + + if (dmi_ident[slot]) + return; + + for (i = 0; i < 16 && (is_ff || is_00); i++) { + if(d[i] != 0x00) is_ff = 0; + if(d[i] != 0xFF) is_00 = 0; + } + + if (is_ff || is_00) + return; + + s = dmi_alloc(16*2+4+1); + if (!s) + return; + + sprintf(s, + "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", + d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7], + d[8], d[9], d[10], d[11], d[12], d[13], d[14], d[15]); + + dmi_ident[slot] = s; +} + +static void __init dmi_save_type(struct dmi_header *dm, int slot, int index) +{ + u8 *d = (u8*) dm + index; + char *s; + + if (dmi_ident[slot]) + return; + + s = dmi_alloc(4); + if (!s) + return; + + sprintf(s, "%u", *d & 0x7F); + dmi_ident[slot] = s; +} + static void __init dmi_save_devices(struct dmi_header *dm) { int i, count = (dm->length - sizeof(struct dmi_header)) / 2; @@ -192,11 +238,21 @@ static void __init dmi_decode(struct dmi_header *dm) dmi_save_ident(dm, DMI_PRODUCT_NAME, 5); dmi_save_ident(dm, DMI_PRODUCT_VERSION, 6); dmi_save_ident(dm, DMI_PRODUCT_SERIAL, 7); + dmi_save_uuid(dm, DMI_PRODUCT_UUID, 8); break; case 2: /* Base Board Information */ dmi_save_ident(dm, DMI_BOARD_VENDOR, 4); dmi_save_ident(dm, DMI_BOARD_NAME, 5); dmi_save_ident(dm, DMI_BOARD_VERSION, 6); + dmi_save_ident(dm, DMI_BOARD_SERIAL, 7); + dmi_save_ident(dm, DMI_BOARD_ASSET_TAG, 8); + break; + case 3: /* Chassis Information */ + dmi_save_ident(dm, DMI_CHASSIS_VENDOR, 4); + dmi_save_type(dm, DMI_CHASSIS_TYPE, 5); + dmi_save_ident(dm, DMI_CHASSIS_VERSION, 6); + dmi_save_ident(dm, DMI_CHASSIS_SERIAL, 7); + dmi_save_ident(dm, DMI_CHASSIS_ASSET_TAG, 8); break; case 10: /* Onboard Devices Information */ dmi_save_devices(dm); @@ -243,18 +299,20 @@ void __init dmi_scan_machine(void) if (efi.smbios == EFI_INVALID_TABLE_ADDR) goto out; - /* This is called as a core_initcall() because it isn't - * needed during early boot. This also means we can - * iounmap the space when we're done with it. - */ + /* This is called as a core_initcall() because it isn't + * needed during early boot. This also means we can + * iounmap the space when we're done with it. + */ p = dmi_ioremap(efi.smbios, 32); if (p == NULL) goto out; rc = dmi_present(p + 0x10); /* offset of _DMI_ string */ dmi_iounmap(p, 32); - if (!rc) + if (!rc) { + dmi_available = 1; return; + } } else { /* @@ -268,8 +326,10 @@ void __init dmi_scan_machine(void) for (q = p; q < p + 0x10000; q += 16) { rc = dmi_present(q); - if (!rc) + if (!rc) { + dmi_available = 1; return; + } } } out: printk(KERN_INFO "DMI not present or invalid.\n"); @@ -404,3 +464,4 @@ int dmi_get_year(int field) return year; } + diff --git a/drivers/misc/msi-laptop.c b/drivers/misc/msi-laptop.c index 41e901f53e7c..932a415197b3 100644 --- a/drivers/misc/msi-laptop.c +++ b/drivers/misc/msi-laptop.c @@ -23,6 +23,8 @@ * msi-laptop.c - MSI S270 laptop support. This laptop is sold under * various brands, including "Cytron/TCM/Medion/Tchibo MD96100". * + * Driver also supports S271, S420 models. + * * This driver exports a few files in /sys/devices/platform/msi-laptop-pf/: * * lcd_level - Screen brightness: contains a single integer in the @@ -281,25 +283,56 @@ static struct platform_device *msipf_device; /* Initialization */ +static int dmi_check_cb(struct dmi_system_id *id) +{ + printk("msi-laptop: Identified laptop model '%s'.\n", id->ident); + return 0; +} + static struct dmi_system_id __initdata msi_dmi_table[] = { { .ident = "MSI S270", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "MICRO-STAR INT'L CO.,LTD"), DMI_MATCH(DMI_PRODUCT_NAME, "MS-1013"), - } + DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), + DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD") + }, + .callback = dmi_check_cb + }, + { + .ident = "MSI S271", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-1058"), + DMI_MATCH(DMI_PRODUCT_VERSION, "0581"), + DMI_MATCH(DMI_BOARD_NAME, "MS-1058") + }, + .callback = dmi_check_cb + }, + { + .ident = "MSI S420", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "MS-1412"), + DMI_MATCH(DMI_BOARD_VENDOR, "MSI"), + DMI_MATCH(DMI_BOARD_NAME, "MS-1412") + }, + .callback = dmi_check_cb }, { .ident = "Medion MD96100", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "NOTEBOOK"), DMI_MATCH(DMI_PRODUCT_NAME, "SAM2000"), - } + DMI_MATCH(DMI_PRODUCT_VERSION, "0131"), + DMI_MATCH(DMI_CHASSIS_VENDOR, "MICRO-STAR INT'L CO.,LTD") + }, + .callback = dmi_check_cb }, { } }; - static int __init msi_init(void) { int ret; @@ -394,3 +427,8 @@ MODULE_AUTHOR("Lennart Poettering"); MODULE_DESCRIPTION("MSI Laptop Support"); MODULE_VERSION(MSI_DRIVER_VERSION); MODULE_LICENSE("GPL"); + +MODULE_ALIAS("dmi:*:svnMICRO-STARINT'LCO.,LTD:pnMS-1013:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); +MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1058:pvr0581:rvnMSI:rnMS-1058:*:ct10:*"); +MODULE_ALIAS("dmi:*:svnMicro-StarInternational:pnMS-1412:*:rvnMSI:rnMS-1412:*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); +MODULE_ALIAS("dmi:*:svnNOTEBOOK:pnSAM2000:pvr0131*:cvnMICRO-STARINT'LCO.,LTD:ct10:*"); diff --git a/include/linux/dmi.h b/include/linux/dmi.h index 904bf3d2d90b..b8ac7b01c45e 100644 --- a/include/linux/dmi.h +++ b/include/linux/dmi.h @@ -12,9 +12,17 @@ enum dmi_field { DMI_PRODUCT_NAME, DMI_PRODUCT_VERSION, DMI_PRODUCT_SERIAL, + DMI_PRODUCT_UUID, DMI_BOARD_VENDOR, DMI_BOARD_NAME, DMI_BOARD_VERSION, + DMI_BOARD_SERIAL, + DMI_BOARD_ASSET_TAG, + DMI_CHASSIS_VENDOR, + DMI_CHASSIS_TYPE, + DMI_CHASSIS_VERSION, + DMI_CHASSIS_SERIAL, + DMI_CHASSIS_ASSET_TAG, DMI_STRING_MAX, }; From 80f03e349f06a261a8e980bf6005c61811a0d66a Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Sat, 26 May 2007 11:21:36 +0200 Subject: [PATCH 04/61] Driver core: add missing kset uevent We get uevents for a bus/class going away, but not one registering. Add the missing uevent in kset_register(), which will send an event for a new bus/class. Suppress all unwanted uevents for bus subdirectories like /bus/*/devices/, /bus/*/drivers/. Now we get for module usbcore: add /module/usbcore (module) add /bus/usb (bus) add /class/usb_host (class) add /bus/usb/drivers/hub (drivers) add /bus/usb/drivers/usb (drivers) remove /bus/usb/drivers/usb (drivers) remove /bus/usb/drivers/hub (drivers) remove /class/usb_host (class) remove /bus/usb (bus) remove /module/usbcore (module) instead of: add /module/usbcore (module) add /bus/usb/drivers/hub (drivers) add /bus/usb/drivers/usb (drivers) remove /bus/usb/drivers/usb (drivers) remove /bus/usb/drivers/hub (drivers) remove /class/usb_host (class) remove /bus/usb/drivers (bus) remove /bus/usb/devices (bus) remove /bus/usb (bus) remove /module/usbcore (module) Signed-off-by: Kay Sievers Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 18 +++++++++++++++--- lib/kobject.c | 9 ++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index dca734819e50..7ff5aafe2167 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -138,12 +138,24 @@ void bus_remove_file(struct bus_type * bus, struct bus_attribute * attr) } } -static struct kobj_type ktype_bus = { +static struct kobj_type bus_ktype = { .sysfs_ops = &bus_sysfs_ops, - }; -static decl_subsys(bus, &ktype_bus, NULL); +static int bus_uevent_filter(struct kset *kset, struct kobject *kobj) +{ + struct kobj_type *ktype = get_ktype(kobj); + + if (ktype == &bus_ktype) + return 1; + return 0; +} + +static struct kset_uevent_ops bus_uevent_ops = { + .filter = bus_uevent_filter, +}; + +static decl_subsys(bus, &bus_ktype, &bus_uevent_ops); #ifdef CONFIG_HOTPLUG diff --git a/lib/kobject.c b/lib/kobject.c index ac1520651b9b..b4ebd7631700 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -597,10 +597,17 @@ int kset_add(struct kset * k) int kset_register(struct kset * k) { + int err; + if (!k) return -EINVAL; + kset_init(k); - return kset_add(k); + err = kset_add(k); + if (err) + return err; + kobject_uevent(&k->kobj, KOBJ_ADD); + return 0; } From 9f3f776bd9e3d52f0204db1df0914b50d6a2372e Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 23 May 2007 14:19:42 -0700 Subject: [PATCH 05/61] sysdev: use mutex instead of semaphore The sysdev code use a semaphore as mutex. Use the mutex API instead of the (binary) semaphore. Signed-off-by: Matthias Kaehlcke Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/sys.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/base/sys.c b/drivers/base/sys.c index 29f1291966c1..18febe26caa1 100644 --- a/drivers/base/sys.c +++ b/drivers/base/sys.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include "base.h" @@ -155,7 +155,7 @@ EXPORT_SYMBOL_GPL(sysdev_class_unregister); static LIST_HEAD(sysdev_drivers); -static DECLARE_MUTEX(sysdev_drivers_lock); +static DEFINE_MUTEX(sysdev_drivers_lock); /** * sysdev_driver_register - Register auxillary driver @@ -172,7 +172,7 @@ static DECLARE_MUTEX(sysdev_drivers_lock); int sysdev_driver_register(struct sysdev_class * cls, struct sysdev_driver * drv) { - down(&sysdev_drivers_lock); + mutex_lock(&sysdev_drivers_lock); if (cls && kset_get(&cls->kset)) { list_add_tail(&drv->entry, &cls->drivers); @@ -184,7 +184,7 @@ int sysdev_driver_register(struct sysdev_class * cls, } } else list_add_tail(&drv->entry, &sysdev_drivers); - up(&sysdev_drivers_lock); + mutex_unlock(&sysdev_drivers_lock); return 0; } @@ -197,7 +197,7 @@ int sysdev_driver_register(struct sysdev_class * cls, void sysdev_driver_unregister(struct sysdev_class * cls, struct sysdev_driver * drv) { - down(&sysdev_drivers_lock); + mutex_lock(&sysdev_drivers_lock); list_del_init(&drv->entry); if (cls) { if (drv->remove) { @@ -207,7 +207,7 @@ void sysdev_driver_unregister(struct sysdev_class * cls, } kset_put(&cls->kset); } - up(&sysdev_drivers_lock); + mutex_unlock(&sysdev_drivers_lock); } EXPORT_SYMBOL_GPL(sysdev_driver_register); @@ -246,7 +246,7 @@ int sysdev_register(struct sys_device * sysdev) if (!error) { struct sysdev_driver * drv; - down(&sysdev_drivers_lock); + mutex_lock(&sysdev_drivers_lock); /* Generic notification is implicit, because it's that * code that should have called us. */ @@ -262,7 +262,7 @@ int sysdev_register(struct sys_device * sysdev) if (drv->add) drv->add(sysdev); } - up(&sysdev_drivers_lock); + mutex_unlock(&sysdev_drivers_lock); } return error; } @@ -271,7 +271,7 @@ void sysdev_unregister(struct sys_device * sysdev) { struct sysdev_driver * drv; - down(&sysdev_drivers_lock); + mutex_lock(&sysdev_drivers_lock); list_for_each_entry(drv, &sysdev_drivers, entry) { if (drv->remove) drv->remove(sysdev); @@ -281,7 +281,7 @@ void sysdev_unregister(struct sys_device * sysdev) if (drv->remove) drv->remove(sysdev); } - up(&sysdev_drivers_lock); + mutex_unlock(&sysdev_drivers_lock); kobject_unregister(&sysdev->kobj); } @@ -308,7 +308,7 @@ void sysdev_shutdown(void) pr_debug("Shutting Down System Devices\n"); - down(&sysdev_drivers_lock); + mutex_lock(&sysdev_drivers_lock); list_for_each_entry_reverse(cls, &system_subsys.list, kset.kobj.entry) { struct sys_device * sysdev; @@ -337,7 +337,7 @@ void sysdev_shutdown(void) cls->shutdown(sysdev); } } - up(&sysdev_drivers_lock); + mutex_unlock(&sysdev_drivers_lock); } static void __sysdev_resume(struct sys_device *dev) From 11048dcf333c414f237bb713c422e68f67b115a3 Mon Sep 17 00:00:00 2001 From: Matthias Kaehlcke Date: Wed, 23 May 2007 14:19:41 -0700 Subject: [PATCH 06/61] Power Management: use mutexes instead of semaphores The Power Management code uses semaphores as mutexes. Use the mutex API instead of the (binary) semaphores. Signed-off-by: Matthias Kaehlcke Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/main.c | 14 ++++++++------ drivers/base/power/power.h | 4 ++-- drivers/base/power/resume.c | 12 ++++++------ drivers/base/power/runtime.c | 12 ++++++------ drivers/base/power/suspend.c | 14 +++++++------- 5 files changed, 29 insertions(+), 27 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 05dc8764e765..7b3cc3c15b9d 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -20,14 +20,16 @@ */ #include +#include + #include "power.h" LIST_HEAD(dpm_active); LIST_HEAD(dpm_off); LIST_HEAD(dpm_off_irq); -DECLARE_MUTEX(dpm_sem); -DECLARE_MUTEX(dpm_list_sem); +DEFINE_MUTEX(dpm_mtx); +DEFINE_MUTEX(dpm_list_mtx); int (*platform_enable_wakeup)(struct device *dev, int is_on); @@ -59,12 +61,12 @@ int device_pm_add(struct device * dev) pr_debug("PM: Adding info for %s:%s\n", dev->bus ? dev->bus->name : "No Bus", kobject_name(&dev->kobj)); - down(&dpm_list_sem); + mutex_lock(&dpm_list_mtx); list_add_tail(&dev->power.entry, &dpm_active); device_pm_set_parent(dev, dev->parent); if ((error = dpm_sysfs_add(dev))) list_del(&dev->power.entry); - up(&dpm_list_sem); + mutex_unlock(&dpm_list_mtx); return error; } @@ -73,11 +75,11 @@ void device_pm_remove(struct device * dev) pr_debug("PM: Removing info for %s:%s\n", dev->bus ? dev->bus->name : "No Bus", kobject_name(&dev->kobj)); - down(&dpm_list_sem); + mutex_lock(&dpm_list_mtx); dpm_sysfs_remove(dev); put_device(dev->power.pm_parent); list_del_init(&dev->power.entry); - up(&dpm_list_sem); + mutex_unlock(&dpm_list_mtx); } diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index fb3d35a9e101..2760f25b3ac5 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -14,12 +14,12 @@ extern void device_shutdown(void); /* * Used to synchronize global power management operations. */ -extern struct semaphore dpm_sem; +extern struct mutex dpm_mtx; /* * Used to serialize changes to the dpm_* lists. */ -extern struct semaphore dpm_list_sem; +extern struct mutex dpm_list_mtx; /* * The PM lists. diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index a2c64188d713..f6cfea496ea0 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -80,7 +80,7 @@ static int resume_device_early(struct device * dev) */ void dpm_resume(void) { - down(&dpm_list_sem); + mutex_lock(&dpm_list_mtx); while(!list_empty(&dpm_off)) { struct list_head * entry = dpm_off.next; struct device * dev = to_device(entry); @@ -88,13 +88,13 @@ void dpm_resume(void) get_device(dev); list_move_tail(entry, &dpm_active); - up(&dpm_list_sem); + mutex_unlock(&dpm_list_mtx); if (!dev->power.prev_state.event) resume_device(dev); - down(&dpm_list_sem); + mutex_lock(&dpm_list_mtx); put_device(dev); } - up(&dpm_list_sem); + mutex_unlock(&dpm_list_mtx); } @@ -108,9 +108,9 @@ void dpm_resume(void) void device_resume(void) { might_sleep(); - down(&dpm_sem); + mutex_lock(&dpm_mtx); dpm_resume(); - up(&dpm_sem); + mutex_unlock(&dpm_mtx); } EXPORT_SYMBOL_GPL(device_resume); diff --git a/drivers/base/power/runtime.c b/drivers/base/power/runtime.c index 96370ec1d673..df6174d85866 100644 --- a/drivers/base/power/runtime.c +++ b/drivers/base/power/runtime.c @@ -32,9 +32,9 @@ static void runtime_resume(struct device * dev) void dpm_runtime_resume(struct device * dev) { - down(&dpm_sem); + mutex_lock(&dpm_mtx); runtime_resume(dev); - up(&dpm_sem); + mutex_unlock(&dpm_mtx); } EXPORT_SYMBOL(dpm_runtime_resume); @@ -49,7 +49,7 @@ int dpm_runtime_suspend(struct device * dev, pm_message_t state) { int error = 0; - down(&dpm_sem); + mutex_lock(&dpm_mtx); if (dev->power.power_state.event == state.event) goto Done; @@ -59,7 +59,7 @@ int dpm_runtime_suspend(struct device * dev, pm_message_t state) if (!(error = suspend_device(dev, state))) dev->power.power_state = state; Done: - up(&dpm_sem); + mutex_unlock(&dpm_mtx); return error; } EXPORT_SYMBOL(dpm_runtime_suspend); @@ -78,8 +78,8 @@ EXPORT_SYMBOL(dpm_runtime_suspend); */ void dpm_set_power_state(struct device * dev, pm_message_t state) { - down(&dpm_sem); + mutex_lock(&dpm_mtx); dev->power.power_state = state; - up(&dpm_sem); + mutex_unlock(&dpm_mtx); } #endif /* 0 */ diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 42d2b86ba765..9d6701cd7f10 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -108,7 +108,7 @@ int suspend_device(struct device * dev, pm_message_t state) /* * This is called with interrupts off, only a single CPU - * running. We can't do down() on a semaphore (and we don't + * running. We can't acquire a mutex or semaphore (and we don't * need the protection) */ static int suspend_device_late(struct device *dev, pm_message_t state) @@ -153,18 +153,18 @@ int device_suspend(pm_message_t state) int error = 0; might_sleep(); - down(&dpm_sem); - down(&dpm_list_sem); + mutex_lock(&dpm_mtx); + mutex_lock(&dpm_list_mtx); while (!list_empty(&dpm_active) && error == 0) { struct list_head * entry = dpm_active.prev; struct device * dev = to_device(entry); get_device(dev); - up(&dpm_list_sem); + mutex_unlock(&dpm_list_mtx); error = suspend_device(dev, state); - down(&dpm_list_sem); + mutex_lock(&dpm_list_mtx); /* Check if the device got removed */ if (!list_empty(&dev->power.entry)) { @@ -179,11 +179,11 @@ int device_suspend(pm_message_t state) error == -EAGAIN ? " (please convert to suspend_late)" : ""); put_device(dev); } - up(&dpm_list_sem); + mutex_unlock(&dpm_list_mtx); if (error) dpm_resume(); - up(&dpm_sem); + mutex_unlock(&dpm_mtx); return error; } From 9cddad77574313fcee36c5e60122718daa7c0361 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 13 Jun 2007 15:53:34 +0200 Subject: [PATCH 07/61] PM: Remove pm_parent from struct dev_pm_info The pm_parent member of struct dev_pm_info (defined in include/linux/pm.h) is only used to check if the device's parent is in the right state while the device is being suspended or resumed. However, this can be done just as well with the help of the parent pointer in struct device, so pm_parent can be removed along with some code that handles it. Signed-off-by: Rafael J. Wysocki Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/main.c | 30 ++++-------------------------- drivers/base/power/resume.c | 7 +++---- drivers/base/power/suspend.c | 7 +++---- include/linux/pm.h | 3 --- 4 files changed, 10 insertions(+), 37 deletions(-) diff --git a/drivers/base/power/main.c b/drivers/base/power/main.c index 7b3cc3c15b9d..eb9f38d0aa58 100644 --- a/drivers/base/power/main.c +++ b/drivers/base/power/main.c @@ -33,28 +33,7 @@ DEFINE_MUTEX(dpm_list_mtx); int (*platform_enable_wakeup)(struct device *dev, int is_on); - -/** - * device_pm_set_parent - Specify power dependency. - * @dev: Device who needs power. - * @parent: Device that supplies power. - * - * This function is used to manually describe a power-dependency - * relationship. It may be used to specify a transversal relationship - * (where the power supplier is not the physical (or electrical) - * ancestor of a specific device. - * The effect of this is that the supplier will not be powered down - * before the power dependent. - */ - -void device_pm_set_parent(struct device * dev, struct device * parent) -{ - put_device(dev->power.pm_parent); - dev->power.pm_parent = get_device(parent); -} -EXPORT_SYMBOL_GPL(device_pm_set_parent); - -int device_pm_add(struct device * dev) +int device_pm_add(struct device *dev) { int error; @@ -63,21 +42,20 @@ int device_pm_add(struct device * dev) kobject_name(&dev->kobj)); mutex_lock(&dpm_list_mtx); list_add_tail(&dev->power.entry, &dpm_active); - device_pm_set_parent(dev, dev->parent); - if ((error = dpm_sysfs_add(dev))) + error = dpm_sysfs_add(dev); + if (error) list_del(&dev->power.entry); mutex_unlock(&dpm_list_mtx); return error; } -void device_pm_remove(struct device * dev) +void device_pm_remove(struct device *dev) { pr_debug("PM: Removing info for %s:%s\n", dev->bus ? dev->bus->name : "No Bus", kobject_name(&dev->kobj)); mutex_lock(&dpm_list_mtx); dpm_sysfs_remove(dev); - put_device(dev->power.pm_parent); list_del_init(&dev->power.entry); mutex_unlock(&dpm_list_mtx); } diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index f6cfea496ea0..99679e7a6cc6 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -29,12 +29,11 @@ int resume_device(struct device * dev) down(&dev->sem); - if (dev->power.pm_parent - && dev->power.pm_parent->power.power_state.event) { + if (dev->parent && dev->parent->power.power_state.event) { dev_err(dev, "PM: resume from %d, parent %s still %d\n", dev->power.power_state.event, - dev->power.pm_parent->bus_id, - dev->power.pm_parent->power.power_state.event); + dev->parent->bus_id, + dev->parent->power.power_state.event); } if (dev->bus && dev->bus->resume) { diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 9d6701cd7f10..19fae88de7b3 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -55,13 +55,12 @@ int suspend_device(struct device * dev, pm_message_t state) dev_dbg(dev, "PM: suspend %d-->%d\n", dev->power.power_state.event, state.event); } - if (dev->power.pm_parent - && dev->power.pm_parent->power.power_state.event) { + if (dev->parent && dev->parent->power.power_state.event) { dev_err(dev, "PM: suspend %d->%d, parent %s already %d\n", dev->power.power_state.event, state.event, - dev->power.pm_parent->bus_id, - dev->power.pm_parent->power.power_state.event); + dev->parent->bus_id, + dev->parent->power.power_state.event); } dev->power.prev_state = dev->power.power_state; diff --git a/include/linux/pm.h b/include/linux/pm.h index b2c4fde4e994..3fd65ad4b097 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -269,13 +269,10 @@ struct dev_pm_info { unsigned should_wakeup:1; pm_message_t prev_state; void * saved_state; - struct device * pm_parent; struct list_head entry; #endif }; -extern void device_pm_set_parent(struct device * dev, struct device * parent); - extern int device_power_down(pm_message_t state); extern void device_power_up(void); extern void device_resume(void); From cc4900690bf77257996e90f0059eb074b8db52e6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 13 Jun 2007 15:55:34 +0200 Subject: [PATCH 08/61] PM: Remove saved_state from struct dev_pm_info The saved_state member of struct dev_pm_info, defined in include/linux/pm.h, is not used anywhere, so it can be removed. Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- include/linux/pm.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/linux/pm.h b/include/linux/pm.h index 3fd65ad4b097..6e7f06671683 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -268,7 +268,6 @@ struct dev_pm_info { #ifdef CONFIG_PM unsigned should_wakeup:1; pm_message_t prev_state; - void * saved_state; struct list_head entry; #endif }; From 9e584a4fe58881e2dc8f09bdf8444b199ab5ecaf Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 13 Jun 2007 16:19:27 +0200 Subject: [PATCH 09/61] PM: Simplify suspend_device Reduce code duplication in drivers/base/suspend.c by introducing a separate function for printing diagnostic messages. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/suspend.c | 49 +++++++++++++----------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 19fae88de7b3..af2cedfbc1b4 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -40,6 +40,14 @@ static inline char *suspend_verb(u32 event) } +static void +suspend_device_dbg(struct device *dev, pm_message_t state, char *info) +{ + dev_dbg(dev, "%s%s%s\n", info, suspend_verb(state.event), + ((state.event == PM_EVENT_SUSPEND) && device_may_wakeup(dev)) ? + ", may wakeup" : ""); +} + /** * suspend_device - Save state of one device. * @dev: Device. @@ -66,37 +74,21 @@ int suspend_device(struct device * dev, pm_message_t state) dev->power.prev_state = dev->power.power_state; if (dev->class && dev->class->suspend && !dev->power.power_state.event) { - dev_dbg(dev, "class %s%s\n", - suspend_verb(state.event), - ((state.event == PM_EVENT_SUSPEND) - && device_may_wakeup(dev)) - ? ", may wakeup" - : "" - ); + suspend_device_dbg(dev, state, "class "); error = dev->class->suspend(dev, state); suspend_report_result(dev->class->suspend, error); } - if (!error && dev->type && dev->type->suspend && !dev->power.power_state.event) { - dev_dbg(dev, "%s%s\n", - suspend_verb(state.event), - ((state.event == PM_EVENT_SUSPEND) - && device_may_wakeup(dev)) - ? ", may wakeup" - : "" - ); + if (!error && dev->type && dev->type->suspend + && !dev->power.power_state.event) { + suspend_device_dbg(dev, state, "type "); error = dev->type->suspend(dev, state); suspend_report_result(dev->type->suspend, error); } - if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) { - dev_dbg(dev, "%s%s\n", - suspend_verb(state.event), - ((state.event == PM_EVENT_SUSPEND) - && device_may_wakeup(dev)) - ? ", may wakeup" - : "" - ); + if (!error && dev->bus && dev->bus->suspend + && !dev->power.power_state.event) { + suspend_device_dbg(dev, state, ""); error = dev->bus->suspend(dev, state); suspend_report_result(dev->bus->suspend, error); } @@ -114,14 +106,9 @@ static int suspend_device_late(struct device *dev, pm_message_t state) { int error = 0; - if (dev->bus && dev->bus->suspend_late && !dev->power.power_state.event) { - dev_dbg(dev, "LATE %s%s\n", - suspend_verb(state.event), - ((state.event == PM_EVENT_SUSPEND) - && device_may_wakeup(dev)) - ? ", may wakeup" - : "" - ); + if (dev->bus && dev->bus->suspend_late + && !dev->power.power_state.event) { + suspend_device_dbg(dev, state, "LATE "); error = dev->bus->suspend_late(dev, state); suspend_report_result(dev->bus->suspend_late, error); } From f8916c11a4dc4cb2367e9bee1788f4e0f1b4eabc Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Sun, 10 Jun 2007 22:39:12 +0300 Subject: [PATCH 10/61] Driver core: include linux/mutex.h from attribute_container.c attribute_container.c uses DEFINE_MUTEX, so while linux/mutex.h seems to be pulled in indirectly by one of the headers it includes, the right thing is to include linux/mutex.h directly. Signed-off-by: Michael S. Tsirkin --- drivers/base/attribute_container.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/base/attribute_container.c b/drivers/base/attribute_container.c index 1ec0654665cf..7370d7cf5988 100644 --- a/drivers/base/attribute_container.c +++ b/drivers/base/attribute_container.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "base.h" From 1f5681aae8feadd113644c9e077152416c12b75c Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 17 Jun 2007 11:01:18 +0200 Subject: [PATCH 11/61] driver core: properly get driver in device_release_driver Signed-off-by: Stefan Richter Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index b0088b0efecd..3c503e1f69a2 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -296,9 +296,8 @@ static void __device_release_driver(struct device * dev) { struct device_driver * drv; - drv = dev->driver; + drv = get_driver(dev->driver); if (drv) { - get_driver(drv); driver_sysfs_remove(dev); sysfs_remove_link(&dev->kobj, "driver"); klist_remove(&dev->knode_driver); From ab71c6f0767bcbc618f3db51f668d5b951c00b60 Mon Sep 17 00:00:00 2001 From: Stefan Richter Date: Sun, 17 Jun 2007 11:02:12 +0200 Subject: [PATCH 12/61] driver core: fix kernel doc of device_release_driver Signed-off-by: Stefan Richter Signed-off-by: Greg Kroah-Hartman --- drivers/base/dd.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 3c503e1f69a2..7ac474db88c5 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -281,17 +281,10 @@ int driver_attach(struct device_driver * drv) return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach); } -/** - * device_release_driver - manually detach device from driver. - * @dev: device. - * - * Manually detach device from driver. - * +/* * __device_release_driver() must be called with @dev->sem held. - * When called for a USB interface, @dev->parent->sem must be held - * as well. + * When called for a USB interface, @dev->parent->sem must be held as well. */ - static void __device_release_driver(struct device * dev) { struct device_driver * drv; @@ -317,6 +310,13 @@ static void __device_release_driver(struct device * dev) } } +/** + * device_release_driver - manually detach device from driver. + * @dev: device. + * + * Manually detach device from driver. + * When called for a USB interface, @dev->parent->sem must be held. + */ void device_release_driver(struct device * dev) { /* From 2a0134554e12f530c6eccb1dab3f0f8954f855c9 Mon Sep 17 00:00:00 2001 From: Adrian Bunk Date: Mon, 18 Jun 2007 01:42:54 +0200 Subject: [PATCH 13/61] Driver core: fix devres_release_all() return value Every file should include the headers containing the prototypes for it's global functions. Since the GNU C compiler is now able to detect that the function prototype of devres_release_all() in the header and the actual function disagree regarding the return value, this patch also fixes this bug. Signed-off-by: Adrian Bunk Acked-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- drivers/base/base.h | 2 +- drivers/base/devres.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/base/base.h b/drivers/base/base.h index 5512d84452f2..47eb02d9f1af 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -44,6 +44,6 @@ struct class_device_attribute *to_class_dev_attr(struct attribute *_attr) extern char *make_class_name(const char *name, struct kobject *kobj); -extern void devres_release_all(struct device *dev); +extern int devres_release_all(struct device *dev); extern struct kset devices_subsys; diff --git a/drivers/base/devres.c b/drivers/base/devres.c index e1c0730a3b99..e8beb8e5b626 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -10,6 +10,8 @@ #include #include +#include "base.h" + struct devres_node { struct list_head entry; dr_release_t release; From 515c53576299e32d6bdb6295cfa2fe1307516eb4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 17 Jun 2007 19:48:06 +0200 Subject: [PATCH 14/61] PM: Remove prev_state from struct dev_pm_info The prev_state member of struct dev_pm_info (defined in include/linux/pm.h) is only used during a resume to check if the device's state before the suspend was 'off', in which case the device is not resumed. However, in such cases the decision whether or not to resume the device should be made on the driver level and the resume callbacks from the device's bus and class should be executed anyway (the may be needed for some things other than just powering on the device). Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/resume.c | 3 +-- drivers/base/power/suspend.c | 2 -- drivers/usb/core/hub.c | 5 ----- include/linux/pm.h | 1 - 4 files changed, 1 insertion(+), 10 deletions(-) diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index 99679e7a6cc6..0c9610688542 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -88,8 +88,7 @@ void dpm_resume(void) list_move_tail(entry, &dpm_active); mutex_unlock(&dpm_list_mtx); - if (!dev->power.prev_state.event) - resume_device(dev); + resume_device(dev); mutex_lock(&dpm_list_mtx); put_device(dev); } diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index af2cedfbc1b4..5178b0fbd82e 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -71,8 +71,6 @@ int suspend_device(struct device * dev, pm_message_t state) dev->parent->power.power_state.event); } - dev->power.prev_state = dev->power.power_state; - if (dev->class && dev->class->suspend && !dev->power.power_state.event) { suspend_device_dbg(dev, state, "class "); error = dev->class->suspend(dev, state); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 24f10a19dbdb..a9cf8b30bccc 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1109,11 +1109,6 @@ void usb_root_hub_lost_power(struct usb_device *rhdev) dev_warn(&rhdev->dev, "root hub lost power or was reset\n"); - /* Make sure no potential wakeup events get lost, - * by forcing the root hub to be resumed. - */ - rhdev->dev.power.prev_state.event = PM_EVENT_ON; - spin_lock_irqsave(&device_state_lock, flags); hub = hdev_to_hub(rhdev); for (port1 = 1; port1 <= rhdev->maxchild; ++port1) { diff --git a/include/linux/pm.h b/include/linux/pm.h index 6e7f06671683..273781c82e4d 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -267,7 +267,6 @@ struct dev_pm_info { unsigned can_wakeup:1; #ifdef CONFIG_PM unsigned should_wakeup:1; - pm_message_t prev_state; struct list_head entry; #endif }; From 1c3f7d1c79425dd8f627b8da139fd1855f096b6e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 17 Jun 2007 19:49:20 +0200 Subject: [PATCH 15/61] PM: Remove power_state.event checks from suspend core code The suspend routines should be called for every device during a system sleep transition, regardless of the device's state, so that drivers can regard these method calls as notifications that the system is about to go to sleep, rather than as directives to put their devices into the 'off' state. This is documented in Documentation/power/devices.txt and is already done in the core resume code, so it seems reasonable to make the core suspend code behave accordingly. Signed-off-by: Rafael J. Wysocki Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/suspend.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 5178b0fbd82e..a3ff3944832a 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -71,21 +71,19 @@ int suspend_device(struct device * dev, pm_message_t state) dev->parent->power.power_state.event); } - if (dev->class && dev->class->suspend && !dev->power.power_state.event) { + if (dev->class && dev->class->suspend) { suspend_device_dbg(dev, state, "class "); error = dev->class->suspend(dev, state); suspend_report_result(dev->class->suspend, error); } - if (!error && dev->type && dev->type->suspend - && !dev->power.power_state.event) { + if (!error && dev->type && dev->type->suspend) { suspend_device_dbg(dev, state, "type "); error = dev->type->suspend(dev, state); suspend_report_result(dev->type->suspend, error); } - if (!error && dev->bus && dev->bus->suspend - && !dev->power.power_state.event) { + if (!error && dev->bus && dev->bus->suspend) { suspend_device_dbg(dev, state, ""); error = dev->bus->suspend(dev, state); suspend_report_result(dev->bus->suspend, error); @@ -104,8 +102,7 @@ static int suspend_device_late(struct device *dev, pm_message_t state) { int error = 0; - if (dev->bus && dev->bus->suspend_late - && !dev->power.power_state.event) { + if (dev->bus && dev->bus->suspend_late) { suspend_device_dbg(dev, state, "LATE "); error = dev->bus->suspend_late(dev, state); suspend_report_result(dev->bus->suspend_late, error); From 43a49f8baa6b0e7e5b68ed0ee141e1efacfd9ae6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 17 Jun 2007 19:50:51 +0200 Subject: [PATCH 16/61] PM: Do not check parent state in suspend and resume core code The checks if the device's parent is in the right state done in drivers/base/power/suspend.c and drivers/base/power/resume.c serve no particular purpose, since if the parent is in a wrong power state, the device's suspend or resume callbacks are supposed to return an error anyway. Moreover, they are also useless from the sanity checking point of view, because they rely on the code being checked to set dev->parent->power.power_state.event appropriately, which need not happen if that code is buggy. For these reasons they can be removed. Signed-off-by: Rafael J. Wysocki Acked-by: David Brownell Signed-off-by: Greg Kroah-Hartman --- drivers/base/power/resume.c | 7 ------- drivers/base/power/suspend.c | 7 ------- 2 files changed, 14 deletions(-) diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c index 0c9610688542..00fd84ae6e66 100644 --- a/drivers/base/power/resume.c +++ b/drivers/base/power/resume.c @@ -29,13 +29,6 @@ int resume_device(struct device * dev) down(&dev->sem); - if (dev->parent && dev->parent->power.power_state.event) { - dev_err(dev, "PM: resume from %d, parent %s still %d\n", - dev->power.power_state.event, - dev->parent->bus_id, - dev->parent->power.power_state.event); - } - if (dev->bus && dev->bus->resume) { dev_dbg(dev,"resuming\n"); error = dev->bus->resume(dev); diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index a3ff3944832a..26df9b231737 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c @@ -63,13 +63,6 @@ int suspend_device(struct device * dev, pm_message_t state) dev_dbg(dev, "PM: suspend %d-->%d\n", dev->power.power_state.event, state.event); } - if (dev->parent && dev->parent->power.power_state.event) { - dev_err(dev, - "PM: suspend %d->%d, parent %s already %d\n", - dev->power.power_state.event, state.event, - dev->parent->bus_id, - dev->parent->power.power_state.event); - } if (dev->class && dev->class->suspend) { suspend_device_dbg(dev, state, "class "); From 93160c6397e460bc4c7ac15323fb698f91ef02e5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 9 Jul 2007 11:39:19 -0700 Subject: [PATCH 17/61] PM: do not use saved_state from struct dev_pm_info on ARM The saved_state member of 'struct dev_pm_info' that's going to be removed is used in arch/arm/common/locomo.c, arch/arm/common/sa1111.c and arch/arm/mach-sa1100/neponset.c. Change the code in there to use local variables for saving the state of devices during suspend. Signed-off-by: Rafael J. Wysocki Cc: Greg KH Cc: David Brownell Acked-by: Russell King Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- arch/arm/common/locomo.c | 11 ++++++++--- arch/arm/common/sa1111.c | 13 ++++++++----- arch/arm/mach-sa1100/neponset.c | 15 ++++----------- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/arch/arm/common/locomo.c b/arch/arm/common/locomo.c index cfe6f4650bc9..ae21755872ed 100644 --- a/arch/arm/common/locomo.c +++ b/arch/arm/common/locomo.c @@ -60,6 +60,9 @@ struct locomo { unsigned int irq; spinlock_t lock; void __iomem *base; +#ifdef CONFIG_PM + void *saved_state; +#endif }; struct locomo_dev_info { @@ -565,7 +568,7 @@ static int locomo_suspend(struct platform_device *dev, pm_message_t state) if (!save) return -ENOMEM; - dev->dev.power.saved_state = (void *) save; + lchip->saved_state = save; spin_lock_irqsave(&lchip->lock, flags); @@ -605,8 +608,8 @@ static int locomo_resume(struct platform_device *dev) struct locomo_save_data *save; unsigned long r; unsigned long flags; - - save = (struct locomo_save_data *) dev->dev.power.saved_state; + + save = lchip->saved_state; if (!save) return 0; @@ -628,6 +631,8 @@ static int locomo_resume(struct platform_device *dev) locomo_writel(0x1, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KCMD); spin_unlock_irqrestore(&lchip->lock, flags); + + lchip->saved_state = NULL; kfree(save); return 0; diff --git a/arch/arm/common/sa1111.c b/arch/arm/common/sa1111.c index 798bbfccafb7..eb06d0b2cb74 100644 --- a/arch/arm/common/sa1111.c +++ b/arch/arm/common/sa1111.c @@ -51,6 +51,9 @@ struct sa1111 { int irq; spinlock_t lock; void __iomem *base; +#ifdef CONFIG_PM + void *saved_state; +#endif }; /* @@ -822,7 +825,7 @@ static int sa1111_suspend(struct platform_device *dev, pm_message_t state) save = kmalloc(sizeof(struct sa1111_save_data), GFP_KERNEL); if (!save) return -ENOMEM; - dev->dev.power.saved_state = save; + sachip->saved_state = save; spin_lock_irqsave(&sachip->lock, flags); @@ -878,7 +881,7 @@ static int sa1111_resume(struct platform_device *dev) unsigned long flags, id; void __iomem *base; - save = (struct sa1111_save_data *)dev->dev.power.saved_state; + save = sachip->saved_state; if (!save) return 0; @@ -923,7 +926,7 @@ static int sa1111_resume(struct platform_device *dev) spin_unlock_irqrestore(&sachip->lock, flags); - dev->dev.power.saved_state = NULL; + sachip->saved_state = NULL; kfree(save); return 0; @@ -958,8 +961,8 @@ static int sa1111_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); #ifdef CONFIG_PM - kfree(pdev->dev.power.saved_state); - pdev->dev.power.saved_state = NULL; + kfree(sachip->saved_state); + sachip->saved_state = NULL; #endif } diff --git a/arch/arm/mach-sa1100/neponset.c b/arch/arm/mach-sa1100/neponset.c index 4cbf9468f654..3a0a1ee2542d 100644 --- a/arch/arm/mach-sa1100/neponset.c +++ b/arch/arm/mach-sa1100/neponset.c @@ -185,28 +185,21 @@ static int __devinit neponset_probe(struct platform_device *dev) /* * LDM power management. */ +static unsigned int neponset_saved_state; + static int neponset_suspend(struct platform_device *dev, pm_message_t state) { /* * Save state. */ - if (!dev->dev.power.saved_state) - dev->dev.power.saved_state = kmalloc(sizeof(unsigned int), GFP_KERNEL); - if (!dev->dev.power.saved_state) - return -ENOMEM; - - *(unsigned int *)dev->dev.power.saved_state = NCR_0; + neponset_saved_state = NCR_0; return 0; } static int neponset_resume(struct platform_device *dev) { - if (dev->dev.power.saved_state) { - NCR_0 = *(unsigned int *)dev->dev.power.saved_state; - kfree(dev->dev.power.saved_state); - dev->dev.power.saved_state = NULL; - } + NCR_0 = neponset_saved_state; return 0; } From dc0afa8388972a9ed7c2203cc46d8df1a4713f65 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 9 Jul 2007 11:39:18 -0700 Subject: [PATCH 18/61] Driver core: coding style cleanup This converts code of the form if ((error = some_func())) goto fixup; to error = some_func(); if (error) goto fixup; Signed-off-by: Cornelia Huck Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 6 ++++-- drivers/base/core.c | 9 ++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 7ff5aafe2167..f299e0d6abc4 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -622,7 +622,8 @@ int bus_add_driver(struct device_driver *drv) if (error) goto out_put_bus; drv->kobj.kset = &bus->drivers; - if ((error = kobject_register(&drv->kobj))) + error = kobject_register(&drv->kobj); + if (error) goto out_put_bus; if (drv->bus->drivers_autoprobe) { @@ -772,7 +773,8 @@ static int bus_add_attrs(struct bus_type * bus) if (bus->bus_attrs) { for (i = 0; attr_name(bus->bus_attrs[i]); i++) { - if ((error = bus_create_file(bus,&bus->bus_attrs[i]))) + error = bus_create_file(bus,&bus->bus_attrs[i]); + if (error) goto Err; } } diff --git a/drivers/base/core.c b/drivers/base/core.c index dd40d78a023d..cff4fbfbb055 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -733,11 +733,14 @@ int device_add(struct device *dev) } } - if ((error = device_add_attrs(dev))) + error = device_add_attrs(dev); + if (error) goto AttrsError; - if ((error = device_pm_add(dev))) + error = device_pm_add(dev); + if (error) goto PMError; - if ((error = bus_add_device(dev))) + error = bus_add_device(dev); + if (error) goto BusError; kobject_uevent(&dev->kobj, KOBJ_ADD); bus_attach_device(dev); From 7aae6dd80e265aa9402ed507caaff4a5dba55069 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:12 +0900 Subject: [PATCH 19/61] idr: fix obscure bug in allocation path In sub_alloc(), when bitmap search fails, it goes up one level to continue search. This is done by updating the id cursor and searching the upper level again. If the cursor was at the end of the upper level, we need to go further than that. This wasn't implemented and when that happens the part of the cursor which indexes into the upper level wraps and sub_alloc() ends up searching the wrong bitmap. It allocates id which doesn't match the actual slot. This patch fixes this by restarting from the top if the search needs to go higher than one level. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- lib/idr.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/lib/idr.c b/lib/idr.c index 305117ca2d41..7b5a59caa989 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -100,10 +100,11 @@ static int sub_alloc(struct idr *idp, void *ptr, int *starting_id) int n, m, sh; struct idr_layer *p, *new; struct idr_layer *pa[MAX_LEVEL]; - int l, id; + int l, id, oid; long bm; id = *starting_id; + restart: p = idp->top; l = idp->layers; pa[l--] = NULL; @@ -117,12 +118,23 @@ static int sub_alloc(struct idr *idp, void *ptr, int *starting_id) if (m == IDR_SIZE) { /* no space available go back to previous layer. */ l++; + oid = id; id = (id | ((1 << (IDR_BITS * l)) - 1)) + 1; + + /* if already at the top layer, we need to grow */ if (!(p = pa[l])) { *starting_id = id; return -2; } - continue; + + /* If we need to go up one layer, continue the + * loop; otherwise, restart from the top. + */ + sh = IDR_BITS * (l + 1); + if (oid >> sh == id >> sh) + continue; + else + goto restart; } if (m != n) { sh = IDR_BITS*l; From e33ac8bdb0c84fe7afd2c45537b763faf28c589e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:12 +0900 Subject: [PATCH 20/61] idr: separate out idr_mark_full() Separate out idr_mark_full() from sub_alloc() and make marking the allocated slot full the responsibility of idr_get_new_above_int(). Allocation part of idr_get_new_above_int() is renamed to idr_get_empty_slot(). New idr_get_new_above_int() allocates a slot using the function, install the user pointer and marks it full using idr_mark_full(). This change doesn't introduce any behavior change. This will be used by ida. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- lib/idr.c | 71 +++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 46 insertions(+), 25 deletions(-) diff --git a/lib/idr.c b/lib/idr.c index 7b5a59caa989..30b33e2e7a50 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -70,6 +70,26 @@ static void free_layer(struct idr *idp, struct idr_layer *p) spin_unlock_irqrestore(&idp->lock, flags); } +static void idr_mark_full(struct idr_layer **pa, int id) +{ + struct idr_layer *p = pa[0]; + int l = 0; + + __set_bit(id & IDR_MASK, &p->bitmap); + /* + * If this layer is full mark the bit in the layer above to + * show that this part of the radix tree is full. This may + * complete the layer above and require walking up the radix + * tree. + */ + while (p->bitmap == IDR_FULL) { + if (!(p = pa[++l])) + break; + id = id >> IDR_BITS; + __set_bit((id & IDR_MASK), &p->bitmap); + } +} + /** * idr_pre_get - reserver resources for idr allocation * @idp: idr handle @@ -95,11 +115,10 @@ int idr_pre_get(struct idr *idp, gfp_t gfp_mask) } EXPORT_SYMBOL(idr_pre_get); -static int sub_alloc(struct idr *idp, void *ptr, int *starting_id) +static int sub_alloc(struct idr *idp, int *starting_id, struct idr_layer **pa) { int n, m, sh; struct idr_layer *p, *new; - struct idr_layer *pa[MAX_LEVEL]; int l, id, oid; long bm; @@ -156,30 +175,13 @@ static int sub_alloc(struct idr *idp, void *ptr, int *starting_id) pa[l--] = p; p = p->ary[m]; } - /* - * We have reached the leaf node, plant the - * users pointer and return the raw id. - */ - p->ary[m] = (struct idr_layer *)ptr; - __set_bit(m, &p->bitmap); - p->count++; - /* - * If this layer is full mark the bit in the layer above - * to show that this part of the radix tree is full. - * This may complete the layer above and require walking - * up the radix tree. - */ - n = id; - while (p->bitmap == IDR_FULL) { - if (!(p = pa[++l])) - break; - n = n >> IDR_BITS; - __set_bit((n & IDR_MASK), &p->bitmap); - } - return(id); + + pa[l] = p; + return id; } -static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id) +static int idr_get_empty_slot(struct idr *idp, int starting_id, + struct idr_layer **pa) { struct idr_layer *p, *new; int layers, v, id; @@ -225,12 +227,31 @@ build_up: } idp->top = p; idp->layers = layers; - v = sub_alloc(idp, ptr, &id); + v = sub_alloc(idp, &id, pa); if (v == -2) goto build_up; return(v); } +static int idr_get_new_above_int(struct idr *idp, void *ptr, int starting_id) +{ + struct idr_layer *pa[MAX_LEVEL]; + int id; + + id = idr_get_empty_slot(idp, starting_id, pa); + if (id >= 0) { + /* + * Successfully found an empty slot. Install the user + * pointer and mark the slot full. + */ + pa[0]->ary[id & IDR_MASK] = (struct idr_layer *)ptr; + pa[0]->count++; + idr_mark_full(pa, id); + } + + return id; +} + /** * idr_get_new_above - allocate new idr entry above or equal to a start id * @idp: idr handle From 72dba584b695d8bc8c1a50ed54ad4cba7c62314d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:13 +0900 Subject: [PATCH 21/61] ida: implement idr based id allocator Implement idr based id allocator. ida is used the same way idr is used but lacks id -> ptr translation and thus consumes much less memory. struct ida_bitmap is attached as leaf nodes to idr tree which is managed by the idr code. Each ida_bitmap is 128bytes long and contains slightly less than a thousand slots. ida is more aggressive with releasing extra resources acquired using ida_pre_get(). After every successful id allocation, ida frees one reserved idr_layer if possible. Reserved ida_bitmap is not freed automatically but only one ida_bitmap is reserved and it's almost always used right away. Under most circumstances, ida won't hold on to memory for too long which isn't actively used. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- include/linux/idr.h | 29 ++++++ lib/idr.c | 245 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 274 insertions(+) diff --git a/include/linux/idr.h b/include/linux/idr.h index 826803449db7..915572fa030b 100644 --- a/include/linux/idr.h +++ b/include/linux/idr.h @@ -83,4 +83,33 @@ void idr_remove(struct idr *idp, int id); void idr_destroy(struct idr *idp); void idr_init(struct idr *idp); + +/* + * IDA - IDR based id allocator, use when translation from id to + * pointer isn't necessary. + */ +#define IDA_CHUNK_SIZE 128 /* 128 bytes per chunk */ +#define IDA_BITMAP_LONGS (128 / sizeof(long) - 1) +#define IDA_BITMAP_BITS (IDA_BITMAP_LONGS * sizeof(long) * 8) + +struct ida_bitmap { + long nr_busy; + unsigned long bitmap[IDA_BITMAP_LONGS]; +}; + +struct ida { + struct idr idr; + struct ida_bitmap *free_bitmap; +}; + +#define IDA_INIT(name) { .idr = IDR_INIT(name), .free_bitmap = NULL, } +#define DEFINE_IDA(name) struct ida name = IDA_INIT(name) + +int ida_pre_get(struct ida *ida, gfp_t gfp_mask); +int ida_get_new_above(struct ida *ida, int starting_id, int *p_id); +int ida_get_new(struct ida *ida, int *p_id); +void ida_remove(struct ida *ida, int id); +void ida_destroy(struct ida *ida); +void ida_init(struct ida *ida); + #endif /* __IDR_H__ */ diff --git a/lib/idr.c b/lib/idr.c index 30b33e2e7a50..b98f01a2eb94 100644 --- a/lib/idr.c +++ b/lib/idr.c @@ -506,3 +506,248 @@ void idr_init(struct idr *idp) spin_lock_init(&idp->lock); } EXPORT_SYMBOL(idr_init); + + +/* + * IDA - IDR based ID allocator + * + * this is id allocator without id -> pointer translation. Memory + * usage is much lower than full blown idr because each id only + * occupies a bit. ida uses a custom leaf node which contains + * IDA_BITMAP_BITS slots. + * + * 2007-04-25 written by Tejun Heo + */ + +static void free_bitmap(struct ida *ida, struct ida_bitmap *bitmap) +{ + unsigned long flags; + + if (!ida->free_bitmap) { + spin_lock_irqsave(&ida->idr.lock, flags); + if (!ida->free_bitmap) { + ida->free_bitmap = bitmap; + bitmap = NULL; + } + spin_unlock_irqrestore(&ida->idr.lock, flags); + } + + kfree(bitmap); +} + +/** + * ida_pre_get - reserve resources for ida allocation + * @ida: ida handle + * @gfp_mask: memory allocation flag + * + * This function should be called prior to locking and calling the + * following function. It preallocates enough memory to satisfy the + * worst possible allocation. + * + * If the system is REALLY out of memory this function returns 0, + * otherwise 1. + */ +int ida_pre_get(struct ida *ida, gfp_t gfp_mask) +{ + /* allocate idr_layers */ + if (!idr_pre_get(&ida->idr, gfp_mask)) + return 0; + + /* allocate free_bitmap */ + if (!ida->free_bitmap) { + struct ida_bitmap *bitmap; + + bitmap = kmalloc(sizeof(struct ida_bitmap), gfp_mask); + if (!bitmap) + return 0; + + free_bitmap(ida, bitmap); + } + + return 1; +} +EXPORT_SYMBOL(ida_pre_get); + +/** + * ida_get_new_above - allocate new ID above or equal to a start id + * @ida: ida handle + * @staring_id: id to start search at + * @p_id: pointer to the allocated handle + * + * Allocate new ID above or equal to @ida. It should be called with + * any required locks. + * + * If memory is required, it will return -EAGAIN, you should unlock + * and go back to the ida_pre_get() call. If the ida is full, it will + * return -ENOSPC. + * + * @p_id returns a value in the range 0 ... 0x7fffffff. + */ +int ida_get_new_above(struct ida *ida, int starting_id, int *p_id) +{ + struct idr_layer *pa[MAX_LEVEL]; + struct ida_bitmap *bitmap; + unsigned long flags; + int idr_id = starting_id / IDA_BITMAP_BITS; + int offset = starting_id % IDA_BITMAP_BITS; + int t, id; + + restart: + /* get vacant slot */ + t = idr_get_empty_slot(&ida->idr, idr_id, pa); + if (t < 0) { + if (t == -1) + return -EAGAIN; + else /* will be -3 */ + return -ENOSPC; + } + + if (t * IDA_BITMAP_BITS >= MAX_ID_BIT) + return -ENOSPC; + + if (t != idr_id) + offset = 0; + idr_id = t; + + /* if bitmap isn't there, create a new one */ + bitmap = (void *)pa[0]->ary[idr_id & IDR_MASK]; + if (!bitmap) { + spin_lock_irqsave(&ida->idr.lock, flags); + bitmap = ida->free_bitmap; + ida->free_bitmap = NULL; + spin_unlock_irqrestore(&ida->idr.lock, flags); + + if (!bitmap) + return -EAGAIN; + + memset(bitmap, 0, sizeof(struct ida_bitmap)); + pa[0]->ary[idr_id & IDR_MASK] = (void *)bitmap; + pa[0]->count++; + } + + /* lookup for empty slot */ + t = find_next_zero_bit(bitmap->bitmap, IDA_BITMAP_BITS, offset); + if (t == IDA_BITMAP_BITS) { + /* no empty slot after offset, continue to the next chunk */ + idr_id++; + offset = 0; + goto restart; + } + + id = idr_id * IDA_BITMAP_BITS + t; + if (id >= MAX_ID_BIT) + return -ENOSPC; + + __set_bit(t, bitmap->bitmap); + if (++bitmap->nr_busy == IDA_BITMAP_BITS) + idr_mark_full(pa, idr_id); + + *p_id = id; + + /* Each leaf node can handle nearly a thousand slots and the + * whole idea of ida is to have small memory foot print. + * Throw away extra resources one by one after each successful + * allocation. + */ + if (ida->idr.id_free_cnt || ida->free_bitmap) { + struct idr_layer *p = alloc_layer(&ida->idr); + if (p) + kmem_cache_free(idr_layer_cache, p); + } + + return 0; +} +EXPORT_SYMBOL(ida_get_new_above); + +/** + * ida_get_new - allocate new ID + * @ida: idr handle + * @p_id: pointer to the allocated handle + * + * Allocate new ID. It should be called with any required locks. + * + * If memory is required, it will return -EAGAIN, you should unlock + * and go back to the idr_pre_get() call. If the idr is full, it will + * return -ENOSPC. + * + * @id returns a value in the range 0 ... 0x7fffffff. + */ +int ida_get_new(struct ida *ida, int *p_id) +{ + return ida_get_new_above(ida, 0, p_id); +} +EXPORT_SYMBOL(ida_get_new); + +/** + * ida_remove - remove the given ID + * @ida: ida handle + * @id: ID to free + */ +void ida_remove(struct ida *ida, int id) +{ + struct idr_layer *p = ida->idr.top; + int shift = (ida->idr.layers - 1) * IDR_BITS; + int idr_id = id / IDA_BITMAP_BITS; + int offset = id % IDA_BITMAP_BITS; + int n; + struct ida_bitmap *bitmap; + + /* clear full bits while looking up the leaf idr_layer */ + while ((shift > 0) && p) { + n = (idr_id >> shift) & IDR_MASK; + __clear_bit(n, &p->bitmap); + p = p->ary[n]; + shift -= IDR_BITS; + } + + if (p == NULL) + goto err; + + n = idr_id & IDR_MASK; + __clear_bit(n, &p->bitmap); + + bitmap = (void *)p->ary[n]; + if (!test_bit(offset, bitmap->bitmap)) + goto err; + + /* update bitmap and remove it if empty */ + __clear_bit(offset, bitmap->bitmap); + if (--bitmap->nr_busy == 0) { + __set_bit(n, &p->bitmap); /* to please idr_remove() */ + idr_remove(&ida->idr, idr_id); + free_bitmap(ida, bitmap); + } + + return; + + err: + printk(KERN_WARNING + "ida_remove called for id=%d which is not allocated.\n", id); +} +EXPORT_SYMBOL(ida_remove); + +/** + * ida_destroy - release all cached layers within an ida tree + * ida: ida handle + */ +void ida_destroy(struct ida *ida) +{ + idr_destroy(&ida->idr); + kfree(ida->free_bitmap); +} +EXPORT_SYMBOL(ida_destroy); + +/** + * ida_init - initialize ida handle + * @ida: ida handle + * + * This function is use to set up the handle (@ida) that you will pass + * to the rest of the functions. + */ +void ida_init(struct ida *ida) +{ + memset(ida, 0, sizeof(struct ida)); + idr_init(&ida->idr); + +} +EXPORT_SYMBOL(ida_init); From fa7f912ad4ae0ed7591add52422e48282389652d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:13 +0900 Subject: [PATCH 22/61] sysfs: move release_sysfs_dirent() to dir.c There is no reason this function should be inlined and soon to follow sysfs object reference simplification will make it heavier. Move it to dir.c. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 12 ++++++++++++ fs/sysfs/sysfs.h | 13 +------------ 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index c4342a019972..2544aae6f583 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -15,6 +15,18 @@ DECLARE_RWSEM(sysfs_rename_sem); spinlock_t sysfs_lock = SPIN_LOCK_UNLOCKED; +void release_sysfs_dirent(struct sysfs_dirent * sd) +{ + if (sd->s_type & SYSFS_KOBJ_LINK) { + struct sysfs_symlink * sl = sd->s_element; + kfree(sl->link_name); + kobject_put(sl->target_kobj); + kfree(sl); + } + kfree(sd->s_iattr); + kmem_cache_free(sysfs_dir_cachep, sd); +} + static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) { struct sysfs_dirent * sd = dentry->d_fsdata; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 502c949c402d..687d959f606d 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -18,6 +18,7 @@ extern void sysfs_delete_inode(struct inode *inode); extern struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent *); extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); +extern void release_sysfs_dirent(struct sysfs_dirent * sd); extern int sysfs_dirent_exist(struct sysfs_dirent *, const unsigned char *); extern int sysfs_make_dirent(struct sysfs_dirent *, struct dentry *, void *, umode_t, int); @@ -99,18 +100,6 @@ static inline struct kobject *sysfs_get_kobject(struct dentry *dentry) return kobj; } -static inline void release_sysfs_dirent(struct sysfs_dirent * sd) -{ - if (sd->s_type & SYSFS_KOBJ_LINK) { - struct sysfs_symlink * sl = sd->s_element; - kfree(sl->link_name); - kobject_put(sl->target_kobj); - kfree(sl); - } - kfree(sd->s_iattr); - kmem_cache_free(sysfs_dir_cachep, sd); -} - static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd) { if (sd) { From 2b611bb7abdcc08278453fc9f6517401fd69ef95 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:13 +0900 Subject: [PATCH 23/61] sysfs: allocate inode number using ida sysfs used simple incrementing allocator which is not guaranteed to be unique. This patch makes sysfs use ida to give each sd a unique and packed inode number. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 45 ++++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 2544aae6f583..f09626cc568a 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -9,12 +9,42 @@ #include #include #include +#include #include #include "sysfs.h" DECLARE_RWSEM(sysfs_rename_sem); spinlock_t sysfs_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t sysfs_ino_lock = SPIN_LOCK_UNLOCKED; +static DEFINE_IDA(sysfs_ino_ida); + +int sysfs_alloc_ino(ino_t *pino) +{ + int ino, rc; + + retry: + spin_lock(&sysfs_ino_lock); + rc = ida_get_new_above(&sysfs_ino_ida, 2, &ino); + spin_unlock(&sysfs_ino_lock); + + if (rc == -EAGAIN) { + if (ida_pre_get(&sysfs_ino_ida, GFP_KERNEL)) + goto retry; + rc = -ENOMEM; + } + + *pino = ino; + return rc; +} + +static void sysfs_free_ino(ino_t ino) +{ + spin_lock(&sysfs_ino_lock); + ida_remove(&sysfs_ino_ida, ino); + spin_unlock(&sysfs_ino_lock); +} + void release_sysfs_dirent(struct sysfs_dirent * sd) { if (sd->s_type & SYSFS_KOBJ_LINK) { @@ -24,6 +54,7 @@ void release_sysfs_dirent(struct sysfs_dirent * sd) kfree(sl); } kfree(sd->s_iattr); + sysfs_free_ino(sd->s_ino); kmem_cache_free(sysfs_dir_cachep, sd); } @@ -54,14 +85,6 @@ static struct dentry_operations sysfs_dentry_ops = { .d_iput = sysfs_d_iput, }; -static unsigned int sysfs_inode_counter; -ino_t sysfs_get_inum(void) -{ - if (unlikely(sysfs_inode_counter < 3)) - sysfs_inode_counter = 3; - return sysfs_inode_counter++; -} - /* * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent */ @@ -73,7 +96,11 @@ static struct sysfs_dirent * __sysfs_new_dirent(void * element) if (!sd) return NULL; - sd->s_ino = sysfs_get_inum(); + if (sysfs_alloc_ino(&sd->s_ino)) { + kmem_cache_free(sysfs_dir_cachep, sd); + return NULL; + } + atomic_set(&sd->s_count, 1); atomic_set(&sd->s_event, 1); INIT_LIST_HEAD(&sd->s_children); From 7a23ad44047b1084a032bc0d127fe08af024593a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:13 +0900 Subject: [PATCH 24/61] sysfs: make sysfs_put() ignore NULL sd Make sysfs_put() ignore NULL sd instead of oopsing. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/sysfs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 687d959f606d..f4fdbbffd571 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -111,7 +111,7 @@ static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd) static inline void sysfs_put(struct sysfs_dirent * sd) { - if (atomic_dec_and_test(&sd->s_count)) + if (sd && atomic_dec_and_test(&sd->s_count)) release_sysfs_dirent(sd); } From 93e3cd8270d036953120eca83610f95d3f7374c6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:13 +0900 Subject: [PATCH 25/61] sysfs: fix error handling in binattr write() Error handling in fs/sysfs/bin.c:write() was wrong because size_t count is used to receive return value from flush_write() which is negative on failure. This patch updates write() such that int variable is used instead. read() is updated the same way for consistency. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/bin.c | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index d3b9f5f07db1..606267a36275 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -33,16 +33,13 @@ fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) } static ssize_t -read(struct file * file, char __user * userbuf, size_t count, loff_t * off) +read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) { char *buffer = file->private_data; struct dentry *dentry = file->f_path.dentry; int size = dentry->d_inode->i_size; loff_t offs = *off; - int ret; - - if (count > PAGE_SIZE) - count = PAGE_SIZE; + int count = min_t(size_t, bytes, PAGE_SIZE); if (size) { if (offs > size) @@ -51,15 +48,14 @@ read(struct file * file, char __user * userbuf, size_t count, loff_t * off) count = size - offs; } - ret = fill_read(dentry, buffer, offs, count); - if (ret < 0) - return ret; - count = ret; + count = fill_read(dentry, buffer, offs, count); + if (count < 0) + return count; if (copy_to_user(userbuf, buffer, count)) return -EFAULT; - pr_debug("offs = %lld, *off = %lld, count = %zd\n", offs, *off, count); + pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count); *off = offs + count; @@ -78,16 +74,15 @@ flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) return attr->write(kobj, buffer, offset, count); } -static ssize_t write(struct file * file, const char __user * userbuf, - size_t count, loff_t * off) +static ssize_t write(struct file *file, const char __user *userbuf, + size_t bytes, loff_t *off) { char *buffer = file->private_data; struct dentry *dentry = file->f_path.dentry; int size = dentry->d_inode->i_size; loff_t offs = *off; + int count = min_t(size_t, bytes, PAGE_SIZE); - if (count > PAGE_SIZE) - count = PAGE_SIZE; if (size) { if (offs > size) return 0; From dfeb9fb0343363aadc3ee00a9347d120bc2a26b1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:14 +0900 Subject: [PATCH 26/61] sysfs: flatten cleanup paths in sysfs_add_link() and create_dir() Flatten cleanup paths in sysfs_add_link() and create_dir() to improve readability and ease further changes to these functions. This is in preparation of object reference simplification. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 73 +++++++++++++++++++++++++++------------------- fs/sysfs/symlink.c | 25 +++++++++------- 2 files changed, 57 insertions(+), 41 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index f09626cc568a..b4c482461403 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -207,40 +207,53 @@ static int init_symlink(struct inode * inode) return 0; } -static int create_dir(struct kobject * k, struct dentry * p, - const char * n, struct dentry ** d) +static int create_dir(struct kobject *kobj, struct dentry *parent, + const char *name, struct dentry **p_dentry) { int error; umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; + struct dentry *dentry; + struct sysfs_dirent *sd; - mutex_lock(&p->d_inode->i_mutex); - *d = lookup_one_len(n, p, strlen(n)); - if (!IS_ERR(*d)) { - if (sysfs_dirent_exist(p->d_fsdata, n)) - error = -EEXIST; - else - error = sysfs_make_dirent(p->d_fsdata, *d, k, mode, - SYSFS_DIR); - if (!error) { - error = sysfs_create(*d, mode, init_dir); - if (!error) { - inc_nlink(p->d_inode); - (*d)->d_op = &sysfs_dentry_ops; - d_rehash(*d); - } - } - if (error && (error != -EEXIST)) { - struct sysfs_dirent *sd = (*d)->d_fsdata; - if (sd) { - list_del_init(&sd->s_sibling); - sysfs_put(sd); - } - d_drop(*d); - } - dput(*d); - } else - error = PTR_ERR(*d); - mutex_unlock(&p->d_inode->i_mutex); + mutex_lock(&parent->d_inode->i_mutex); + + dentry = lookup_one_len(name, parent, strlen(name)); + if (IS_ERR(dentry)) { + error = PTR_ERR(dentry); + goto out_unlock; + } + + error = -EEXIST; + if (sysfs_dirent_exist(parent->d_fsdata, name)) + goto out_dput; + + error = sysfs_make_dirent(parent->d_fsdata, dentry, kobj, mode, + SYSFS_DIR); + if (error) + goto out_drop; + + error = sysfs_create(dentry, mode, init_dir); + if (error) + goto out_sput; + + inc_nlink(parent->d_inode); + dentry->d_op = &sysfs_dentry_ops; + d_rehash(dentry); + + *p_dentry = dentry; + error = 0; + goto out_dput; + + out_sput: + sd = dentry->d_fsdata; + list_del_init(&sd->s_sibling); + sysfs_put(sd); + out_drop: + d_drop(dentry); + out_dput: + dput(dentry); + out_unlock: + mutex_unlock(&parent->d_inode->i_mutex); return error; } diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 7b9c5bfde920..b463f17f6638 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -49,30 +49,33 @@ static int sysfs_add_link(struct dentry * parent, const char * name, struct kobj { struct sysfs_dirent * parent_sd = parent->d_fsdata; struct sysfs_symlink * sl; - int error = 0; + int error; error = -ENOMEM; - sl = kmalloc(sizeof(*sl), GFP_KERNEL); + sl = kzalloc(sizeof(*sl), GFP_KERNEL); if (!sl) - goto exit1; + goto err_out; sl->link_name = kmalloc(strlen(name) + 1, GFP_KERNEL); if (!sl->link_name) - goto exit2; + goto err_out; strcpy(sl->link_name, name); sl->target_kobj = kobject_get(target); error = sysfs_make_dirent(parent_sd, NULL, sl, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); - if (!error) - return 0; + if (error) + goto err_out; - kobject_put(target); - kfree(sl->link_name); -exit2: - kfree(sl); -exit1: + return 0; + + err_out: + if (sl) { + kobject_put(sl->target_kobj); + kfree(sl->link_name); + kfree(sl); + } return error; } From 996b73764e9bb9d5e751fd15b130ba38637d66a8 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:14 +0900 Subject: [PATCH 27/61] sysfs: flatten and fix sysfs_rename_dir() error handling Error handling in sysfs_rename_dir() was broken. * When lookup_one_len() fails, 0 is returned. * If parent inode check fails, returns with inode mutex and rename rwsem held. This patch fixes the above bugs and flattens error handling such that it's more readable and easier to modify. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 73 ++++++++++++++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index b4c482461403..90bed5df254f 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -452,8 +452,9 @@ void sysfs_remove_dir(struct kobject * kobj) int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, const char *new_name) { - int error = 0; + int error; struct dentry * new_dentry; + struct sysfs_dirent *sd, *parent_sd; if (!new_parent) return -EFAULT; @@ -462,40 +463,48 @@ int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, mutex_lock(&new_parent->d_inode->i_mutex); new_dentry = lookup_one_len(new_name, new_parent, strlen(new_name)); - if (!IS_ERR(new_dentry)) { - /* By allowing two different directories with the - * same d_parent we allow this routine to move - * between different shadows of the same directory - */ - if (kobj->dentry->d_parent->d_inode != new_parent->d_inode) - return -EINVAL; - else if (new_dentry->d_parent->d_inode != new_parent->d_inode) - error = -EINVAL; - else if (new_dentry == kobj->dentry) - error = -EINVAL; - else if (!new_dentry->d_inode) { - error = kobject_set_name(kobj, "%s", new_name); - if (!error) { - struct sysfs_dirent *sd, *parent_sd; - - d_add(new_dentry, NULL); - d_move(kobj->dentry, new_dentry); - - sd = kobj->dentry->d_fsdata; - parent_sd = new_parent->d_fsdata; - - list_del_init(&sd->s_sibling); - list_add(&sd->s_sibling, &parent_sd->s_children); - } - else - d_drop(new_dentry); - } else - error = -EEXIST; - dput(new_dentry); + if (IS_ERR(new_dentry)) { + error = PTR_ERR(new_dentry); + goto out_unlock; } + + /* By allowing two different directories with the same + * d_parent we allow this routine to move between different + * shadows of the same directory + */ + error = -EINVAL; + if (kobj->dentry->d_parent->d_inode != new_parent->d_inode || + new_dentry->d_parent->d_inode != new_parent->d_inode || + new_dentry == kobj->dentry) + goto out_dput; + + error = -EEXIST; + if (new_dentry->d_inode) + goto out_dput; + + error = kobject_set_name(kobj, "%s", new_name); + if (error) + goto out_drop; + + d_add(new_dentry, NULL); + d_move(kobj->dentry, new_dentry); + + sd = kobj->dentry->d_fsdata; + parent_sd = new_parent->d_fsdata; + + list_del_init(&sd->s_sibling); + list_add(&sd->s_sibling, &parent_sd->s_children); + + error = 0; + goto out_unlock; + + out_drop: + d_drop(new_dentry); + out_dput: + dput(new_dentry); + out_unlock: mutex_unlock(&new_parent->d_inode->i_mutex); up_write(&sysfs_rename_sem); - return error; } From a26cd7226c24c3be5dd5f48a74832fe64beb8489 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:14 +0900 Subject: [PATCH 28/61] sysfs: consolidate sysfs_dirent creation functions Currently there are four functions to create sysfs_dirent - __sysfs_new_dirent(), sysfs_new_dirent(), __sysfs_make_dirent() and sysfs_make_dirent(). Other than sysfs_make_dirent(), no function has two users if calls to implement other functions are excluded. This patch consolidates sysfs_dirent creation functions into the following two. * sysfs_new_dirent() : allocate and initialize * sysfs_attach_dirent() : attach to sysfs_dirent hierarchy and/or associate with dentry This simplifies interface and gives callers more flexibility. This is in preparation of object reference simplification. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 82 ++++++++++++++-------------------------------- fs/sysfs/file.c | 21 +++++++++--- fs/sysfs/symlink.c | 7 ++-- fs/sysfs/sysfs.h | 7 ++-- 4 files changed, 50 insertions(+), 67 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 90bed5df254f..f16aa7e3eafc 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -85,10 +85,7 @@ static struct dentry_operations sysfs_dentry_ops = { .d_iput = sysfs_d_iput, }; -/* - * Allocates a new sysfs_dirent and links it to the parent sysfs_dirent - */ -static struct sysfs_dirent * __sysfs_new_dirent(void * element) +struct sysfs_dirent *sysfs_new_dirent(void *element, umode_t mode, int type) { struct sysfs_dirent * sd; @@ -105,27 +102,27 @@ static struct sysfs_dirent * __sysfs_new_dirent(void * element) atomic_set(&sd->s_event, 1); INIT_LIST_HEAD(&sd->s_children); INIT_LIST_HEAD(&sd->s_sibling); + sd->s_element = element; + sd->s_mode = mode; + sd->s_type = type; return sd; } -static void __sysfs_list_dirent(struct sysfs_dirent *parent_sd, - struct sysfs_dirent *sd) +void sysfs_attach_dirent(struct sysfs_dirent *sd, + struct sysfs_dirent *parent_sd, struct dentry *dentry) { - if (sd) + if (dentry) { + sd->s_dentry = dentry; + dentry->d_fsdata = sysfs_get(sd); + dentry->d_op = &sysfs_dentry_ops; + } + + if (parent_sd) list_add(&sd->s_sibling, &parent_sd->s_children); } -static struct sysfs_dirent * sysfs_new_dirent(struct sysfs_dirent *parent_sd, - void * element) -{ - struct sysfs_dirent *sd; - sd = __sysfs_new_dirent(element); - __sysfs_list_dirent(parent_sd, sd); - return sd; -} - /* * * Return -EEXIST if there is already a sysfs element with the same name for @@ -151,39 +148,6 @@ int sysfs_dirent_exist(struct sysfs_dirent *parent_sd, return 0; } - -static struct sysfs_dirent * -__sysfs_make_dirent(struct dentry *dentry, void *element, mode_t mode, int type) -{ - struct sysfs_dirent * sd; - - sd = __sysfs_new_dirent(element); - if (!sd) - goto out; - - sd->s_mode = mode; - sd->s_type = type; - sd->s_dentry = dentry; - if (dentry) { - dentry->d_fsdata = sysfs_get(sd); - dentry->d_op = &sysfs_dentry_ops; - } - -out: - return sd; -} - -int sysfs_make_dirent(struct sysfs_dirent * parent_sd, struct dentry * dentry, - void * element, umode_t mode, int type) -{ - struct sysfs_dirent *sd; - - sd = __sysfs_make_dirent(dentry, element, mode, type); - __sysfs_list_dirent(parent_sd, sd); - - return sd ? 0 : -ENOMEM; -} - static int init_dir(struct inode * inode) { inode->i_op = &sysfs_dir_inode_operations; @@ -227,10 +191,11 @@ static int create_dir(struct kobject *kobj, struct dentry *parent, if (sysfs_dirent_exist(parent->d_fsdata, name)) goto out_dput; - error = sysfs_make_dirent(parent->d_fsdata, dentry, kobj, mode, - SYSFS_DIR); - if (error) + error = -ENOMEM; + sd = sysfs_new_dirent(kobj, mode, SYSFS_DIR); + if (!sd) goto out_drop; + sysfs_attach_dirent(sd, parent->d_fsdata, dentry); error = sysfs_create(dentry, mode, init_dir); if (error) @@ -245,7 +210,6 @@ static int create_dir(struct kobject *kobj, struct dentry *parent, goto out_dput; out_sput: - sd = dentry->d_fsdata; list_del_init(&sd->s_sibling); sysfs_put(sd); out_drop: @@ -557,13 +521,16 @@ static int sysfs_dir_open(struct inode *inode, struct file *file) { struct dentry * dentry = file->f_path.dentry; struct sysfs_dirent * parent_sd = dentry->d_fsdata; + struct sysfs_dirent * sd; mutex_lock(&dentry->d_inode->i_mutex); - file->private_data = sysfs_new_dirent(parent_sd, NULL); + sd = sysfs_new_dirent(NULL, 0, 0); + if (sd) + sysfs_attach_dirent(sd, parent_sd, NULL); mutex_unlock(&dentry->d_inode->i_mutex); - return file->private_data ? 0 : -ENOMEM; - + file->private_data = sd; + return sd ? 0 : -ENOMEM; } static int sysfs_dir_close(struct inode *inode, struct file *file) @@ -736,9 +703,10 @@ struct dentry *sysfs_create_shadow_dir(struct kobject *kobj) if (!shadow) goto nomem; - sd = __sysfs_make_dirent(shadow, kobj, inode->i_mode, SYSFS_DIR); + sd = sysfs_new_dirent(kobj, inode->i_mode, SYSFS_DIR); if (!sd) goto nomem; + sysfs_attach_dirent(sd, NULL, shadow); d_instantiate(shadow, igrab(inode)); inc_nlink(inode); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index b502c7197ec0..fd4b6dc03d2d 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -444,14 +444,25 @@ int sysfs_add_file(struct dentry * dir, const struct attribute * attr, int type) { struct sysfs_dirent * parent_sd = dir->d_fsdata; umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG; - int error = -EEXIST; + struct sysfs_dirent *sd; + int error = 0; mutex_lock(&dir->d_inode->i_mutex); - if (!sysfs_dirent_exist(parent_sd, attr->name)) - error = sysfs_make_dirent(parent_sd, NULL, (void *)attr, - mode, type); - mutex_unlock(&dir->d_inode->i_mutex); + if (sysfs_dirent_exist(parent_sd, attr->name)) { + error = -EEXIST; + goto out_unlock; + } + + sd = sysfs_new_dirent((void *)attr, mode, type); + if (!sd) { + error = -ENOMEM; + goto out_unlock; + } + sysfs_attach_dirent(sd, parent_sd, NULL); + + out_unlock: + mutex_unlock(&dir->d_inode->i_mutex); return error; } diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index b463f17f6638..d96bb9cbc9d4 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -49,6 +49,7 @@ static int sysfs_add_link(struct dentry * parent, const char * name, struct kobj { struct sysfs_dirent * parent_sd = parent->d_fsdata; struct sysfs_symlink * sl; + struct sysfs_dirent * sd; int error; error = -ENOMEM; @@ -63,10 +64,10 @@ static int sysfs_add_link(struct dentry * parent, const char * name, struct kobj strcpy(sl->link_name, name); sl->target_kobj = kobject_get(target); - error = sysfs_make_dirent(parent_sd, NULL, sl, S_IFLNK|S_IRWXUGO, - SYSFS_KOBJ_LINK); - if (error) + sd = sysfs_new_dirent(sl, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); + if (!sd) goto err_out; + sysfs_attach_dirent(sd, parent_sd, NULL); return 0; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index f4fdbbffd571..f8f49cc5c852 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -20,8 +20,11 @@ extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); extern void release_sysfs_dirent(struct sysfs_dirent * sd); extern int sysfs_dirent_exist(struct sysfs_dirent *, const unsigned char *); -extern int sysfs_make_dirent(struct sysfs_dirent *, struct dentry *, void *, - umode_t, int); +extern struct sysfs_dirent *sysfs_new_dirent(void *element, umode_t mode, + int type); +extern void sysfs_attach_dirent(struct sysfs_dirent *sd, + struct sysfs_dirent *parent_sd, + struct dentry *dentry); extern int sysfs_add_file(struct dentry *, const struct attribute *, int); extern int sysfs_hash_and_remove(struct dentry * dir, const char * name); From 13b3086d2ea483cbcae5a4236446cecc082a72cf Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:14 +0900 Subject: [PATCH 29/61] sysfs: add sysfs_dirent->s_parent Add sysfs_dirent->s_parent. With this patch, each sd points to and holds a reference to its parent. This allows walking sysfs tree without referencing sd->s_dentry which can go away anytime if the user doesn't control when it's deleted. sd->s_parent is initialized and parent is referenced in sysfs_attach_dirent(). Reference to parent is released when the sd is released, so as long as reference to a sd is held, s_parent can be followed. dentry walk in sysfs_readdir() is convereted to s_parent walk. This will be used to reimplement symlink such that it uses only sysfs_dirent tree. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 30 +++++++++++++++++++++++------- fs/sysfs/mount.c | 1 + fs/sysfs/sysfs.h | 1 + 3 files changed, 25 insertions(+), 7 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index f16aa7e3eafc..5d50e1ddfbd1 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -47,6 +47,11 @@ static void sysfs_free_ino(ino_t ino) void release_sysfs_dirent(struct sysfs_dirent * sd) { + struct sysfs_dirent *parent_sd; + + repeat: + parent_sd = sd->s_parent; + if (sd->s_type & SYSFS_KOBJ_LINK) { struct sysfs_symlink * sl = sd->s_element; kfree(sl->link_name); @@ -56,6 +61,10 @@ void release_sysfs_dirent(struct sysfs_dirent * sd) kfree(sd->s_iattr); sysfs_free_ino(sd->s_ino); kmem_cache_free(sysfs_dir_cachep, sd); + + sd = parent_sd; + if (sd && atomic_dec_and_test(&sd->s_count)) + goto repeat; } static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) @@ -119,8 +128,10 @@ void sysfs_attach_dirent(struct sysfs_dirent *sd, dentry->d_op = &sysfs_dentry_ops; } - if (parent_sd) + if (parent_sd) { + sd->s_parent = sysfs_get(parent_sd); list_add(&sd->s_sibling, &parent_sd->s_children); + } } /* @@ -571,7 +582,10 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) i++; /* fallthrough */ case 1: - ino = parent_ino(dentry); + if (parent_sd->s_parent) + ino = parent_sd->s_parent->s_ino; + else + ino = parent_sd->s_ino; if (filldir(dirent, "..", 2, i, ino, DT_DIR) < 0) break; filp->f_pos++; @@ -688,13 +702,13 @@ int sysfs_make_shadowed_dir(struct kobject *kobj, struct dentry *sysfs_create_shadow_dir(struct kobject *kobj) { + struct dentry *dir = kobj->dentry; + struct inode *inode = dir->d_inode; + struct dentry *parent = dir->d_parent; + struct sysfs_dirent *parent_sd = parent->d_fsdata; + struct dentry *shadow; struct sysfs_dirent *sd; - struct dentry *parent, *dir, *shadow; - struct inode *inode; - dir = kobj->dentry; - inode = dir->d_inode; - parent = dir->d_parent; shadow = ERR_PTR(-EINVAL); if (!sysfs_is_shadowed_inode(inode)) goto out; @@ -706,6 +720,8 @@ struct dentry *sysfs_create_shadow_dir(struct kobject *kobj) sd = sysfs_new_dirent(kobj, inode->i_mode, SYSFS_DIR); if (!sd) goto nomem; + /* point to parent_sd but don't attach to it */ + sd->s_parent = sysfs_get(parent_sd); sysfs_attach_dirent(sd, NULL, shadow); d_instantiate(shadow, igrab(inode)); diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 00ab9125d398..31c1fc67f604 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -28,6 +28,7 @@ static const struct super_operations sysfs_ops = { }; static struct sysfs_dirent sysfs_root = { + .s_count = ATOMIC_INIT(1), .s_sibling = LIST_HEAD_INIT(sysfs_root.s_sibling), .s_children = LIST_HEAD_INIT(sysfs_root.s_children), .s_element = NULL, diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index f8f49cc5c852..ce05d6fd7522 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -1,5 +1,6 @@ struct sysfs_dirent { atomic_t s_count; + struct sysfs_dirent * s_parent; struct list_head s_sibling; struct list_head s_children; void * s_element; From 0c096b507f15397da890051ee73de4266d3941fb Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:15 +0900 Subject: [PATCH 30/61] sysfs: add sysfs_dirent->s_name Add s_name to sysfs_dirent. This is to further reduce dependency to the associated dentry. Name is copied for directories and symlinks but not for attributes. Where possible, name dereferences are converted to use sd->s_name. sysfs_symlink->link_name and sysfs_get_name() are unused now and removed. This change allows symlink to be implemented using sysfs_dirent tree proper, which is the last remaining dentry-dependent sysfs walk. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 69 ++++++++++++++++++++++++++++--------------- fs/sysfs/file.c | 2 +- fs/sysfs/inode.c | 33 +-------------------- fs/sysfs/symlink.c | 8 +---- fs/sysfs/sysfs.h | 7 ++--- include/linux/sysfs.h | 1 + 6 files changed, 52 insertions(+), 68 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 5d50e1ddfbd1..6e8d6f54f082 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -54,10 +54,11 @@ void release_sysfs_dirent(struct sysfs_dirent * sd) if (sd->s_type & SYSFS_KOBJ_LINK) { struct sysfs_symlink * sl = sd->s_element; - kfree(sl->link_name); kobject_put(sl->target_kobj); kfree(sl); } + if (sd->s_type & SYSFS_COPY_NAME) + kfree(sd->s_name); kfree(sd->s_iattr); sysfs_free_ino(sd->s_ino); kmem_cache_free(sysfs_dir_cachep, sd); @@ -94,29 +95,41 @@ static struct dentry_operations sysfs_dentry_ops = { .d_iput = sysfs_d_iput, }; -struct sysfs_dirent *sysfs_new_dirent(void *element, umode_t mode, int type) +struct sysfs_dirent *sysfs_new_dirent(const char *name, void *element, + umode_t mode, int type) { - struct sysfs_dirent * sd; + char *dup_name = NULL; + struct sysfs_dirent *sd = NULL; + + if (type & SYSFS_COPY_NAME) { + name = dup_name = kstrdup(name, GFP_KERNEL); + if (!name) + goto err_out; + } sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); if (!sd) - return NULL; + goto err_out; - if (sysfs_alloc_ino(&sd->s_ino)) { - kmem_cache_free(sysfs_dir_cachep, sd); - return NULL; - } + if (sysfs_alloc_ino(&sd->s_ino)) + goto err_out; atomic_set(&sd->s_count, 1); atomic_set(&sd->s_event, 1); INIT_LIST_HEAD(&sd->s_children); INIT_LIST_HEAD(&sd->s_sibling); + sd->s_name = name; sd->s_element = element; sd->s_mode = mode; sd->s_type = type; return sd; + + err_out: + kfree(dup_name); + kmem_cache_free(sysfs_dir_cachep, sd); + return NULL; } void sysfs_attach_dirent(struct sysfs_dirent *sd, @@ -148,8 +161,7 @@ int sysfs_dirent_exist(struct sysfs_dirent *parent_sd, list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { if (sd->s_element) { - const unsigned char *existing = sysfs_get_name(sd); - if (strcmp(existing, new)) + if (strcmp(sd->s_name, new)) continue; else return -EEXIST; @@ -203,7 +215,7 @@ static int create_dir(struct kobject *kobj, struct dentry *parent, goto out_dput; error = -ENOMEM; - sd = sysfs_new_dirent(kobj, mode, SYSFS_DIR); + sd = sysfs_new_dirent(name, kobj, mode, SYSFS_DIR); if (!sd) goto out_drop; sysfs_attach_dirent(sd, parent->d_fsdata, dentry); @@ -334,9 +346,7 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { if (sd->s_type & SYSFS_NOT_PINNED) { - const unsigned char * name = sysfs_get_name(sd); - - if (strcmp(name, dentry->d_name.name)) + if (strcmp(sd->s_name, dentry->d_name.name)) continue; if (sd->s_type & SYSFS_KOBJ_LINK) @@ -427,9 +437,11 @@ void sysfs_remove_dir(struct kobject * kobj) int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, const char *new_name) { + struct sysfs_dirent *sd = kobj->dentry->d_fsdata; + struct sysfs_dirent *parent_sd = new_parent->d_fsdata; + struct dentry *new_dentry; + char *dup_name; int error; - struct dentry * new_dentry; - struct sysfs_dirent *sd, *parent_sd; if (!new_parent) return -EFAULT; @@ -457,22 +469,31 @@ int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, if (new_dentry->d_inode) goto out_dput; - error = kobject_set_name(kobj, "%s", new_name); - if (error) + /* rename kobject and sysfs_dirent */ + error = -ENOMEM; + new_name = dup_name = kstrdup(new_name, GFP_KERNEL); + if (!new_name) goto out_drop; + error = kobject_set_name(kobj, "%s", new_name); + if (error) + goto out_free; + + kfree(sd->s_name); + sd->s_name = new_name; + + /* move under the new parent */ d_add(new_dentry, NULL); d_move(kobj->dentry, new_dentry); - sd = kobj->dentry->d_fsdata; - parent_sd = new_parent->d_fsdata; - list_del_init(&sd->s_sibling); list_add(&sd->s_sibling, &parent_sd->s_children); error = 0; goto out_unlock; + out_free: + kfree(dup_name); out_drop: d_drop(new_dentry); out_dput: @@ -535,7 +556,7 @@ static int sysfs_dir_open(struct inode *inode, struct file *file) struct sysfs_dirent * sd; mutex_lock(&dentry->d_inode->i_mutex); - sd = sysfs_new_dirent(NULL, 0, 0); + sd = sysfs_new_dirent("_DIR_", NULL, 0, 0); if (sd) sysfs_attach_dirent(sd, parent_sd, NULL); mutex_unlock(&dentry->d_inode->i_mutex); @@ -605,7 +626,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) if (!next->s_element) continue; - name = sysfs_get_name(next); + name = next->s_name; len = strlen(name); ino = next->s_ino; @@ -717,7 +738,7 @@ struct dentry *sysfs_create_shadow_dir(struct kobject *kobj) if (!shadow) goto nomem; - sd = sysfs_new_dirent(kobj, inode->i_mode, SYSFS_DIR); + sd = sysfs_new_dirent("_SHADOW_", kobj, inode->i_mode, SYSFS_DIR); if (!sd) goto nomem; /* point to parent_sd but don't attach to it */ diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index fd4b6dc03d2d..8240b1687dd0 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -454,7 +454,7 @@ int sysfs_add_file(struct dentry * dir, const struct attribute * attr, int type) goto out_unlock; } - sd = sysfs_new_dirent((void *)attr, mode, type); + sd = sysfs_new_dirent(attr->name, (void *)attr, mode, type); if (!sd) { error = -ENOMEM; goto out_unlock; diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 5266eec15f6e..5c605b0003a8 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -191,37 +191,6 @@ int sysfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *)) return error; } -/* - * Get the name for corresponding element represented by the given sysfs_dirent - */ -const unsigned char * sysfs_get_name(struct sysfs_dirent *sd) -{ - struct attribute * attr; - struct bin_attribute * bin_attr; - struct sysfs_symlink * sl; - - BUG_ON(!sd || !sd->s_element); - - switch (sd->s_type) { - case SYSFS_DIR: - /* Always have a dentry so use that */ - return sd->s_dentry->d_name.name; - - case SYSFS_KOBJ_ATTR: - attr = sd->s_element; - return attr->name; - - case SYSFS_KOBJ_BIN_ATTR: - bin_attr = sd->s_element; - return bin_attr->attr.name; - - case SYSFS_KOBJ_LINK: - sl = sd->s_element; - return sl->link_name; - } - return NULL; -} - static inline void orphan_all_buffers(struct inode *node) { struct sysfs_buffer_collection *set; @@ -305,7 +274,7 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { if (!sd->s_element) continue; - if (!strcmp(sysfs_get_name(sd), name)) { + if (!strcmp(sd->s_name, name)) { list_del_init(&sd->s_sibling); sysfs_drop_dentry(sd, dir); sysfs_put(sd); diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index d96bb9cbc9d4..c72820450e7c 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -57,14 +57,9 @@ static int sysfs_add_link(struct dentry * parent, const char * name, struct kobj if (!sl) goto err_out; - sl->link_name = kmalloc(strlen(name) + 1, GFP_KERNEL); - if (!sl->link_name) - goto err_out; - - strcpy(sl->link_name, name); sl->target_kobj = kobject_get(target); - sd = sysfs_new_dirent(sl, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); + sd = sysfs_new_dirent(name, sl, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); if (!sd) goto err_out; sysfs_attach_dirent(sd, parent_sd, NULL); @@ -74,7 +69,6 @@ static int sysfs_add_link(struct dentry * parent, const char * name, struct kobj err_out: if (sl) { kobject_put(sl->target_kobj); - kfree(sl->link_name); kfree(sl); } return error; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index ce05d6fd7522..d34b008537d5 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -3,6 +3,7 @@ struct sysfs_dirent { struct sysfs_dirent * s_parent; struct list_head s_sibling; struct list_head s_children; + const char * s_name; void * s_element; int s_type; umode_t s_mode; @@ -21,8 +22,8 @@ extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); extern void release_sysfs_dirent(struct sysfs_dirent * sd); extern int sysfs_dirent_exist(struct sysfs_dirent *, const unsigned char *); -extern struct sysfs_dirent *sysfs_new_dirent(void *element, umode_t mode, - int type); +extern struct sysfs_dirent *sysfs_new_dirent(const char *name, void *element, + umode_t mode, int type); extern void sysfs_attach_dirent(struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd, struct dentry *dentry); @@ -34,7 +35,6 @@ extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * na extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); extern void sysfs_remove_subdir(struct dentry *); -extern const unsigned char * sysfs_get_name(struct sysfs_dirent *sd); extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent); extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); @@ -48,7 +48,6 @@ extern const struct inode_operations sysfs_dir_inode_operations; extern const struct inode_operations sysfs_symlink_inode_operations; struct sysfs_symlink { - char * link_name; struct kobject * target_kobj; }; diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 7d5d1ec95c2e..2f86b080b39d 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -76,6 +76,7 @@ struct sysfs_ops { #define SYSFS_KOBJ_BIN_ATTR 0x0008 #define SYSFS_KOBJ_LINK 0x0020 #define SYSFS_NOT_PINNED (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR | SYSFS_KOBJ_LINK) +#define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) #ifdef CONFIG_SYSFS From 3e5190380ebef77f2b015c9e7a4ca225a3d75021 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:15 +0900 Subject: [PATCH 31/61] sysfs: make sysfs_dirent->s_element a union Make sd->s_element a union of sysfs_elem_{dir|symlink|attr|bin_attr} and rename it to s_elem. This is to achieve... * some level of type checking : changing symlink to point to sysfs_dirent instead of kobject is much safer and less painful now. * easier / standardized dereferencing * allow sysfs_elem_* to contain more than one entry Where possible, pointer is obtained by directly deferencing from sd instead of going through other entities. This reduces dependencies to dentry, inode and kobject. to_attr() and to_bin_attr() are unused now and removed. This is in preparation of object reference simplification. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/bin.c | 18 +++++++++------ fs/sysfs/dir.c | 31 ++++++++++++------------- fs/sysfs/file.c | 19 ++++++++-------- fs/sysfs/inode.c | 2 +- fs/sysfs/mount.c | 1 - fs/sysfs/symlink.c | 23 ++++--------------- fs/sysfs/sysfs.h | 56 +++++++++++++++++++++++++--------------------- 7 files changed, 71 insertions(+), 79 deletions(-) diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index 606267a36275..67a0d5030c96 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -23,7 +23,8 @@ static int fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) { - struct bin_attribute * attr = to_bin_attr(dentry); + struct sysfs_dirent *attr_sd = dentry->d_fsdata; + struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; struct kobject * kobj = to_kobj(dentry->d_parent); if (!attr->read) @@ -65,7 +66,8 @@ read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) static int flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) { - struct bin_attribute *attr = to_bin_attr(dentry); + struct sysfs_dirent *attr_sd = dentry->d_fsdata; + struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; struct kobject *kobj = to_kobj(dentry->d_parent); if (!attr->write) @@ -101,9 +103,9 @@ static ssize_t write(struct file *file, const char __user *userbuf, static int mmap(struct file *file, struct vm_area_struct *vma) { - struct dentry *dentry = file->f_path.dentry; - struct bin_attribute *attr = to_bin_attr(dentry); - struct kobject *kobj = to_kobj(dentry->d_parent); + struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; + struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; + struct kobject *kobj = to_kobj(file->f_path.dentry->d_parent); if (!attr->mmap) return -EINVAL; @@ -114,7 +116,8 @@ static int mmap(struct file *file, struct vm_area_struct *vma) static int open(struct inode * inode, struct file * file) { struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); - struct bin_attribute * attr = to_bin_attr(file->f_path.dentry); + struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; + struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; int error = -EINVAL; if (!kobj || !attr) @@ -150,7 +153,8 @@ static int open(struct inode * inode, struct file * file) static int release(struct inode * inode, struct file * file) { struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent); - struct bin_attribute * attr = to_bin_attr(file->f_path.dentry); + struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; + struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; u8 * buffer = file->private_data; kobject_put(kobj); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 6e8d6f54f082..079122695675 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -52,11 +52,8 @@ void release_sysfs_dirent(struct sysfs_dirent * sd) repeat: parent_sd = sd->s_parent; - if (sd->s_type & SYSFS_KOBJ_LINK) { - struct sysfs_symlink * sl = sd->s_element; - kobject_put(sl->target_kobj); - kfree(sl); - } + if (sd->s_type & SYSFS_KOBJ_LINK) + kobject_put(sd->s_elem.symlink.target_kobj); if (sd->s_type & SYSFS_COPY_NAME) kfree(sd->s_name); kfree(sd->s_iattr); @@ -95,8 +92,7 @@ static struct dentry_operations sysfs_dentry_ops = { .d_iput = sysfs_d_iput, }; -struct sysfs_dirent *sysfs_new_dirent(const char *name, void *element, - umode_t mode, int type) +struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) { char *dup_name = NULL; struct sysfs_dirent *sd = NULL; @@ -120,7 +116,6 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, void *element, INIT_LIST_HEAD(&sd->s_sibling); sd->s_name = name; - sd->s_element = element; sd->s_mode = mode; sd->s_type = type; @@ -160,7 +155,7 @@ int sysfs_dirent_exist(struct sysfs_dirent *parent_sd, struct sysfs_dirent * sd; list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { - if (sd->s_element) { + if (sd->s_type) { if (strcmp(sd->s_name, new)) continue; else @@ -215,9 +210,10 @@ static int create_dir(struct kobject *kobj, struct dentry *parent, goto out_dput; error = -ENOMEM; - sd = sysfs_new_dirent(name, kobj, mode, SYSFS_DIR); + sd = sysfs_new_dirent(name, mode, SYSFS_DIR); if (!sd) goto out_drop; + sd->s_elem.dir.kobj = kobj; sysfs_attach_dirent(sd, parent->d_fsdata, dentry); error = sysfs_create(dentry, mode, init_dir); @@ -290,10 +286,10 @@ static int sysfs_attach_attr(struct sysfs_dirent * sd, struct dentry * dentry) int error = 0; if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) { - bin_attr = sd->s_element; + bin_attr = sd->s_elem.bin_attr.bin_attr; attr = &bin_attr->attr; } else { - attr = sd->s_element; + attr = sd->s_elem.attr.attr; init = init_file; } @@ -404,7 +400,7 @@ static void __sysfs_remove_dir(struct dentry *dentry) mutex_lock(&dentry->d_inode->i_mutex); parent_sd = dentry->d_fsdata; list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { - if (!sd->s_element || !(sd->s_type & SYSFS_NOT_PINNED)) + if (!sd->s_type || !(sd->s_type & SYSFS_NOT_PINNED)) continue; list_del_init(&sd->s_sibling); sysfs_drop_dentry(sd, dentry); @@ -556,7 +552,7 @@ static int sysfs_dir_open(struct inode *inode, struct file *file) struct sysfs_dirent * sd; mutex_lock(&dentry->d_inode->i_mutex); - sd = sysfs_new_dirent("_DIR_", NULL, 0, 0); + sd = sysfs_new_dirent("_DIR_", 0, 0); if (sd) sysfs_attach_dirent(sd, parent_sd, NULL); mutex_unlock(&dentry->d_inode->i_mutex); @@ -623,7 +619,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) next = list_entry(p, struct sysfs_dirent, s_sibling); - if (!next->s_element) + if (!next->s_type) continue; name = next->s_name; @@ -671,7 +667,7 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) struct sysfs_dirent *next; next = list_entry(p, struct sysfs_dirent, s_sibling); - if (next->s_element) + if (next->s_type) n--; p = p->next; } @@ -738,9 +734,10 @@ struct dentry *sysfs_create_shadow_dir(struct kobject *kobj) if (!shadow) goto nomem; - sd = sysfs_new_dirent("_SHADOW_", kobj, inode->i_mode, SYSFS_DIR); + sd = sysfs_new_dirent("_SHADOW_", inode->i_mode, SYSFS_DIR); if (!sd) goto nomem; + sd->s_elem.dir.kobj = kobj; /* point to parent_sd but don't attach to it */ sd->s_parent = sysfs_get(parent_sd); sysfs_attach_dirent(sd, NULL, shadow); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 8240b1687dd0..04f6b0ebc889 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -88,7 +88,6 @@ remove_from_collection(struct sysfs_buffer *buffer, struct inode *node) static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) { struct sysfs_dirent * sd = dentry->d_fsdata; - struct attribute * attr = to_attr(dentry); struct kobject * kobj = to_kobj(dentry->d_parent); struct sysfs_ops * ops = buffer->ops; int ret = 0; @@ -100,7 +99,7 @@ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer return -ENOMEM; buffer->event = atomic_read(&sd->s_event); - count = ops->show(kobj,attr,buffer->page); + count = ops->show(kobj, sd->s_elem.attr.attr, buffer->page); BUG_ON(count > (ssize_t)PAGE_SIZE); if (count >= 0) { buffer->needs_read_fill = 0; @@ -199,11 +198,11 @@ fill_write_buffer(struct sysfs_buffer * buffer, const char __user * buf, size_t static int flush_write_buffer(struct dentry * dentry, struct sysfs_buffer * buffer, size_t count) { - struct attribute * attr = to_attr(dentry); + struct sysfs_dirent *attr_sd = dentry->d_fsdata; struct kobject * kobj = to_kobj(dentry->d_parent); struct sysfs_ops * ops = buffer->ops; - return ops->store(kobj,attr,buffer->page,count); + return ops->store(kobj, attr_sd->s_elem.attr.attr, buffer->page, count); } @@ -248,7 +247,8 @@ out: static int sysfs_open_file(struct inode *inode, struct file *file) { struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); - struct attribute * attr = to_attr(file->f_path.dentry); + struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; + struct attribute *attr = attr_sd->s_elem.attr.attr; struct sysfs_buffer_collection *set; struct sysfs_buffer * buffer; struct sysfs_ops * ops = NULL; @@ -341,15 +341,15 @@ static int sysfs_open_file(struct inode *inode, struct file *file) static int sysfs_release(struct inode * inode, struct file * filp) { struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent); - struct attribute * attr = to_attr(filp->f_path.dentry); - struct module * owner = attr->owner; + struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; + struct attribute *attr = attr_sd->s_elem.attr.attr; struct sysfs_buffer * buffer = filp->private_data; if (buffer) remove_from_collection(buffer, inode); kobject_put(kobj); /* After this point, attr should not be accessed. */ - module_put(owner); + module_put(attr->owner); if (buffer) { if (buffer->page) @@ -454,11 +454,12 @@ int sysfs_add_file(struct dentry * dir, const struct attribute * attr, int type) goto out_unlock; } - sd = sysfs_new_dirent(attr->name, (void *)attr, mode, type); + sd = sysfs_new_dirent(attr->name, mode, type); if (!sd) { error = -ENOMEM; goto out_unlock; } + sd->s_elem.attr.attr = (void *)attr; sysfs_attach_dirent(sd, parent_sd, NULL); out_unlock: diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 5c605b0003a8..617d10cea07d 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -272,7 +272,7 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) parent_sd = dir->d_fsdata; mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { - if (!sd->s_element) + if (!sd->s_type) continue; if (!strcmp(sd->s_name, name)) { list_del_init(&sd->s_sibling); diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 31c1fc67f604..8f6d8b1b211f 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -31,7 +31,6 @@ static struct sysfs_dirent sysfs_root = { .s_count = ATOMIC_INIT(1), .s_sibling = LIST_HEAD_INIT(sysfs_root.s_sibling), .s_children = LIST_HEAD_INIT(sysfs_root.s_children), - .s_element = NULL, .s_type = SYSFS_ROOT, .s_iattr = NULL, .s_ino = 1, diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index c72820450e7c..27df635b786a 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -48,30 +48,15 @@ static void fill_object_path(struct kobject * kobj, char * buffer, int length) static int sysfs_add_link(struct dentry * parent, const char * name, struct kobject * target) { struct sysfs_dirent * parent_sd = parent->d_fsdata; - struct sysfs_symlink * sl; struct sysfs_dirent * sd; - int error; - error = -ENOMEM; - sl = kzalloc(sizeof(*sl), GFP_KERNEL); - if (!sl) - goto err_out; - - sl->target_kobj = kobject_get(target); - - sd = sysfs_new_dirent(name, sl, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); + sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); if (!sd) - goto err_out; + return -ENOMEM; + + sd->s_elem.symlink.target_kobj = kobject_get(target); sysfs_attach_dirent(sd, parent_sd, NULL); - return 0; - - err_out: - if (sl) { - kobject_put(sl->target_kobj); - kfree(sl); - } - return error; } /** diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index d34b008537d5..39ab0481379c 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -1,10 +1,33 @@ +struct sysfs_elem_dir { + struct kobject * kobj; +}; + +struct sysfs_elem_symlink { + struct kobject * target_kobj; +}; + +struct sysfs_elem_attr { + struct attribute * attr; +}; + +struct sysfs_elem_bin_attr { + struct bin_attribute * bin_attr; +}; + struct sysfs_dirent { atomic_t s_count; struct sysfs_dirent * s_parent; struct list_head s_sibling; struct list_head s_children; const char * s_name; - void * s_element; + + union { + struct sysfs_elem_dir dir; + struct sysfs_elem_symlink symlink; + struct sysfs_elem_attr attr; + struct sysfs_elem_bin_attr bin_attr; + } s_elem; + int s_type; umode_t s_mode; ino_t s_ino; @@ -22,8 +45,8 @@ extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); extern void release_sysfs_dirent(struct sysfs_dirent * sd); extern int sysfs_dirent_exist(struct sysfs_dirent *, const unsigned char *); -extern struct sysfs_dirent *sysfs_new_dirent(const char *name, void *element, - umode_t mode, int type); +extern struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, + int type); extern void sysfs_attach_dirent(struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd, struct dentry *dentry); @@ -47,10 +70,6 @@ extern const struct file_operations bin_fops; extern const struct inode_operations sysfs_dir_inode_operations; extern const struct inode_operations sysfs_symlink_inode_operations; -struct sysfs_symlink { - struct kobject * target_kobj; -}; - struct sysfs_buffer { struct list_head associates; size_t count; @@ -70,19 +89,7 @@ struct sysfs_buffer_collection { static inline struct kobject * to_kobj(struct dentry * dentry) { struct sysfs_dirent * sd = dentry->d_fsdata; - return ((struct kobject *) sd->s_element); -} - -static inline struct attribute * to_attr(struct dentry * dentry) -{ - struct sysfs_dirent * sd = dentry->d_fsdata; - return ((struct attribute *) sd->s_element); -} - -static inline struct bin_attribute * to_bin_attr(struct dentry * dentry) -{ - struct sysfs_dirent * sd = dentry->d_fsdata; - return ((struct bin_attribute *) sd->s_element); + return sd->s_elem.dir.kobj; } static inline struct kobject *sysfs_get_kobject(struct dentry *dentry) @@ -92,11 +99,10 @@ static inline struct kobject *sysfs_get_kobject(struct dentry *dentry) spin_lock(&dcache_lock); if (!d_unhashed(dentry)) { struct sysfs_dirent * sd = dentry->d_fsdata; - if (sd->s_type & SYSFS_KOBJ_LINK) { - struct sysfs_symlink * sl = sd->s_element; - kobj = kobject_get(sl->target_kobj); - } else - kobj = kobject_get(sd->s_element); + if (sd->s_type & SYSFS_KOBJ_LINK) + kobj = kobject_get(sd->s_elem.symlink.target_kobj); + else + kobj = kobject_get(sd->s_elem.dir.kobj); } spin_unlock(&dcache_lock); From aecdcedaab49ca40620dc7dd70f67ee7269a66c9 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:15 +0900 Subject: [PATCH 32/61] sysfs: implement kobj_sysfs_assoc_lock kobj->dentry can go away anytime unless the user controls when the associated sysfs node is deleted. This patch implements kobj_sysfs_assoc_lock which protects kobj->dentry. This will be used to maintain kobj based API when converting sysfs to use sysfs_dirent tree instead of dentry/kobject. Note that this lock belongs to kobject/driver-model not sysfs. Once sysfs is converted to not use kobject in its interface, this can be removed from sysfs. This is in preparation of object reference simplification. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 8 +++++++- fs/sysfs/sysfs.h | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 079122695675..e9fddcc59447 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -15,6 +15,7 @@ DECLARE_RWSEM(sysfs_rename_sem); spinlock_t sysfs_lock = SPIN_LOCK_UNLOCKED; +spinlock_t kobj_sysfs_assoc_lock = SPIN_LOCK_UNLOCKED; static spinlock_t sysfs_ino_lock = SPIN_LOCK_UNLOCKED; static DEFINE_IDA(sysfs_ino_ida); @@ -426,8 +427,13 @@ static void __sysfs_remove_dir(struct dentry *dentry) void sysfs_remove_dir(struct kobject * kobj) { - __sysfs_remove_dir(kobj->dentry); + struct dentry *d = kobj->dentry; + + spin_lock(&kobj_sysfs_assoc_lock); kobj->dentry = NULL; + spin_unlock(&kobj_sysfs_assoc_lock); + + __sysfs_remove_dir(d); } int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 39ab0481379c..718e2e123fae 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -62,6 +62,7 @@ extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent); extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); extern spinlock_t sysfs_lock; +extern spinlock_t kobj_sysfs_assoc_lock; extern struct rw_semaphore sysfs_rename_sem; extern struct super_block * sysfs_sb; extern const struct file_operations sysfs_dir_operations; From 2b29ac252afff87b8465b064ca2d9740cf1f6e52 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:15 +0900 Subject: [PATCH 33/61] sysfs: reimplement symlink using sysfs_dirent tree sysfs symlink is implemented by referencing dentry and kobject from sysfs_dirent - symlink entry references kobject, dentry is used to walk the tree. This complicates object lifetimes rules and is dangerous - for example, there is no way to tell to which module the target of a symlink belongs and referencing that kobject can make it linger after the module is gone. This patch reimplements symlink using only sysfs_dirent tree. sd for a symlink points and holds reference to the target sysfs_dirent and all walking is done using sysfs_dirent tree. Simpler and safer. Please read the following message for more info. http://article.gmane.org/gmane.linux.kernel/510293 Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 2 +- fs/sysfs/symlink.c | 88 +++++++++++++++++++++++++--------------------- fs/sysfs/sysfs.h | 9 ++--- 3 files changed, 53 insertions(+), 46 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index e9fddcc59447..2a94dc36d166 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -54,7 +54,7 @@ void release_sysfs_dirent(struct sysfs_dirent * sd) parent_sd = sd->s_parent; if (sd->s_type & SYSFS_KOBJ_LINK) - kobject_put(sd->s_elem.symlink.target_kobj); + sysfs_put(sd->s_elem.symlink.target_sd); if (sd->s_type & SYSFS_COPY_NAME) kfree(sd->s_name); kfree(sd->s_iattr); diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 27df635b786a..ff605d3f4d33 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -11,50 +11,49 @@ #include "sysfs.h" -static int object_depth(struct kobject * kobj) +static int object_depth(struct sysfs_dirent *sd) { - struct kobject * p = kobj; int depth = 0; - do { depth++; } while ((p = p->parent)); + + for (; sd->s_parent; sd = sd->s_parent) + depth++; + return depth; } -static int object_path_length(struct kobject * kobj) +static int object_path_length(struct sysfs_dirent * sd) { - struct kobject * p = kobj; int length = 1; - do { - length += strlen(kobject_name(p)) + 1; - p = p->parent; - } while (p); + + for (; sd->s_parent; sd = sd->s_parent) + length += strlen(sd->s_name) + 1; + return length; } -static void fill_object_path(struct kobject * kobj, char * buffer, int length) +static void fill_object_path(struct sysfs_dirent *sd, char *buffer, int length) { - struct kobject * p; - --length; - for (p = kobj; p; p = p->parent) { - int cur = strlen(kobject_name(p)); + for (; sd->s_parent; sd = sd->s_parent) { + int cur = strlen(sd->s_name); /* back up enough to print this bus id with '/' */ length -= cur; - strncpy(buffer + length,kobject_name(p),cur); + strncpy(buffer + length, sd->s_name, cur); *(buffer + --length) = '/'; } } -static int sysfs_add_link(struct dentry * parent, const char * name, struct kobject * target) +static int sysfs_add_link(struct sysfs_dirent * parent_sd, const char * name, + struct sysfs_dirent * target_sd) { - struct sysfs_dirent * parent_sd = parent->d_fsdata; struct sysfs_dirent * sd; sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); if (!sd) return -ENOMEM; - sd->s_elem.symlink.target_kobj = kobject_get(target); + sd->s_elem.symlink.target_sd = target_sd; sysfs_attach_dirent(sd, parent_sd, NULL); return 0; } @@ -68,6 +67,8 @@ static int sysfs_add_link(struct dentry * parent, const char * name, struct kobj int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) { struct dentry *dentry = NULL; + struct sysfs_dirent *parent_sd = NULL; + struct sysfs_dirent *target_sd = NULL; int error = -EEXIST; BUG_ON(!name); @@ -80,11 +81,27 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char if (!dentry) return -EFAULT; + parent_sd = dentry->d_fsdata; + + /* target->dentry can go away beneath us but is protected with + * kobj_sysfs_assoc_lock. Fetch target_sd from it. + */ + spin_lock(&kobj_sysfs_assoc_lock); + if (target->dentry) + target_sd = sysfs_get(target->dentry->d_fsdata); + spin_unlock(&kobj_sysfs_assoc_lock); + + if (!target_sd) + return -ENOENT; mutex_lock(&dentry->d_inode->i_mutex); if (!sysfs_dirent_exist(dentry->d_fsdata, name)) - error = sysfs_add_link(dentry, name, target); + error = sysfs_add_link(parent_sd, name, target_sd); mutex_unlock(&dentry->d_inode->i_mutex); + + if (error) + sysfs_put(target_sd); + return error; } @@ -100,14 +117,14 @@ void sysfs_remove_link(struct kobject * kobj, const char * name) sysfs_hash_and_remove(kobj->dentry,name); } -static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, - char *path) +static int sysfs_get_target_path(struct sysfs_dirent * parent_sd, + struct sysfs_dirent * target_sd, char *path) { char * s; int depth, size; - depth = object_depth(kobj); - size = object_path_length(target) + depth * 3 - 1; + depth = object_depth(parent_sd); + size = object_path_length(target_sd) + depth * 3 - 1; if (size > PATH_MAX) return -ENAMETOOLONG; @@ -116,7 +133,7 @@ static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, for (s = path; depth--; s += 3) strcpy(s,"../"); - fill_object_path(target, path, size); + fill_object_path(target_sd, path, size); pr_debug("%s: path = '%s'\n", __FUNCTION__, path); return 0; @@ -124,27 +141,16 @@ static int sysfs_get_target_path(struct kobject * kobj, struct kobject * target, static int sysfs_getlink(struct dentry *dentry, char * path) { - struct kobject *kobj, *target_kobj; - int error = 0; - - kobj = sysfs_get_kobject(dentry->d_parent); - if (!kobj) - return -EINVAL; - - target_kobj = sysfs_get_kobject(dentry); - if (!target_kobj) { - kobject_put(kobj); - return -EINVAL; - } + struct sysfs_dirent *sd = dentry->d_fsdata; + struct sysfs_dirent *parent_sd = sd->s_parent; + struct sysfs_dirent *target_sd = sd->s_elem.symlink.target_sd; + int error; down_read(&sysfs_rename_sem); - error = sysfs_get_target_path(kobj, target_kobj, path); + error = sysfs_get_target_path(parent_sd, target_sd, path); up_read(&sysfs_rename_sem); - - kobject_put(kobj); - kobject_put(target_kobj); - return error; + return error; } static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 718e2e123fae..60717660ac55 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -3,7 +3,7 @@ struct sysfs_elem_dir { }; struct sysfs_elem_symlink { - struct kobject * target_kobj; + struct sysfs_dirent * target_sd; }; struct sysfs_elem_attr { @@ -100,10 +100,11 @@ static inline struct kobject *sysfs_get_kobject(struct dentry *dentry) spin_lock(&dcache_lock); if (!d_unhashed(dentry)) { struct sysfs_dirent * sd = dentry->d_fsdata; + if (sd->s_type & SYSFS_KOBJ_LINK) - kobj = kobject_get(sd->s_elem.symlink.target_kobj); - else - kobj = kobject_get(sd->s_elem.dir.kobj); + sd = sd->s_elem.symlink.target_sd; + + kobj = kobject_get(sd->s_elem.dir.kobj); } spin_unlock(&dcache_lock); From eb36165353d0e5ac32b063f555acedcbaf6d3b75 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:16 +0900 Subject: [PATCH 34/61] sysfs: implement bin_buffer Implement bin_buffer which contains a mutex and pointer to PAGE_SIZE buffer to properly synchronize accesses to per-openfile buffer and prepare for immediate-kobj-disconnect. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/bin.c | 66 ++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 50 insertions(+), 16 deletions(-) diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index 67a0d5030c96..5dc47fe5de5e 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -20,6 +20,11 @@ #include "sysfs.h" +struct bin_buffer { + struct mutex mutex; + void *buffer; +}; + static int fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) { @@ -36,7 +41,7 @@ fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) static ssize_t read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) { - char *buffer = file->private_data; + struct bin_buffer *bb = file->private_data; struct dentry *dentry = file->f_path.dentry; int size = dentry->d_inode->i_size; loff_t offs = *off; @@ -49,17 +54,23 @@ read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) count = size - offs; } - count = fill_read(dentry, buffer, offs, count); - if (count < 0) - return count; + mutex_lock(&bb->mutex); - if (copy_to_user(userbuf, buffer, count)) - return -EFAULT; + count = fill_read(dentry, bb->buffer, offs, count); + if (count < 0) + goto out_unlock; + + if (copy_to_user(userbuf, bb->buffer, count)) { + count = -EFAULT; + goto out_unlock; + } pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count); *off = offs + count; + out_unlock: + mutex_unlock(&bb->mutex); return count; } @@ -79,7 +90,7 @@ flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) static ssize_t write(struct file *file, const char __user *userbuf, size_t bytes, loff_t *off) { - char *buffer = file->private_data; + struct bin_buffer *bb = file->private_data; struct dentry *dentry = file->f_path.dentry; int size = dentry->d_inode->i_size; loff_t offs = *off; @@ -92,25 +103,38 @@ static ssize_t write(struct file *file, const char __user *userbuf, count = size - offs; } - if (copy_from_user(buffer, userbuf, count)) - return -EFAULT; + mutex_lock(&bb->mutex); - count = flush_write(dentry, buffer, offs, count); + if (copy_from_user(bb->buffer, userbuf, count)) { + count = -EFAULT; + goto out_unlock; + } + + count = flush_write(dentry, bb->buffer, offs, count); if (count > 0) *off = offs + count; + + out_unlock: + mutex_unlock(&bb->mutex); return count; } static int mmap(struct file *file, struct vm_area_struct *vma) { + struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; struct kobject *kobj = to_kobj(file->f_path.dentry->d_parent); + int rc; if (!attr->mmap) return -EINVAL; - return attr->mmap(kobj, attr, vma); + mutex_lock(&bb->mutex); + rc = attr->mmap(kobj, attr, vma); + mutex_unlock(&bb->mutex); + + return rc; } static int open(struct inode * inode, struct file * file) @@ -118,6 +142,7 @@ static int open(struct inode * inode, struct file * file) struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; + struct bin_buffer *bb = NULL; int error = -EINVAL; if (!kobj || !attr) @@ -135,14 +160,22 @@ static int open(struct inode * inode, struct file * file) goto Error; error = -ENOMEM; - file->private_data = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!file->private_data) + bb = kzalloc(sizeof(*bb), GFP_KERNEL); + if (!bb) goto Error; + bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!bb->buffer) + goto Error; + + mutex_init(&bb->mutex); + file->private_data = bb; + error = 0; - goto Done; + goto Done; Error: + kfree(bb); module_put(attr->attr.owner); Done: if (error) @@ -155,11 +188,12 @@ static int release(struct inode * inode, struct file * file) struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent); struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; - u8 * buffer = file->private_data; + struct bin_buffer *bb = file->private_data; kobject_put(kobj); module_put(attr->attr.owner); - kfree(buffer); + kfree(bb->buffer); + kfree(bb); return 0; } From 0ab66088c855eca68513bdd7442a426c4b374ced Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:16 +0900 Subject: [PATCH 35/61] sysfs: implement sysfs_dirent active reference and immediate disconnect sysfs: implement sysfs_dirent active reference and immediate disconnect Opening a sysfs node references its associated kobject, so userland can arbitrarily prolong lifetime of a kobject which complicates lifetime rules in drivers. This patch implements active reference and makes the association between kobject and sysfs immediately breakable. Now each sysfs_dirent has two reference counts - s_count and s_active. s_count is a regular reference count which guarantees that the containing sysfs_dirent is accessible. As long as s_count reference is held, all sysfs internal fields in sysfs_dirent are accessible including s_parent and s_name. The newly added s_active is active reference count. This is acquired by invoking sysfs_get_active() and it's the caller's responsibility to ensure sysfs_dirent itself is accessible (should be holding s_count one way or the other). Dereferencing sysfs_dirent to access objects out of sysfs proper requires active reference. This includes access to the associated kobjects, attributes and ops. The active references can be drained and denied by calling sysfs_deactivate(). All active sysfs_dirents must be deactivated after deletion but before the default reference is dropped. This enables immediate disconnect of sysfs nodes. Once a sysfs_dirent is deleted, it won't access any entity external to sysfs proper. Because attr/bin_attr ops access both the node itself and its parent for kobject, they need to hold active references to both. sysfs_get/put_active_two() helpers are provided to help grabbing both references. Parent's is acquired first and released last. Unlike other operations, mmapped area lingers on after mmap() is finished and the module implement implementing it and kobj need to stay referenced till all the mapped pages are gone. This is accomplished by holding one set of active references to the bin_attr and its parent if there have been any mmap during lifetime of an openfile. The references are dropped when the openfile is released. This change makes sysfs lifetime rules independent from both kobject's and module's. It not only fixes several race conditions caused by sysfs not holding onto the proper module when referencing kobject, but also helps fixing and simplifying lifetime management in driver model and drivers by taking sysfs out of the equation. Please read the following message for more info. http://article.gmane.org/gmane.linux.kernel/510293 Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/bin.c | 95 ++++++++++++++++++++++------------ fs/sysfs/dir.c | 28 ++++++++-- fs/sysfs/file.c | 130 +++++++++++++++++++++++++++-------------------- fs/sysfs/inode.c | 8 ++- fs/sysfs/sysfs.h | 129 +++++++++++++++++++++++++++++++++++++--------- 5 files changed, 274 insertions(+), 116 deletions(-) diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index 5dc47fe5de5e..618b8aea6a7b 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -23,6 +23,7 @@ struct bin_buffer { struct mutex mutex; void *buffer; + int mmapped; }; static int @@ -30,12 +31,20 @@ fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) { struct sysfs_dirent *attr_sd = dentry->d_fsdata; struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; - struct kobject * kobj = to_kobj(dentry->d_parent); + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; + int rc; - if (!attr->read) - return -EIO; + /* need attr_sd for attr, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; - return attr->read(kobj, buffer, off, count); + rc = -EIO; + if (attr->read) + rc = attr->read(kobj, buffer, off, count); + + sysfs_put_active_two(attr_sd); + + return rc; } static ssize_t @@ -79,12 +88,20 @@ flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) { struct sysfs_dirent *attr_sd = dentry->d_fsdata; struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; - struct kobject *kobj = to_kobj(dentry->d_parent); + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; + int rc; - if (!attr->write) - return -EIO; + /* need attr_sd for attr, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; - return attr->write(kobj, buffer, offset, count); + rc = -EIO; + if (attr->write) + rc = attr->write(kobj, buffer, offset, count); + + sysfs_put_active_two(attr_sd); + + return rc; } static ssize_t write(struct file *file, const char __user *userbuf, @@ -124,14 +141,24 @@ static int mmap(struct file *file, struct vm_area_struct *vma) struct bin_buffer *bb = file->private_data; struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; - struct kobject *kobj = to_kobj(file->f_path.dentry->d_parent); + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; int rc; - if (!attr->mmap) - return -EINVAL; - mutex_lock(&bb->mutex); - rc = attr->mmap(kobj, attr, vma); + + /* need attr_sd for attr, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; + + rc = -EINVAL; + if (attr->mmap) + rc = attr->mmap(kobj, attr, vma); + + if (rc == 0 && !bb->mmapped) + bb->mmapped = 1; + else + sysfs_put_active_two(attr_sd); + mutex_unlock(&bb->mutex); return rc; @@ -139,58 +166,60 @@ static int mmap(struct file *file, struct vm_area_struct *vma) static int open(struct inode * inode, struct file * file) { - struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; struct bin_buffer *bb = NULL; - int error = -EINVAL; + int error; - if (!kobj || !attr) - goto Done; + /* need attr_sd for attr */ + if (!sysfs_get_active(attr_sd)) + return -ENODEV; - /* Grab the module reference for this attribute if we have one */ + /* Grab the module reference for this attribute */ error = -ENODEV; - if (!try_module_get(attr->attr.owner)) - goto Done; + if (!try_module_get(attr->attr.owner)) + goto err_sput; error = -EACCES; if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap)) - goto Error; + goto err_mput; if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap)) - goto Error; + goto err_mput; error = -ENOMEM; bb = kzalloc(sizeof(*bb), GFP_KERNEL); if (!bb) - goto Error; + goto err_mput; bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!bb->buffer) - goto Error; + goto err_mput; mutex_init(&bb->mutex); file->private_data = bb; - error = 0; - goto Done; + /* open succeeded, put active reference and pin attr_sd */ + sysfs_put_active(attr_sd); + sysfs_get(attr_sd); + return 0; - Error: - kfree(bb); + err_mput: module_put(attr->attr.owner); - Done: - if (error) - kobject_put(kobj); + err_sput: + sysfs_put_active(attr_sd); + kfree(bb); return error; } static int release(struct inode * inode, struct file * file) { - struct kobject * kobj = to_kobj(file->f_path.dentry->d_parent); struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; struct bin_buffer *bb = file->private_data; - kobject_put(kobj); + if (bb->mmapped) + sysfs_put_active_two(attr_sd); + sysfs_put(attr_sd); module_put(attr->attr.owner); kfree(bb->buffer); kfree(bb); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 2a94dc36d166..e0d377aaf2cc 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -53,6 +53,19 @@ void release_sysfs_dirent(struct sysfs_dirent * sd) repeat: parent_sd = sd->s_parent; + /* If @sd is being released after deletion, s_active is write + * locked. If @sd is cursor for directory walk or being + * released prematurely, s_active has no reader or writer. + * + * sysfs_deactivate() lies to lockdep that s_active is + * unlocked immediately. Lie one more time to cover the + * previous lie. + */ + if (!down_write_trylock(&sd->s_active)) + rwsem_acquire(&sd->s_active.dep_map, + SYSFS_S_ACTIVE_DEACTIVATE, 0, _RET_IP_); + up_write(&sd->s_active); + if (sd->s_type & SYSFS_KOBJ_LINK) sysfs_put(sd->s_elem.symlink.target_sd); if (sd->s_type & SYSFS_COPY_NAME) @@ -113,6 +126,7 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) atomic_set(&sd->s_count, 1); atomic_set(&sd->s_event, 1); + init_rwsem(&sd->s_active); INIT_LIST_HEAD(&sd->s_children); INIT_LIST_HEAD(&sd->s_sibling); @@ -371,7 +385,6 @@ static void remove_dir(struct dentry * d) d_delete(d); sd = d->d_fsdata; list_del_init(&sd->s_sibling); - sysfs_put(sd); if (d->d_inode) simple_rmdir(parent->d_inode,d); @@ -380,6 +393,9 @@ static void remove_dir(struct dentry * d) mutex_unlock(&parent->d_inode->i_mutex); dput(parent); + + sysfs_deactivate(sd); + sysfs_put(sd); } void sysfs_remove_subdir(struct dentry * d) @@ -390,6 +406,7 @@ void sysfs_remove_subdir(struct dentry * d) static void __sysfs_remove_dir(struct dentry *dentry) { + LIST_HEAD(removed); struct sysfs_dirent * parent_sd; struct sysfs_dirent * sd, * tmp; @@ -403,12 +420,17 @@ static void __sysfs_remove_dir(struct dentry *dentry) list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { if (!sd->s_type || !(sd->s_type & SYSFS_NOT_PINNED)) continue; - list_del_init(&sd->s_sibling); + list_move(&sd->s_sibling, &removed); sysfs_drop_dentry(sd, dentry); - sysfs_put(sd); } mutex_unlock(&dentry->d_inode->i_mutex); + list_for_each_entry_safe(sd, tmp, &removed, s_sibling) { + list_del_init(&sd->s_sibling); + sysfs_deactivate(sd); + sysfs_put(sd); + } + remove_dir(dentry); /** * Drop reference from dget() on entrance. diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 04f6b0ebc889..310430baf572 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -87,8 +87,8 @@ remove_from_collection(struct sysfs_buffer *buffer, struct inode *node) */ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) { - struct sysfs_dirent * sd = dentry->d_fsdata; - struct kobject * kobj = to_kobj(dentry->d_parent); + struct sysfs_dirent *attr_sd = dentry->d_fsdata; + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; struct sysfs_ops * ops = buffer->ops; int ret = 0; ssize_t count; @@ -98,8 +98,15 @@ static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer if (!buffer->page) return -ENOMEM; - buffer->event = atomic_read(&sd->s_event); - count = ops->show(kobj, sd->s_elem.attr.attr, buffer->page); + /* need attr_sd for attr and ops, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; + + buffer->event = atomic_read(&attr_sd->s_event); + count = ops->show(kobj, attr_sd->s_elem.attr.attr, buffer->page); + + sysfs_put_active_two(attr_sd); + BUG_ON(count > (ssize_t)PAGE_SIZE); if (count >= 0) { buffer->needs_read_fill = 0; @@ -195,14 +202,23 @@ fill_write_buffer(struct sysfs_buffer * buffer, const char __user * buf, size_t * passing the buffer that we acquired in fill_write_buffer(). */ -static int +static int flush_write_buffer(struct dentry * dentry, struct sysfs_buffer * buffer, size_t count) { struct sysfs_dirent *attr_sd = dentry->d_fsdata; - struct kobject * kobj = to_kobj(dentry->d_parent); + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; struct sysfs_ops * ops = buffer->ops; + int rc; - return ops->store(kobj, attr_sd->s_elem.attr.attr, buffer->page, count); + /* need attr_sd for attr and ops, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; + + rc = ops->store(kobj, attr_sd->s_elem.attr.attr, buffer->page, count); + + sysfs_put_active_two(attr_sd); + + return rc; } @@ -246,22 +262,22 @@ out: static int sysfs_open_file(struct inode *inode, struct file *file) { - struct kobject *kobj = sysfs_get_kobject(file->f_path.dentry->d_parent); struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct attribute *attr = attr_sd->s_elem.attr.attr; + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; struct sysfs_buffer_collection *set; struct sysfs_buffer * buffer; struct sysfs_ops * ops = NULL; - int error = 0; + int error; - if (!kobj || !attr) - goto Einval; + /* need attr_sd for attr and ops, its parent for kobj */ + if (!sysfs_get_active_two(attr_sd)) + return -ENODEV; - /* Grab the module reference for this attribute if we have one */ - if (!try_module_get(attr->owner)) { - error = -ENODEV; - goto Done; - } + /* Grab the module reference for this attribute */ + error = -ENODEV; + if (!try_module_get(attr->owner)) + goto err_sput; /* if the kobject has no ktype, then we assume that it is a subsystem * itself, and use ops for it. @@ -276,30 +292,30 @@ static int sysfs_open_file(struct inode *inode, struct file *file) /* No sysfs operations, either from having no subsystem, * or the subsystem have no operations. */ + error = -EACCES; if (!ops) - goto Eaccess; + goto err_mput; /* make sure we have a collection to add our buffers to */ mutex_lock(&inode->i_mutex); if (!(set = inode->i_private)) { - if (!(set = inode->i_private = kmalloc(sizeof(struct sysfs_buffer_collection), GFP_KERNEL))) { - error = -ENOMEM; - goto Done; - } else { + error = -ENOMEM; + if (!(set = inode->i_private = kmalloc(sizeof(struct sysfs_buffer_collection), GFP_KERNEL))) + goto err_mput; + else INIT_LIST_HEAD(&set->associates); - } } mutex_unlock(&inode->i_mutex); + error = -EACCES; + /* File needs write support. * The inode's perms must say it's ok, * and we must have a store method. */ if (file->f_mode & FMODE_WRITE) { - if (!(inode->i_mode & S_IWUGO) || !ops->store) - goto Eaccess; - + goto err_mput; } /* File needs read support. @@ -308,46 +324,45 @@ static int sysfs_open_file(struct inode *inode, struct file *file) */ if (file->f_mode & FMODE_READ) { if (!(inode->i_mode & S_IRUGO) || !ops->show) - goto Eaccess; + goto err_mput; } /* No error? Great, allocate a buffer for the file, and store it * it in file->private_data for easy access. */ + error = -ENOMEM; buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); - if (buffer) { - INIT_LIST_HEAD(&buffer->associates); - init_MUTEX(&buffer->sem); - buffer->needs_read_fill = 1; - buffer->ops = ops; - add_to_collection(buffer, inode); - file->private_data = buffer; - } else - error = -ENOMEM; - goto Done; + if (!buffer) + goto err_mput; - Einval: - error = -EINVAL; - goto Done; - Eaccess: - error = -EACCES; + INIT_LIST_HEAD(&buffer->associates); + init_MUTEX(&buffer->sem); + buffer->needs_read_fill = 1; + buffer->ops = ops; + add_to_collection(buffer, inode); + file->private_data = buffer; + + /* open succeeded, put active references and pin attr_sd */ + sysfs_put_active_two(attr_sd); + sysfs_get(attr_sd); + return 0; + + err_mput: module_put(attr->owner); - Done: - if (error) - kobject_put(kobj); + err_sput: + sysfs_put_active_two(attr_sd); return error; } static int sysfs_release(struct inode * inode, struct file * filp) { - struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent); struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; struct attribute *attr = attr_sd->s_elem.attr.attr; struct sysfs_buffer * buffer = filp->private_data; if (buffer) remove_from_collection(buffer, inode); - kobject_put(kobj); + sysfs_put(attr_sd); /* After this point, attr should not be accessed. */ module_put(attr->owner); @@ -376,18 +391,25 @@ static int sysfs_release(struct inode * inode, struct file * filp) static unsigned int sysfs_poll(struct file *filp, poll_table *wait) { struct sysfs_buffer * buffer = filp->private_data; - struct kobject * kobj = to_kobj(filp->f_path.dentry->d_parent); - struct sysfs_dirent * sd = filp->f_path.dentry->d_fsdata; - int res = 0; + struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; + struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; + + /* need parent for the kobj, grab both */ + if (!sysfs_get_active_two(attr_sd)) + goto trigger; poll_wait(filp, &kobj->poll, wait); - if (buffer->event != atomic_read(&sd->s_event)) { - res = POLLERR|POLLPRI; - buffer->needs_read_fill = 1; - } + sysfs_put_active_two(attr_sd); - return res; + if (buffer->event != atomic_read(&attr_sd->s_event)) + goto trigger; + + return 0; + + trigger: + buffer->needs_read_fill = 1; + return POLLERR|POLLPRI; } diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 617d10cea07d..7b9a8f132d5a 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -277,12 +277,16 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) if (!strcmp(sd->s_name, name)) { list_del_init(&sd->s_sibling); sysfs_drop_dentry(sd, dir); - sysfs_put(sd); found = 1; break; } } mutex_unlock(&dir->d_inode->i_mutex); - return found ? 0 : -ENOENT; + if (!found) + return -ENOENT; + + sysfs_deactivate(sd); + sysfs_put(sd); + return 0; } diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 60717660ac55..d998e8e27841 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -14,8 +14,14 @@ struct sysfs_elem_bin_attr { struct bin_attribute * bin_attr; }; +/* + * As long as s_count reference is held, the sysfs_dirent itself is + * accessible. Dereferencing s_elem or any other outer entity + * requires s_active reference. + */ struct sysfs_dirent { atomic_t s_count; + struct rw_semaphore s_active; struct sysfs_dirent * s_parent; struct list_head s_sibling; struct list_head s_children; @@ -36,6 +42,17 @@ struct sysfs_dirent { atomic_t s_event; }; +/* + * A sysfs file which deletes another file when written to need to + * write lock the s_active of the victim while its s_active is read + * locked for the write operation. Tell lockdep that this is okay. + */ +enum sysfs_s_active_class +{ + SYSFS_S_ACTIVE_NORMAL, /* file r/w access, etc - default */ + SYSFS_S_ACTIVE_DEACTIVATE, /* file deactivation */ +}; + extern struct vfsmount * sysfs_mount; extern struct kmem_cache *sysfs_dir_cachep; @@ -87,30 +104,6 @@ struct sysfs_buffer_collection { struct list_head associates; }; -static inline struct kobject * to_kobj(struct dentry * dentry) -{ - struct sysfs_dirent * sd = dentry->d_fsdata; - return sd->s_elem.dir.kobj; -} - -static inline struct kobject *sysfs_get_kobject(struct dentry *dentry) -{ - struct kobject * kobj = NULL; - - spin_lock(&dcache_lock); - if (!d_unhashed(dentry)) { - struct sysfs_dirent * sd = dentry->d_fsdata; - - if (sd->s_type & SYSFS_KOBJ_LINK) - sd = sd->s_elem.symlink.target_sd; - - kobj = kobject_get(sd->s_elem.dir.kobj); - } - spin_unlock(&dcache_lock); - - return kobj; -} - static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd) { if (sd) { @@ -126,6 +119,94 @@ static inline void sysfs_put(struct sysfs_dirent * sd) release_sysfs_dirent(sd); } +/** + * sysfs_get_active - get an active reference to sysfs_dirent + * @sd: sysfs_dirent to get an active reference to + * + * Get an active reference of @sd. This function is noop if @sd + * is NULL. + * + * RETURNS: + * Pointer to @sd on success, NULL on failure. + */ +static inline struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) +{ + if (sd) { + if (unlikely(!down_read_trylock(&sd->s_active))) + sd = NULL; + } + return sd; +} + +/** + * sysfs_put_active - put an active reference to sysfs_dirent + * @sd: sysfs_dirent to put an active reference to + * + * Put an active reference to @sd. This function is noop if @sd + * is NULL. + */ +static inline void sysfs_put_active(struct sysfs_dirent *sd) +{ + if (sd) + up_read(&sd->s_active); +} + +/** + * sysfs_get_active_two - get active references to sysfs_dirent and parent + * @sd: sysfs_dirent of interest + * + * Get active reference to @sd and its parent. Parent's active + * reference is grabbed first. This function is noop if @sd is + * NULL. + * + * RETURNS: + * Pointer to @sd on success, NULL on failure. + */ +static inline struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd) +{ + if (sd) { + if (sd->s_parent && unlikely(!sysfs_get_active(sd->s_parent))) + return NULL; + if (unlikely(!sysfs_get_active(sd))) { + sysfs_put_active(sd->s_parent); + return NULL; + } + } + return sd; +} + +/** + * sysfs_put_active_two - put active references to sysfs_dirent and parent + * @sd: sysfs_dirent of interest + * + * Put active references to @sd and its parent. This function is + * noop if @sd is NULL. + */ +static inline void sysfs_put_active_two(struct sysfs_dirent *sd) +{ + if (sd) { + sysfs_put_active(sd); + sysfs_put_active(sd->s_parent); + } +} + +/** + * sysfs_deactivate - deactivate sysfs_dirent + * @sd: sysfs_dirent to deactivate + * + * Deny new active references and drain existing ones. s_active + * will be unlocked when the sysfs_dirent is released. + */ +static inline void sysfs_deactivate(struct sysfs_dirent *sd) +{ + down_write_nested(&sd->s_active, SYSFS_S_ACTIVE_DEACTIVATE); + + /* s_active will be unlocked by the thread doing the final put + * on @sd. Lie to lockdep. + */ + rwsem_release(&sd->s_active.dep_map, 1, _RET_IP_); +} + static inline int sysfs_is_shadowed_inode(struct inode *inode) { return S_ISDIR(inode->i_mode) && inode->i_op->follow_link; From 73107cb3ad3963c0f929ae681c05081eafb1c079 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:16 +0900 Subject: [PATCH 36/61] sysfs: kill attribute file orphaning Now that sysfs_dirent can be disconnected from kobject on deletion, there is no need to orphan each attribute files. All [bin_]attribute nodes are automatically orphaned when the parent node is deleted. Kill attribute file orphaning. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 65 ++++++++++-------------------------------------- fs/sysfs/inode.c | 25 ------------------- fs/sysfs/mount.c | 8 ------ fs/sysfs/sysfs.h | 16 ------------ 4 files changed, 13 insertions(+), 101 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 310430baf572..d673d9b5d33f 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -50,29 +50,15 @@ static struct sysfs_ops subsys_sysfs_ops = { .store = subsys_attr_store, }; -/** - * add_to_collection - add buffer to a collection - * @buffer: buffer to be added - * @node: inode of set to add to - */ - -static inline void -add_to_collection(struct sysfs_buffer *buffer, struct inode *node) -{ - struct sysfs_buffer_collection *set = node->i_private; - - mutex_lock(&node->i_mutex); - list_add(&buffer->associates, &set->associates); - mutex_unlock(&node->i_mutex); -} - -static inline void -remove_from_collection(struct sysfs_buffer *buffer, struct inode *node) -{ - mutex_lock(&node->i_mutex); - list_del(&buffer->associates); - mutex_unlock(&node->i_mutex); -} +struct sysfs_buffer { + size_t count; + loff_t pos; + char * page; + struct sysfs_ops * ops; + struct semaphore sem; + int needs_read_fill; + int event; +}; /** * fill_read_buffer - allocate and fill buffer from object. @@ -144,10 +130,7 @@ sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) down(&buffer->sem); if (buffer->needs_read_fill) { - if (buffer->orphaned) - retval = -ENODEV; - else - retval = fill_read_buffer(file->f_path.dentry,buffer); + retval = fill_read_buffer(file->f_path.dentry,buffer); if (retval) goto out; } @@ -246,16 +229,11 @@ sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t ssize_t len; down(&buffer->sem); - if (buffer->orphaned) { - len = -ENODEV; - goto out; - } len = fill_write_buffer(buffer, buf, count); if (len > 0) len = flush_write_buffer(file->f_path.dentry, buffer, len); if (len > 0) *ppos += len; -out: up(&buffer->sem); return len; } @@ -265,7 +243,6 @@ static int sysfs_open_file(struct inode *inode, struct file *file) struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct attribute *attr = attr_sd->s_elem.attr.attr; struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; - struct sysfs_buffer_collection *set; struct sysfs_buffer * buffer; struct sysfs_ops * ops = NULL; int error; @@ -289,26 +266,14 @@ static int sysfs_open_file(struct inode *inode, struct file *file) else ops = &subsys_sysfs_ops; + error = -EACCES; + /* No sysfs operations, either from having no subsystem, * or the subsystem have no operations. */ - error = -EACCES; if (!ops) goto err_mput; - /* make sure we have a collection to add our buffers to */ - mutex_lock(&inode->i_mutex); - if (!(set = inode->i_private)) { - error = -ENOMEM; - if (!(set = inode->i_private = kmalloc(sizeof(struct sysfs_buffer_collection), GFP_KERNEL))) - goto err_mput; - else - INIT_LIST_HEAD(&set->associates); - } - mutex_unlock(&inode->i_mutex); - - error = -EACCES; - /* File needs write support. * The inode's perms must say it's ok, * and we must have a store method. @@ -335,11 +300,9 @@ static int sysfs_open_file(struct inode *inode, struct file *file) if (!buffer) goto err_mput; - INIT_LIST_HEAD(&buffer->associates); init_MUTEX(&buffer->sem); buffer->needs_read_fill = 1; buffer->ops = ops; - add_to_collection(buffer, inode); file->private_data = buffer; /* open succeeded, put active references and pin attr_sd */ @@ -358,10 +321,8 @@ static int sysfs_release(struct inode * inode, struct file * filp) { struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; struct attribute *attr = attr_sd->s_elem.attr.attr; - struct sysfs_buffer * buffer = filp->private_data; + struct sysfs_buffer *buffer = filp->private_data; - if (buffer) - remove_from_collection(buffer, inode); sysfs_put(attr_sd); /* After this point, attr should not be accessed. */ module_put(attr->owner); diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 7b9a8f132d5a..d9ccc830b73a 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -191,24 +191,6 @@ int sysfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *)) return error; } -static inline void orphan_all_buffers(struct inode *node) -{ - struct sysfs_buffer_collection *set; - struct sysfs_buffer *buf; - - mutex_lock_nested(&node->i_mutex, I_MUTEX_CHILD); - 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); -} - - /* * Unhashes the dentry corresponding to given sysfs_dirent * Called with parent inode's i_mutex held. @@ -216,7 +198,6 @@ static inline void orphan_all_buffers(struct inode *node) void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) { struct dentry *dentry = NULL; - struct inode *inode; /* We're not holding a reference to ->s_dentry dentry but the * field will stay valid as long as sysfs_lock is held. @@ -236,17 +217,11 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) spin_lock(&dcache_lock); spin_lock(&dentry->d_lock); if (!d_unhashed(dentry) && dentry->d_inode) { - inode = dentry->d_inode; - spin_lock(&inode->i_lock); - __iget(inode); - spin_unlock(&inode->i_lock); dget_locked(dentry); __d_drop(dentry); spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); simple_unlink(parent->d_inode, dentry); - orphan_all_buffers(inode); - iput(inode); } else { spin_unlock(&dentry->d_lock); spin_unlock(&dcache_lock); diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 8f6d8b1b211f..37ff9ffc55f0 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -19,12 +19,9 @@ struct vfsmount *sysfs_mount; struct super_block * sysfs_sb = NULL; struct kmem_cache *sysfs_dir_cachep; -static void sysfs_clear_inode(struct inode *inode); - static const struct super_operations sysfs_ops = { .statfs = simple_statfs, .drop_inode = sysfs_delete_inode, - .clear_inode = sysfs_clear_inode, }; static struct sysfs_dirent sysfs_root = { @@ -36,11 +33,6 @@ static struct sysfs_dirent sysfs_root = { .s_ino = 1, }; -static void sysfs_clear_inode(struct inode *inode) -{ - kfree(inode->i_private); -} - static int sysfs_fill_super(struct super_block *sb, void *data, int silent) { struct inode *inode; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index d998e8e27841..8b09e9d882c2 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -88,22 +88,6 @@ extern const struct file_operations bin_fops; extern const struct inode_operations sysfs_dir_inode_operations; extern const struct inode_operations sysfs_symlink_inode_operations; -struct sysfs_buffer { - struct list_head associates; - size_t count; - loff_t pos; - char * page; - struct sysfs_ops * ops; - struct semaphore sem; - int orphaned; - int needs_read_fill; - int event; -}; - -struct sysfs_buffer_collection { - struct list_head associates; -}; - static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd) { if (sd) { From 198a2a847015805c6f57d8cc732bdaaccb494007 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:16 +0900 Subject: [PATCH 37/61] sysfs: separate out sysfs_attach_dentry() Consolidate sd <-> dentry association into sysfs_attach_dentry() and call it after dentry and inode are properly set up. This is in preparation of sysfs_drop_dentry() updates. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 59 +++++++++++++++++++++--------------------------- fs/sysfs/inode.c | 4 ++-- fs/sysfs/sysfs.h | 3 ++- 3 files changed, 30 insertions(+), 36 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index e0d377aaf2cc..01eeb4b954b1 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -142,14 +142,24 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) return NULL; } +static void sysfs_attach_dentry(struct sysfs_dirent *sd, struct dentry *dentry) +{ + dentry->d_op = &sysfs_dentry_ops; + dentry->d_fsdata = sysfs_get(sd); + + /* protect sd->s_dentry against sysfs_d_iput */ + spin_lock(&sysfs_lock); + sd->s_dentry = dentry; + spin_unlock(&sysfs_lock); + + d_rehash(dentry); +} + void sysfs_attach_dirent(struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd, struct dentry *dentry) { - if (dentry) { - sd->s_dentry = dentry; - dentry->d_fsdata = sysfs_get(sd); - dentry->d_op = &sysfs_dentry_ops; - } + if (dentry) + sysfs_attach_dentry(sd, dentry); if (parent_sd) { sd->s_parent = sysfs_get(parent_sd); @@ -229,15 +239,13 @@ static int create_dir(struct kobject *kobj, struct dentry *parent, if (!sd) goto out_drop; sd->s_elem.dir.kobj = kobj; - sysfs_attach_dirent(sd, parent->d_fsdata, dentry); - error = sysfs_create(dentry, mode, init_dir); + error = sysfs_create(sd, dentry, mode, init_dir); if (error) goto out_sput; inc_nlink(parent->d_inode); - dentry->d_op = &sysfs_dentry_ops; - d_rehash(dentry); + sysfs_attach_dirent(sd, parent->d_fsdata, dentry); *p_dentry = dentry; error = 0; @@ -308,42 +316,28 @@ static int sysfs_attach_attr(struct sysfs_dirent * sd, struct dentry * dentry) init = init_file; } - dentry->d_fsdata = sysfs_get(sd); - /* protect sd->s_dentry against sysfs_d_iput */ - spin_lock(&sysfs_lock); - sd->s_dentry = dentry; - spin_unlock(&sysfs_lock); - error = sysfs_create(dentry, (attr->mode & S_IALLUGO) | S_IFREG, init); - if (error) { - sysfs_put(sd); + error = sysfs_create(sd, dentry, + (attr->mode & S_IALLUGO) | S_IFREG, init); + if (error) return error; - } if (bin_attr) { dentry->d_inode->i_size = bin_attr->size; dentry->d_inode->i_fop = &bin_fops; } - dentry->d_op = &sysfs_dentry_ops; - d_rehash(dentry); + + sysfs_attach_dentry(sd, dentry); return 0; } static int sysfs_attach_link(struct sysfs_dirent * sd, struct dentry * dentry) { - int err = 0; + int err; - dentry->d_fsdata = sysfs_get(sd); - /* protect sd->s_dentry against sysfs_d_iput */ - spin_lock(&sysfs_lock); - sd->s_dentry = dentry; - spin_unlock(&sysfs_lock); - err = sysfs_create(dentry, S_IFLNK|S_IRWXUGO, init_symlink); - if (!err) { - dentry->d_op = &sysfs_dentry_ops; - d_rehash(dentry); - } else - sysfs_put(sd); + err = sysfs_create(sd, dentry, S_IFLNK|S_IRWXUGO, init_symlink); + if (!err) + sysfs_attach_dentry(sd, dentry); return err; } @@ -773,7 +767,6 @@ struct dentry *sysfs_create_shadow_dir(struct kobject *kobj) d_instantiate(shadow, igrab(inode)); inc_nlink(inode); inc_nlink(parent->d_inode); - shadow->d_op = &sysfs_dentry_ops; dget(shadow); /* Extra count - pin the dentry in core */ diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index d9ccc830b73a..88857a399d0c 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -156,13 +156,13 @@ struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent * sd) return inode; } -int sysfs_create(struct dentry * dentry, int mode, int (*init)(struct inode *)) +int sysfs_create(struct sysfs_dirent *sd, struct dentry *dentry, int mode, + int (*init)(struct inode *)) { int error = 0; struct inode * inode = NULL; if (dentry) { if (!dentry->d_inode) { - struct sysfs_dirent * sd = dentry->d_fsdata; if ((inode = sysfs_new_inode(mode, sd))) { if (dentry->d_parent && dentry->d_parent->d_inode) { struct inode *p_inode = dentry->d_parent->d_inode; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 8b09e9d882c2..9fa77d648aa5 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -58,7 +58,8 @@ extern struct kmem_cache *sysfs_dir_cachep; extern void sysfs_delete_inode(struct inode *inode); extern struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent *); -extern int sysfs_create(struct dentry *, int mode, int (*init)(struct inode *)); +extern int sysfs_create(struct sysfs_dirent *sd, struct dentry *dentry, + int mode, int (*init)(struct inode *)); extern void release_sysfs_dirent(struct sysfs_dirent * sd); extern int sysfs_dirent_exist(struct sysfs_dirent *, const unsigned char *); From dbde0fcf9f8f6d477af3c32d9979e789ee680cde Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:16 +0900 Subject: [PATCH 38/61] sysfs: reimplement sysfs_drop_dentry() This patch reimplements sysfs_drop_dentry() such that remove_dir() can use it to drop dentry instead of using a separate mechanism. With this change, making directories reclaimable is much easier. This patch used to contain fixes for two race conditions around sd->s_dentry but that part has been separated out and included into mainline early as commit 6aa054aadfea613a437ad0b15d38eca2b963fc0a and dd14cbc994709a1c5a64ed3621f583c49a27e521. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 18 +++------- fs/sysfs/inode.c | 86 +++++++++++++++++++++++++++++++++++------------- fs/sysfs/sysfs.h | 2 +- 3 files changed, 69 insertions(+), 37 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 01eeb4b954b1..bc11a263aa53 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -372,22 +372,19 @@ const struct inode_operations sysfs_dir_inode_operations = { static void remove_dir(struct dentry * d) { - struct dentry * parent = dget(d->d_parent); - struct sysfs_dirent * sd; + struct dentry *parent = d->d_parent; + struct sysfs_dirent *sd = d->d_fsdata; mutex_lock(&parent->d_inode->i_mutex); - d_delete(d); - sd = d->d_fsdata; + list_del_init(&sd->s_sibling); - if (d->d_inode) - simple_rmdir(parent->d_inode,d); pr_debug(" o %s removing done (%d)\n",d->d_name.name, atomic_read(&d->d_count)); mutex_unlock(&parent->d_inode->i_mutex); - dput(parent); + sysfs_drop_dentry(sd); sysfs_deactivate(sd); sysfs_put(sd); } @@ -404,7 +401,6 @@ static void __sysfs_remove_dir(struct dentry *dentry) struct sysfs_dirent * parent_sd; struct sysfs_dirent * sd, * tmp; - dget(dentry); if (!dentry) return; @@ -415,21 +411,17 @@ static void __sysfs_remove_dir(struct dentry *dentry) if (!sd->s_type || !(sd->s_type & SYSFS_NOT_PINNED)) continue; list_move(&sd->s_sibling, &removed); - sysfs_drop_dentry(sd, dentry); } mutex_unlock(&dentry->d_inode->i_mutex); list_for_each_entry_safe(sd, tmp, &removed, s_sibling) { list_del_init(&sd->s_sibling); + sysfs_drop_dentry(sd); sysfs_deactivate(sd); sysfs_put(sd); } remove_dir(dentry); - /** - * Drop reference from dget() on entrance. - */ - dput(dentry); } /** diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 88857a399d0c..6ad47c13b94d 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -191,13 +191,25 @@ int sysfs_create(struct sysfs_dirent *sd, struct dentry *dentry, int mode, return error; } -/* - * Unhashes the dentry corresponding to given sysfs_dirent - * Called with parent inode's i_mutex held. +/** + * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent + * @sd: target sysfs_dirent + * + * Drop dentry for @sd. @sd must have been unlinked from its + * parent on entry to this function such that it can't be looked + * up anymore. + * + * @sd->s_dentry which is protected with sysfs_lock points to the + * currently associated dentry but we're not holding a reference + * to it and racing with dput(). Grab dcache_lock and verify + * dentry before dropping it. If @sd->s_dentry is NULL or dput() + * beats us, no need to bother. */ -void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) +void sysfs_drop_dentry(struct sysfs_dirent *sd) { - struct dentry *dentry = NULL; + struct dentry *dentry = NULL, *parent = NULL; + struct inode *dir; + struct timespec curtime; /* We're not holding a reference to ->s_dentry dentry but the * field will stay valid as long as sysfs_lock is held. @@ -205,30 +217,57 @@ void sysfs_drop_dentry(struct sysfs_dirent * sd, struct dentry * parent) spin_lock(&sysfs_lock); spin_lock(&dcache_lock); - /* dget dentry if it's still alive */ - if (sd->s_dentry && sd->s_dentry->d_inode) + if (sd->s_dentry && sd->s_dentry->d_inode) { + /* get dentry if it's there and dput() didn't kill it yet */ dentry = dget_locked(sd->s_dentry); + parent = dentry->d_parent; + } else if (sd->s_parent->s_dentry->d_inode) { + /* We need to update the parent even if dentry for the + * victim itself doesn't exist. + */ + parent = dget_locked(sd->s_parent->s_dentry); + } + + /* drop */ + if (dentry) { + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); + } spin_unlock(&dcache_lock); spin_unlock(&sysfs_lock); - /* drop dentry */ - if (dentry) { - spin_lock(&dcache_lock); - spin_lock(&dentry->d_lock); - if (!d_unhashed(dentry) && dentry->d_inode) { - dget_locked(dentry); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); - spin_unlock(&dcache_lock); - simple_unlink(parent->d_inode, dentry); - } else { - spin_unlock(&dentry->d_lock); - spin_unlock(&dcache_lock); - } + /* nothing to do if the parent isn't in dcache */ + if (!parent) + return; - dput(dentry); + /* adjust nlink and update timestamp */ + dir = parent->d_inode; + mutex_lock(&dir->i_mutex); + + curtime = CURRENT_TIME; + + dir->i_ctime = dir->i_mtime = curtime; + + if (dentry) { + dentry->d_inode->i_ctime = curtime; + drop_nlink(dentry->d_inode); + if (sd->s_type & SYSFS_DIR) { + drop_nlink(dentry->d_inode); + drop_nlink(dir); + /* XXX: unpin if directory, this will go away soon */ + dput(dentry); + } } + + mutex_unlock(&dir->i_mutex); + + /* bye bye */ + if (dentry) + dput(dentry); + else + dput(parent); } int sysfs_hash_and_remove(struct dentry * dir, const char * name) @@ -251,7 +290,6 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) continue; if (!strcmp(sd->s_name, name)) { list_del_init(&sd->s_sibling); - sysfs_drop_dentry(sd, dir); found = 1; break; } @@ -261,7 +299,9 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) if (!found) return -ENOENT; + sysfs_drop_dentry(sd); sysfs_deactivate(sd); sysfs_put(sd); + return 0; } diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 9fa77d648aa5..fc6aa863b947 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -76,7 +76,7 @@ extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * na extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); extern void sysfs_remove_subdir(struct dentry *); -extern void sysfs_drop_dentry(struct sysfs_dirent *sd, struct dentry *parent); +extern void sysfs_drop_dentry(struct sysfs_dirent *sd); extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); extern spinlock_t sysfs_lock; From 7b595756ec1f49e0049a9e01a1298d53a7faaa15 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:17 +0900 Subject: [PATCH 39/61] sysfs: kill unnecessary attribute->owner sysfs is now completely out of driver/module lifetime game. After deletion, a sysfs node doesn't access anything outside sysfs proper, so there's no reason to hold onto the attribute owners. Note that often the wrong modules were accounted for as owners leading to accessing removed modules. This patch kills now unnecessary attribute->owner. Note that with this change, userland holding a sysfs node does not prevent the backing module from being unloaded. For more info regarding lifetime rule cleanup, please read the following message. http://article.gmane.org/gmane.linux.kernel/510293 (tweaked by Greg to not delete the field just yet, to make it easier to merge things properly.) Signed-off-by: Tejun Heo Cc: Cornelia Huck Cc: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- arch/ppc/syslib/mv64x60.c | 1 - arch/s390/kernel/ipl.c | 2 -- drivers/base/bus.c | 2 -- drivers/base/class.c | 2 -- drivers/base/core.c | 4 ---- drivers/base/firmware_class.c | 2 +- drivers/block/pktcdvd.c | 3 +-- drivers/char/ipmi/ipmi_msghandler.c | 10 ---------- drivers/cpufreq/cpufreq_stats.c | 3 +-- drivers/cpufreq/cpufreq_userspace.c | 2 +- drivers/cpufreq/freq_table.c | 1 - drivers/firmware/dcdbas.h | 3 +-- drivers/firmware/dell_rbu.c | 6 +++--- drivers/firmware/edd.c | 2 +- drivers/firmware/efivars.c | 6 +++--- drivers/i2c/chips/eeprom.c | 1 - drivers/i2c/chips/max6875.c | 1 - drivers/infiniband/core/sysfs.c | 1 - drivers/input/mouse/psmouse.h | 1 - drivers/macintosh/windfarm_core.c | 2 -- drivers/misc/asus-laptop.c | 3 +-- drivers/net/ibmveth.c | 2 +- drivers/parisc/pdc_stable.c | 4 ++-- drivers/pci/hotplug/acpiphp_ibm.c | 1 - drivers/pci/pci-sysfs.c | 4 ---- drivers/pci/probe.c | 2 -- drivers/pcmcia/socket_sysfs.c | 2 +- drivers/rapidio/rio-sysfs.c | 1 - drivers/rtc/rtc-ds1553.c | 1 - drivers/rtc/rtc-ds1742.c | 1 - drivers/s390/cio/chp.c | 2 -- drivers/s390/net/qeth_sys.c | 2 +- drivers/scsi/arcmsr/arcmsr_attr.c | 3 --- drivers/scsi/libsas/sas_expander.c | 1 - drivers/scsi/lpfc/lpfc_attr.c | 2 -- drivers/scsi/qla2xxx/qla_attr.c | 6 ------ drivers/spi/at25.c | 1 - drivers/video/aty/radeon_base.c | 2 -- drivers/video/backlight/backlight.c | 2 +- drivers/video/backlight/lcd.c | 2 +- drivers/w1/slaves/w1_ds2433.c | 1 - drivers/w1/slaves/w1_therm.c | 1 - drivers/w1/w1.c | 2 -- drivers/zorro/zorro-sysfs.c | 1 - fs/ecryptfs/main.c | 2 -- fs/ocfs2/cluster/masklog.c | 1 - fs/partitions/check.c | 1 - fs/sysfs/bin.c | 19 +++++-------------- fs/sysfs/file.c | 21 +++++---------------- include/linux/sysdev.h | 3 +-- include/linux/sysfs.h | 12 ++++++++---- kernel/module.c | 9 +++------ kernel/params.c | 1 - net/bridge/br_sysfs_br.c | 3 +-- net/bridge/br_sysfs_if.c | 3 +-- 55 files changed, 44 insertions(+), 135 deletions(-) diff --git a/arch/ppc/syslib/mv64x60.c b/arch/ppc/syslib/mv64x60.c index 8485a68cd475..032f4b7f4225 100644 --- a/arch/ppc/syslib/mv64x60.c +++ b/arch/ppc/syslib/mv64x60.c @@ -2415,7 +2415,6 @@ static struct bin_attribute mv64xxx_hs_reg_attr = { /* Hotswap register */ .attr = { .name = "hs_reg", .mode = S_IRUGO | S_IWUSR, - .owner = THIS_MODULE, }, .size = VAL_LEN_MAX, .read = mv64xxx_hs_reg_read, diff --git a/arch/s390/kernel/ipl.c b/arch/s390/kernel/ipl.c index 82b131ddd7ff..9a13b24ee1ab 100644 --- a/arch/s390/kernel/ipl.c +++ b/arch/s390/kernel/ipl.c @@ -312,7 +312,6 @@ static struct bin_attribute ipl_parameter_attr = { .attr = { .name = "binary_parameter", .mode = S_IRUGO, - .owner = THIS_MODULE, }, .size = PAGE_SIZE, .read = &ipl_parameter_read, @@ -336,7 +335,6 @@ static struct bin_attribute ipl_scp_data_attr = { .attr = { .name = "scp_data", .mode = S_IRUGO, - .owner = THIS_MODULE, }, .size = PAGE_SIZE, .read = &ipl_scp_data_read, diff --git a/drivers/base/bus.c b/drivers/base/bus.c index f299e0d6abc4..61c67526a656 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -574,7 +574,6 @@ static int add_probe_files(struct bus_type *bus) bus->drivers_probe_attr.attr.name = "drivers_probe"; bus->drivers_probe_attr.attr.mode = S_IWUSR; - bus->drivers_probe_attr.attr.owner = bus->owner; bus->drivers_probe_attr.store = store_drivers_probe; retval = bus_create_file(bus, &bus->drivers_probe_attr); if (retval) @@ -582,7 +581,6 @@ static int add_probe_files(struct bus_type *bus) bus->drivers_autoprobe_attr.attr.name = "drivers_autoprobe"; bus->drivers_autoprobe_attr.attr.mode = S_IWUSR | S_IRUGO; - bus->drivers_autoprobe_attr.attr.owner = bus->owner; bus->drivers_autoprobe_attr.show = show_drivers_autoprobe; bus->drivers_autoprobe_attr.store = store_drivers_autoprobe; retval = bus_create_file(bus, &bus->drivers_autoprobe_attr); diff --git a/drivers/base/class.c b/drivers/base/class.c index 8c506dbe3913..9cbfde23b9e3 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -605,7 +605,6 @@ int class_device_add(struct class_device *class_dev) goto out3; class_dev->uevent_attr.attr.name = "uevent"; class_dev->uevent_attr.attr.mode = S_IWUSR; - class_dev->uevent_attr.attr.owner = parent_class->owner; class_dev->uevent_attr.store = store_uevent; error = class_device_create_file(class_dev, &class_dev->uevent_attr); if (error) @@ -620,7 +619,6 @@ int class_device_add(struct class_device *class_dev) } attr->attr.name = "dev"; attr->attr.mode = S_IRUGO; - attr->attr.owner = parent_class->owner; attr->show = show_dev; error = class_device_create_file(class_dev, attr); if (error) { diff --git a/drivers/base/core.c b/drivers/base/core.c index cff4fbfbb055..e3fb87bfc6e1 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -683,8 +683,6 @@ int device_add(struct device *dev) dev->uevent_attr.attr.name = "uevent"; dev->uevent_attr.attr.mode = S_IRUGO | S_IWUSR; - if (dev->driver) - dev->uevent_attr.attr.owner = dev->driver->owner; dev->uevent_attr.store = store_uevent; dev->uevent_attr.show = show_uevent; error = device_create_file(dev, &dev->uevent_attr); @@ -700,8 +698,6 @@ int device_add(struct device *dev) } attr->attr.name = "dev"; attr->attr.mode = S_IRUGO; - if (dev->driver) - attr->attr.owner = dev->driver->owner; attr->show = show_dev; error = device_create_file(dev, attr); if (error) { diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 89a5f4a54913..0e511485d2e6 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -271,7 +271,7 @@ out: } static struct bin_attribute firmware_attr_data_tmpl = { - .attr = {.name = "data", .mode = 0644, .owner = THIS_MODULE}, + .attr = {.name = "data", .mode = 0644}, .size = 0, .read = firmware_data_read, .write = firmware_data_write, diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index f1b9dd7d47d6..ce64e86d6ffb 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -146,8 +146,7 @@ static void pkt_kobj_release(struct kobject *kobj) **********************************************************/ #define DEF_ATTR(_obj,_name,_mode) \ - static struct attribute _obj = { \ - .name = _name, .owner = THIS_MODULE, .mode = _mode } + static struct attribute _obj = { .name = _name, .mode = _mode } /********************************************************** /sys/class/pktcdvd/pktcdvd[0-7]/ diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 8e222f2b80cc..b5df7e61aeb2 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -2171,52 +2171,42 @@ static int create_files(struct bmc_device *bmc) int err; bmc->device_id_attr.attr.name = "device_id"; - bmc->device_id_attr.attr.owner = THIS_MODULE; bmc->device_id_attr.attr.mode = S_IRUGO; bmc->device_id_attr.show = device_id_show; bmc->provides_dev_sdrs_attr.attr.name = "provides_device_sdrs"; - bmc->provides_dev_sdrs_attr.attr.owner = THIS_MODULE; bmc->provides_dev_sdrs_attr.attr.mode = S_IRUGO; bmc->provides_dev_sdrs_attr.show = provides_dev_sdrs_show; bmc->revision_attr.attr.name = "revision"; - bmc->revision_attr.attr.owner = THIS_MODULE; bmc->revision_attr.attr.mode = S_IRUGO; bmc->revision_attr.show = revision_show; bmc->firmware_rev_attr.attr.name = "firmware_revision"; - bmc->firmware_rev_attr.attr.owner = THIS_MODULE; bmc->firmware_rev_attr.attr.mode = S_IRUGO; bmc->firmware_rev_attr.show = firmware_rev_show; bmc->version_attr.attr.name = "ipmi_version"; - bmc->version_attr.attr.owner = THIS_MODULE; bmc->version_attr.attr.mode = S_IRUGO; bmc->version_attr.show = ipmi_version_show; bmc->add_dev_support_attr.attr.name = "additional_device_support"; - bmc->add_dev_support_attr.attr.owner = THIS_MODULE; bmc->add_dev_support_attr.attr.mode = S_IRUGO; bmc->add_dev_support_attr.show = add_dev_support_show; bmc->manufacturer_id_attr.attr.name = "manufacturer_id"; - bmc->manufacturer_id_attr.attr.owner = THIS_MODULE; bmc->manufacturer_id_attr.attr.mode = S_IRUGO; bmc->manufacturer_id_attr.show = manufacturer_id_show; bmc->product_id_attr.attr.name = "product_id"; - bmc->product_id_attr.attr.owner = THIS_MODULE; bmc->product_id_attr.attr.mode = S_IRUGO; bmc->product_id_attr.show = product_id_show; bmc->guid_attr.attr.name = "guid"; - bmc->guid_attr.attr.owner = THIS_MODULE; bmc->guid_attr.attr.mode = S_IRUGO; bmc->guid_attr.show = guid_show; bmc->aux_firmware_rev_attr.attr.name = "aux_firmware_revision"; - bmc->aux_firmware_rev_attr.attr.owner = THIS_MODULE; bmc->aux_firmware_rev_attr.attr.mode = S_IRUGO; bmc->aux_firmware_rev_attr.show = aux_firmware_rev_show; diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c index d2f0cbd8b8f3..917b9bab9ccb 100644 --- a/drivers/cpufreq/cpufreq_stats.c +++ b/drivers/cpufreq/cpufreq_stats.c @@ -25,8 +25,7 @@ static spinlock_t cpufreq_stats_lock; #define CPUFREQ_STATDEVICE_ATTR(_name,_mode,_show) \ static struct freq_attr _attr_##_name = {\ - .attr = {.name = __stringify(_name), .owner = THIS_MODULE, \ - .mode = _mode, }, \ + .attr = {.name = __stringify(_name), .mode = _mode, }, \ .show = _show,\ }; diff --git a/drivers/cpufreq/cpufreq_userspace.c b/drivers/cpufreq/cpufreq_userspace.c index 860345c7799a..a648970338b0 100644 --- a/drivers/cpufreq/cpufreq_userspace.c +++ b/drivers/cpufreq/cpufreq_userspace.c @@ -120,7 +120,7 @@ store_speed (struct cpufreq_policy *policy, const char *buf, size_t count) static struct freq_attr freq_attr_scaling_setspeed = { - .attr = { .name = "scaling_setspeed", .mode = 0644, .owner = THIS_MODULE }, + .attr = { .name = "scaling_setspeed", .mode = 0644 }, .show = show_speed, .store = store_speed, }; diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index e7490925fdcf..5409f3afb3f8 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -199,7 +199,6 @@ static ssize_t show_available_freqs (struct cpufreq_policy *policy, char *buf) struct freq_attr cpufreq_freq_attr_scaling_available_freqs = { .attr = { .name = "scaling_available_frequencies", .mode = 0444, - .owner=THIS_MODULE }, .show = show_available_freqs, }; diff --git a/drivers/firmware/dcdbas.h b/drivers/firmware/dcdbas.h index 58a85182b3e8..dcdba0f1b32c 100644 --- a/drivers/firmware/dcdbas.h +++ b/drivers/firmware/dcdbas.h @@ -67,8 +67,7 @@ #define DCDBAS_BIN_ATTR_RW(_name) \ struct bin_attribute bin_attr_##_name = { \ .attr = { .name = __stringify(_name), \ - .mode = 0600, \ - .owner = THIS_MODULE }, \ + .mode = 0600 }, \ .read = _name##_read, \ .write = _name##_write, \ } diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c index fc702e40bd43..f8afecb7d0cf 100644 --- a/drivers/firmware/dell_rbu.c +++ b/drivers/firmware/dell_rbu.c @@ -687,18 +687,18 @@ static ssize_t write_rbu_packet_size(struct kobject *kobj, char *buffer, } static struct bin_attribute rbu_data_attr = { - .attr = {.name = "data",.owner = THIS_MODULE,.mode = 0444}, + .attr = {.name = "data", .mode = 0444}, .read = read_rbu_data, }; static struct bin_attribute rbu_image_type_attr = { - .attr = {.name = "image_type",.owner = THIS_MODULE,.mode = 0644}, + .attr = {.name = "image_type", .mode = 0644}, .read = read_rbu_image_type, .write = write_rbu_image_type, }; static struct bin_attribute rbu_packet_size_attr = { - .attr = {.name = "packet_size",.owner = THIS_MODULE,.mode = 0644}, + .attr = {.name = "packet_size", .mode = 0644}, .read = read_rbu_packet_size, .write = write_rbu_packet_size, }; diff --git a/drivers/firmware/edd.c b/drivers/firmware/edd.c index d8806e4f1829..15232271d848 100644 --- a/drivers/firmware/edd.c +++ b/drivers/firmware/edd.c @@ -74,7 +74,7 @@ static struct edd_device *edd_devices[EDD_MBR_SIG_MAX]; #define EDD_DEVICE_ATTR(_name,_mode,_show,_test) \ struct edd_attribute edd_attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \ + .attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .test = _test, \ }; diff --git a/drivers/firmware/efivars.c b/drivers/firmware/efivars.c index 1324984a4c35..bfd2d67df689 100644 --- a/drivers/firmware/efivars.c +++ b/drivers/firmware/efivars.c @@ -131,21 +131,21 @@ struct efivar_attribute { #define EFI_ATTR(_name, _mode, _show, _store) \ struct subsys_attribute efi_attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ .show = _show, \ .store = _store, \ }; #define EFIVAR_ATTR(_name, _mode, _show, _store) \ struct efivar_attribute efivar_attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ .show = _show, \ .store = _store, \ }; #define VAR_SUBSYS_ATTR(_name, _mode, _show, _store) \ struct subsys_attribute var_subsys_attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ .show = _show, \ .store = _store, \ }; diff --git a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c index bfce13c8f1ff..5990dd5fc773 100644 --- a/drivers/i2c/chips/eeprom.c +++ b/drivers/i2c/chips/eeprom.c @@ -143,7 +143,6 @@ static struct bin_attribute eeprom_attr = { .attr = { .name = "eeprom", .mode = S_IRUGO, - .owner = THIS_MODULE, }, .size = EEPROM_SIZE, .read = eeprom_read, diff --git a/drivers/i2c/chips/max6875.c b/drivers/i2c/chips/max6875.c index 76645c142977..1405ce5b236c 100644 --- a/drivers/i2c/chips/max6875.c +++ b/drivers/i2c/chips/max6875.c @@ -152,7 +152,6 @@ static struct bin_attribute user_eeprom_attr = { .attr = { .name = "eeprom", .mode = S_IRUGO, - .owner = THIS_MODULE, }, .size = USER_EEPROM_SIZE, .read = max6875_read, diff --git a/drivers/infiniband/core/sysfs.c b/drivers/infiniband/core/sysfs.c index 08c299ebf4a8..bf9b99292048 100644 --- a/drivers/infiniband/core/sysfs.c +++ b/drivers/infiniband/core/sysfs.c @@ -479,7 +479,6 @@ alloc_group_attrs(ssize_t (*show)(struct ib_port *, element->attr.attr.name = element->name; element->attr.attr.mode = S_IRUGO; - element->attr.attr.owner = THIS_MODULE; element->attr.show = show; element->index = i; diff --git a/drivers/input/mouse/psmouse.h b/drivers/input/mouse/psmouse.h index 27a68835b5ba..1317bdd8cc7c 100644 --- a/drivers/input/mouse/psmouse.h +++ b/drivers/input/mouse/psmouse.h @@ -119,7 +119,6 @@ static struct psmouse_attribute psmouse_attr_##_name = { \ .attr = { \ .name = __stringify(_name), \ .mode = _mode, \ - .owner = THIS_MODULE, \ }, \ .show = psmouse_attr_show_helper, \ .store = psmouse_attr_set_helper, \ diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index 11ced17f438a..4fcb245ba184 100644 --- a/drivers/macintosh/windfarm_core.c +++ b/drivers/macintosh/windfarm_core.c @@ -212,7 +212,6 @@ int wf_register_control(struct wf_control *new_ct) list_add(&new_ct->link, &wf_controls); new_ct->attr.attr.name = new_ct->name; - new_ct->attr.attr.owner = THIS_MODULE; new_ct->attr.attr.mode = 0644; new_ct->attr.show = wf_show_control; new_ct->attr.store = wf_store_control; @@ -325,7 +324,6 @@ int wf_register_sensor(struct wf_sensor *new_sr) list_add(&new_sr->link, &wf_sensors); new_sr->attr.attr.name = new_sr->name; - new_sr->attr.attr.owner = THIS_MODULE; new_sr->attr.attr.mode = 0444; new_sr->attr.show = wf_show_sensor; new_sr->attr.store = NULL; diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index 4f9060a2a2f2..7798f590e5aa 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c @@ -737,8 +737,7 @@ static void asus_hotk_notify(acpi_handle handle, u32 event, void *data) struct device_attribute dev_attr_##_name = { \ .attr = { \ .name = __stringify(_name), \ - .mode = 0, \ - .owner = THIS_MODULE }, \ + .mode = 0 }, \ .show = NULL, \ .store = NULL, \ } diff --git a/drivers/net/ibmveth.c b/drivers/net/ibmveth.c index 6ec3d500f334..d96eb7229548 100644 --- a/drivers/net/ibmveth.c +++ b/drivers/net/ibmveth.c @@ -1337,7 +1337,7 @@ const char * buf, size_t count) #define ATTR(_name, _mode) \ struct attribute veth_##_name##_attr = { \ - .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE \ + .name = __stringify(_name), .mode = _mode, \ }; static ATTR(active, 0644); diff --git a/drivers/parisc/pdc_stable.c b/drivers/parisc/pdc_stable.c index 924ef0609460..fc4bde259dc7 100644 --- a/drivers/parisc/pdc_stable.c +++ b/drivers/parisc/pdc_stable.c @@ -121,14 +121,14 @@ struct pdcspath_entry pdcspath_entry_##_name = { \ #define PDCS_ATTR(_name, _mode, _show, _store) \ struct subsys_attribute pdcs_attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ .show = _show, \ .store = _store, \ }; #define PATHS_ATTR(_name, _mode, _show, _store) \ struct pdcspath_attribute paths_attr_##_name = { \ - .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE}, \ + .attr = {.name = __stringify(_name), .mode = _mode}, \ .show = _show, \ .store = _store, \ }; diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index e7322c25d377..74556ec31a5b 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -117,7 +117,6 @@ static struct notification ibm_note; static struct bin_attribute ibm_apci_table_attr = { .attr = { .name = "apci_table", - .owner = THIS_MODULE, .mode = S_IRUGO, }, .read = ibm_read_apci_table, diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 284e83a527f9..d448f8df8613 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -499,7 +499,6 @@ static int pci_create_resource_files(struct pci_dev *pdev) sprintf(res_attr_name, "resource%d", i); res_attr->attr.name = res_attr_name; res_attr->attr.mode = S_IRUSR | S_IWUSR; - res_attr->attr.owner = THIS_MODULE; res_attr->size = pci_resource_len(pdev, i); res_attr->mmap = pci_mmap_resource; res_attr->private = &pdev->resource[i]; @@ -582,7 +581,6 @@ static struct bin_attribute pci_config_attr = { .attr = { .name = "config", .mode = S_IRUGO | S_IWUSR, - .owner = THIS_MODULE, }, .size = 256, .read = pci_read_config, @@ -593,7 +591,6 @@ static struct bin_attribute pcie_config_attr = { .attr = { .name = "config", .mode = S_IRUGO | S_IWUSR, - .owner = THIS_MODULE, }, .size = 4096, .read = pci_read_config, @@ -628,7 +625,6 @@ int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) rom_attr->size = pci_resource_len(pdev, PCI_ROM_RESOURCE); rom_attr->attr.name = "rom"; rom_attr->attr.mode = S_IRUSR; - rom_attr->attr.owner = THIS_MODULE; rom_attr->read = pci_read_rom; rom_attr->write = pci_write_rom; retval = sysfs_create_bin_file(&pdev->dev.kobj, rom_attr); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index e48fcf089621..08783bd381f5 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -39,7 +39,6 @@ static void pci_create_legacy_files(struct pci_bus *b) b->legacy_io->attr.name = "legacy_io"; b->legacy_io->size = 0xffff; b->legacy_io->attr.mode = S_IRUSR | S_IWUSR; - b->legacy_io->attr.owner = THIS_MODULE; b->legacy_io->read = pci_read_legacy_io; b->legacy_io->write = pci_write_legacy_io; class_device_create_bin_file(&b->class_dev, b->legacy_io); @@ -49,7 +48,6 @@ static void pci_create_legacy_files(struct pci_bus *b) b->legacy_mem->attr.name = "legacy_mem"; b->legacy_mem->size = 1024*1024; b->legacy_mem->attr.mode = S_IRUSR | S_IWUSR; - b->legacy_mem->attr.owner = THIS_MODULE; b->legacy_mem->mmap = pci_mmap_legacy_mem; class_device_create_bin_file(&b->class_dev, b->legacy_mem); } diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index a2bb46526b56..dbfbe65779e5 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -366,7 +366,7 @@ static struct device_attribute *pccard_socket_attributes[] = { }; static struct bin_attribute pccard_cis_attr = { - .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR, .owner = THIS_MODULE}, + .attr = { .name = "cis", .mode = S_IRUGO | S_IWUSR }, .size = 0x200, .read = pccard_show_cis, .write = pccard_store_cis, diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c index eed91434417d..a3972b9f96e6 100644 --- a/drivers/rapidio/rio-sysfs.c +++ b/drivers/rapidio/rio-sysfs.c @@ -197,7 +197,6 @@ static struct bin_attribute rio_config_attr = { .attr = { .name = "config", .mode = S_IRUGO | S_IWUSR, - .owner = THIS_MODULE, }, .size = 0x200000, .read = rio_read_config, diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c index afa64c7fa2e2..b024cfb558f4 100644 --- a/drivers/rtc/rtc-ds1553.c +++ b/drivers/rtc/rtc-ds1553.c @@ -290,7 +290,6 @@ static struct bin_attribute ds1553_nvram_attr = { .attr = { .name = "nvram", .mode = S_IRUGO | S_IWUGO, - .owner = THIS_MODULE, }, .size = RTC_OFFSET, .read = ds1553_nvram_read, diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c index d68288b389dc..1638acdbc913 100644 --- a/drivers/rtc/rtc-ds1742.c +++ b/drivers/rtc/rtc-ds1742.c @@ -159,7 +159,6 @@ static struct bin_attribute ds1742_nvram_attr = { .attr = { .name = "nvram", .mode = S_IRUGO | S_IWUGO, - .owner = THIS_MODULE, }, .read = ds1742_nvram_read, .write = ds1742_nvram_write, diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index ac289e6eadfe..96a8a72a6083 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -165,7 +165,6 @@ static struct bin_attribute chp_measurement_chars_attr = { .attr = { .name = "measurement_chars", .mode = S_IRUSR, - .owner = THIS_MODULE, }, .size = sizeof(struct cmg_chars), .read = chp_measurement_chars_read, @@ -217,7 +216,6 @@ static struct bin_attribute chp_measurement_attr = { .attr = { .name = "measurement", .mode = S_IRUSR, - .owner = THIS_MODULE, }, .size = sizeof(struct cmg_entry), .read = chp_measurement_read, diff --git a/drivers/s390/net/qeth_sys.c b/drivers/s390/net/qeth_sys.c index 65ffc21afc37..bb0287ad1aac 100644 --- a/drivers/s390/net/qeth_sys.c +++ b/drivers/s390/net/qeth_sys.c @@ -991,7 +991,7 @@ static struct attribute_group qeth_osn_device_attr_group = { #define QETH_DEVICE_ATTR(_id,_name,_mode,_show,_store) \ struct device_attribute dev_attr_##_id = { \ - .attr = {.name=__stringify(_name), .mode=_mode, .owner=THIS_MODULE },\ + .attr = {.name=__stringify(_name), .mode=_mode, },\ .show = _show, \ .store = _store, \ }; diff --git a/drivers/scsi/arcmsr/arcmsr_attr.c b/drivers/scsi/arcmsr/arcmsr_attr.c index 03bfed61bffc..8908228bc134 100644 --- a/drivers/scsi/arcmsr/arcmsr_attr.c +++ b/drivers/scsi/arcmsr/arcmsr_attr.c @@ -188,7 +188,6 @@ static struct bin_attribute arcmsr_sysfs_message_read_attr = { .attr = { .name = "mu_read", .mode = S_IRUSR , - .owner = THIS_MODULE, }, .size = 1032, .read = arcmsr_sysfs_iop_message_read, @@ -198,7 +197,6 @@ static struct bin_attribute arcmsr_sysfs_message_write_attr = { .attr = { .name = "mu_write", .mode = S_IWUSR, - .owner = THIS_MODULE, }, .size = 1032, .write = arcmsr_sysfs_iop_message_write, @@ -208,7 +206,6 @@ static struct bin_attribute arcmsr_sysfs_message_clear_attr = { .attr = { .name = "mu_clear", .mode = S_IWUSR, - .owner = THIS_MODULE, }, .size = 1, .write = arcmsr_sysfs_iop_message_clear, diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index e34442e405e8..578ed79f4148 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -1368,7 +1368,6 @@ static void sas_ex_smp_hook(struct domain_device *dev) memset(bin_attr, 0, sizeof(*bin_attr)); bin_attr->attr.name = SMP_BIN_ATTR_NAME; - bin_attr->attr.owner = THIS_MODULE; bin_attr->attr.mode = 0600; bin_attr->size = 0; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 95fe77e816f8..f81fe501a4a1 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -1200,7 +1200,6 @@ static struct bin_attribute sysfs_ctlreg_attr = { .attr = { .name = "ctlreg", .mode = S_IRUSR | S_IWUSR, - .owner = THIS_MODULE, }, .size = 256, .read = sysfs_ctlreg_read, @@ -1422,7 +1421,6 @@ static struct bin_attribute sysfs_mbox_attr = { .attr = { .name = "mbox", .mode = S_IRUSR | S_IWUSR, - .owner = THIS_MODULE, }, .size = MAILBOX_CMD_SIZE, .read = sysfs_mbox_read, diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 8081b637d97e..96587253bfa9 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -73,7 +73,6 @@ static struct bin_attribute sysfs_fw_dump_attr = { .attr = { .name = "fw_dump", .mode = S_IRUSR | S_IWUSR, - .owner = THIS_MODULE, }, .size = 0, .read = qla2x00_sysfs_read_fw_dump, @@ -149,7 +148,6 @@ static struct bin_attribute sysfs_nvram_attr = { .attr = { .name = "nvram", .mode = S_IRUSR | S_IWUSR, - .owner = THIS_MODULE, }, .size = 512, .read = qla2x00_sysfs_read_nvram, @@ -198,7 +196,6 @@ static struct bin_attribute sysfs_optrom_attr = { .attr = { .name = "optrom", .mode = S_IRUSR | S_IWUSR, - .owner = THIS_MODULE, }, .size = OPTROM_SIZE_24XX, .read = qla2x00_sysfs_read_optrom, @@ -279,7 +276,6 @@ static struct bin_attribute sysfs_optrom_ctl_attr = { .attr = { .name = "optrom_ctl", .mode = S_IWUSR, - .owner = THIS_MODULE, }, .size = 0, .write = qla2x00_sysfs_write_optrom_ctl, @@ -327,7 +323,6 @@ static struct bin_attribute sysfs_vpd_attr = { .attr = { .name = "vpd", .mode = S_IRUSR | S_IWUSR, - .owner = THIS_MODULE, }, .size = 0, .read = qla2x00_sysfs_read_vpd, @@ -375,7 +370,6 @@ static struct bin_attribute sysfs_sfp_attr = { .attr = { .name = "sfp", .mode = S_IRUSR | S_IWUSR, - .owner = THIS_MODULE, }, .size = SFP_DEV_SIZE * 2, .read = qla2x00_sysfs_read_sfp, diff --git a/drivers/spi/at25.c b/drivers/spi/at25.c index 8efa07e8b8c2..fde1dededba3 100644 --- a/drivers/spi/at25.c +++ b/drivers/spi/at25.c @@ -314,7 +314,6 @@ static int at25_probe(struct spi_device *spi) */ at25->bin.attr.name = "eeprom"; at25->bin.attr.mode = S_IRUSR; - at25->bin.attr.owner = THIS_MODULE; at25->bin.read = at25_bin_read; at25->bin.size = at25->chip.byte_len; diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 2ce050193018..3b3c6571f583 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -2126,7 +2126,6 @@ static ssize_t radeon_show_edid2(struct kobject *kobj, char *buf, loff_t off, si static struct bin_attribute edid1_attr = { .attr = { .name = "edid1", - .owner = THIS_MODULE, .mode = 0444, }, .size = EDID_LENGTH, @@ -2136,7 +2135,6 @@ static struct bin_attribute edid1_attr = { static struct bin_attribute edid2_attr = { .attr = { .name = "edid2", - .owner = THIS_MODULE, .mode = 0444, }, .size = EDID_LENGTH, diff --git a/drivers/video/backlight/backlight.c b/drivers/video/backlight/backlight.c index c65e81ff3578..7e06223bca94 100644 --- a/drivers/video/backlight/backlight.c +++ b/drivers/video/backlight/backlight.c @@ -172,7 +172,7 @@ static struct class backlight_class = { #define DECLARE_ATTR(_name,_mode,_show,_store) \ { \ - .attr = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \ + .attr = { .name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ } diff --git a/drivers/video/backlight/lcd.c b/drivers/video/backlight/lcd.c index 6ef8f0a7a137..648b53c1fdea 100644 --- a/drivers/video/backlight/lcd.c +++ b/drivers/video/backlight/lcd.c @@ -157,7 +157,7 @@ static struct class lcd_class = { #define DECLARE_ATTR(_name,_mode,_show,_store) \ { \ - .attr = { .name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \ + .attr = { .name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ } diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 8ea17a53eed8..4e13aa71adea 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -252,7 +252,6 @@ static struct bin_attribute w1_f23_bin_attr = { .attr = { .name = "eeprom", .mode = S_IRUGO | S_IWUSR, - .owner = THIS_MODULE, }, .size = W1_EEPROM_SIZE, .read = w1_f23_read_bin, diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 1a6937dc190b..8ba4e572e09c 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -48,7 +48,6 @@ static struct bin_attribute w1_therm_bin_attr = { .attr = { .name = "w1_slave", .mode = S_IRUGO, - .owner = THIS_MODULE, }, .size = W1_SLAVE_DATA_SIZE, .read = w1_therm_read_bin, diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 7d6876dbcc96..1838cb29b646 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -128,7 +128,6 @@ static struct bin_attribute w1_slave_attr_bin_id = { .attr = { .name = "id", .mode = S_IRUGO, - .owner = THIS_MODULE, }, .size = 8, .read = w1_slave_read_id, @@ -167,7 +166,6 @@ static struct bin_attribute w1_default_attr = { .attr = { .name = "rw", .mode = S_IRUGO | S_IWUSR, - .owner = THIS_MODULE, }, .size = PAGE_SIZE, .read = w1_default_read, diff --git a/drivers/zorro/zorro-sysfs.c b/drivers/zorro/zorro-sysfs.c index c3ba0ec334c4..7e03cc68b182 100644 --- a/drivers/zorro/zorro-sysfs.c +++ b/drivers/zorro/zorro-sysfs.c @@ -78,7 +78,6 @@ static struct bin_attribute zorro_config_attr = { .attr = { .name = "config", .mode = S_IRUGO | S_IWUSR, - .owner = THIS_MODULE }, .size = sizeof(struct ConfigDev), .read = zorro_read_config, diff --git a/fs/ecryptfs/main.c b/fs/ecryptfs/main.c index 606128f5c927..02ca6f1e55d7 100644 --- a/fs/ecryptfs/main.c +++ b/fs/ecryptfs/main.c @@ -840,8 +840,6 @@ static int __init ecryptfs_init(void) goto out; } kobj_set_kset_s(&ecryptfs_subsys, fs_subsys); - sysfs_attr_version.attr.owner = THIS_MODULE; - sysfs_attr_version_str.attr.owner = THIS_MODULE; rc = do_sysfs_registration(); if (rc) { printk(KERN_ERR "sysfs registration failed\n"); diff --git a/fs/ocfs2/cluster/masklog.c b/fs/ocfs2/cluster/masklog.c index 2b205f5d5790..e9e042b93dbf 100644 --- a/fs/ocfs2/cluster/masklog.c +++ b/fs/ocfs2/cluster/masklog.c @@ -74,7 +74,6 @@ struct mlog_attribute { #define define_mask(_name) { \ .attr = { \ .name = #_name, \ - .owner = THIS_MODULE, \ .mode = S_IRUGO | S_IWUSR, \ }, \ .mask = ML_##_name, \ diff --git a/fs/partitions/check.c b/fs/partitions/check.c index 9a3a058f3553..98e0b85a9bb2 100644 --- a/fs/partitions/check.c +++ b/fs/partitions/check.c @@ -397,7 +397,6 @@ void add_partition(struct gendisk *disk, int part, sector_t start, sector_t len, static struct attribute addpartattr = { .name = "whole_disk", .mode = S_IRUSR | S_IRGRP | S_IROTH, - .owner = THIS_MODULE, }; sysfs_create_file(&p->kobj, &addpartattr); diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index 618b8aea6a7b..3c5574a40b09 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -175,25 +175,20 @@ static int open(struct inode * inode, struct file * file) if (!sysfs_get_active(attr_sd)) return -ENODEV; - /* Grab the module reference for this attribute */ - error = -ENODEV; - if (!try_module_get(attr->attr.owner)) - goto err_sput; - error = -EACCES; if ((file->f_mode & FMODE_WRITE) && !(attr->write || attr->mmap)) - goto err_mput; + goto err_out; if ((file->f_mode & FMODE_READ) && !(attr->read || attr->mmap)) - goto err_mput; + goto err_out; error = -ENOMEM; bb = kzalloc(sizeof(*bb), GFP_KERNEL); if (!bb) - goto err_mput; + goto err_out; bb->buffer = kmalloc(PAGE_SIZE, GFP_KERNEL); if (!bb->buffer) - goto err_mput; + goto err_out; mutex_init(&bb->mutex); file->private_data = bb; @@ -203,9 +198,7 @@ static int open(struct inode * inode, struct file * file) sysfs_get(attr_sd); return 0; - err_mput: - module_put(attr->attr.owner); - err_sput: + err_out: sysfs_put_active(attr_sd); kfree(bb); return error; @@ -214,13 +207,11 @@ static int open(struct inode * inode, struct file * file) static int release(struct inode * inode, struct file * file) { struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; - struct bin_attribute *attr = attr_sd->s_elem.bin_attr.bin_attr; struct bin_buffer *bb = file->private_data; if (bb->mmapped) sysfs_put_active_two(attr_sd); sysfs_put(attr_sd); - module_put(attr->attr.owner); kfree(bb->buffer); kfree(bb); return 0; diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index d673d9b5d33f..a84b734f7b29 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -241,7 +241,6 @@ sysfs_write_file(struct file *file, const char __user *buf, size_t count, loff_t static int sysfs_open_file(struct inode *inode, struct file *file) { struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; - struct attribute *attr = attr_sd->s_elem.attr.attr; struct kobject *kobj = attr_sd->s_parent->s_elem.dir.kobj; struct sysfs_buffer * buffer; struct sysfs_ops * ops = NULL; @@ -251,11 +250,6 @@ static int sysfs_open_file(struct inode *inode, struct file *file) if (!sysfs_get_active_two(attr_sd)) return -ENODEV; - /* Grab the module reference for this attribute */ - error = -ENODEV; - if (!try_module_get(attr->owner)) - goto err_sput; - /* if the kobject has no ktype, then we assume that it is a subsystem * itself, and use ops for it. */ @@ -272,7 +266,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file) * or the subsystem have no operations. */ if (!ops) - goto err_mput; + goto err_out; /* File needs write support. * The inode's perms must say it's ok, @@ -280,7 +274,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file) */ if (file->f_mode & FMODE_WRITE) { if (!(inode->i_mode & S_IWUGO) || !ops->store) - goto err_mput; + goto err_out; } /* File needs read support. @@ -289,7 +283,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file) */ if (file->f_mode & FMODE_READ) { if (!(inode->i_mode & S_IRUGO) || !ops->show) - goto err_mput; + goto err_out; } /* No error? Great, allocate a buffer for the file, and store it @@ -298,7 +292,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file) error = -ENOMEM; buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); if (!buffer) - goto err_mput; + goto err_out; init_MUTEX(&buffer->sem); buffer->needs_read_fill = 1; @@ -310,9 +304,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file) sysfs_get(attr_sd); return 0; - err_mput: - module_put(attr->owner); - err_sput: + err_out: sysfs_put_active_two(attr_sd); return error; } @@ -320,12 +312,9 @@ static int sysfs_open_file(struct inode *inode, struct file *file) static int sysfs_release(struct inode * inode, struct file * filp) { struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; - struct attribute *attr = attr_sd->s_elem.attr.attr; struct sysfs_buffer *buffer = filp->private_data; sysfs_put(attr_sd); - /* After this point, attr should not be accessed. */ - module_put(attr->owner); if (buffer) { if (buffer->page) diff --git a/include/linux/sysdev.h b/include/linux/sysdev.h index e699ab279c2c..e285746588d6 100644 --- a/include/linux/sysdev.h +++ b/include/linux/sysdev.h @@ -101,8 +101,7 @@ struct sysdev_attribute { #define _SYSDEV_ATTR(_name,_mode,_show,_store) \ { \ - .attr = { .name = __stringify(_name), .mode = _mode, \ - .owner = THIS_MODULE }, \ + .attr = { .name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ } diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 2f86b080b39d..161e19aa2b4f 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -20,9 +20,13 @@ struct module; struct nameidata; struct dentry; +/* FIXME + * The *owner field is no longer used, but leave around + * until the tree gets cleaned up fully. + */ struct attribute { const char * name; - struct module * owner; + struct module * owner; mode_t mode; }; @@ -39,14 +43,14 @@ struct attribute_group { */ #define __ATTR(_name,_mode,_show,_store) { \ - .attr = {.name = __stringify(_name), .mode = _mode, .owner = THIS_MODULE }, \ + .attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ } #define __ATTR_RO(_name) { \ - .attr = { .name = __stringify(_name), .mode = 0444, .owner = THIS_MODULE }, \ - .show = _name##_show, \ + .attr = { .name = __stringify(_name), .mode = 0444 }, \ + .show = _name##_show, \ } #define __ATTR_NULL { .attr = { .name = NULL } } diff --git a/kernel/module.c b/kernel/module.c index 9bd93de01f4a..015d60cfd90e 100644 --- a/kernel/module.c +++ b/kernel/module.c @@ -488,8 +488,7 @@ static void free_modinfo_##field(struct module *mod) \ mod->field = NULL; \ } \ static struct module_attribute modinfo_##field = { \ - .attr = { .name = __stringify(field), .mode = 0444, \ - .owner = THIS_MODULE }, \ + .attr = { .name = __stringify(field), .mode = 0444 }, \ .show = show_modinfo_##field, \ .setup = setup_modinfo_##field, \ .test = modinfo_##field##_exists, \ @@ -793,7 +792,7 @@ static ssize_t show_refcnt(struct module_attribute *mattr, } static struct module_attribute refcnt = { - .attr = { .name = "refcnt", .mode = 0444, .owner = THIS_MODULE }, + .attr = { .name = "refcnt", .mode = 0444 }, .show = show_refcnt, }; @@ -851,7 +850,7 @@ static ssize_t show_initstate(struct module_attribute *mattr, } static struct module_attribute initstate = { - .attr = { .name = "initstate", .mode = 0444, .owner = THIS_MODULE }, + .attr = { .name = "initstate", .mode = 0444 }, .show = show_initstate, }; @@ -1032,7 +1031,6 @@ static void add_sect_attrs(struct module *mod, unsigned int nsect, sattr->mattr.show = module_sect_show; sattr->mattr.store = NULL; sattr->mattr.attr.name = sattr->name; - sattr->mattr.attr.owner = mod; sattr->mattr.attr.mode = S_IRUGO; *(gattr++) = &(sattr++)->mattr.attr; } @@ -1090,7 +1088,6 @@ int module_add_modinfo_attrs(struct module *mod) if (!attr->test || (attr->test && attr->test(mod))) { memcpy(temp_attr, attr, sizeof(*temp_attr)); - temp_attr->attr.owner = mod; error = sysfs_create_file(&mod->mkobj.kobj,&temp_attr->attr); ++temp_attr; } diff --git a/kernel/params.c b/kernel/params.c index e61c46c97ce7..effbaaedd7f3 100644 --- a/kernel/params.c +++ b/kernel/params.c @@ -491,7 +491,6 @@ param_sysfs_setup(struct module_kobject *mk, pattr->mattr.show = param_attr_show; pattr->mattr.store = param_attr_store; pattr->mattr.attr.name = (char *)&kp->name[name_skip]; - pattr->mattr.attr.owner = mk->mod; pattr->mattr.attr.mode = kp->perm; *(gattr++) = &(pattr++)->mattr.attr; } diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index 33c6c4a7c689..31ace23a0914 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -383,8 +383,7 @@ static ssize_t brforward_read(struct kobject *kobj, char *buf, static struct bin_attribute bridge_forward = { .attr = { .name = SYSFS_BRIDGE_FDB, - .mode = S_IRUGO, - .owner = THIS_MODULE, }, + .mode = S_IRUGO, }, .read = brforward_read, }; diff --git a/net/bridge/br_sysfs_if.c b/net/bridge/br_sysfs_if.c index 2da22927d8dd..79db51fcb476 100644 --- a/net/bridge/br_sysfs_if.c +++ b/net/bridge/br_sysfs_if.c @@ -29,8 +29,7 @@ struct brport_attribute { #define BRPORT_ATTR(_name,_mode,_show,_store) \ struct brport_attribute brport_attr_##_name = { \ .attr = {.name = __stringify(_name), \ - .mode = _mode, \ - .owner = THIS_MODULE, }, \ + .mode = _mode }, \ .show = _show, \ .store = _store, \ }; From ad6a1e1c66009ba9dcd2f5c90ffa1fb4ce72fce0 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:17 +0900 Subject: [PATCH 40/61] driver-core: make devt_attr and uevent_attr static devt_attr and uevent_attr are either allocated dynamically with or embedded in device and class_device as they needed their owner field set to the module implementing the driver. Now that sysfs implements immediate disconnect and owner field removed from struct attribute, there is no reason to do this. Remove these attributes from [class_]device and use static attribute structures instead. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- drivers/base/class.c | 44 +++++++++++++++-------------------------- drivers/base/core.c | 45 ++++++++++++++---------------------------- include/linux/device.h | 5 ----- 3 files changed, 31 insertions(+), 63 deletions(-) diff --git a/drivers/base/class.c b/drivers/base/class.c index 9cbfde23b9e3..4d2222618b78 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c @@ -312,9 +312,6 @@ static void class_dev_release(struct kobject * kobj) pr_debug("device class '%s': release.\n", cd->class_id); - kfree(cd->devt_attr); - cd->devt_attr = NULL; - if (cd->release) cd->release(cd); else if (cls->release) @@ -547,6 +544,9 @@ static ssize_t show_dev(struct class_device *class_dev, char *buf) return print_dev_t(buf, class_dev->devt); } +static struct class_device_attribute class_devt_attr = + __ATTR(dev, S_IRUGO, show_dev, NULL); + static ssize_t store_uevent(struct class_device *class_dev, const char *buf, size_t count) { @@ -554,6 +554,9 @@ static ssize_t store_uevent(struct class_device *class_dev, return count; } +static struct class_device_attribute class_uevent_attr = + __ATTR(uevent, S_IWUSR, NULL, store_uevent); + void class_device_initialize(struct class_device *class_dev) { kobj_set_kset_s(class_dev, class_obj_subsys); @@ -603,30 +606,15 @@ int class_device_add(struct class_device *class_dev) &parent_class->subsys.kobj, "subsystem"); if (error) goto out3; - class_dev->uevent_attr.attr.name = "uevent"; - class_dev->uevent_attr.attr.mode = S_IWUSR; - class_dev->uevent_attr.store = store_uevent; - error = class_device_create_file(class_dev, &class_dev->uevent_attr); + + error = class_device_create_file(class_dev, &class_uevent_attr); if (error) goto out3; if (MAJOR(class_dev->devt)) { - struct class_device_attribute *attr; - attr = kzalloc(sizeof(*attr), GFP_KERNEL); - if (!attr) { - error = -ENOMEM; + error = class_device_create_file(class_dev, &class_devt_attr); + if (error) goto out4; - } - attr->attr.name = "dev"; - attr->attr.mode = S_IRUGO; - attr->show = show_dev; - error = class_device_create_file(class_dev, attr); - if (error) { - kfree(attr); - goto out4; - } - - class_dev->devt_attr = attr; } error = class_device_add_attrs(class_dev); @@ -669,10 +657,10 @@ int class_device_add(struct class_device *class_dev) out6: class_device_remove_attrs(class_dev); out5: - if (class_dev->devt_attr) - class_device_remove_file(class_dev, class_dev->devt_attr); + if (MAJOR(class_dev->devt)) + class_device_remove_file(class_dev, &class_devt_attr); out4: - class_device_remove_file(class_dev, &class_dev->uevent_attr); + class_device_remove_file(class_dev, &class_uevent_attr); out3: kobject_del(&class_dev->kobj); out2: @@ -772,9 +760,9 @@ void class_device_del(struct class_device *class_dev) sysfs_remove_link(&class_dev->kobj, "device"); } sysfs_remove_link(&class_dev->kobj, "subsystem"); - class_device_remove_file(class_dev, &class_dev->uevent_attr); - if (class_dev->devt_attr) - class_device_remove_file(class_dev, class_dev->devt_attr); + class_device_remove_file(class_dev, &class_uevent_attr); + if (MAJOR(class_dev->devt)) + class_device_remove_file(class_dev, &class_devt_attr); class_device_remove_attrs(class_dev); class_device_remove_groups(class_dev); diff --git a/drivers/base/core.c b/drivers/base/core.c index e3fb87bfc6e1..0455aa78fa13 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -310,6 +310,9 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr, return count; } +static struct device_attribute uevent_attr = + __ATTR(uevent, S_IRUGO | S_IWUSR, show_uevent, store_uevent); + static int device_add_attributes(struct device *dev, struct device_attribute *attrs) { @@ -423,6 +426,9 @@ static ssize_t show_dev(struct device *dev, struct device_attribute *attr, return print_dev_t(buf, dev->devt); } +static struct device_attribute devt_attr = + __ATTR(dev, S_IRUGO, show_dev, NULL); + /* * devices_subsys - structure to be registered with kobject core. */ @@ -681,31 +687,14 @@ int device_add(struct device *dev) blocking_notifier_call_chain(&dev->bus->bus_notifier, BUS_NOTIFY_ADD_DEVICE, dev); - dev->uevent_attr.attr.name = "uevent"; - dev->uevent_attr.attr.mode = S_IRUGO | S_IWUSR; - dev->uevent_attr.store = store_uevent; - dev->uevent_attr.show = show_uevent; - error = device_create_file(dev, &dev->uevent_attr); + error = device_create_file(dev, &uevent_attr); if (error) goto attrError; if (MAJOR(dev->devt)) { - struct device_attribute *attr; - attr = kzalloc(sizeof(*attr), GFP_KERNEL); - if (!attr) { - error = -ENOMEM; + error = device_create_file(dev, &devt_attr); + if (error) goto ueventattrError; - } - attr->attr.name = "dev"; - attr->attr.mode = S_IRUGO; - attr->show = show_dev; - error = device_create_file(dev, attr); - if (error) { - kfree(attr); - goto ueventattrError; - } - - dev->devt_attr = attr; } if (dev->class) { @@ -766,10 +755,8 @@ int device_add(struct device *dev) BUS_NOTIFY_DEL_DEVICE, dev); device_remove_attrs(dev); AttrsError: - if (dev->devt_attr) { - device_remove_file(dev, dev->devt_attr); - kfree(dev->devt_attr); - } + if (MAJOR(dev->devt)) + device_remove_file(dev, &devt_attr); if (dev->class) { sysfs_remove_link(&dev->kobj, "subsystem"); @@ -791,7 +778,7 @@ int device_add(struct device *dev) } } ueventattrError: - device_remove_file(dev, &dev->uevent_attr); + device_remove_file(dev, &uevent_attr); attrError: kobject_uevent(&dev->kobj, KOBJ_REMOVE); kobject_del(&dev->kobj); @@ -868,10 +855,8 @@ void device_del(struct device * dev) if (parent) klist_del(&dev->knode_parent); - if (dev->devt_attr) { - device_remove_file(dev, dev->devt_attr); - kfree(dev->devt_attr); - } + if (MAJOR(dev->devt)) + device_remove_file(dev, &devt_attr); if (dev->class) { sysfs_remove_link(&dev->kobj, "subsystem"); /* If this is not a "fake" compatible device, remove the @@ -925,7 +910,7 @@ void device_del(struct device * dev) up(&dev->class->sem); } } - device_remove_file(dev, &dev->uevent_attr); + device_remove_file(dev, &uevent_attr); device_remove_attrs(dev); bus_remove_device(dev); diff --git a/include/linux/device.h b/include/linux/device.h index 2e1a2988b7e1..be2debed70d2 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -238,7 +238,6 @@ extern int __must_check class_device_create_file(struct class_device *, * @devt: for internal use by the driver core only. * @node: for internal use by the driver core only. * @kobj: for internal use by the driver core only. - * @devt_attr: for internal use by the driver core only. * @groups: optional additional groups to be created * @dev: if set, a symlink to the struct device is created in the sysfs * directory for this struct class device. @@ -263,8 +262,6 @@ struct class_device { struct kobject kobj; struct class * class; /* required */ dev_t devt; /* dev_t, creates the sysfs "dev" */ - struct class_device_attribute *devt_attr; - struct class_device_attribute uevent_attr; struct device * dev; /* not necessary, but nice to have */ void * class_data; /* class-specific data */ struct class_device *parent; /* parent of this child device, if there is one */ @@ -419,8 +416,6 @@ struct device { struct device_type *type; unsigned is_registered:1; unsigned uevent_suppress:1; - struct device_attribute uevent_attr; - struct device_attribute *devt_attr; struct semaphore sem; /* semaphore to synchronize calls to * its driver. From 42b37df6abb42ae021e15bf865b43f3629c7f3ab Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:17 +0900 Subject: [PATCH 41/61] sysfs: make sysfs_alloc_ino() static sysfs_alloc_ino() isn't used out side of fs/sysfs/dir.c. Make it static. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index bc11a263aa53..a63d12e4be56 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -20,7 +20,7 @@ spinlock_t kobj_sysfs_assoc_lock = SPIN_LOCK_UNLOCKED; static spinlock_t sysfs_ino_lock = SPIN_LOCK_UNLOCKED; static DEFINE_IDA(sysfs_ino_ida); -int sysfs_alloc_ino(ino_t *pino) +static int sysfs_alloc_ino(ino_t *pino) { int ino, rc; From 7f7cfffe60ed6271c4028ec79ae1c297b44bcb14 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:17 +0900 Subject: [PATCH 42/61] sysfs: fix parent refcounting during rename and move Parent reference wasn't properly transferred during rename and move. Fix it. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index a63d12e4be56..a26e3db89432 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -497,6 +497,9 @@ int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, d_move(kobj->dentry, new_dentry); list_del_init(&sd->s_sibling); + sysfs_get(parent_sd); + sysfs_put(sd->s_parent); + sd->s_parent = parent_sd; list_add(&sd->s_sibling, &parent_sd->s_children); error = 0; @@ -550,6 +553,9 @@ again: /* Remove from old parent's list and insert into new parent's list. */ list_del_init(&sd->s_sibling); + sysfs_get(new_parent_sd); + sysfs_put(sd->s_parent); + sd->s_parent = new_parent_sd; list_add(&sd->s_sibling, &new_parent_sd->s_children); out: From fc9f54b9982e14e6dbe023425c87ffbfd6992c45 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:17 +0900 Subject: [PATCH 43/61] sysfs: reorganize sysfs_new_indoe() and sysfs_create() Reorganize/clean up sysfs_new_inode() and sysfs_create(). * sysfs_init_inode() is separated out from sysfs_new_inode() and is responsible for basic initialization. * sysfs_instantiate() replaces the last step of sysfs_create() and is responsible for dentry instantitaion. * type-specific initialization is moved out to the callers. * mode is specified only once when creating a sysfs_dirent. * spurious list_del_init(&sd->s_sibling) dropped from create_dir() This change is to * prepare for inode allocation fix. * separate alloc and init code for synchronization update. * make dentry/inode initialization more flexible for later changes. This patch doesn't introduce visible behavior change. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 130 +++++++++++++++++------------------------------ fs/sysfs/inode.c | 104 +++++++++++++++++++------------------ fs/sysfs/mount.c | 18 ++++--- fs/sysfs/sysfs.h | 6 +-- 4 files changed, 116 insertions(+), 142 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index a26e3db89432..bbf3525fd222 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -191,39 +191,18 @@ int sysfs_dirent_exist(struct sysfs_dirent *parent_sd, return 0; } -static int init_dir(struct inode * inode) -{ - inode->i_op = &sysfs_dir_inode_operations; - inode->i_fop = &sysfs_dir_operations; - - /* directory inodes start off with i_nlink == 2 (for "." entry) */ - inc_nlink(inode); - return 0; -} - -static int init_file(struct inode * inode) -{ - inode->i_size = PAGE_SIZE; - inode->i_fop = &sysfs_file_operations; - return 0; -} - -static int init_symlink(struct inode * inode) -{ - inode->i_op = &sysfs_symlink_inode_operations; - return 0; -} - static int create_dir(struct kobject *kobj, struct dentry *parent, const char *name, struct dentry **p_dentry) { int error; umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; struct dentry *dentry; + struct inode *inode; struct sysfs_dirent *sd; mutex_lock(&parent->d_inode->i_mutex); + /* allocate */ dentry = lookup_one_len(name, parent, strlen(name)); if (IS_ERR(dentry)) { error = PTR_ERR(dentry); @@ -231,7 +210,7 @@ static int create_dir(struct kobject *kobj, struct dentry *parent, } error = -EEXIST; - if (sysfs_dirent_exist(parent->d_fsdata, name)) + if (dentry->d_inode) goto out_dput; error = -ENOMEM; @@ -240,19 +219,31 @@ static int create_dir(struct kobject *kobj, struct dentry *parent, goto out_drop; sd->s_elem.dir.kobj = kobj; - error = sysfs_create(sd, dentry, mode, init_dir); - if (error) + inode = sysfs_new_inode(sd); + if (!inode) goto out_sput; + inode->i_op = &sysfs_dir_inode_operations; + inode->i_fop = &sysfs_dir_operations; + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inc_nlink(inode); + + /* link in */ + error = -EEXIST; + if (sysfs_dirent_exist(parent->d_fsdata, name)) + goto out_iput; + + sysfs_instantiate(dentry, inode); inc_nlink(parent->d_inode); sysfs_attach_dirent(sd, parent->d_fsdata, dentry); *p_dentry = dentry; error = 0; - goto out_dput; + goto out_unlock; /* pin directory dentry in core */ + out_iput: + iput(inode); out_sput: - list_del_init(&sd->s_sibling); sysfs_put(sd); out_drop: d_drop(dentry); @@ -298,71 +289,46 @@ int sysfs_create_dir(struct kobject * kobj, struct dentry *shadow_parent) return error; } -/* attaches attribute's sysfs_dirent to the dentry corresponding to the - * attribute file - */ -static int sysfs_attach_attr(struct sysfs_dirent * sd, struct dentry * dentry) -{ - struct attribute * attr = NULL; - struct bin_attribute * bin_attr = NULL; - int (* init) (struct inode *) = NULL; - int error = 0; - - if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) { - bin_attr = sd->s_elem.bin_attr.bin_attr; - attr = &bin_attr->attr; - } else { - attr = sd->s_elem.attr.attr; - init = init_file; - } - - error = sysfs_create(sd, dentry, - (attr->mode & S_IALLUGO) | S_IFREG, init); - if (error) - return error; - - if (bin_attr) { - dentry->d_inode->i_size = bin_attr->size; - dentry->d_inode->i_fop = &bin_fops; - } - - sysfs_attach_dentry(sd, dentry); - - return 0; -} - -static int sysfs_attach_link(struct sysfs_dirent * sd, struct dentry * dentry) -{ - int err; - - err = sysfs_create(sd, dentry, S_IFLNK|S_IRWXUGO, init_symlink); - if (!err) - sysfs_attach_dentry(sd, dentry); - - return err; -} - static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { struct sysfs_dirent * parent_sd = dentry->d_parent->d_fsdata; struct sysfs_dirent * sd; - int err = 0; + struct inode *inode; + int found = 0; list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { - if (sd->s_type & SYSFS_NOT_PINNED) { - if (strcmp(sd->s_name, dentry->d_name.name)) - continue; - - if (sd->s_type & SYSFS_KOBJ_LINK) - err = sysfs_attach_link(sd, dentry); - else - err = sysfs_attach_attr(sd, dentry); + if ((sd->s_type & SYSFS_NOT_PINNED) && + !strcmp(sd->s_name, dentry->d_name.name)) { + found = 1; break; } } - return ERR_PTR(err); + /* no such entry */ + if (!found) + return NULL; + + /* attach dentry and inode */ + inode = sysfs_new_inode(sd); + if (!inode) + return ERR_PTR(-ENOMEM); + + /* initialize inode according to type */ + if (sd->s_type & SYSFS_KOBJ_ATTR) { + inode->i_size = PAGE_SIZE; + inode->i_fop = &sysfs_file_operations; + } else if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) { + struct bin_attribute *bin_attr = sd->s_elem.bin_attr.bin_attr; + inode->i_size = bin_attr->size; + inode->i_fop = &bin_fops; + } else if (sd->s_type & SYSFS_KOBJ_LINK) + inode->i_op = &sysfs_symlink_inode_operations; + + sysfs_instantiate(dentry, inode); + sysfs_attach_dentry(sd, dentry); + + return NULL; } const struct inode_operations sysfs_dir_inode_operations = { diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 6ad47c13b94d..26d8503c8997 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -133,62 +133,68 @@ static inline void set_inode_attr(struct inode * inode, struct iattr * iattr) */ static struct lock_class_key sysfs_inode_imutex_key; -struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent * sd) +void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) { - struct inode * inode = new_inode(sysfs_sb); - if (inode) { - inode->i_blocks = 0; - inode->i_mapping->a_ops = &sysfs_aops; - inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; - inode->i_op = &sysfs_inode_operations; - inode->i_ino = sd->s_ino; - lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key); + inode->i_blocks = 0; + inode->i_mapping->a_ops = &sysfs_aops; + inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; + inode->i_op = &sysfs_inode_operations; + inode->i_ino = sd->s_ino; + lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key); + + if (sd->s_iattr) { + /* sysfs_dirent has non-default attributes + * get them for the new inode from persistent copy + * in sysfs_dirent + */ + set_inode_attr(inode, sd->s_iattr); + } else + set_default_inode_attr(inode, sd->s_mode); +} + +/** + * sysfs_new_inode - allocate new inode for sysfs_dirent + * @sd: sysfs_dirent to allocate inode for + * + * Allocate inode for @sd and initialize basics. + * + * LOCKING: + * Kernel thread context (may sleep). + * + * RETURNS: + * Pointer to allocated inode on success, NULL on failure. + */ +struct inode * sysfs_new_inode(struct sysfs_dirent *sd) +{ + struct inode *inode; + + inode = new_inode(sysfs_sb); + if (inode) + sysfs_init_inode(sd, inode); - if (sd->s_iattr) { - /* sysfs_dirent has non-default attributes - * get them for the new inode from persistent copy - * in sysfs_dirent - */ - set_inode_attr(inode, sd->s_iattr); - } else - set_default_inode_attr(inode, mode); - } return inode; } -int sysfs_create(struct sysfs_dirent *sd, struct dentry *dentry, int mode, - int (*init)(struct inode *)) +/** + * sysfs_instantiate - instantiate dentry + * @dentry: dentry to be instantiated + * @inode: inode associated with @sd + * + * Instantiate @dentry with @inode. + * + * LOCKING: + * None. + */ +void sysfs_instantiate(struct dentry *dentry, struct inode *inode) { - int error = 0; - struct inode * inode = NULL; - if (dentry) { - if (!dentry->d_inode) { - if ((inode = sysfs_new_inode(mode, sd))) { - if (dentry->d_parent && dentry->d_parent->d_inode) { - struct inode *p_inode = dentry->d_parent->d_inode; - p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME; - } - goto Proceed; - } - else - error = -ENOMEM; - } else - error = -EEXIST; - } else - error = -ENOENT; - goto Done; + BUG_ON(!dentry || dentry->d_inode); - Proceed: - if (init) - error = init(inode); - if (!error) { - d_instantiate(dentry, inode); - if (S_ISDIR(mode)) - dget(dentry); /* pin only directory dentry in core */ - } else - iput(inode); - Done: - return error; + if (dentry->d_parent && dentry->d_parent->d_inode) { + struct inode *p_inode = dentry->d_parent->d_inode; + p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME; + } + + d_instantiate(dentry, inode); } /** diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 37ff9ffc55f0..6d3a6249d21c 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -29,6 +29,7 @@ static struct sysfs_dirent sysfs_root = { .s_sibling = LIST_HEAD_INIT(sysfs_root.s_sibling), .s_children = LIST_HEAD_INIT(sysfs_root.s_children), .s_type = SYSFS_ROOT, + .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, .s_iattr = NULL, .s_ino = 1, }; @@ -45,18 +46,19 @@ static int sysfs_fill_super(struct super_block *sb, void *data, int silent) sb->s_time_gran = 1; sysfs_sb = sb; - inode = sysfs_new_inode(S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, - &sysfs_root); - if (inode) { - inode->i_op = &sysfs_dir_inode_operations; - inode->i_fop = &sysfs_dir_operations; - /* directory inodes start off with i_nlink == 2 (for "." entry) */ - inc_nlink(inode); - } else { + inode = new_inode(sysfs_sb); + if (!inode) { pr_debug("sysfs: could not get root inode\n"); return -ENOMEM; } + sysfs_init_inode(&sysfs_root, inode); + + inode->i_op = &sysfs_dir_inode_operations; + inode->i_fop = &sysfs_dir_operations; + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inc_nlink(inode); + root = d_alloc_root(inode); if (!root) { pr_debug("%s: could not get root dentry!\n",__FUNCTION__); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index fc6aa863b947..143fdbe56c14 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -57,9 +57,9 @@ extern struct vfsmount * sysfs_mount; extern struct kmem_cache *sysfs_dir_cachep; extern void sysfs_delete_inode(struct inode *inode); -extern struct inode * sysfs_new_inode(mode_t mode, struct sysfs_dirent *); -extern int sysfs_create(struct sysfs_dirent *sd, struct dentry *dentry, - int mode, int (*init)(struct inode *)); +extern void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode); +extern struct inode * sysfs_new_inode(struct sysfs_dirent *sd); +extern void sysfs_instantiate(struct dentry *dentry, struct inode *inode); extern void release_sysfs_dirent(struct sysfs_dirent * sd); extern int sysfs_dirent_exist(struct sysfs_dirent *, const unsigned char *); From 8312a8d7c1d19d31027bd4ca127ce671962c23d4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:17 +0900 Subject: [PATCH 44/61] sysfs: use iget_locked() instead of new_inode() After dentry is reclaimed, sysfs always used to allocate new dentry and inode if the file is accessed again. This causes problem with operations which only pin the inode. For example, if inotify watch is added to a sysfs file and the dentry for the file is reclaimed, the next update event creates new dentry and new inode making the inotify watch miss all the events from there on. This patch fixes it by using iget_locked() instead of new_inode(). sysfs_new_inode() is renamed to sysfs_get_inode() and inode is initialized iff the inode is newly allocated. sysfs_instantiate() is responsible for unlocking new inodes. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 37 +++++++++++++++++++++---------------- fs/sysfs/inode.c | 24 +++++++++++++++--------- fs/sysfs/sysfs.h | 2 +- 3 files changed, 37 insertions(+), 26 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index bbf3525fd222..06dff2c30c9b 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -219,14 +219,16 @@ static int create_dir(struct kobject *kobj, struct dentry *parent, goto out_drop; sd->s_elem.dir.kobj = kobj; - inode = sysfs_new_inode(sd); + inode = sysfs_get_inode(sd); if (!inode) goto out_sput; - inode->i_op = &sysfs_dir_inode_operations; - inode->i_fop = &sysfs_dir_operations; - /* directory inodes start off with i_nlink == 2 (for "." entry) */ - inc_nlink(inode); + if (inode->i_state & I_NEW) { + inode->i_op = &sysfs_dir_inode_operations; + inode->i_fop = &sysfs_dir_operations; + /* directory inodes start off with i_nlink == 2 (for ".") */ + inc_nlink(inode); + } /* link in */ error = -EEXIST; @@ -310,20 +312,23 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, return NULL; /* attach dentry and inode */ - inode = sysfs_new_inode(sd); + inode = sysfs_get_inode(sd); if (!inode) return ERR_PTR(-ENOMEM); - /* initialize inode according to type */ - if (sd->s_type & SYSFS_KOBJ_ATTR) { - inode->i_size = PAGE_SIZE; - inode->i_fop = &sysfs_file_operations; - } else if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) { - struct bin_attribute *bin_attr = sd->s_elem.bin_attr.bin_attr; - inode->i_size = bin_attr->size; - inode->i_fop = &bin_fops; - } else if (sd->s_type & SYSFS_KOBJ_LINK) - inode->i_op = &sysfs_symlink_inode_operations; + if (inode->i_state & I_NEW) { + /* initialize inode according to type */ + if (sd->s_type & SYSFS_KOBJ_ATTR) { + inode->i_size = PAGE_SIZE; + inode->i_fop = &sysfs_file_operations; + } else if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) { + struct bin_attribute *bin_attr = + sd->s_elem.bin_attr.bin_attr; + inode->i_size = bin_attr->size; + inode->i_fop = &bin_fops; + } else if (sd->s_type & SYSFS_KOBJ_LINK) + inode->i_op = &sysfs_symlink_inode_operations; + } sysfs_instantiate(dentry, inode); sysfs_attach_dentry(sd, dentry); diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 26d8503c8997..3eab9c46a71b 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -153,10 +153,12 @@ void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) } /** - * sysfs_new_inode - allocate new inode for sysfs_dirent + * sysfs_get_inode - get inode for sysfs_dirent * @sd: sysfs_dirent to allocate inode for * - * Allocate inode for @sd and initialize basics. + * Get inode for @sd. If such inode doesn't exist, a new inode + * is allocated and basics are initialized. New inode is + * returned locked. * * LOCKING: * Kernel thread context (may sleep). @@ -164,12 +166,12 @@ void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) * RETURNS: * Pointer to allocated inode on success, NULL on failure. */ -struct inode * sysfs_new_inode(struct sysfs_dirent *sd) +struct inode * sysfs_get_inode(struct sysfs_dirent *sd) { struct inode *inode; - inode = new_inode(sysfs_sb); - if (inode) + inode = iget_locked(sysfs_sb, sd->s_ino); + if (inode && (inode->i_state & I_NEW)) sysfs_init_inode(sd, inode); return inode; @@ -180,7 +182,7 @@ struct inode * sysfs_new_inode(struct sysfs_dirent *sd) * @dentry: dentry to be instantiated * @inode: inode associated with @sd * - * Instantiate @dentry with @inode. + * Unlock @inode if locked and instantiate @dentry with @inode. * * LOCKING: * None. @@ -189,9 +191,13 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode) { BUG_ON(!dentry || dentry->d_inode); - if (dentry->d_parent && dentry->d_parent->d_inode) { - struct inode *p_inode = dentry->d_parent->d_inode; - p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME; + if (inode->i_state & I_NEW) { + unlock_new_inode(inode); + + if (dentry->d_parent && dentry->d_parent->d_inode) { + struct inode *p_inode = dentry->d_parent->d_inode; + p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME; + } } d_instantiate(dentry, inode); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 143fdbe56c14..627bf3940dfa 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -58,7 +58,7 @@ extern struct kmem_cache *sysfs_dir_cachep; extern void sysfs_delete_inode(struct inode *inode); extern void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode); -extern struct inode * sysfs_new_inode(struct sysfs_dirent *sd); +extern struct inode * sysfs_get_inode(struct sysfs_dirent *sd); extern void sysfs_instantiate(struct dentry *dentry, struct inode *inode); extern void release_sysfs_dirent(struct sysfs_dirent * sd); From 0b8ead82f5d9d8f08c0d1236f2e350b70a977753 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:18 +0900 Subject: [PATCH 45/61] sysfs: fix root sysfs_dirent -> root dentry association The root sysfs_dirent didn't point to the root dentry fix it. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/mount.c | 1 + 1 file changed, 1 insertion(+) diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 6d3a6249d21c..d0e160307c5d 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -65,6 +65,7 @@ static int sysfs_fill_super(struct super_block *sb, void *data, int silent) iput(inode); return -ENOMEM; } + sysfs_root.s_dentry = root; root->d_fsdata = &sysfs_root; sb->s_root = root; return 0; From b6b4a4399c2a83d1af77c99dee0d0b5cc15ec268 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:18 +0900 Subject: [PATCH 46/61] sysfs: move s_active functions to fs/sysfs/dir.c These functions are about to receive more complexity and doesn't really need to be inlined in the first place. Move them from fs/sysfs/sysfs.h to fs/sysfs/dir.c. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 88 +++++++++++++++++++++++++++++++++++++++++++++ fs/sysfs/sysfs.h | 94 ++++-------------------------------------------- 2 files changed, 94 insertions(+), 88 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 06dff2c30c9b..f5f0b936f181 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -20,6 +20,94 @@ spinlock_t kobj_sysfs_assoc_lock = SPIN_LOCK_UNLOCKED; static spinlock_t sysfs_ino_lock = SPIN_LOCK_UNLOCKED; static DEFINE_IDA(sysfs_ino_ida); +/** + * sysfs_get_active - get an active reference to sysfs_dirent + * @sd: sysfs_dirent to get an active reference to + * + * Get an active reference of @sd. This function is noop if @sd + * is NULL. + * + * RETURNS: + * Pointer to @sd on success, NULL on failure. + */ +struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) +{ + if (sd) { + if (unlikely(!down_read_trylock(&sd->s_active))) + sd = NULL; + } + return sd; +} + +/** + * sysfs_put_active - put an active reference to sysfs_dirent + * @sd: sysfs_dirent to put an active reference to + * + * Put an active reference to @sd. This function is noop if @sd + * is NULL. + */ +void sysfs_put_active(struct sysfs_dirent *sd) +{ + if (sd) + up_read(&sd->s_active); +} + +/** + * sysfs_get_active_two - get active references to sysfs_dirent and parent + * @sd: sysfs_dirent of interest + * + * Get active reference to @sd and its parent. Parent's active + * reference is grabbed first. This function is noop if @sd is + * NULL. + * + * RETURNS: + * Pointer to @sd on success, NULL on failure. + */ +struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd) +{ + if (sd) { + if (sd->s_parent && unlikely(!sysfs_get_active(sd->s_parent))) + return NULL; + if (unlikely(!sysfs_get_active(sd))) { + sysfs_put_active(sd->s_parent); + return NULL; + } + } + return sd; +} + +/** + * sysfs_put_active_two - put active references to sysfs_dirent and parent + * @sd: sysfs_dirent of interest + * + * Put active references to @sd and its parent. This function is + * noop if @sd is NULL. + */ +void sysfs_put_active_two(struct sysfs_dirent *sd) +{ + if (sd) { + sysfs_put_active(sd); + sysfs_put_active(sd->s_parent); + } +} + +/** + * sysfs_deactivate - deactivate sysfs_dirent + * @sd: sysfs_dirent to deactivate + * + * Deny new active references and drain existing ones. s_active + * will be unlocked when the sysfs_dirent is released. + */ +void sysfs_deactivate(struct sysfs_dirent *sd) +{ + down_write_nested(&sd->s_active, SYSFS_S_ACTIVE_DEACTIVATE); + + /* s_active will be unlocked by the thread doing the final put + * on @sd. Lie to lockdep. + */ + rwsem_release(&sd->s_active.dep_map, 1, _RET_IP_); +} + static int sysfs_alloc_ino(ino_t *pino) { int ino, rc; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 627bf3940dfa..f8779eaa53ff 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -56,6 +56,12 @@ enum sysfs_s_active_class extern struct vfsmount * sysfs_mount; extern struct kmem_cache *sysfs_dir_cachep; +extern struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd); +extern void sysfs_put_active(struct sysfs_dirent *sd); +extern struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd); +extern void sysfs_put_active_two(struct sysfs_dirent *sd); +extern void sysfs_deactivate(struct sysfs_dirent *sd); + extern void sysfs_delete_inode(struct inode *inode); extern void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode); extern struct inode * sysfs_get_inode(struct sysfs_dirent *sd); @@ -104,94 +110,6 @@ static inline void sysfs_put(struct sysfs_dirent * sd) release_sysfs_dirent(sd); } -/** - * sysfs_get_active - get an active reference to sysfs_dirent - * @sd: sysfs_dirent to get an active reference to - * - * Get an active reference of @sd. This function is noop if @sd - * is NULL. - * - * RETURNS: - * Pointer to @sd on success, NULL on failure. - */ -static inline struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) -{ - if (sd) { - if (unlikely(!down_read_trylock(&sd->s_active))) - sd = NULL; - } - return sd; -} - -/** - * sysfs_put_active - put an active reference to sysfs_dirent - * @sd: sysfs_dirent to put an active reference to - * - * Put an active reference to @sd. This function is noop if @sd - * is NULL. - */ -static inline void sysfs_put_active(struct sysfs_dirent *sd) -{ - if (sd) - up_read(&sd->s_active); -} - -/** - * sysfs_get_active_two - get active references to sysfs_dirent and parent - * @sd: sysfs_dirent of interest - * - * Get active reference to @sd and its parent. Parent's active - * reference is grabbed first. This function is noop if @sd is - * NULL. - * - * RETURNS: - * Pointer to @sd on success, NULL on failure. - */ -static inline struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd) -{ - if (sd) { - if (sd->s_parent && unlikely(!sysfs_get_active(sd->s_parent))) - return NULL; - if (unlikely(!sysfs_get_active(sd))) { - sysfs_put_active(sd->s_parent); - return NULL; - } - } - return sd; -} - -/** - * sysfs_put_active_two - put active references to sysfs_dirent and parent - * @sd: sysfs_dirent of interest - * - * Put active references to @sd and its parent. This function is - * noop if @sd is NULL. - */ -static inline void sysfs_put_active_two(struct sysfs_dirent *sd) -{ - if (sd) { - sysfs_put_active(sd); - sysfs_put_active(sd->s_parent); - } -} - -/** - * sysfs_deactivate - deactivate sysfs_dirent - * @sd: sysfs_dirent to deactivate - * - * Deny new active references and drain existing ones. s_active - * will be unlocked when the sysfs_dirent is released. - */ -static inline void sysfs_deactivate(struct sysfs_dirent *sd) -{ - down_write_nested(&sd->s_active, SYSFS_S_ACTIVE_DEACTIVATE); - - /* s_active will be unlocked by the thread doing the final put - * on @sd. Lie to lockdep. - */ - rwsem_release(&sd->s_active.dep_map, 1, _RET_IP_); -} - static inline int sysfs_is_shadowed_inode(struct inode *inode) { return S_ISDIR(inode->i_mode) && inode->i_op->follow_link; From 8619f979898397582e366877fd5feeba7560d70c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:18 +0900 Subject: [PATCH 47/61] sysfs: slim down sysfs_dirent->s_active Make sysfs_dirent->s_active an atomic_t instead of rwsem. This reduces the size of sysfs_dirent from 136 to 104 on 64bit and from 76 to 60 on 32bit with lock debugging turned off. With lock debugging turned on the reduction is much larger. s_active starts at zero and each active reference increments s_active. Putting a reference decrements s_active. Deactivation subtracts SD_DEACTIVATED_BIAS which is currently INT_MIN and assumed to be small enough to make s_active negative. If s_active is negative, sysfs_get() no longer grants new references. Deactivation succeeds immediately if there is no active user; otherwise, it waits using a completion for the last put. Due to the removal of lockdep tricks, this change makes things less trickier in release_sysfs_dirent(). As all the complexity is contained in three s_active functions, I think it's more readable this way. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 74 +++++++++++++++++++++++++++++++----------------- fs/sysfs/sysfs.h | 13 ++------- 2 files changed, 50 insertions(+), 37 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index f5f0b936f181..40596a0eee52 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include "sysfs.h" @@ -32,11 +33,24 @@ static DEFINE_IDA(sysfs_ino_ida); */ struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) { - if (sd) { - if (unlikely(!down_read_trylock(&sd->s_active))) - sd = NULL; + if (unlikely(!sd)) + return NULL; + + while (1) { + int v, t; + + v = atomic_read(&sd->s_active); + if (unlikely(v < 0)) + return NULL; + + t = atomic_cmpxchg(&sd->s_active, v, v + 1); + if (likely(t == v)) + return sd; + if (t < 0) + return NULL; + + cpu_relax(); } - return sd; } /** @@ -48,8 +62,21 @@ struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) */ void sysfs_put_active(struct sysfs_dirent *sd) { - if (sd) - up_read(&sd->s_active); + struct completion *cmpl; + int v; + + if (unlikely(!sd)) + return; + + v = atomic_dec_return(&sd->s_active); + if (likely(v != SD_DEACTIVATED_BIAS)) + return; + + /* atomic_dec_return() is a mb(), we'll always see the updated + * sd->s_sibling.next. + */ + cmpl = (void *)sd->s_sibling.next; + complete(cmpl); } /** @@ -95,17 +122,25 @@ void sysfs_put_active_two(struct sysfs_dirent *sd) * sysfs_deactivate - deactivate sysfs_dirent * @sd: sysfs_dirent to deactivate * - * Deny new active references and drain existing ones. s_active - * will be unlocked when the sysfs_dirent is released. + * Deny new active references and drain existing ones. */ void sysfs_deactivate(struct sysfs_dirent *sd) { - down_write_nested(&sd->s_active, SYSFS_S_ACTIVE_DEACTIVATE); + DECLARE_COMPLETION_ONSTACK(wait); + int v; - /* s_active will be unlocked by the thread doing the final put - * on @sd. Lie to lockdep. + BUG_ON(!list_empty(&sd->s_sibling)); + sd->s_sibling.next = (void *)&wait; + + /* atomic_add_return() is a mb(), put_active() will always see + * the updated sd->s_sibling.next. */ - rwsem_release(&sd->s_active.dep_map, 1, _RET_IP_); + v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active); + + if (v != SD_DEACTIVATED_BIAS) + wait_for_completion(&wait); + + INIT_LIST_HEAD(&sd->s_sibling); } static int sysfs_alloc_ino(ino_t *pino) @@ -141,19 +176,6 @@ void release_sysfs_dirent(struct sysfs_dirent * sd) repeat: parent_sd = sd->s_parent; - /* If @sd is being released after deletion, s_active is write - * locked. If @sd is cursor for directory walk or being - * released prematurely, s_active has no reader or writer. - * - * sysfs_deactivate() lies to lockdep that s_active is - * unlocked immediately. Lie one more time to cover the - * previous lie. - */ - if (!down_write_trylock(&sd->s_active)) - rwsem_acquire(&sd->s_active.dep_map, - SYSFS_S_ACTIVE_DEACTIVATE, 0, _RET_IP_); - up_write(&sd->s_active); - if (sd->s_type & SYSFS_KOBJ_LINK) sysfs_put(sd->s_elem.symlink.target_sd); if (sd->s_type & SYSFS_COPY_NAME) @@ -213,8 +235,8 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) goto err_out; atomic_set(&sd->s_count, 1); + atomic_set(&sd->s_active, 0); atomic_set(&sd->s_event, 1); - init_rwsem(&sd->s_active); INIT_LIST_HEAD(&sd->s_children); INIT_LIST_HEAD(&sd->s_sibling); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index f8779eaa53ff..ae006b070bf0 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -21,7 +21,7 @@ struct sysfs_elem_bin_attr { */ struct sysfs_dirent { atomic_t s_count; - struct rw_semaphore s_active; + atomic_t s_active; struct sysfs_dirent * s_parent; struct list_head s_sibling; struct list_head s_children; @@ -42,16 +42,7 @@ struct sysfs_dirent { atomic_t s_event; }; -/* - * A sysfs file which deletes another file when written to need to - * write lock the s_active of the victim while its s_active is read - * locked for the write operation. Tell lockdep that this is okay. - */ -enum sysfs_s_active_class -{ - SYSFS_S_ACTIVE_NORMAL, /* file r/w access, etc - default */ - SYSFS_S_ACTIVE_DEACTIVATE, /* file deactivation */ -}; +#define SD_DEACTIVATED_BIAS INT_MIN extern struct vfsmount * sysfs_mount; extern struct kmem_cache *sysfs_dir_cachep; From 0c73f18b7d95de8a007039337063a770b5fc8e7a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 03:45:18 +0900 Subject: [PATCH 48/61] sysfs: use singly-linked list for sysfs_dirent tree Make sysfs_dirent use singly linked list for its tree structure. sysfs_link_sibling() and sysfs_unlink_sibling() functions are added to handle simpler cases. It adds some complexity and cpu cycle overhead but reduced memory footprint is worthwhile on big machines. This change reduces the sizeof sysfs_dirent from 104 to 88 on 64bit and from 60 to 52 on 32bit. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 148 +++++++++++++++++++++++++++++++++-------------- fs/sysfs/inode.c | 12 ++-- fs/sysfs/mount.c | 3 - fs/sysfs/sysfs.h | 4 +- 4 files changed, 112 insertions(+), 55 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 40596a0eee52..b4074adbab01 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -21,6 +21,48 @@ spinlock_t kobj_sysfs_assoc_lock = SPIN_LOCK_UNLOCKED; static spinlock_t sysfs_ino_lock = SPIN_LOCK_UNLOCKED; static DEFINE_IDA(sysfs_ino_ida); +/** + * sysfs_link_sibling - link sysfs_dirent into sibling list + * @sd: sysfs_dirent of interest + * + * Link @sd into its sibling list which starts from + * sd->s_parent->s_children. + * + * Locking: + * mutex_lock(sd->s_parent->dentry->d_inode->i_mutex) + */ +static void sysfs_link_sibling(struct sysfs_dirent *sd) +{ + struct sysfs_dirent *parent_sd = sd->s_parent; + + BUG_ON(sd->s_sibling); + sd->s_sibling = parent_sd->s_children; + parent_sd->s_children = sd; +} + +/** + * sysfs_unlink_sibling - unlink sysfs_dirent from sibling list + * @sd: sysfs_dirent of interest + * + * Unlink @sd from its sibling list which starts from + * sd->s_parent->s_children. + * + * Locking: + * mutex_lock(sd->s_parent->dentry->d_inode->i_mutex) + */ +static void sysfs_unlink_sibling(struct sysfs_dirent *sd) +{ + struct sysfs_dirent **pos; + + for (pos = &sd->s_parent->s_children; *pos; pos = &(*pos)->s_sibling) { + if (*pos == sd) { + *pos = sd->s_sibling; + sd->s_sibling = NULL; + break; + } + } +} + /** * sysfs_get_active - get an active reference to sysfs_dirent * @sd: sysfs_dirent to get an active reference to @@ -73,9 +115,9 @@ void sysfs_put_active(struct sysfs_dirent *sd) return; /* atomic_dec_return() is a mb(), we'll always see the updated - * sd->s_sibling.next. + * sd->s_sibling. */ - cmpl = (void *)sd->s_sibling.next; + cmpl = (void *)sd->s_sibling; complete(cmpl); } @@ -129,18 +171,18 @@ void sysfs_deactivate(struct sysfs_dirent *sd) DECLARE_COMPLETION_ONSTACK(wait); int v; - BUG_ON(!list_empty(&sd->s_sibling)); - sd->s_sibling.next = (void *)&wait; + BUG_ON(sd->s_sibling); + sd->s_sibling = (void *)&wait; /* atomic_add_return() is a mb(), put_active() will always see - * the updated sd->s_sibling.next. + * the updated sd->s_sibling. */ v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active); if (v != SD_DEACTIVATED_BIAS) wait_for_completion(&wait); - INIT_LIST_HEAD(&sd->s_sibling); + sd->s_sibling = NULL; } static int sysfs_alloc_ino(ino_t *pino) @@ -237,8 +279,6 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) atomic_set(&sd->s_count, 1); atomic_set(&sd->s_active, 0); atomic_set(&sd->s_event, 1); - INIT_LIST_HEAD(&sd->s_children); - INIT_LIST_HEAD(&sd->s_sibling); sd->s_name = name; sd->s_mode = mode; @@ -273,7 +313,7 @@ void sysfs_attach_dirent(struct sysfs_dirent *sd, if (parent_sd) { sd->s_parent = sysfs_get(parent_sd); - list_add(&sd->s_sibling, &parent_sd->s_children); + sysfs_link_sibling(sd); } } @@ -289,7 +329,7 @@ int sysfs_dirent_exist(struct sysfs_dirent *parent_sd, { struct sysfs_dirent * sd; - list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { + for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) { if (sd->s_type) { if (strcmp(sd->s_name, new)) continue; @@ -409,7 +449,7 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, struct inode *inode; int found = 0; - list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { + for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) { if ((sd->s_type & SYSFS_NOT_PINNED) && !strcmp(sd->s_name, dentry->d_name.name)) { found = 1; @@ -458,7 +498,7 @@ static void remove_dir(struct dentry * d) mutex_lock(&parent->d_inode->i_mutex); - list_del_init(&sd->s_sibling); + sysfs_unlink_sibling(sd); pr_debug(" o %s removing done (%d)\n",d->d_name.name, atomic_read(&d->d_count)); @@ -478,9 +518,9 @@ void sysfs_remove_subdir(struct dentry * d) static void __sysfs_remove_dir(struct dentry *dentry) { - LIST_HEAD(removed); - struct sysfs_dirent * parent_sd; - struct sysfs_dirent * sd, * tmp; + struct sysfs_dirent *removed = NULL; + struct sysfs_dirent *parent_sd; + struct sysfs_dirent **pos; if (!dentry) return; @@ -488,15 +528,25 @@ static void __sysfs_remove_dir(struct dentry *dentry) pr_debug("sysfs %s: removing dir\n",dentry->d_name.name); mutex_lock(&dentry->d_inode->i_mutex); parent_sd = dentry->d_fsdata; - list_for_each_entry_safe(sd, tmp, &parent_sd->s_children, s_sibling) { - if (!sd->s_type || !(sd->s_type & SYSFS_NOT_PINNED)) - continue; - list_move(&sd->s_sibling, &removed); + pos = &parent_sd->s_children; + while (*pos) { + struct sysfs_dirent *sd = *pos; + + if (sd->s_type && (sd->s_type & SYSFS_NOT_PINNED)) { + *pos = sd->s_sibling; + sd->s_sibling = removed; + removed = sd; + } else + pos = &(*pos)->s_sibling; } mutex_unlock(&dentry->d_inode->i_mutex); - list_for_each_entry_safe(sd, tmp, &removed, s_sibling) { - list_del_init(&sd->s_sibling); + while (removed) { + struct sysfs_dirent *sd = removed; + + removed = sd->s_sibling; + sd->s_sibling = NULL; + sysfs_drop_dentry(sd); sysfs_deactivate(sd); sysfs_put(sd); @@ -577,11 +627,11 @@ int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, d_add(new_dentry, NULL); d_move(kobj->dentry, new_dentry); - list_del_init(&sd->s_sibling); + sysfs_unlink_sibling(sd); sysfs_get(parent_sd); sysfs_put(sd->s_parent); sd->s_parent = parent_sd; - list_add(&sd->s_sibling, &parent_sd->s_children); + sysfs_link_sibling(sd); error = 0; goto out_unlock; @@ -633,11 +683,11 @@ again: dput(new_dentry); /* Remove from old parent's list and insert into new parent's list. */ - list_del_init(&sd->s_sibling); + sysfs_unlink_sibling(sd); sysfs_get(new_parent_sd); sysfs_put(sd->s_parent); sd->s_parent = new_parent_sd; - list_add(&sd->s_sibling, &new_parent_sd->s_children); + sysfs_link_sibling(sd); out: mutex_unlock(&new_parent_dentry->d_inode->i_mutex); @@ -668,7 +718,7 @@ static int sysfs_dir_close(struct inode *inode, struct file *file) struct sysfs_dirent * cursor = file->private_data; mutex_lock(&dentry->d_inode->i_mutex); - list_del_init(&cursor->s_sibling); + sysfs_unlink_sibling(cursor); mutex_unlock(&dentry->d_inode->i_mutex); release_sysfs_dirent(cursor); @@ -687,7 +737,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) struct dentry *dentry = filp->f_path.dentry; struct sysfs_dirent * parent_sd = dentry->d_fsdata; struct sysfs_dirent *cursor = filp->private_data; - struct list_head *p, *q = &cursor->s_sibling; + struct sysfs_dirent **pos; ino_t ino; int i = filp->f_pos; @@ -710,16 +760,21 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) i++; /* fallthrough */ default: - if (filp->f_pos == 2) - list_move(q, &parent_sd->s_children); + pos = &parent_sd->s_children; + while (*pos != cursor) + pos = &(*pos)->s_sibling; - for (p=q->next; p!= &parent_sd->s_children; p=p->next) { - struct sysfs_dirent *next; + /* unlink cursor */ + *pos = cursor->s_sibling; + + if (filp->f_pos == 2) + pos = &parent_sd->s_children; + + for ( ; *pos; pos = &(*pos)->s_sibling) { + struct sysfs_dirent *next = *pos; const char * name; int len; - next = list_entry(p, struct sysfs_dirent, - s_sibling); if (!next->s_type) continue; @@ -729,12 +784,14 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) if (filldir(dirent, name, len, filp->f_pos, ino, dt_type(next)) < 0) - return 0; + break; - list_move(q, p); - p = q; filp->f_pos++; } + + /* put cursor back in */ + cursor->s_sibling = *pos; + *pos = cursor; } return 0; } @@ -759,20 +816,21 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) if (file->f_pos >= 2) { struct sysfs_dirent *sd = dentry->d_fsdata; struct sysfs_dirent *cursor = file->private_data; - struct list_head *p; + struct sysfs_dirent **pos; loff_t n = file->f_pos - 2; - list_del(&cursor->s_sibling); - p = sd->s_children.next; - while (n && p != &sd->s_children) { - struct sysfs_dirent *next; - next = list_entry(p, struct sysfs_dirent, - s_sibling); + sysfs_unlink_sibling(cursor); + + pos = &sd->s_children; + while (n && *pos) { + struct sysfs_dirent *next = *pos; if (next->s_type) n--; - p = p->next; + pos = &(*pos)->s_sibling; } - list_add_tail(&cursor->s_sibling, p); + + cursor->s_sibling = *pos; + *pos = cursor; } } mutex_unlock(&dentry->d_inode->i_mutex); diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 3eab9c46a71b..732fd7f371e0 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -284,8 +284,8 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) int sysfs_hash_and_remove(struct dentry * dir, const char * name) { - struct sysfs_dirent * sd; - struct sysfs_dirent * parent_sd; + struct sysfs_dirent **pos, *sd; + struct sysfs_dirent *parent_sd = dir->d_fsdata; int found = 0; if (!dir) @@ -295,13 +295,15 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) /* no inode means this hasn't been made visible yet */ return -ENOENT; - parent_sd = dir->d_fsdata; mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); - list_for_each_entry(sd, &parent_sd->s_children, s_sibling) { + for (pos = &parent_sd->s_children; *pos; pos = &(*pos)->s_sibling) { + sd = *pos; + if (!sd->s_type) continue; if (!strcmp(sd->s_name, name)) { - list_del_init(&sd->s_sibling); + *pos = sd->s_sibling; + sd->s_sibling = NULL; found = 1; break; } diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index d0e160307c5d..4be9593ea000 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -26,11 +26,8 @@ static const struct super_operations sysfs_ops = { static struct sysfs_dirent sysfs_root = { .s_count = ATOMIC_INIT(1), - .s_sibling = LIST_HEAD_INIT(sysfs_root.s_sibling), - .s_children = LIST_HEAD_INIT(sysfs_root.s_children), .s_type = SYSFS_ROOT, .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, - .s_iattr = NULL, .s_ino = 1, }; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index ae006b070bf0..6f8aaf3805d2 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -23,8 +23,8 @@ struct sysfs_dirent { atomic_t s_count; atomic_t s_active; struct sysfs_dirent * s_parent; - struct list_head s_sibling; - struct list_head s_children; + struct sysfs_dirent * s_sibling; + struct sysfs_dirent * s_children; const char * s_name; union { From 9d9307dabb3de8140fb3801bf6eb01f231dbd83d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 14 Jun 2007 03:45:18 +0900 Subject: [PATCH 49/61] sysfs: Fix oops in sysfs_drop_dentry on x86_64 Fix oops on x86_64 caused by the dereference of dir in sysfs_drop_dentry() made before checking if dir is not NULL (cf. http://marc.info/?l=linux-kernel&m=118151626704924&w=2). Signed-off-by: Rafael J. Wysocki Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 732fd7f371e0..ee31bf369a88 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -285,7 +285,7 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) int sysfs_hash_and_remove(struct dentry * dir, const char * name) { struct sysfs_dirent **pos, *sd; - struct sysfs_dirent *parent_sd = dir->d_fsdata; + struct sysfs_dirent *parent_sd; int found = 0; if (!dir) @@ -295,6 +295,7 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) /* no inode means this hasn't been made visible yet */ return -ENOENT; + parent_sd = dir->d_fsdata; mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); for (pos = &parent_sd->s_children; *pos; pos = &(*pos)->s_sibling) { sd = *pos; From d0bcb5689a521df98bff7549fcb8b17499660a99 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:21 +0900 Subject: [PATCH 50/61] sysfs: make sysfs_drop_dentry() access inodes using ilookup() sysfs_drop_dentry() used to go through sd->s_dentry and sd->s_parent->s_dentry to access the inodes. This is incorrect because inode can be cached without dentry. This patch makes sysfs_drop_dentry() access inodes using ilookup() on sd->s_ino. This is both correct and simpler. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/inode.c | 63 +++++++++++++++++++++--------------------------- 1 file changed, 28 insertions(+), 35 deletions(-) diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index ee31bf369a88..63daa06c4194 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -219,9 +219,9 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode) */ void sysfs_drop_dentry(struct sysfs_dirent *sd) { - struct dentry *dentry = NULL, *parent = NULL; - struct inode *dir; + struct dentry *dentry = NULL; struct timespec curtime; + struct inode *inode; /* We're not holding a reference to ->s_dentry dentry but the * field will stay valid as long as sysfs_lock is held. @@ -229,19 +229,9 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) spin_lock(&sysfs_lock); spin_lock(&dcache_lock); + /* drop dentry if it's there and dput() didn't kill it yet */ if (sd->s_dentry && sd->s_dentry->d_inode) { - /* get dentry if it's there and dput() didn't kill it yet */ dentry = dget_locked(sd->s_dentry); - parent = dentry->d_parent; - } else if (sd->s_parent->s_dentry->d_inode) { - /* We need to update the parent even if dentry for the - * victim itself doesn't exist. - */ - parent = dget_locked(sd->s_parent->s_dentry); - } - - /* drop */ - if (dentry) { spin_lock(&dentry->d_lock); __d_drop(dentry); spin_unlock(&dentry->d_lock); @@ -250,36 +240,39 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) spin_unlock(&dcache_lock); spin_unlock(&sysfs_lock); - /* nothing to do if the parent isn't in dcache */ - if (!parent) - return; + dput(dentry); + /* XXX: unpin if directory, this will go away soon */ + if (sd->s_type & SYSFS_DIR) + dput(dentry); /* adjust nlink and update timestamp */ - dir = parent->d_inode; - mutex_lock(&dir->i_mutex); - curtime = CURRENT_TIME; - dir->i_ctime = dir->i_mtime = curtime; + inode = ilookup(sysfs_sb, sd->s_ino); + if (inode) { + mutex_lock(&inode->i_mutex); - if (dentry) { - dentry->d_inode->i_ctime = curtime; - drop_nlink(dentry->d_inode); - if (sd->s_type & SYSFS_DIR) { - drop_nlink(dentry->d_inode); - drop_nlink(dir); - /* XXX: unpin if directory, this will go away soon */ - dput(dentry); - } + inode->i_ctime = curtime; + drop_nlink(inode); + if (sd->s_type & SYSFS_DIR) + drop_nlink(inode); + + mutex_unlock(&inode->i_mutex); + iput(inode); } - mutex_unlock(&dir->i_mutex); + /* adjust nlink and udpate timestamp of the parent */ + inode = ilookup(sysfs_sb, sd->s_parent->s_ino); + if (inode) { + mutex_lock(&inode->i_mutex); - /* bye bye */ - if (dentry) - dput(dentry); - else - dput(parent); + inode->i_ctime = inode->i_mtime = curtime; + if (sd->s_type & SYSFS_DIR) + drop_nlink(inode); + + mutex_unlock(&inode->i_mutex); + iput(inode); + } } int sysfs_hash_and_remove(struct dentry * dir, const char * name) From b402d72cf7b338a074e3c12b305ec79284e18845 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:21 +0900 Subject: [PATCH 51/61] sysfs: rename sysfs_dirent->s_type to s_flags and make room for flags Rename sysfs_dirent->s_type to s_flags, pack type into lower eight bits and reserve the rest for flags. sysfs_type() can used to access the type. All existing sd->s_type accesses are converted to use sysfs_type(). While at it, type test is changed to equality test instead of bit-and test where appropriate. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 33 ++++++++++++++++++++------------- fs/sysfs/inode.c | 8 ++++---- fs/sysfs/mount.c | 2 +- fs/sysfs/sysfs.h | 7 ++++++- include/linux/sysfs.h | 3 +++ 5 files changed, 34 insertions(+), 19 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index b4074adbab01..eb9bc0a8717b 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -218,9 +218,9 @@ void release_sysfs_dirent(struct sysfs_dirent * sd) repeat: parent_sd = sd->s_parent; - if (sd->s_type & SYSFS_KOBJ_LINK) + if (sysfs_type(sd) == SYSFS_KOBJ_LINK) sysfs_put(sd->s_elem.symlink.target_sd); - if (sd->s_type & SYSFS_COPY_NAME) + if (sysfs_type(sd) & SYSFS_COPY_NAME) kfree(sd->s_name); kfree(sd->s_iattr); sysfs_free_ino(sd->s_ino); @@ -282,7 +282,7 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) sd->s_name = name; sd->s_mode = mode; - sd->s_type = type; + sd->s_flags = type; return sd; @@ -330,7 +330,7 @@ int sysfs_dirent_exist(struct sysfs_dirent *parent_sd, struct sysfs_dirent * sd; for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) { - if (sd->s_type) { + if (sysfs_type(sd)) { if (strcmp(sd->s_name, new)) continue; else @@ -446,11 +446,12 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, { struct sysfs_dirent * parent_sd = dentry->d_parent->d_fsdata; struct sysfs_dirent * sd; + struct bin_attribute *bin_attr; struct inode *inode; int found = 0; for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) { - if ((sd->s_type & SYSFS_NOT_PINNED) && + if ((sysfs_type(sd) & SYSFS_NOT_PINNED) && !strcmp(sd->s_name, dentry->d_name.name)) { found = 1; break; @@ -468,16 +469,22 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, if (inode->i_state & I_NEW) { /* initialize inode according to type */ - if (sd->s_type & SYSFS_KOBJ_ATTR) { + switch (sysfs_type(sd)) { + case SYSFS_KOBJ_ATTR: inode->i_size = PAGE_SIZE; inode->i_fop = &sysfs_file_operations; - } else if (sd->s_type & SYSFS_KOBJ_BIN_ATTR) { - struct bin_attribute *bin_attr = - sd->s_elem.bin_attr.bin_attr; + break; + case SYSFS_KOBJ_BIN_ATTR: + bin_attr = sd->s_elem.bin_attr.bin_attr; inode->i_size = bin_attr->size; inode->i_fop = &bin_fops; - } else if (sd->s_type & SYSFS_KOBJ_LINK) + break; + case SYSFS_KOBJ_LINK: inode->i_op = &sysfs_symlink_inode_operations; + break; + default: + BUG(); + } } sysfs_instantiate(dentry, inode); @@ -532,7 +539,7 @@ static void __sysfs_remove_dir(struct dentry *dentry) while (*pos) { struct sysfs_dirent *sd = *pos; - if (sd->s_type && (sd->s_type & SYSFS_NOT_PINNED)) { + if (sysfs_type(sd) && (sysfs_type(sd) & SYSFS_NOT_PINNED)) { *pos = sd->s_sibling; sd->s_sibling = removed; removed = sd; @@ -775,7 +782,7 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) const char * name; int len; - if (!next->s_type) + if (!sysfs_type(next)) continue; name = next->s_name; @@ -824,7 +831,7 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) pos = &sd->s_children; while (n && *pos) { struct sysfs_dirent *next = *pos; - if (next->s_type) + if (sysfs_type(next)) n--; pos = &(*pos)->s_sibling; } diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 63daa06c4194..ee3a5d957051 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -242,7 +242,7 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) dput(dentry); /* XXX: unpin if directory, this will go away soon */ - if (sd->s_type & SYSFS_DIR) + if (sysfs_type(sd) == SYSFS_DIR) dput(dentry); /* adjust nlink and update timestamp */ @@ -254,7 +254,7 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) inode->i_ctime = curtime; drop_nlink(inode); - if (sd->s_type & SYSFS_DIR) + if (sysfs_type(sd) == SYSFS_DIR) drop_nlink(inode); mutex_unlock(&inode->i_mutex); @@ -267,7 +267,7 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) mutex_lock(&inode->i_mutex); inode->i_ctime = inode->i_mtime = curtime; - if (sd->s_type & SYSFS_DIR) + if (sysfs_type(sd) == SYSFS_DIR) drop_nlink(inode); mutex_unlock(&inode->i_mutex); @@ -293,7 +293,7 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) for (pos = &parent_sd->s_children; *pos; pos = &(*pos)->s_sibling) { sd = *pos; - if (!sd->s_type) + if (!sysfs_type(sd)) continue; if (!strcmp(sd->s_name, name)) { *pos = sd->s_sibling; diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 4be9593ea000..078537e5d696 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -26,7 +26,7 @@ static const struct super_operations sysfs_ops = { static struct sysfs_dirent sysfs_root = { .s_count = ATOMIC_INIT(1), - .s_type = SYSFS_ROOT, + .s_flags = SYSFS_ROOT, .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, .s_ino = 1, }; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 6f8aaf3805d2..06b5085804a1 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -34,7 +34,7 @@ struct sysfs_dirent { struct sysfs_elem_bin_attr bin_attr; } s_elem; - int s_type; + unsigned int s_flags; umode_t s_mode; ino_t s_ino; struct dentry * s_dentry; @@ -86,6 +86,11 @@ extern const struct file_operations bin_fops; extern const struct inode_operations sysfs_dir_inode_operations; extern const struct inode_operations sysfs_symlink_inode_operations; +static inline unsigned int sysfs_type(struct sysfs_dirent *sd) +{ + return sd->s_flags & SYSFS_TYPE_MASK; +} + static inline struct sysfs_dirent * sysfs_get(struct sysfs_dirent * sd) { if (sd) { diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 161e19aa2b4f..58135509023e 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -74,6 +74,7 @@ struct sysfs_ops { ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t); }; +#define SYSFS_TYPE_MASK 0x00ff #define SYSFS_ROOT 0x0001 #define SYSFS_DIR 0x0002 #define SYSFS_KOBJ_ATTR 0x0004 @@ -82,6 +83,8 @@ struct sysfs_ops { #define SYSFS_NOT_PINNED (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR | SYSFS_KOBJ_LINK) #define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) +#define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK + #ifdef CONFIG_SYSFS extern int sysfs_schedule_callback(struct kobject *kobj, From 380e6fbb729a55b73d5d8409551474884e0d93fc Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:22 +0900 Subject: [PATCH 52/61] sysfs: implement SYSFS_FLAG_REMOVED flag Implement SYSFS_FLAG_REMOVED flag which currently is used only to improve sanity check in sysfs_deactivate(). The flag will be used to make directory entries reclamiable. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 4 +++- fs/sysfs/inode.c | 1 + include/linux/sysfs.h | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index eb9bc0a8717b..f2ea00683ec9 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -171,7 +171,7 @@ void sysfs_deactivate(struct sysfs_dirent *sd) DECLARE_COMPLETION_ONSTACK(wait); int v; - BUG_ON(sd->s_sibling); + BUG_ON(sd->s_sibling || !(sd->s_flags & SYSFS_FLAG_REMOVED)); sd->s_sibling = (void *)&wait; /* atomic_add_return() is a mb(), put_active() will always see @@ -506,6 +506,7 @@ static void remove_dir(struct dentry * d) mutex_lock(&parent->d_inode->i_mutex); sysfs_unlink_sibling(sd); + sd->s_flags |= SYSFS_FLAG_REMOVED; pr_debug(" o %s removing done (%d)\n",d->d_name.name, atomic_read(&d->d_count)); @@ -540,6 +541,7 @@ static void __sysfs_remove_dir(struct dentry *dentry) struct sysfs_dirent *sd = *pos; if (sysfs_type(sd) && (sysfs_type(sd) & SYSFS_NOT_PINNED)) { + sd->s_flags |= SYSFS_FLAG_REMOVED; *pos = sd->s_sibling; sd->s_sibling = removed; removed = sd; diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index ee3a5d957051..e2f6ef138d20 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -296,6 +296,7 @@ int sysfs_hash_and_remove(struct dentry * dir, const char * name) if (!sysfs_type(sd)) continue; if (!strcmp(sd->s_name, name)) { + sd->s_flags |= SYSFS_FLAG_REMOVED; *pos = sd->s_sibling; sd->s_sibling = NULL; found = 1; diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 58135509023e..2a6df6444e69 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -84,6 +84,7 @@ struct sysfs_ops { #define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) #define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK +#define SYSFS_FLAG_REMOVED 0x0100 #ifdef CONFIG_SYSFS From f0b0af4792d751106e2003f96af76fa95e10c68d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:22 +0900 Subject: [PATCH 53/61] sysfs: implement sysfs_find_dirent() and sysfs_get_dirent() Implement sysfs_find_dirent() and sysfs_get_dirent(). sysfs_dirent_exist() is replaced by sysfs_find_dirent(). These will be used to make directory entries reclamiable. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 61 +++++++++++++++++++++++++++++++++------------- fs/sysfs/file.c | 2 +- fs/sysfs/symlink.c | 2 +- fs/sysfs/sysfs.h | 5 +++- 4 files changed, 50 insertions(+), 20 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index f2ea00683ec9..4762a9aa0b27 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -317,28 +317,55 @@ void sysfs_attach_dirent(struct sysfs_dirent *sd, } } -/* +/** + * sysfs_find_dirent - find sysfs_dirent with the given name + * @parent_sd: sysfs_dirent to search under + * @name: name to look for * - * Return -EEXIST if there is already a sysfs element with the same name for - * the same parent. + * Look for sysfs_dirent with name @name under @parent_sd. * - * called with parent inode's i_mutex held + * LOCKING: + * mutex_lock(parent->i_mutex) + * + * RETURNS: + * Pointer to sysfs_dirent if found, NULL if not. */ -int sysfs_dirent_exist(struct sysfs_dirent *parent_sd, - const unsigned char *new) +struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, + const unsigned char *name) { - struct sysfs_dirent * sd; + struct sysfs_dirent *sd; - for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) { - if (sysfs_type(sd)) { - if (strcmp(sd->s_name, new)) - continue; - else - return -EEXIST; - } - } + for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) + if (sysfs_type(sd) && !strcmp(sd->s_name, name)) + return sd; + return NULL; +} - return 0; +/** + * sysfs_get_dirent - find and get sysfs_dirent with the given name + * @parent_sd: sysfs_dirent to search under + * @name: name to look for + * + * Look for sysfs_dirent with name @name under @parent_sd and get + * it if found. + * + * LOCKING: + * Kernel thread context (may sleep) + * + * RETURNS: + * Pointer to sysfs_dirent if found, NULL if not. + */ +struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, + const unsigned char *name) +{ + struct sysfs_dirent *sd; + + mutex_lock(&parent_sd->s_dentry->d_inode->i_mutex); + sd = sysfs_find_dirent(parent_sd, name); + sysfs_get(sd); + mutex_unlock(&parent_sd->s_dentry->d_inode->i_mutex); + + return sd; } static int create_dir(struct kobject *kobj, struct dentry *parent, @@ -382,7 +409,7 @@ static int create_dir(struct kobject *kobj, struct dentry *parent, /* link in */ error = -EEXIST; - if (sysfs_dirent_exist(parent->d_fsdata, name)) + if (sysfs_find_dirent(parent->d_fsdata, name)) goto out_iput; sysfs_instantiate(dentry, inode); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index a84b734f7b29..e448b88e313e 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -421,7 +421,7 @@ int sysfs_add_file(struct dentry * dir, const struct attribute * attr, int type) mutex_lock(&dir->d_inode->i_mutex); - if (sysfs_dirent_exist(parent_sd, attr->name)) { + if (sysfs_find_dirent(parent_sd, attr->name)) { error = -EEXIST; goto out_unlock; } diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index ff605d3f4d33..45b62e229627 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -95,7 +95,7 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char return -ENOENT; mutex_lock(&dentry->d_inode->i_mutex); - if (!sysfs_dirent_exist(dentry->d_fsdata, name)) + if (!sysfs_find_dirent(dentry->d_fsdata, name)) error = sysfs_add_link(parent_sd, name, target_sd); mutex_unlock(&dentry->d_inode->i_mutex); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 06b5085804a1..f1629b4520aa 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -59,7 +59,10 @@ extern struct inode * sysfs_get_inode(struct sysfs_dirent *sd); extern void sysfs_instantiate(struct dentry *dentry, struct inode *inode); extern void release_sysfs_dirent(struct sysfs_dirent * sd); -extern int sysfs_dirent_exist(struct sysfs_dirent *, const unsigned char *); +extern struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, + const unsigned char *name); +extern struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, + const unsigned char *name); extern struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type); extern void sysfs_attach_dirent(struct sysfs_dirent *sd, From 608e266a2d4e62c1b98c1c573064b6afe8c06a58 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:22 +0900 Subject: [PATCH 54/61] sysfs: make kobj point to sysfs_dirent instead of dentry As kobj sysfs dentries and inodes are gonna be made reclaimable, dentry can't be used as naming token for sysfs file/directory, replace kobj->dentry with kobj->sd. The only external interface change is shadow directory handling. All other changes are contained in kobj and sysfs. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/bin.c | 6 +- fs/sysfs/dir.c | 119 ++++++++++++++++++++-------------------- fs/sysfs/file.c | 47 ++++++++-------- fs/sysfs/group.c | 55 +++++++++---------- fs/sysfs/inode.c | 11 ++-- fs/sysfs/symlink.c | 22 ++++---- fs/sysfs/sysfs.h | 10 ++-- include/linux/kobject.h | 9 +-- include/linux/sysfs.h | 19 ++++--- lib/kobject.c | 10 ++-- 10 files changed, 156 insertions(+), 152 deletions(-) diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index 3c5574a40b09..55796bdacd3d 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -234,9 +234,9 @@ const struct file_operations bin_fops = { int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr) { - BUG_ON(!kobj || !kobj->dentry || !attr); + BUG_ON(!kobj || !kobj->sd || !attr); - return sysfs_add_file(kobj->dentry, &attr->attr, SYSFS_KOBJ_BIN_ATTR); + return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR); } @@ -248,7 +248,7 @@ int sysfs_create_bin_file(struct kobject * kobj, struct bin_attribute * attr) void sysfs_remove_bin_file(struct kobject * kobj, struct bin_attribute * attr) { - if (sysfs_hash_and_remove(kobj->dentry, attr->attr.name) < 0) { + if (sysfs_hash_and_remove(kobj->sd, attr->attr.name) < 0) { printk(KERN_ERR "%s: " "bad dentry or inode or no such file: \"%s\"\n", __FUNCTION__, attr->attr.name); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 4762a9aa0b27..31b6cf30636d 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -368,9 +368,10 @@ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, return sd; } -static int create_dir(struct kobject *kobj, struct dentry *parent, - const char *name, struct dentry **p_dentry) +static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, + const char *name, struct sysfs_dirent **p_sd) { + struct dentry *parent = parent_sd->s_dentry; int error; umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; struct dentry *dentry; @@ -409,14 +410,14 @@ static int create_dir(struct kobject *kobj, struct dentry *parent, /* link in */ error = -EEXIST; - if (sysfs_find_dirent(parent->d_fsdata, name)) + if (sysfs_find_dirent(parent_sd, name)) goto out_iput; sysfs_instantiate(dentry, inode); inc_nlink(parent->d_inode); - sysfs_attach_dirent(sd, parent->d_fsdata, dentry); + sysfs_attach_dirent(sd, parent_sd, dentry); - *p_dentry = dentry; + *p_sd = sd; error = 0; goto out_unlock; /* pin directory dentry in core */ @@ -433,38 +434,37 @@ static int create_dir(struct kobject *kobj, struct dentry *parent, return error; } - -int sysfs_create_subdir(struct kobject * k, const char * n, struct dentry ** d) +int sysfs_create_subdir(struct kobject *kobj, const char *name, + struct sysfs_dirent **p_sd) { - return create_dir(k,k->dentry,n,d); + return create_dir(kobj, kobj->sd, name, p_sd); } /** * sysfs_create_dir - create a directory for an object. * @kobj: object we're creating directory for. - * @shadow_parent: parent parent object. + * @shadow_parent: parent object. */ - -int sysfs_create_dir(struct kobject * kobj, struct dentry *shadow_parent) +int sysfs_create_dir(struct kobject *kobj, + struct sysfs_dirent *shadow_parent_sd) { - struct dentry * dentry = NULL; - struct dentry * parent; + struct sysfs_dirent *parent_sd, *sd; int error = 0; BUG_ON(!kobj); - if (shadow_parent) - parent = shadow_parent; + if (shadow_parent_sd) + parent_sd = shadow_parent_sd; else if (kobj->parent) - parent = kobj->parent->dentry; + parent_sd = kobj->parent->sd; else if (sysfs_mount && sysfs_mount->mnt_sb) - parent = sysfs_mount->mnt_sb->s_root; + parent_sd = sysfs_mount->mnt_sb->s_root->d_fsdata; else return -EFAULT; - error = create_dir(kobj,parent,kobject_name(kobj),&dentry); + error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd); if (!error) - kobj->dentry = dentry; + kobj->sd = sd; return error; } @@ -525,18 +525,16 @@ const struct inode_operations sysfs_dir_inode_operations = { .setattr = sysfs_setattr, }; -static void remove_dir(struct dentry * d) +static void remove_dir(struct sysfs_dirent *sd) { - struct dentry *parent = d->d_parent; - struct sysfs_dirent *sd = d->d_fsdata; + struct dentry *parent = sd->s_parent->s_dentry; mutex_lock(&parent->d_inode->i_mutex); sysfs_unlink_sibling(sd); sd->s_flags |= SYSFS_FLAG_REMOVED; - pr_debug(" o %s removing done (%d)\n",d->d_name.name, - atomic_read(&d->d_count)); + pr_debug(" o %s removing done\n", sd->s_name); mutex_unlock(&parent->d_inode->i_mutex); @@ -545,25 +543,26 @@ static void remove_dir(struct dentry * d) sysfs_put(sd); } -void sysfs_remove_subdir(struct dentry * d) +void sysfs_remove_subdir(struct sysfs_dirent *sd) { - remove_dir(d); + remove_dir(sd); } -static void __sysfs_remove_dir(struct dentry *dentry) +static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) { struct sysfs_dirent *removed = NULL; - struct sysfs_dirent *parent_sd; struct sysfs_dirent **pos; + struct dentry *dir; - if (!dentry) + if (!dir_sd) return; - pr_debug("sysfs %s: removing dir\n",dentry->d_name.name); - mutex_lock(&dentry->d_inode->i_mutex); - parent_sd = dentry->d_fsdata; - pos = &parent_sd->s_children; + dir = dir_sd->s_dentry; + + pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); + mutex_lock(&dir->d_inode->i_mutex); + pos = &dir_sd->s_children; while (*pos) { struct sysfs_dirent *sd = *pos; @@ -575,7 +574,7 @@ static void __sysfs_remove_dir(struct dentry *dentry) } else pos = &(*pos)->s_sibling; } - mutex_unlock(&dentry->d_inode->i_mutex); + mutex_unlock(&dir->d_inode->i_mutex); while (removed) { struct sysfs_dirent *sd = removed; @@ -588,7 +587,7 @@ static void __sysfs_remove_dir(struct dentry *dentry) sysfs_put(sd); } - remove_dir(dentry); + remove_dir(dir_sd); } /** @@ -602,25 +601,25 @@ static void __sysfs_remove_dir(struct dentry *dentry) void sysfs_remove_dir(struct kobject * kobj) { - struct dentry *d = kobj->dentry; + struct sysfs_dirent *sd = kobj->sd; spin_lock(&kobj_sysfs_assoc_lock); - kobj->dentry = NULL; + kobj->sd = NULL; spin_unlock(&kobj_sysfs_assoc_lock); - __sysfs_remove_dir(d); + __sysfs_remove_dir(sd); } -int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, +int sysfs_rename_dir(struct kobject *kobj, struct sysfs_dirent *new_parent_sd, const char *new_name) { - struct sysfs_dirent *sd = kobj->dentry->d_fsdata; - struct sysfs_dirent *parent_sd = new_parent->d_fsdata; + struct sysfs_dirent *sd = kobj->sd; + struct dentry *new_parent = new_parent_sd->s_dentry; struct dentry *new_dentry; char *dup_name; int error; - if (!new_parent) + if (!new_parent_sd) return -EFAULT; down_write(&sysfs_rename_sem); @@ -637,9 +636,9 @@ int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, * shadows of the same directory */ error = -EINVAL; - if (kobj->dentry->d_parent->d_inode != new_parent->d_inode || + if (sd->s_parent->s_dentry->d_inode != new_parent->d_inode || new_dentry->d_parent->d_inode != new_parent->d_inode || - new_dentry == kobj->dentry) + new_dentry == sd->s_dentry) goto out_dput; error = -EEXIST; @@ -661,12 +660,12 @@ int sysfs_rename_dir(struct kobject * kobj, struct dentry *new_parent, /* move under the new parent */ d_add(new_dentry, NULL); - d_move(kobj->dentry, new_dentry); + d_move(sd->s_dentry, new_dentry); sysfs_unlink_sibling(sd); - sysfs_get(parent_sd); + sysfs_get(new_parent_sd); sysfs_put(sd->s_parent); - sd->s_parent = parent_sd; + sd->s_parent = new_parent_sd; sysfs_link_sibling(sd); error = 0; @@ -691,9 +690,9 @@ int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent) int error; old_parent_dentry = kobj->parent ? - kobj->parent->dentry : sysfs_mount->mnt_sb->s_root; + kobj->parent->sd->s_dentry : sysfs_mount->mnt_sb->s_root; new_parent_dentry = new_parent ? - new_parent->dentry : sysfs_mount->mnt_sb->s_root; + new_parent->sd->s_dentry : sysfs_mount->mnt_sb->s_root; if (old_parent_dentry->d_inode == new_parent_dentry->d_inode) return 0; /* nothing to move */ @@ -705,7 +704,7 @@ again: } new_parent_sd = new_parent_dentry->d_fsdata; - sd = kobj->dentry->d_fsdata; + sd = kobj->sd; new_dentry = lookup_one_len(kobj->name, new_parent_dentry, strlen(kobj->name)); @@ -715,7 +714,7 @@ again: } else error = 0; d_add(new_dentry, NULL); - d_move(kobj->dentry, new_dentry); + d_move(sd->s_dentry, new_dentry); dput(new_dentry); /* Remove from old parent's list and insert into new parent's list. */ @@ -885,7 +884,7 @@ int sysfs_make_shadowed_dir(struct kobject *kobj, struct inode *inode; struct inode_operations *i_op; - inode = kobj->dentry->d_inode; + inode = kobj->sd->s_dentry->d_inode; if (inode->i_op != &sysfs_dir_inode_operations) return -EINVAL; @@ -912,16 +911,16 @@ int sysfs_make_shadowed_dir(struct kobject *kobj, * directory. */ -struct dentry *sysfs_create_shadow_dir(struct kobject *kobj) +struct sysfs_dirent *sysfs_create_shadow_dir(struct kobject *kobj) { - struct dentry *dir = kobj->dentry; + struct dentry *dir = kobj->sd->s_dentry; struct inode *inode = dir->d_inode; struct dentry *parent = dir->d_parent; struct sysfs_dirent *parent_sd = parent->d_fsdata; struct dentry *shadow; struct sysfs_dirent *sd; - shadow = ERR_PTR(-EINVAL); + sd = ERR_PTR(-EINVAL); if (!sysfs_is_shadowed_inode(inode)) goto out; @@ -944,25 +943,25 @@ struct dentry *sysfs_create_shadow_dir(struct kobject *kobj) dget(shadow); /* Extra count - pin the dentry in core */ out: - return shadow; + return sd; nomem: dput(shadow); - shadow = ERR_PTR(-ENOMEM); + sd = ERR_PTR(-ENOMEM); goto out; } /** * sysfs_remove_shadow_dir - remove an object's directory. - * @shadow: dentry of shadow directory + * @shadow_sd: sysfs_dirent of shadow directory * * The only thing special about this is that we remove any files in * the directory before we remove the directory, and we've inlined * what used to be sysfs_rmdir() below, instead of calling separately. */ -void sysfs_remove_shadow_dir(struct dentry *shadow) +void sysfs_remove_shadow_dir(struct sysfs_dirent *shadow_sd) { - __sysfs_remove_dir(shadow); + __sysfs_remove_dir(shadow_sd); } const struct file_operations sysfs_dir_operations = { diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index e448b88e313e..20703b9ee064 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -385,7 +385,7 @@ static struct dentry *step_down(struct dentry *dir, const char * name) void sysfs_notify(struct kobject * k, char *dir, char *attr) { - struct dentry *de = k->dentry; + struct dentry *de = k->sd->s_dentry; if (de) dget(de); if (de && dir) @@ -412,16 +412,17 @@ const struct file_operations sysfs_file_operations = { }; -int sysfs_add_file(struct dentry * dir, const struct attribute * attr, int type) +int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, + int type) { - struct sysfs_dirent * parent_sd = dir->d_fsdata; + struct dentry *dir = dir_sd->s_dentry; umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG; struct sysfs_dirent *sd; int error = 0; mutex_lock(&dir->d_inode->i_mutex); - if (sysfs_find_dirent(parent_sd, attr->name)) { + if (sysfs_find_dirent(dir_sd, attr->name)) { error = -EEXIST; goto out_unlock; } @@ -432,7 +433,7 @@ int sysfs_add_file(struct dentry * dir, const struct attribute * attr, int type) goto out_unlock; } sd->s_elem.attr.attr = (void *)attr; - sysfs_attach_dirent(sd, parent_sd, NULL); + sysfs_attach_dirent(sd, dir_sd, NULL); out_unlock: mutex_unlock(&dir->d_inode->i_mutex); @@ -448,9 +449,9 @@ int sysfs_add_file(struct dentry * dir, const struct attribute * attr, int type) int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) { - BUG_ON(!kobj || !kobj->dentry || !attr); + BUG_ON(!kobj || !kobj->sd || !attr); - return sysfs_add_file(kobj->dentry, attr, SYSFS_KOBJ_ATTR); + return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR); } @@ -464,16 +465,16 @@ int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) int sysfs_add_file_to_group(struct kobject *kobj, const struct attribute *attr, const char *group) { - struct dentry *dir; + struct sysfs_dirent *dir_sd; 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); - } + dir_sd = sysfs_get_dirent(kobj->sd, group); + if (!dir_sd) + return -ENOENT; + + error = sysfs_add_file(dir_sd, attr, SYSFS_KOBJ_ATTR); + sysfs_put(dir_sd); + return error; } EXPORT_SYMBOL_GPL(sysfs_add_file_to_group); @@ -486,7 +487,7 @@ EXPORT_SYMBOL_GPL(sysfs_add_file_to_group); */ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) { - struct dentry * dir = kobj->dentry; + struct dentry *dir = kobj->sd->s_dentry; struct dentry * victim; int res = -ENOENT; @@ -522,7 +523,7 @@ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) */ int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode) { - struct dentry *dir = kobj->dentry; + struct dentry *dir = kobj->sd->s_dentry; struct dentry *victim; struct inode * inode; struct iattr newattrs; @@ -560,7 +561,7 @@ EXPORT_SYMBOL_GPL(sysfs_chmod_file); void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) { - sysfs_hash_and_remove(kobj->dentry, attr->name); + sysfs_hash_and_remove(kobj->sd, attr->name); } @@ -573,12 +574,12 @@ void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr) void sysfs_remove_file_from_group(struct kobject *kobj, const struct attribute *attr, const char *group) { - struct dentry *dir; + struct sysfs_dirent *dir_sd; - dir = lookup_one_len(group, kobj->dentry, strlen(group)); - if (!IS_ERR(dir)) { - sysfs_hash_and_remove(dir, attr->name); - dput(dir); + dir_sd = sysfs_get_dirent(kobj->sd, group); + if (dir_sd) { + sysfs_hash_and_remove(dir_sd, attr->name); + sysfs_put(dir_sd); } } EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 52eed2a7a5ef..f318b73c790c 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -18,26 +18,25 @@ #include "sysfs.h" -static void remove_files(struct dentry * dir, - const struct attribute_group * grp) +static void remove_files(struct sysfs_dirent *dir_sd, + const struct attribute_group *grp) { struct attribute *const* attr; for (attr = grp->attrs; *attr; attr++) - sysfs_hash_and_remove(dir,(*attr)->name); + sysfs_hash_and_remove(dir_sd, (*attr)->name); } -static int create_files(struct dentry * dir, - const struct attribute_group * grp) +static int create_files(struct sysfs_dirent *dir_sd, + const struct attribute_group *grp) { struct attribute *const* attr; int error = 0; - for (attr = grp->attrs; *attr && !error; attr++) { - error = sysfs_add_file(dir, *attr, SYSFS_KOBJ_ATTR); - } + for (attr = grp->attrs; *attr && !error; attr++) + error = sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR); if (error) - remove_files(dir,grp); + remove_files(dir_sd, grp); return error; } @@ -45,44 +44,44 @@ static int create_files(struct dentry * dir, int sysfs_create_group(struct kobject * kobj, const struct attribute_group * grp) { - struct dentry * dir; + struct sysfs_dirent *sd; int error; - BUG_ON(!kobj || !kobj->dentry); + BUG_ON(!kobj || !kobj->sd); if (grp->name) { - error = sysfs_create_subdir(kobj,grp->name,&dir); + error = sysfs_create_subdir(kobj, grp->name, &sd); if (error) return error; } else - dir = kobj->dentry; - dir = dget(dir); - if ((error = create_files(dir,grp))) { + sd = kobj->sd; + sysfs_get(sd); + error = create_files(sd, grp); + if (error) { if (grp->name) - sysfs_remove_subdir(dir); + sysfs_remove_subdir(sd); } - dput(dir); + sysfs_put(sd); return error; } void sysfs_remove_group(struct kobject * kobj, const struct attribute_group * grp) { - struct dentry * dir; + struct sysfs_dirent *dir_sd = kobj->sd; + struct sysfs_dirent *sd; if (grp->name) { - dir = lookup_one_len_kern(grp->name, kobj->dentry, - strlen(grp->name)); - BUG_ON(IS_ERR(dir)); - } - else - dir = dget(kobj->dentry); + sd = sysfs_get_dirent(dir_sd, grp->name); + BUG_ON(!sd); + } else + sd = sysfs_get(dir_sd); - remove_files(dir,grp); + remove_files(sd, grp); if (grp->name) - sysfs_remove_subdir(dir); - /* release the ref. taken in this routine */ - dput(dir); + sysfs_remove_subdir(sd); + + sysfs_put(sd); } diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index e2f6ef138d20..1be853706e99 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -275,22 +275,23 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) } } -int sysfs_hash_and_remove(struct dentry * dir, const char * name) +int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) { + struct dentry *dir; struct sysfs_dirent **pos, *sd; - struct sysfs_dirent *parent_sd; int found = 0; - if (!dir) + if (!dir_sd) return -ENOENT; + dir = dir_sd->s_dentry; + if (dir->d_inode == NULL) /* no inode means this hasn't been made visible yet */ return -ENOENT; - parent_sd = dir->d_fsdata; mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); - for (pos = &parent_sd->s_children; *pos; pos = &(*pos)->s_sibling) { + for (pos = &dir_sd->s_children; *pos; pos = &(*pos)->s_sibling) { sd = *pos; if (!sysfs_type(sd)) diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 45b62e229627..43cc5222f136 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -66,7 +66,6 @@ static int sysfs_add_link(struct sysfs_dirent * parent_sd, const char * name, */ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char * name) { - struct dentry *dentry = NULL; struct sysfs_dirent *parent_sd = NULL; struct sysfs_dirent *target_sd = NULL; int error = -EEXIST; @@ -75,29 +74,28 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char if (!kobj) { if (sysfs_mount && sysfs_mount->mnt_sb) - dentry = sysfs_mount->mnt_sb->s_root; + parent_sd = sysfs_mount->mnt_sb->s_root->d_fsdata; } else - dentry = kobj->dentry; + parent_sd = kobj->sd; - if (!dentry) + if (!parent_sd) return -EFAULT; - parent_sd = dentry->d_fsdata; - /* target->dentry can go away beneath us but is protected with + /* target->sd can go away beneath us but is protected with * kobj_sysfs_assoc_lock. Fetch target_sd from it. */ spin_lock(&kobj_sysfs_assoc_lock); - if (target->dentry) - target_sd = sysfs_get(target->dentry->d_fsdata); + if (target->sd) + target_sd = sysfs_get(target->sd); spin_unlock(&kobj_sysfs_assoc_lock); if (!target_sd) return -ENOENT; - mutex_lock(&dentry->d_inode->i_mutex); - if (!sysfs_find_dirent(dentry->d_fsdata, name)) + mutex_lock(&parent_sd->s_dentry->d_inode->i_mutex); + if (!sysfs_find_dirent(parent_sd, name)) error = sysfs_add_link(parent_sd, name, target_sd); - mutex_unlock(&dentry->d_inode->i_mutex); + mutex_unlock(&parent_sd->s_dentry->d_inode->i_mutex); if (error) sysfs_put(target_sd); @@ -114,7 +112,7 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char void sysfs_remove_link(struct kobject * kobj, const char * name) { - sysfs_hash_and_remove(kobj->dentry,name); + sysfs_hash_and_remove(kobj->sd, name); } static int sysfs_get_target_path(struct sysfs_dirent * parent_sd, diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index f1629b4520aa..27a5f4b4e3b0 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -69,12 +69,14 @@ extern void sysfs_attach_dirent(struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd, struct dentry *dentry); -extern int sysfs_add_file(struct dentry *, const struct attribute *, int); -extern int sysfs_hash_and_remove(struct dentry * dir, const char * name); +extern int sysfs_add_file(struct sysfs_dirent *dir_sd, + const struct attribute *attr, int type); +extern int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name); extern struct sysfs_dirent *sysfs_find(struct sysfs_dirent *dir, const char * name); -extern int sysfs_create_subdir(struct kobject *, const char *, struct dentry **); -extern void sysfs_remove_subdir(struct dentry *); +extern int sysfs_create_subdir(struct kobject *kobj, const char *name, + struct sysfs_dirent **p_sd); +extern void sysfs_remove_subdir(struct sysfs_dirent *sd); extern void sysfs_drop_dentry(struct sysfs_dirent *sd); extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); diff --git a/include/linux/kobject.h b/include/linux/kobject.h index c288e41ba331..06cbf41d32d2 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -55,7 +55,7 @@ struct kobject { struct kobject * parent; struct kset * kset; struct kobj_type * ktype; - struct dentry * dentry; + struct sysfs_dirent * sd; wait_queue_head_t poll; }; @@ -71,13 +71,14 @@ extern void kobject_init(struct kobject *); extern void kobject_cleanup(struct kobject *); extern int __must_check kobject_add(struct kobject *); -extern int __must_check kobject_shadow_add(struct kobject *, struct dentry *); +extern int __must_check kobject_shadow_add(struct kobject *kobj, + struct sysfs_dirent *shadow_parent); extern void kobject_del(struct kobject *); extern int __must_check kobject_rename(struct kobject *, const char *new_name); extern int __must_check kobject_shadow_rename(struct kobject *kobj, - struct dentry *new_parent, - const char *new_name); + struct sysfs_dirent *new_parent, + const char *new_name); extern int __must_check kobject_move(struct kobject *, struct kobject *); extern int __must_check kobject_register(struct kobject *); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 2a6df6444e69..4c43030fae5d 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -19,6 +19,7 @@ struct kobject; struct module; struct nameidata; struct dentry; +struct sysfs_dirent; /* FIXME * The *owner field is no longer used, but leave around @@ -92,13 +93,14 @@ extern int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), void *data, struct module *owner); extern int __must_check -sysfs_create_dir(struct kobject *, struct dentry *); +sysfs_create_dir(struct kobject *kobj, struct sysfs_dirent *shadow_parent_sd); extern void sysfs_remove_dir(struct kobject *); extern int __must_check -sysfs_rename_dir(struct kobject *, struct dentry *, const char *new_name); +sysfs_rename_dir(struct kobject *kobj, struct sysfs_dirent *new_parent_sd, + const char *new_name); extern int __must_check sysfs_move_dir(struct kobject *, struct kobject *); @@ -138,8 +140,8 @@ void sysfs_notify(struct kobject * k, char *dir, char *attr); extern int sysfs_make_shadowed_dir(struct kobject *kobj, void * (*follow_link)(struct dentry *, struct nameidata *)); -extern struct dentry *sysfs_create_shadow_dir(struct kobject *kobj); -extern void sysfs_remove_shadow_dir(struct dentry *dir); +extern struct sysfs_dirent *sysfs_create_shadow_dir(struct kobject *kobj); +extern void sysfs_remove_shadow_dir(struct sysfs_dirent *shadow_sd); extern int __must_check sysfs_init(void); @@ -151,7 +153,8 @@ static inline int sysfs_schedule_callback(struct kobject *kobj, return -ENOSYS; } -static inline int sysfs_create_dir(struct kobject * k, struct dentry *shadow) +static inline int sysfs_create_dir(struct kobject *kobj, + struct sysfs_dirent *shadow_parent_sd) { return 0; } @@ -161,9 +164,9 @@ static inline void sysfs_remove_dir(struct kobject * k) ; } -static inline int sysfs_rename_dir(struct kobject * k, - struct dentry *new_parent, - const char *new_name) +static inline int sysfs_rename_dir(struct kobject *kobj, + struct sysfs_dirent *new_parent_sd, + const char *new_name) { return 0; } diff --git a/lib/kobject.c b/lib/kobject.c index b4ebd7631700..4b08e0ff95c8 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -44,7 +44,7 @@ static int populate_dir(struct kobject * kobj) return error; } -static int create_dir(struct kobject * kobj, struct dentry *shadow_parent) +static int create_dir(struct kobject *kobj, struct sysfs_dirent *shadow_parent) { int error = 0; if (kobject_name(kobj)) { @@ -162,7 +162,7 @@ static void unlink(struct kobject * kobj) * @shadow_parent: sysfs directory to add to. */ -int kobject_shadow_add(struct kobject * kobj, struct dentry *shadow_parent) +int kobject_shadow_add(struct kobject *kobj, struct sysfs_dirent *shadow_parent) { int error = 0; struct kobject * parent; @@ -338,7 +338,7 @@ int kobject_rename(struct kobject * kobj, const char *new_name) /* Note : if we want to send the new name alone, not the full path, * we could probably use kobject_name(kobj); */ - error = sysfs_rename_dir(kobj, kobj->parent->dentry, new_name); + error = sysfs_rename_dir(kobj, kobj->parent->sd, new_name); /* This function is mostly/only used for network interface. * Some hotplug package track interfaces by their name and @@ -361,8 +361,8 @@ out: * @new_name: object's new name */ -int kobject_shadow_rename(struct kobject * kobj, struct dentry *new_parent, - const char *new_name) +int kobject_shadow_rename(struct kobject *kobj, + struct sysfs_dirent *new_parent, const char *new_name) { int error = 0; From 5f9953237f684ea1778adb9d26162da00b282225 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:23 +0900 Subject: [PATCH 55/61] sysfs: consolidate sysfs spinlocks Replace sysfs_lock and kobj_sysfs_assoc_lock with sysfs_assoc_lock. sysfs_lock was originally to be used to protect sysfs_dirent tree but mutex seems better choice, so there is no reason to keep sysfs_lock separate. Merge the two spinlocks into one. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 19 +++++++++---------- fs/sysfs/inode.c | 16 ++++++++-------- fs/sysfs/symlink.c | 6 +++--- fs/sysfs/sysfs.h | 3 +-- 4 files changed, 21 insertions(+), 23 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 31b6cf30636d..1b5643407a95 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -15,8 +15,7 @@ #include "sysfs.h" DECLARE_RWSEM(sysfs_rename_sem); -spinlock_t sysfs_lock = SPIN_LOCK_UNLOCKED; -spinlock_t kobj_sysfs_assoc_lock = SPIN_LOCK_UNLOCKED; +spinlock_t sysfs_assoc_lock = SPIN_LOCK_UNLOCKED; static spinlock_t sysfs_ino_lock = SPIN_LOCK_UNLOCKED; static DEFINE_IDA(sysfs_ino_ida); @@ -236,10 +235,10 @@ static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) struct sysfs_dirent * sd = dentry->d_fsdata; if (sd) { - /* sd->s_dentry is protected with sysfs_lock. This - * allows sysfs_drop_dentry() to dereference it. + /* sd->s_dentry is protected with sysfs_assoc_lock. + * This allows sysfs_drop_dentry() to dereference it. */ - spin_lock(&sysfs_lock); + spin_lock(&sysfs_assoc_lock); /* The dentry might have been deleted or another * lookup could have happened updating sd->s_dentry to @@ -248,7 +247,7 @@ static void sysfs_d_iput(struct dentry * dentry, struct inode * inode) */ if (sd->s_dentry == dentry) sd->s_dentry = NULL; - spin_unlock(&sysfs_lock); + spin_unlock(&sysfs_assoc_lock); sysfs_put(sd); } iput(inode); @@ -298,9 +297,9 @@ static void sysfs_attach_dentry(struct sysfs_dirent *sd, struct dentry *dentry) dentry->d_fsdata = sysfs_get(sd); /* protect sd->s_dentry against sysfs_d_iput */ - spin_lock(&sysfs_lock); + spin_lock(&sysfs_assoc_lock); sd->s_dentry = dentry; - spin_unlock(&sysfs_lock); + spin_unlock(&sysfs_assoc_lock); d_rehash(dentry); } @@ -603,9 +602,9 @@ void sysfs_remove_dir(struct kobject * kobj) { struct sysfs_dirent *sd = kobj->sd; - spin_lock(&kobj_sysfs_assoc_lock); + spin_lock(&sysfs_assoc_lock); kobj->sd = NULL; - spin_unlock(&kobj_sysfs_assoc_lock); + spin_unlock(&sysfs_assoc_lock); __sysfs_remove_dir(sd); } diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 1be853706e99..e4c23939fb36 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -211,11 +211,11 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode) * parent on entry to this function such that it can't be looked * up anymore. * - * @sd->s_dentry which is protected with sysfs_lock points to the - * currently associated dentry but we're not holding a reference - * to it and racing with dput(). Grab dcache_lock and verify - * dentry before dropping it. If @sd->s_dentry is NULL or dput() - * beats us, no need to bother. + * @sd->s_dentry which is protected with sysfs_assoc_lock points + * to the currently associated dentry but we're not holding a + * reference to it and racing with dput(). Grab dcache_lock and + * verify dentry before dropping it. If @sd->s_dentry is NULL or + * dput() beats us, no need to bother. */ void sysfs_drop_dentry(struct sysfs_dirent *sd) { @@ -224,9 +224,9 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) struct inode *inode; /* We're not holding a reference to ->s_dentry dentry but the - * field will stay valid as long as sysfs_lock is held. + * field will stay valid as long as sysfs_assoc_lock is held. */ - spin_lock(&sysfs_lock); + spin_lock(&sysfs_assoc_lock); spin_lock(&dcache_lock); /* drop dentry if it's there and dput() didn't kill it yet */ @@ -238,7 +238,7 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) } spin_unlock(&dcache_lock); - spin_unlock(&sysfs_lock); + spin_unlock(&sysfs_assoc_lock); dput(dentry); /* XXX: unpin if directory, this will go away soon */ diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 43cc5222f136..cbd95a4109de 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -82,12 +82,12 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char return -EFAULT; /* target->sd can go away beneath us but is protected with - * kobj_sysfs_assoc_lock. Fetch target_sd from it. + * sysfs_assoc_lock. Fetch target_sd from it. */ - spin_lock(&kobj_sysfs_assoc_lock); + spin_lock(&sysfs_assoc_lock); if (target->sd) target_sd = sysfs_get(target->sd); - spin_unlock(&kobj_sysfs_assoc_lock); + spin_unlock(&sysfs_assoc_lock); if (!target_sd) return -ENOENT; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 27a5f4b4e3b0..457267721f4e 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -81,8 +81,7 @@ extern void sysfs_remove_subdir(struct sysfs_dirent *sd); extern void sysfs_drop_dentry(struct sysfs_dirent *sd); extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); -extern spinlock_t sysfs_lock; -extern spinlock_t kobj_sysfs_assoc_lock; +extern spinlock_t sysfs_assoc_lock; extern struct rw_semaphore sysfs_rename_sem; extern struct super_block * sysfs_sb; extern const struct file_operations sysfs_dir_operations; From 3007e997de91ec59af39a3f9c91595b31ae6e08b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:23 +0900 Subject: [PATCH 56/61] sysfs: use sysfs_mutex to protect the sysfs_dirent tree As kobj sysfs dentries and inodes are gonna be made reclaimable, i_mutex can't be used to protect sysfs_dirent tree. Use sysfs_mutex globally instead. As the whole tree is protected with sysfs_mutex, there is no reason to keep sysfs_rename_sem. Drop it. While at it, add docbook comments to functions which require sysfs_mutex locking. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 101 +++++++++++++++++++++++++++++++-------------- fs/sysfs/file.c | 33 +++++++-------- fs/sysfs/inode.c | 11 ++--- fs/sysfs/symlink.c | 51 ++++++++++++----------- fs/sysfs/sysfs.h | 2 +- 5 files changed, 117 insertions(+), 81 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 1b5643407a95..9fc8558fd86c 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -14,7 +14,7 @@ #include #include "sysfs.h" -DECLARE_RWSEM(sysfs_rename_sem); +DEFINE_MUTEX(sysfs_mutex); spinlock_t sysfs_assoc_lock = SPIN_LOCK_UNLOCKED; static spinlock_t sysfs_ino_lock = SPIN_LOCK_UNLOCKED; @@ -28,7 +28,7 @@ static DEFINE_IDA(sysfs_ino_ida); * sd->s_parent->s_children. * * Locking: - * mutex_lock(sd->s_parent->dentry->d_inode->i_mutex) + * mutex_lock(sysfs_mutex) */ static void sysfs_link_sibling(struct sysfs_dirent *sd) { @@ -47,7 +47,7 @@ static void sysfs_link_sibling(struct sysfs_dirent *sd) * sd->s_parent->s_children. * * Locking: - * mutex_lock(sd->s_parent->dentry->d_inode->i_mutex) + * mutex_lock(sysfs_mutex) */ static void sysfs_unlink_sibling(struct sysfs_dirent *sd) { @@ -215,6 +215,9 @@ void release_sysfs_dirent(struct sysfs_dirent * sd) struct sysfs_dirent *parent_sd; repeat: + /* Moving/renaming is always done while holding reference. + * sd->s_parent won't change beneath us. + */ parent_sd = sd->s_parent; if (sysfs_type(sd) == SYSFS_KOBJ_LINK) @@ -291,6 +294,17 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) return NULL; } +/** + * sysfs_attach_dentry - associate sysfs_dirent with dentry + * @sd: target sysfs_dirent + * @dentry: dentry to associate + * + * Associate @sd with @dentry. This is protected by + * sysfs_assoc_lock to avoid race with sysfs_d_iput(). + * + * LOCKING: + * mutex_lock(sysfs_mutex) + */ static void sysfs_attach_dentry(struct sysfs_dirent *sd, struct dentry *dentry) { dentry->d_op = &sysfs_dentry_ops; @@ -304,6 +318,17 @@ static void sysfs_attach_dentry(struct sysfs_dirent *sd, struct dentry *dentry) d_rehash(dentry); } +/** + * sysfs_attach_dirent - attach sysfs_dirent to its parent and dentry + * @sd: sysfs_dirent to attach + * @parent_sd: parent to attach to (optional) + * @dentry: dentry to be associated to @sd (optional) + * + * Attach @sd to @parent_sd and/or @dentry. Both are optional. + * + * LOCKING: + * mutex_lock(sysfs_mutex) + */ void sysfs_attach_dirent(struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd, struct dentry *dentry) { @@ -324,7 +349,7 @@ void sysfs_attach_dirent(struct sysfs_dirent *sd, * Look for sysfs_dirent with name @name under @parent_sd. * * LOCKING: - * mutex_lock(parent->i_mutex) + * mutex_lock(sysfs_mutex) * * RETURNS: * Pointer to sysfs_dirent if found, NULL if not. @@ -349,7 +374,7 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, * it if found. * * LOCKING: - * Kernel thread context (may sleep) + * Kernel thread context (may sleep). Grabs sysfs_mutex. * * RETURNS: * Pointer to sysfs_dirent if found, NULL if not. @@ -359,10 +384,10 @@ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, { struct sysfs_dirent *sd; - mutex_lock(&parent_sd->s_dentry->d_inode->i_mutex); + mutex_lock(&sysfs_mutex); sd = sysfs_find_dirent(parent_sd, name); sysfs_get(sd); - mutex_unlock(&parent_sd->s_dentry->d_inode->i_mutex); + mutex_unlock(&sysfs_mutex); return sd; } @@ -408,14 +433,20 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, } /* link in */ + mutex_lock(&sysfs_mutex); + error = -EEXIST; - if (sysfs_find_dirent(parent_sd, name)) + if (sysfs_find_dirent(parent_sd, name)) { + mutex_unlock(&sysfs_mutex); goto out_iput; + } sysfs_instantiate(dentry, inode); inc_nlink(parent->d_inode); sysfs_attach_dirent(sd, parent_sd, dentry); + mutex_unlock(&sysfs_mutex); + *p_sd = sd; error = 0; goto out_unlock; /* pin directory dentry in core */ @@ -493,6 +524,8 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, if (!inode) return ERR_PTR(-ENOMEM); + mutex_lock(&sysfs_mutex); + if (inode->i_state & I_NEW) { /* initialize inode according to type */ switch (sysfs_type(sd)) { @@ -516,6 +549,8 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, sysfs_instantiate(dentry, inode); sysfs_attach_dentry(sd, dentry); + mutex_unlock(&sysfs_mutex); + return NULL; } @@ -526,17 +561,13 @@ const struct inode_operations sysfs_dir_inode_operations = { static void remove_dir(struct sysfs_dirent *sd) { - struct dentry *parent = sd->s_parent->s_dentry; - - mutex_lock(&parent->d_inode->i_mutex); - + mutex_lock(&sysfs_mutex); sysfs_unlink_sibling(sd); sd->s_flags |= SYSFS_FLAG_REMOVED; + mutex_unlock(&sysfs_mutex); pr_debug(" o %s removing done\n", sd->s_name); - mutex_unlock(&parent->d_inode->i_mutex); - sysfs_drop_dentry(sd); sysfs_deactivate(sd); sysfs_put(sd); @@ -552,15 +583,12 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) { struct sysfs_dirent *removed = NULL; struct sysfs_dirent **pos; - struct dentry *dir; if (!dir_sd) return; - dir = dir_sd->s_dentry; - pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); - mutex_lock(&dir->d_inode->i_mutex); + mutex_lock(&sysfs_mutex); pos = &dir_sd->s_children; while (*pos) { struct sysfs_dirent *sd = *pos; @@ -573,7 +601,7 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) } else pos = &(*pos)->s_sibling; } - mutex_unlock(&dir->d_inode->i_mutex); + mutex_unlock(&sysfs_mutex); while (removed) { struct sysfs_dirent *sd = removed; @@ -621,7 +649,6 @@ int sysfs_rename_dir(struct kobject *kobj, struct sysfs_dirent *new_parent_sd, if (!new_parent_sd) return -EFAULT; - down_write(&sysfs_rename_sem); mutex_lock(&new_parent->d_inode->i_mutex); new_dentry = lookup_one_len(new_name, new_parent, strlen(new_name)); @@ -661,12 +688,16 @@ int sysfs_rename_dir(struct kobject *kobj, struct sysfs_dirent *new_parent_sd, d_add(new_dentry, NULL); d_move(sd->s_dentry, new_dentry); + mutex_lock(&sysfs_mutex); + sysfs_unlink_sibling(sd); sysfs_get(new_parent_sd); sysfs_put(sd->s_parent); sd->s_parent = new_parent_sd; sysfs_link_sibling(sd); + mutex_unlock(&sysfs_mutex); + error = 0; goto out_unlock; @@ -678,7 +709,6 @@ int sysfs_rename_dir(struct kobject *kobj, struct sysfs_dirent *new_parent_sd, dput(new_dentry); out_unlock: mutex_unlock(&new_parent->d_inode->i_mutex); - up_write(&sysfs_rename_sem); return error; } @@ -717,12 +747,15 @@ again: dput(new_dentry); /* Remove from old parent's list and insert into new parent's list. */ + mutex_lock(&sysfs_mutex); + sysfs_unlink_sibling(sd); sysfs_get(new_parent_sd); sysfs_put(sd->s_parent); sd->s_parent = new_parent_sd; sysfs_link_sibling(sd); + mutex_unlock(&sysfs_mutex); out: mutex_unlock(&new_parent_dentry->d_inode->i_mutex); mutex_unlock(&old_parent_dentry->d_inode->i_mutex); @@ -736,11 +769,12 @@ static int sysfs_dir_open(struct inode *inode, struct file *file) struct sysfs_dirent * parent_sd = dentry->d_fsdata; struct sysfs_dirent * sd; - mutex_lock(&dentry->d_inode->i_mutex); sd = sysfs_new_dirent("_DIR_", 0, 0); - if (sd) + if (sd) { + mutex_lock(&sysfs_mutex); sysfs_attach_dirent(sd, parent_sd, NULL); - mutex_unlock(&dentry->d_inode->i_mutex); + mutex_unlock(&sysfs_mutex); + } file->private_data = sd; return sd ? 0 : -ENOMEM; @@ -748,12 +782,11 @@ static int sysfs_dir_open(struct inode *inode, struct file *file) static int sysfs_dir_close(struct inode *inode, struct file *file) { - struct dentry * dentry = file->f_path.dentry; struct sysfs_dirent * cursor = file->private_data; - mutex_lock(&dentry->d_inode->i_mutex); + mutex_lock(&sysfs_mutex); sysfs_unlink_sibling(cursor); - mutex_unlock(&dentry->d_inode->i_mutex); + mutex_unlock(&sysfs_mutex); release_sysfs_dirent(cursor); @@ -794,6 +827,8 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) i++; /* fallthrough */ default: + mutex_lock(&sysfs_mutex); + pos = &parent_sd->s_children; while (*pos != cursor) pos = &(*pos)->s_sibling; @@ -826,6 +861,8 @@ static int sysfs_readdir(struct file * filp, void * dirent, filldir_t filldir) /* put cursor back in */ cursor->s_sibling = *pos; *pos = cursor; + + mutex_unlock(&sysfs_mutex); } return 0; } @@ -834,7 +871,6 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) { struct dentry * dentry = file->f_path.dentry; - mutex_lock(&dentry->d_inode->i_mutex); switch (origin) { case 1: offset += file->f_pos; @@ -842,10 +878,11 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) if (offset >= 0) break; default: - mutex_unlock(&file->f_path.dentry->d_inode->i_mutex); return -EINVAL; } if (offset != file->f_pos) { + mutex_lock(&sysfs_mutex); + file->f_pos = offset; if (file->f_pos >= 2) { struct sysfs_dirent *sd = dentry->d_fsdata; @@ -866,8 +903,10 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) cursor->s_sibling = *pos; *pos = cursor; } + + mutex_unlock(&sysfs_mutex); } - mutex_unlock(&dentry->d_inode->i_mutex); + return offset; } @@ -933,7 +972,9 @@ struct sysfs_dirent *sysfs_create_shadow_dir(struct kobject *kobj) sd->s_elem.dir.kobj = kobj; /* point to parent_sd but don't attach to it */ sd->s_parent = sysfs_get(parent_sd); + mutex_lock(&sysfs_mutex); sysfs_attach_dirent(sd, NULL, shadow); + mutex_unlock(&sysfs_mutex); d_instantiate(shadow, igrab(inode)); inc_nlink(inode); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 20703b9ee064..d0deed3e60b5 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -415,29 +415,28 @@ const struct file_operations sysfs_file_operations = { int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, int type) { - struct dentry *dir = dir_sd->s_dentry; umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG; struct sysfs_dirent *sd; - int error = 0; - - mutex_lock(&dir->d_inode->i_mutex); - - if (sysfs_find_dirent(dir_sd, attr->name)) { - error = -EEXIST; - goto out_unlock; - } sd = sysfs_new_dirent(attr->name, mode, type); - if (!sd) { - error = -ENOMEM; - goto out_unlock; - } + if (!sd) + return -ENOMEM; sd->s_elem.attr.attr = (void *)attr; - sysfs_attach_dirent(sd, dir_sd, NULL); - out_unlock: - mutex_unlock(&dir->d_inode->i_mutex); - return error; + mutex_lock(&sysfs_mutex); + + if (!sysfs_find_dirent(dir_sd, attr->name)) { + sysfs_attach_dirent(sd, dir_sd, NULL); + sd = NULL; + } + + mutex_unlock(&sysfs_mutex); + + if (sd) { + sysfs_put(sd); + return -EEXIST; + } + return 0; } diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index e4c23939fb36..d439c0b4bfce 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -277,20 +277,14 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) { - struct dentry *dir; struct sysfs_dirent **pos, *sd; int found = 0; if (!dir_sd) return -ENOENT; - dir = dir_sd->s_dentry; + mutex_lock(&sysfs_mutex); - if (dir->d_inode == NULL) - /* no inode means this hasn't been made visible yet */ - return -ENOENT; - - mutex_lock_nested(&dir->d_inode->i_mutex, I_MUTEX_PARENT); for (pos = &dir_sd->s_children; *pos; pos = &(*pos)->s_sibling) { sd = *pos; @@ -304,7 +298,8 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) break; } } - mutex_unlock(&dir->d_inode->i_mutex); + + mutex_unlock(&sysfs_mutex); if (!found) return -ENOENT; diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index cbd95a4109de..683316f0aa96 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -44,20 +44,6 @@ static void fill_object_path(struct sysfs_dirent *sd, char *buffer, int length) } } -static int sysfs_add_link(struct sysfs_dirent * parent_sd, const char * name, - struct sysfs_dirent * target_sd) -{ - struct sysfs_dirent * sd; - - sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); - if (!sd) - return -ENOMEM; - - sd->s_elem.symlink.target_sd = target_sd; - sysfs_attach_dirent(sd, parent_sd, NULL); - return 0; -} - /** * sysfs_create_link - create symlink between two objects. * @kobj: object whose directory we're creating the link in. @@ -68,7 +54,8 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char { struct sysfs_dirent *parent_sd = NULL; struct sysfs_dirent *target_sd = NULL; - int error = -EEXIST; + struct sysfs_dirent *sd = NULL; + int error; BUG_ON(!name); @@ -78,8 +65,9 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char } else parent_sd = kobj->sd; + error = -EFAULT; if (!parent_sd) - return -EFAULT; + goto out_put; /* target->sd can go away beneath us but is protected with * sysfs_assoc_lock. Fetch target_sd from it. @@ -89,17 +77,30 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char target_sd = sysfs_get(target->sd); spin_unlock(&sysfs_assoc_lock); + error = -ENOENT; if (!target_sd) - return -ENOENT; + goto out_put; - mutex_lock(&parent_sd->s_dentry->d_inode->i_mutex); - if (!sysfs_find_dirent(parent_sd, name)) - error = sysfs_add_link(parent_sd, name, target_sd); - mutex_unlock(&parent_sd->s_dentry->d_inode->i_mutex); + error = -ENOMEM; + sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); + if (!sd) + goto out_put; + sd->s_elem.symlink.target_sd = target_sd; - if (error) - sysfs_put(target_sd); + mutex_lock(&sysfs_mutex); + error = -EEXIST; + if (sysfs_find_dirent(parent_sd, name)) + goto out_unlock; + sysfs_attach_dirent(sd, parent_sd, NULL); + mutex_unlock(&sysfs_mutex); + return 0; + + out_unlock: + mutex_unlock(&sysfs_mutex); + out_put: + sysfs_put(target_sd); + sysfs_put(sd); return error; } @@ -144,9 +145,9 @@ static int sysfs_getlink(struct dentry *dentry, char * path) struct sysfs_dirent *target_sd = sd->s_elem.symlink.target_sd; int error; - down_read(&sysfs_rename_sem); + mutex_lock(&sysfs_mutex); error = sysfs_get_target_path(parent_sd, target_sd, path); - up_read(&sysfs_rename_sem); + mutex_unlock(&sysfs_mutex); return error; } diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 457267721f4e..26051616ed11 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -82,7 +82,7 @@ extern void sysfs_drop_dentry(struct sysfs_dirent *sd); extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); extern spinlock_t sysfs_assoc_lock; -extern struct rw_semaphore sysfs_rename_sem; +extern struct mutex sysfs_mutex; extern struct super_block * sysfs_sb; extern const struct file_operations sysfs_dir_operations; extern const struct file_operations sysfs_file_operations; From fb6896da37f19be4b75154c14d1cd79231255b17 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:24 +0900 Subject: [PATCH 57/61] sysfs: restructure add/remove paths and fix inode update The original add/remove code had the following problems. * parent's timestamps are updated on dentry instantiation. this is incorrect with reclaimable files. * updating parent's timestamps isn't synchronized. * parent nlink update assumes the inode is accessible which won't be true once directory dentries are made reclaimable. This patch restructures add/remove paths to resolve the above problems. Add/removal are done in the following steps. 1. sysfs_addrm_start() : acquire locks including sysfs_mutex and other resources. 2-a. sysfs_add_one() : add new sd. linking the new sd into the children list is caller's responsibility. 2-b. sysfs_remove_one() : remove a sd. unlinking the sd from the children list is caller's responsibility. 3. sysfs_addrm_finish() : release all resources and clean up. Steps 2-a and/or 2-b can be repeated multiple times. Parent's inode is looked up during sysfs_addrm_start(). If available (always at the moment), it's pinned and nlink is updated as sd's are added and removed. Timestamps are updated during finish if any sd has been added or removed. If parent's inode is not available during start, sysfs_mutex ensures that parent inode is not created till add/remove is complete. All the complexity is contained inside the helper functions. Especially, dentry/inode handling is properly hidden from the rest of sysfs which now mostly operate on sysfs_dirents. As an added bonus, codes which use these helpers to add and remove sysfs_dirents are now more structured and simpler. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 250 +++++++++++++++++++++++++++++++++------------ fs/sysfs/file.c | 17 ++- fs/sysfs/inode.c | 46 ++------- fs/sysfs/symlink.c | 22 ++-- fs/sysfs/sysfs.h | 20 +++- 5 files changed, 230 insertions(+), 125 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 9fc8558fd86c..edb30621b82f 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -30,7 +30,7 @@ static DEFINE_IDA(sysfs_ino_ida); * Locking: * mutex_lock(sysfs_mutex) */ -static void sysfs_link_sibling(struct sysfs_dirent *sd) +void sysfs_link_sibling(struct sysfs_dirent *sd) { struct sysfs_dirent *parent_sd = sd->s_parent; @@ -49,7 +49,7 @@ static void sysfs_link_sibling(struct sysfs_dirent *sd) * Locking: * mutex_lock(sysfs_mutex) */ -static void sysfs_unlink_sibling(struct sysfs_dirent *sd) +void sysfs_unlink_sibling(struct sysfs_dirent *sd) { struct sysfs_dirent **pos; @@ -165,7 +165,7 @@ void sysfs_put_active_two(struct sysfs_dirent *sd) * * Deny new active references and drain existing ones. */ -void sysfs_deactivate(struct sysfs_dirent *sd) +static void sysfs_deactivate(struct sysfs_dirent *sd) { DECLARE_COMPLETION_ONSTACK(wait); int v; @@ -318,27 +318,164 @@ static void sysfs_attach_dentry(struct sysfs_dirent *sd, struct dentry *dentry) d_rehash(dentry); } +static int sysfs_ilookup_test(struct inode *inode, void *arg) +{ + struct sysfs_dirent *sd = arg; + return inode->i_ino == sd->s_ino; +} + /** - * sysfs_attach_dirent - attach sysfs_dirent to its parent and dentry - * @sd: sysfs_dirent to attach - * @parent_sd: parent to attach to (optional) - * @dentry: dentry to be associated to @sd (optional) + * sysfs_addrm_start - prepare for sysfs_dirent add/remove + * @acxt: pointer to sysfs_addrm_cxt to be used + * @parent_sd: parent sysfs_dirent * - * Attach @sd to @parent_sd and/or @dentry. Both are optional. + * This function is called when the caller is about to add or + * remove sysfs_dirent under @parent_sd. This function acquires + * sysfs_mutex, grabs inode for @parent_sd if available and lock + * i_mutex of it. @acxt is used to keep and pass context to + * other addrm functions. * * LOCKING: - * mutex_lock(sysfs_mutex) + * Kernel thread context (may sleep). sysfs_mutex is locked on + * return. i_mutex of parent inode is locked on return if + * available. */ -void sysfs_attach_dirent(struct sysfs_dirent *sd, - struct sysfs_dirent *parent_sd, struct dentry *dentry) +void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt, + struct sysfs_dirent *parent_sd) { - if (dentry) - sysfs_attach_dentry(sd, dentry); + struct inode *inode; - if (parent_sd) { - sd->s_parent = sysfs_get(parent_sd); - sysfs_link_sibling(sd); + memset(acxt, 0, sizeof(*acxt)); + acxt->parent_sd = parent_sd; + + /* Lookup parent inode. inode initialization and I_NEW + * clearing are protected by sysfs_mutex. By grabbing it and + * looking up with _nowait variant, inode state can be + * determined reliably. + */ + mutex_lock(&sysfs_mutex); + + inode = ilookup5_nowait(sysfs_sb, parent_sd->s_ino, sysfs_ilookup_test, + parent_sd); + + if (inode && !(inode->i_state & I_NEW)) { + /* parent inode available */ + acxt->parent_inode = inode; + + /* sysfs_mutex is below i_mutex in lock hierarchy. + * First, trylock i_mutex. If fails, unlock + * sysfs_mutex and lock them in order. + */ + if (!mutex_trylock(&inode->i_mutex)) { + mutex_unlock(&sysfs_mutex); + mutex_lock(&inode->i_mutex); + mutex_lock(&sysfs_mutex); + } + } else + iput(inode); +} + +/** + * sysfs_add_one - add sysfs_dirent to parent + * @acxt: addrm context to use + * @sd: sysfs_dirent to be added + * + * Get @acxt->parent_sd and set sd->s_parent to it and increment + * nlink of parent inode if @sd is a directory. @sd is NOT + * linked into the children list of the parent. The caller + * should invoke sysfs_link_sibling() after this function + * completes if @sd needs to be on the children list. + * + * This function should be called between calls to + * sysfs_addrm_start() and sysfs_addrm_finish() and should be + * passed the same @acxt as passed to sysfs_addrm_start(). + * + * LOCKING: + * Determined by sysfs_addrm_start(). + */ +void sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) +{ + sd->s_parent = sysfs_get(acxt->parent_sd); + + if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode) + inc_nlink(acxt->parent_inode); + + acxt->cnt++; +} + +/** + * sysfs_remove_one - remove sysfs_dirent from parent + * @acxt: addrm context to use + * @sd: sysfs_dirent to be added + * + * Mark @sd removed and drop nlink of parent inode if @sd is a + * directory. @sd is NOT unlinked from the children list of the + * parent. The caller is repsonsible for removing @sd from the + * children list before calling this function. + * + * This function should be called between calls to + * sysfs_addrm_start() and sysfs_addrm_finish() and should be + * passed the same @acxt as passed to sysfs_addrm_start(). + * + * LOCKING: + * Determined by sysfs_addrm_start(). + */ +void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) +{ + BUG_ON(sd->s_sibling || (sd->s_flags & SYSFS_FLAG_REMOVED)); + + sd->s_flags |= SYSFS_FLAG_REMOVED; + sd->s_sibling = acxt->removed; + acxt->removed = sd; + + if (sysfs_type(sd) == SYSFS_DIR && acxt->parent_inode) + drop_nlink(acxt->parent_inode); + + acxt->cnt++; +} + +/** + * sysfs_addrm_finish - finish up sysfs_dirent add/remove + * @acxt: addrm context to finish up + * + * Finish up sysfs_dirent add/remove. Resources acquired by + * sysfs_addrm_start() are released and removed sysfs_dirents are + * cleaned up. Timestamps on the parent inode are updated. + * + * LOCKING: + * All mutexes acquired by sysfs_addrm_start() are released. + * + * RETURNS: + * Number of added/removed sysfs_dirents since sysfs_addrm_start(). + */ +int sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) +{ + /* release resources acquired by sysfs_addrm_start() */ + mutex_unlock(&sysfs_mutex); + if (acxt->parent_inode) { + struct inode *inode = acxt->parent_inode; + + /* if added/removed, update timestamps on the parent */ + if (acxt->cnt) + inode->i_ctime = inode->i_mtime = CURRENT_TIME; + + mutex_unlock(&inode->i_mutex); + iput(inode); } + + /* kill removed sysfs_dirents */ + while (acxt->removed) { + struct sysfs_dirent *sd = acxt->removed; + + acxt->removed = sd->s_sibling; + sd->s_sibling = NULL; + + sysfs_drop_dentry(sd); + sysfs_deactivate(sd); + sysfs_put(sd); + } + + return acxt->cnt; } /** @@ -396,19 +533,20 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, const char *name, struct sysfs_dirent **p_sd) { struct dentry *parent = parent_sd->s_dentry; + struct sysfs_addrm_cxt acxt; int error; umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; struct dentry *dentry; struct inode *inode; struct sysfs_dirent *sd; - mutex_lock(&parent->d_inode->i_mutex); + sysfs_addrm_start(&acxt, parent_sd); /* allocate */ dentry = lookup_one_len(name, parent, strlen(name)); if (IS_ERR(dentry)) { error = PTR_ERR(dentry); - goto out_unlock; + goto out_finish; } error = -EEXIST; @@ -433,23 +571,18 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, } /* link in */ - mutex_lock(&sysfs_mutex); - error = -EEXIST; - if (sysfs_find_dirent(parent_sd, name)) { - mutex_unlock(&sysfs_mutex); + if (sysfs_find_dirent(parent_sd, name)) goto out_iput; - } + sysfs_add_one(&acxt, sd); + sysfs_link_sibling(sd); sysfs_instantiate(dentry, inode); - inc_nlink(parent->d_inode); - sysfs_attach_dirent(sd, parent_sd, dentry); - - mutex_unlock(&sysfs_mutex); + sysfs_attach_dentry(sd, dentry); *p_sd = sd; error = 0; - goto out_unlock; /* pin directory dentry in core */ + goto out_finish; /* pin directory dentry in core */ out_iput: iput(inode); @@ -459,8 +592,8 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, d_drop(dentry); out_dput: dput(dentry); - out_unlock: - mutex_unlock(&parent->d_inode->i_mutex); + out_finish: + sysfs_addrm_finish(&acxt); return error; } @@ -561,16 +694,12 @@ const struct inode_operations sysfs_dir_inode_operations = { static void remove_dir(struct sysfs_dirent *sd) { - mutex_lock(&sysfs_mutex); + struct sysfs_addrm_cxt acxt; + + sysfs_addrm_start(&acxt, sd->s_parent); sysfs_unlink_sibling(sd); - sd->s_flags |= SYSFS_FLAG_REMOVED; - mutex_unlock(&sysfs_mutex); - - pr_debug(" o %s removing done\n", sd->s_name); - - sysfs_drop_dentry(sd); - sysfs_deactivate(sd); - sysfs_put(sd); + sysfs_remove_one(&acxt, sd); + sysfs_addrm_finish(&acxt); } void sysfs_remove_subdir(struct sysfs_dirent *sd) @@ -581,38 +710,26 @@ void sysfs_remove_subdir(struct sysfs_dirent *sd) static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) { - struct sysfs_dirent *removed = NULL; + struct sysfs_addrm_cxt acxt; struct sysfs_dirent **pos; if (!dir_sd) return; pr_debug("sysfs %s: removing dir\n", dir_sd->s_name); - mutex_lock(&sysfs_mutex); + sysfs_addrm_start(&acxt, dir_sd); pos = &dir_sd->s_children; while (*pos) { struct sysfs_dirent *sd = *pos; if (sysfs_type(sd) && (sysfs_type(sd) & SYSFS_NOT_PINNED)) { - sd->s_flags |= SYSFS_FLAG_REMOVED; *pos = sd->s_sibling; - sd->s_sibling = removed; - removed = sd; + sd->s_sibling = NULL; + sysfs_remove_one(&acxt, sd); } else pos = &(*pos)->s_sibling; } - mutex_unlock(&sysfs_mutex); - - while (removed) { - struct sysfs_dirent *sd = removed; - - removed = sd->s_sibling; - sd->s_sibling = NULL; - - sysfs_drop_dentry(sd); - sysfs_deactivate(sd); - sysfs_put(sd); - } + sysfs_addrm_finish(&acxt); remove_dir(dir_sd); } @@ -772,7 +889,8 @@ static int sysfs_dir_open(struct inode *inode, struct file *file) sd = sysfs_new_dirent("_DIR_", 0, 0); if (sd) { mutex_lock(&sysfs_mutex); - sysfs_attach_dirent(sd, parent_sd, NULL); + sd->s_parent = sysfs_get(parent_sd); + sysfs_link_sibling(sd); mutex_unlock(&sysfs_mutex); } @@ -957,6 +1075,7 @@ struct sysfs_dirent *sysfs_create_shadow_dir(struct kobject *kobj) struct sysfs_dirent *parent_sd = parent->d_fsdata; struct dentry *shadow; struct sysfs_dirent *sd; + struct sysfs_addrm_cxt acxt; sd = ERR_PTR(-EINVAL); if (!sysfs_is_shadowed_inode(inode)) @@ -970,15 +1089,18 @@ struct sysfs_dirent *sysfs_create_shadow_dir(struct kobject *kobj) if (!sd) goto nomem; sd->s_elem.dir.kobj = kobj; - /* point to parent_sd but don't attach to it */ - sd->s_parent = sysfs_get(parent_sd); - mutex_lock(&sysfs_mutex); - sysfs_attach_dirent(sd, NULL, shadow); - mutex_unlock(&sysfs_mutex); + sysfs_addrm_start(&acxt, parent_sd); + + /* add but don't link into children list */ + sysfs_add_one(&acxt, sd); + + /* attach and instantiate dentry */ + sysfs_attach_dentry(sd, shadow); d_instantiate(shadow, igrab(inode)); - inc_nlink(inode); - inc_nlink(parent->d_inode); + inc_nlink(inode); /* tj: synchronization? */ + + sysfs_addrm_finish(&acxt); dget(shadow); /* Extra count - pin the dentry in core */ diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index d0deed3e60b5..69bacf1db596 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -416,6 +416,7 @@ int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, int type) { umode_t mode = (attr->mode & S_IALLUGO) | S_IFREG; + struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; sd = sysfs_new_dirent(attr->name, mode, type); @@ -423,20 +424,18 @@ int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, return -ENOMEM; sd->s_elem.attr.attr = (void *)attr; - mutex_lock(&sysfs_mutex); + sysfs_addrm_start(&acxt, dir_sd); if (!sysfs_find_dirent(dir_sd, attr->name)) { - sysfs_attach_dirent(sd, dir_sd, NULL); - sd = NULL; + sysfs_add_one(&acxt, sd); + sysfs_link_sibling(sd); } - mutex_unlock(&sysfs_mutex); + if (sysfs_addrm_finish(&acxt)) + return 0; - if (sd) { - sysfs_put(sd); - return -EEXIST; - } - return 0; + sysfs_put(sd); + return -EEXIST; } diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index d439c0b4bfce..f95966847a81 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -191,15 +191,9 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode) { BUG_ON(!dentry || dentry->d_inode); - if (inode->i_state & I_NEW) { + if (inode->i_state & I_NEW) unlock_new_inode(inode); - if (dentry->d_parent && dentry->d_parent->d_inode) { - struct inode *p_inode = dentry->d_parent->d_inode; - p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME; - } - } - d_instantiate(dentry, inode); } @@ -220,7 +214,6 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode) void sysfs_drop_dentry(struct sysfs_dirent *sd) { struct dentry *dentry = NULL; - struct timespec curtime; struct inode *inode; /* We're not holding a reference to ->s_dentry dentry but the @@ -246,13 +239,11 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) dput(dentry); /* adjust nlink and update timestamp */ - curtime = CURRENT_TIME; - inode = ilookup(sysfs_sb, sd->s_ino); if (inode) { mutex_lock(&inode->i_mutex); - inode->i_ctime = curtime; + inode->i_ctime = CURRENT_TIME; drop_nlink(inode); if (sysfs_type(sd) == SYSFS_DIR) drop_nlink(inode); @@ -260,30 +251,17 @@ void sysfs_drop_dentry(struct sysfs_dirent *sd) mutex_unlock(&inode->i_mutex); iput(inode); } - - /* adjust nlink and udpate timestamp of the parent */ - inode = ilookup(sysfs_sb, sd->s_parent->s_ino); - if (inode) { - mutex_lock(&inode->i_mutex); - - inode->i_ctime = inode->i_mtime = curtime; - if (sysfs_type(sd) == SYSFS_DIR) - drop_nlink(inode); - - mutex_unlock(&inode->i_mutex); - iput(inode); - } } int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) { + struct sysfs_addrm_cxt acxt; struct sysfs_dirent **pos, *sd; - int found = 0; if (!dir_sd) return -ENOENT; - mutex_lock(&sysfs_mutex); + sysfs_addrm_start(&acxt, dir_sd); for (pos = &dir_sd->s_children; *pos; pos = &(*pos)->s_sibling) { sd = *pos; @@ -291,22 +269,14 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) if (!sysfs_type(sd)) continue; if (!strcmp(sd->s_name, name)) { - sd->s_flags |= SYSFS_FLAG_REMOVED; *pos = sd->s_sibling; sd->s_sibling = NULL; - found = 1; + sysfs_remove_one(&acxt, sd); break; } } - mutex_unlock(&sysfs_mutex); - - if (!found) - return -ENOENT; - - sysfs_drop_dentry(sd); - sysfs_deactivate(sd); - sysfs_put(sd); - - return 0; + if (sysfs_addrm_finish(&acxt)) + return 0; + return -ENOENT; } diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 683316f0aa96..2f86e0422290 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -55,6 +55,7 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char struct sysfs_dirent *parent_sd = NULL; struct sysfs_dirent *target_sd = NULL; struct sysfs_dirent *sd = NULL; + struct sysfs_addrm_cxt acxt; int error; BUG_ON(!name); @@ -87,17 +88,18 @@ int sysfs_create_link(struct kobject * kobj, struct kobject * target, const char goto out_put; sd->s_elem.symlink.target_sd = target_sd; - mutex_lock(&sysfs_mutex); + sysfs_addrm_start(&acxt, parent_sd); + + if (!sysfs_find_dirent(parent_sd, name)) { + sysfs_add_one(&acxt, sd); + sysfs_link_sibling(sd); + } + + if (sysfs_addrm_finish(&acxt)) + return 0; + error = -EEXIST; - if (sysfs_find_dirent(parent_sd, name)) - goto out_unlock; - sysfs_attach_dirent(sd, parent_sd, NULL); - mutex_unlock(&sysfs_mutex); - - return 0; - - out_unlock: - mutex_unlock(&sysfs_mutex); + /* fall through */ out_put: sysfs_put(target_sd); sysfs_put(sd); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 26051616ed11..3e9a5ee38233 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -44,14 +44,29 @@ struct sysfs_dirent { #define SD_DEACTIVATED_BIAS INT_MIN +struct sysfs_addrm_cxt { + struct sysfs_dirent *parent_sd; + struct inode *parent_inode; + struct sysfs_dirent *removed; + int cnt; +}; + extern struct vfsmount * sysfs_mount; extern struct kmem_cache *sysfs_dir_cachep; +extern void sysfs_link_sibling(struct sysfs_dirent *sd); +extern void sysfs_unlink_sibling(struct sysfs_dirent *sd); extern struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd); extern void sysfs_put_active(struct sysfs_dirent *sd); extern struct sysfs_dirent *sysfs_get_active_two(struct sysfs_dirent *sd); extern void sysfs_put_active_two(struct sysfs_dirent *sd); -extern void sysfs_deactivate(struct sysfs_dirent *sd); +extern void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt, + struct sysfs_dirent *parent_sd); +extern void sysfs_add_one(struct sysfs_addrm_cxt *acxt, + struct sysfs_dirent *sd); +extern void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, + struct sysfs_dirent *sd); +extern int sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); extern void sysfs_delete_inode(struct inode *inode); extern void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode); @@ -65,9 +80,6 @@ extern struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, const unsigned char *name); extern struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type); -extern void sysfs_attach_dirent(struct sysfs_dirent *sd, - struct sysfs_dirent *parent_sd, - struct dentry *dentry); extern int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, int type); From a0edd7c848945a75e2f41673f43bc37d0a5fed15 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:24 +0900 Subject: [PATCH 58/61] sysfs: move sysfs_drop_dentry() to dir.c and make it static After add/remove path restructuring, the only user of sysfs_drop_dentry() is sysfs_addrm_finish(). Move sysfs_drop_dentry() to dir.c and make it static. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/sysfs/inode.c | 56 ------------------------------------------------ fs/sysfs/sysfs.h | 1 - 3 files changed, 56 insertions(+), 57 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index edb30621b82f..c6f3b697064c 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -434,6 +434,62 @@ void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd) acxt->cnt++; } +/** + * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent + * @sd: target sysfs_dirent + * + * Drop dentry for @sd. @sd must have been unlinked from its + * parent on entry to this function such that it can't be looked + * up anymore. + * + * @sd->s_dentry which is protected with sysfs_assoc_lock points + * to the currently associated dentry but we're not holding a + * reference to it and racing with dput(). Grab dcache_lock and + * verify dentry before dropping it. If @sd->s_dentry is NULL or + * dput() beats us, no need to bother. + */ +static void sysfs_drop_dentry(struct sysfs_dirent *sd) +{ + struct dentry *dentry = NULL; + struct inode *inode; + + /* We're not holding a reference to ->s_dentry dentry but the + * field will stay valid as long as sysfs_assoc_lock is held. + */ + spin_lock(&sysfs_assoc_lock); + spin_lock(&dcache_lock); + + /* drop dentry if it's there and dput() didn't kill it yet */ + if (sd->s_dentry && sd->s_dentry->d_inode) { + dentry = dget_locked(sd->s_dentry); + spin_lock(&dentry->d_lock); + __d_drop(dentry); + spin_unlock(&dentry->d_lock); + } + + spin_unlock(&dcache_lock); + spin_unlock(&sysfs_assoc_lock); + + dput(dentry); + /* XXX: unpin if directory, this will go away soon */ + if (sysfs_type(sd) == SYSFS_DIR) + dput(dentry); + + /* adjust nlink and update timestamp */ + inode = ilookup(sysfs_sb, sd->s_ino); + if (inode) { + mutex_lock(&inode->i_mutex); + + inode->i_ctime = CURRENT_TIME; + drop_nlink(inode); + if (sysfs_type(sd) == SYSFS_DIR) + drop_nlink(inode); + + mutex_unlock(&inode->i_mutex); + iput(inode); + } +} + /** * sysfs_addrm_finish - finish up sysfs_dirent add/remove * @acxt: addrm context to finish up diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index f95966847a81..3756e152285a 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -197,62 +197,6 @@ void sysfs_instantiate(struct dentry *dentry, struct inode *inode) d_instantiate(dentry, inode); } -/** - * sysfs_drop_dentry - drop dentry for the specified sysfs_dirent - * @sd: target sysfs_dirent - * - * Drop dentry for @sd. @sd must have been unlinked from its - * parent on entry to this function such that it can't be looked - * up anymore. - * - * @sd->s_dentry which is protected with sysfs_assoc_lock points - * to the currently associated dentry but we're not holding a - * reference to it and racing with dput(). Grab dcache_lock and - * verify dentry before dropping it. If @sd->s_dentry is NULL or - * dput() beats us, no need to bother. - */ -void sysfs_drop_dentry(struct sysfs_dirent *sd) -{ - struct dentry *dentry = NULL; - struct inode *inode; - - /* We're not holding a reference to ->s_dentry dentry but the - * field will stay valid as long as sysfs_assoc_lock is held. - */ - spin_lock(&sysfs_assoc_lock); - spin_lock(&dcache_lock); - - /* drop dentry if it's there and dput() didn't kill it yet */ - if (sd->s_dentry && sd->s_dentry->d_inode) { - dentry = dget_locked(sd->s_dentry); - spin_lock(&dentry->d_lock); - __d_drop(dentry); - spin_unlock(&dentry->d_lock); - } - - spin_unlock(&dcache_lock); - spin_unlock(&sysfs_assoc_lock); - - dput(dentry); - /* XXX: unpin if directory, this will go away soon */ - if (sysfs_type(sd) == SYSFS_DIR) - dput(dentry); - - /* adjust nlink and update timestamp */ - inode = ilookup(sysfs_sb, sd->s_ino); - if (inode) { - mutex_lock(&inode->i_mutex); - - inode->i_ctime = CURRENT_TIME; - drop_nlink(inode); - if (sysfs_type(sd) == SYSFS_DIR) - drop_nlink(inode); - - mutex_unlock(&inode->i_mutex); - iput(inode); - } -} - int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name) { struct sysfs_addrm_cxt acxt; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 3e9a5ee38233..92fe1e51a29b 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -90,7 +90,6 @@ extern int sysfs_create_subdir(struct kobject *kobj, const char *name, struct sysfs_dirent **p_sd); extern void sysfs_remove_subdir(struct sysfs_dirent *sd); -extern void sysfs_drop_dentry(struct sysfs_dirent *sd); extern int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); extern spinlock_t sysfs_assoc_lock; From 53e0ae92690c52eceb997905d85fbb42de5fff63 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:25 +0900 Subject: [PATCH 59/61] sysfs: implement sysfs_get_dentry() Some sysfs operations require dentry and inode. sysfs_get_dentry() looks up and gets dentry for the specified sysfs_dirent. It finds the first ancestor with dentry attached and starts looking up dentries from there. Looking up from the nearest ancestor is necessary to support shadowed directories because we can't reliably lookup dentry for one of the shadows. Dentries for each shadow will be pinned in memory such that they can serve as the starting point for dentry lookup. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ fs/sysfs/sysfs.h | 1 + 2 files changed, 99 insertions(+) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index c6f3b697064c..987211296106 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -62,6 +62,104 @@ void sysfs_unlink_sibling(struct sysfs_dirent *sd) } } +/** + * sysfs_get_dentry - get dentry for the given sysfs_dirent + * @sd: sysfs_dirent of interest + * + * Get dentry for @sd. Dentry is looked up if currently not + * present. This function climbs sysfs_dirent tree till it + * reaches a sysfs_dirent with valid dentry attached and descends + * down from there looking up dentry for each step. + * + * LOCKING: + * Kernel thread context (may sleep) + * + * RETURNS: + * Pointer to found dentry on success, ERR_PTR() value on error. + */ +struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd) +{ + struct sysfs_dirent *cur; + struct dentry *parent_dentry, *dentry; + int i, depth; + + /* Find the first parent which has valid s_dentry and get the + * dentry. + */ + mutex_lock(&sysfs_mutex); + restart0: + spin_lock(&sysfs_assoc_lock); + restart1: + spin_lock(&dcache_lock); + + dentry = NULL; + depth = 0; + cur = sd; + while (!cur->s_dentry || !cur->s_dentry->d_inode) { + if (cur->s_flags & SYSFS_FLAG_REMOVED) { + dentry = ERR_PTR(-ENOENT); + depth = 0; + break; + } + cur = cur->s_parent; + depth++; + } + if (!IS_ERR(dentry)) + dentry = dget_locked(cur->s_dentry); + + spin_unlock(&dcache_lock); + spin_unlock(&sysfs_assoc_lock); + + /* from the found dentry, look up depth times */ + while (depth--) { + /* find and get depth'th ancestor */ + for (cur = sd, i = 0; cur && i < depth; i++) + cur = cur->s_parent; + + /* This can happen if tree structure was modified due + * to move/rename. Restart. + */ + if (i != depth) { + dput(dentry); + goto restart0; + } + + sysfs_get(cur); + + mutex_unlock(&sysfs_mutex); + + /* look it up */ + parent_dentry = dentry; + dentry = lookup_one_len_kern(cur->s_name, parent_dentry, + strlen(cur->s_name)); + dput(parent_dentry); + + if (IS_ERR(dentry)) { + sysfs_put(cur); + return dentry; + } + + mutex_lock(&sysfs_mutex); + spin_lock(&sysfs_assoc_lock); + + /* This, again, can happen if tree structure has + * changed and we looked up the wrong thing. Restart. + */ + if (cur->s_dentry != dentry) { + dput(dentry); + sysfs_put(cur); + goto restart1; + } + + spin_unlock(&sysfs_assoc_lock); + + sysfs_put(cur); + } + + mutex_unlock(&sysfs_mutex); + return dentry; +} + /** * sysfs_get_active - get an active reference to sysfs_dirent * @sd: sysfs_dirent to get an active reference to diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 92fe1e51a29b..72530dc666fd 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -54,6 +54,7 @@ struct sysfs_addrm_cxt { extern struct vfsmount * sysfs_mount; extern struct kmem_cache *sysfs_dir_cachep; +extern struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd); extern void sysfs_link_sibling(struct sysfs_dirent *sd); extern void sysfs_unlink_sibling(struct sysfs_dirent *sd); extern struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd); From 51225039f3cf9d250596d1344494b293274b9169 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 14 Jun 2007 04:27:25 +0900 Subject: [PATCH 60/61] sysfs: make directory dentries and inodes reclaimable This patch makes dentries and inodes for sysfs directories reclaimable. * sysfs_notify() is modified to walk sysfs_dirent tree instead of dentry tree. * sysfs_update_file() and sysfs_chmod_file() use sysfs_get_dentry() to grab the victim dentry. * sysfs_rename_dir() and sysfs_move_dir() grab all dentries using sysfs_get_dentry() on startup. * Dentries for all shadowed directories are pinned in memory to serve as lookup start point. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 231 +++++++++++++++++++++++------------------- fs/sysfs/file.c | 128 ++++++++++------------- fs/sysfs/mount.c | 2 +- fs/sysfs/sysfs.h | 1 + include/linux/sysfs.h | 1 - 5 files changed, 184 insertions(+), 179 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 987211296106..aee966c44aac 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -568,10 +568,10 @@ static void sysfs_drop_dentry(struct sysfs_dirent *sd) spin_unlock(&dcache_lock); spin_unlock(&sysfs_assoc_lock); - dput(dentry); - /* XXX: unpin if directory, this will go away soon */ - if (sysfs_type(sd) == SYSFS_DIR) + /* dentries for shadowed inodes are pinned, unpin */ + if (dentry && sysfs_is_shadowed_inode(dentry->d_inode)) dput(dentry); + dput(dentry); /* adjust nlink and update timestamp */ inode = ilookup(sysfs_sb, sd->s_ino); @@ -686,69 +686,29 @@ struct sysfs_dirent *sysfs_get_dirent(struct sysfs_dirent *parent_sd, static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, const char *name, struct sysfs_dirent **p_sd) { - struct dentry *parent = parent_sd->s_dentry; - struct sysfs_addrm_cxt acxt; - int error; umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO; - struct dentry *dentry; - struct inode *inode; + struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; - sysfs_addrm_start(&acxt, parent_sd); - /* allocate */ - dentry = lookup_one_len(name, parent, strlen(name)); - if (IS_ERR(dentry)) { - error = PTR_ERR(dentry); - goto out_finish; - } - - error = -EEXIST; - if (dentry->d_inode) - goto out_dput; - - error = -ENOMEM; sd = sysfs_new_dirent(name, mode, SYSFS_DIR); if (!sd) - goto out_drop; + return -ENOMEM; sd->s_elem.dir.kobj = kobj; - inode = sysfs_get_inode(sd); - if (!inode) - goto out_sput; - - if (inode->i_state & I_NEW) { - inode->i_op = &sysfs_dir_inode_operations; - inode->i_fop = &sysfs_dir_operations; - /* directory inodes start off with i_nlink == 2 (for ".") */ - inc_nlink(inode); + /* link in */ + sysfs_addrm_start(&acxt, parent_sd); + if (!sysfs_find_dirent(parent_sd, name)) { + sysfs_add_one(&acxt, sd); + sysfs_link_sibling(sd); + } + if (sysfs_addrm_finish(&acxt)) { + *p_sd = sd; + return 0; } - /* link in */ - error = -EEXIST; - if (sysfs_find_dirent(parent_sd, name)) - goto out_iput; - - sysfs_add_one(&acxt, sd); - sysfs_link_sibling(sd); - sysfs_instantiate(dentry, inode); - sysfs_attach_dentry(sd, dentry); - - *p_sd = sd; - error = 0; - goto out_finish; /* pin directory dentry in core */ - - out_iput: - iput(inode); - out_sput: sysfs_put(sd); - out_drop: - d_drop(dentry); - out_dput: - dput(dentry); - out_finish: - sysfs_addrm_finish(&acxt); - return error; + return -EEXIST; } int sysfs_create_subdir(struct kobject *kobj, const char *name, @@ -785,6 +745,17 @@ int sysfs_create_dir(struct kobject *kobj, return error; } +static int sysfs_count_nlink(struct sysfs_dirent *sd) +{ + struct sysfs_dirent *child; + int nr = 0; + + for (child = sd->s_children; child; child = child->s_sibling) + if (sysfs_type(child) == SYSFS_DIR) + nr++; + return nr + 2; +} + static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd) { @@ -795,7 +766,7 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, int found = 0; for (sd = parent_sd->s_children; sd; sd = sd->s_sibling) { - if ((sysfs_type(sd) & SYSFS_NOT_PINNED) && + if (sysfs_type(sd) && !strcmp(sd->s_name, dentry->d_name.name)) { found = 1; break; @@ -816,6 +787,11 @@ static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry, if (inode->i_state & I_NEW) { /* initialize inode according to type */ switch (sysfs_type(sd)) { + case SYSFS_DIR: + inode->i_op = &sysfs_dir_inode_operations; + inode->i_fop = &sysfs_dir_operations; + inode->i_nlink = sysfs_count_nlink(sd); + break; case SYSFS_KOBJ_ATTR: inode->i_size = PAGE_SIZE; inode->i_fop = &sysfs_file_operations; @@ -876,7 +852,7 @@ static void __sysfs_remove_dir(struct sysfs_dirent *dir_sd) while (*pos) { struct sysfs_dirent *sd = *pos; - if (sysfs_type(sd) && (sysfs_type(sd) & SYSFS_NOT_PINNED)) { + if (sysfs_type(sd) && sysfs_type(sd) != SYSFS_DIR) { *pos = sd->s_sibling; sd->s_sibling = NULL; sysfs_remove_one(&acxt, sd); @@ -912,14 +888,25 @@ int sysfs_rename_dir(struct kobject *kobj, struct sysfs_dirent *new_parent_sd, const char *new_name) { struct sysfs_dirent *sd = kobj->sd; - struct dentry *new_parent = new_parent_sd->s_dentry; - struct dentry *new_dentry; - char *dup_name; + struct dentry *new_parent = NULL; + struct dentry *old_dentry = NULL, *new_dentry = NULL; + const char *dup_name = NULL; int error; - if (!new_parent_sd) - return -EFAULT; + /* get dentries */ + old_dentry = sysfs_get_dentry(sd); + if (IS_ERR(old_dentry)) { + error = PTR_ERR(old_dentry); + goto out_dput; + } + new_parent = sysfs_get_dentry(new_parent_sd); + if (IS_ERR(new_parent)) { + error = PTR_ERR(new_parent); + goto out_dput; + } + + /* lock new_parent and get dentry for new name */ mutex_lock(&new_parent->d_inode->i_mutex); new_dentry = lookup_one_len(new_name, new_parent, strlen(new_name)); @@ -933,14 +920,14 @@ int sysfs_rename_dir(struct kobject *kobj, struct sysfs_dirent *new_parent_sd, * shadows of the same directory */ error = -EINVAL; - if (sd->s_parent->s_dentry->d_inode != new_parent->d_inode || + if (old_dentry->d_parent->d_inode != new_parent->d_inode || new_dentry->d_parent->d_inode != new_parent->d_inode || - new_dentry == sd->s_dentry) - goto out_dput; + old_dentry == new_dentry) + goto out_unlock; error = -EEXIST; if (new_dentry->d_inode) - goto out_dput; + goto out_unlock; /* rename kobject and sysfs_dirent */ error = -ENOMEM; @@ -950,9 +937,9 @@ int sysfs_rename_dir(struct kobject *kobj, struct sysfs_dirent *new_parent_sd, error = kobject_set_name(kobj, "%s", new_name); if (error) - goto out_free; + goto out_drop; - kfree(sd->s_name); + dup_name = sd->s_name; sd->s_name = new_name; /* move under the new parent */ @@ -972,45 +959,58 @@ int sysfs_rename_dir(struct kobject *kobj, struct sysfs_dirent *new_parent_sd, error = 0; goto out_unlock; - out_free: - kfree(dup_name); out_drop: d_drop(new_dentry); - out_dput: - dput(new_dentry); out_unlock: mutex_unlock(&new_parent->d_inode->i_mutex); + out_dput: + kfree(dup_name); + dput(new_parent); + dput(old_dentry); + dput(new_dentry); return error; } -int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent) +int sysfs_move_dir(struct kobject *kobj, struct kobject *new_parent_kobj) { - struct dentry *old_parent_dentry, *new_parent_dentry, *new_dentry; - struct sysfs_dirent *new_parent_sd, *sd; + struct sysfs_dirent *sd = kobj->sd; + struct sysfs_dirent *new_parent_sd; + struct dentry *old_parent, *new_parent = NULL; + struct dentry *old_dentry = NULL, *new_dentry = NULL; int error; - old_parent_dentry = kobj->parent ? - kobj->parent->sd->s_dentry : sysfs_mount->mnt_sb->s_root; - new_parent_dentry = new_parent ? - new_parent->sd->s_dentry : sysfs_mount->mnt_sb->s_root; + BUG_ON(!sd->s_parent); + new_parent_sd = new_parent_kobj->sd ? new_parent_kobj->sd : &sysfs_root; - if (old_parent_dentry->d_inode == new_parent_dentry->d_inode) - return 0; /* nothing to move */ + /* get dentries */ + old_dentry = sysfs_get_dentry(sd); + if (IS_ERR(old_dentry)) { + error = PTR_ERR(old_dentry); + goto out_dput; + } + old_parent = sd->s_parent->s_dentry; + + new_parent = sysfs_get_dentry(new_parent_sd); + if (IS_ERR(new_parent)) { + error = PTR_ERR(new_parent); + goto out_dput; + } + + if (old_parent->d_inode == new_parent->d_inode) { + error = 0; + goto out_dput; /* nothing to move */ + } again: - mutex_lock(&old_parent_dentry->d_inode->i_mutex); - if (!mutex_trylock(&new_parent_dentry->d_inode->i_mutex)) { - mutex_unlock(&old_parent_dentry->d_inode->i_mutex); + mutex_lock(&old_parent->d_inode->i_mutex); + if (!mutex_trylock(&new_parent->d_inode->i_mutex)) { + mutex_unlock(&old_parent->d_inode->i_mutex); goto again; } - new_parent_sd = new_parent_dentry->d_fsdata; - sd = kobj->sd; - - new_dentry = lookup_one_len(kobj->name, new_parent_dentry, - strlen(kobj->name)); + new_dentry = lookup_one_len(kobj->name, new_parent, strlen(kobj->name)); if (IS_ERR(new_dentry)) { error = PTR_ERR(new_dentry); - goto out; + goto out_unlock; } else error = 0; d_add(new_dentry, NULL); @@ -1027,10 +1027,14 @@ again: sysfs_link_sibling(sd); mutex_unlock(&sysfs_mutex); -out: - mutex_unlock(&new_parent_dentry->d_inode->i_mutex); - mutex_unlock(&old_parent_dentry->d_inode->i_mutex); + out_unlock: + mutex_unlock(&new_parent->d_inode->i_mutex); + mutex_unlock(&old_parent->d_inode->i_mutex); + out_dput: + dput(new_parent); + dput(old_dentry); + dput(new_dentry); return error; } @@ -1191,12 +1195,20 @@ static loff_t sysfs_dir_lseek(struct file * file, loff_t offset, int origin) int sysfs_make_shadowed_dir(struct kobject *kobj, void * (*follow_link)(struct dentry *, struct nameidata *)) { + struct dentry *dentry; struct inode *inode; struct inode_operations *i_op; - inode = kobj->sd->s_dentry->d_inode; - if (inode->i_op != &sysfs_dir_inode_operations) + /* get dentry for @kobj->sd, dentry of a shadowed dir is pinned */ + dentry = sysfs_get_dentry(kobj->sd); + if (IS_ERR(dentry)) + return PTR_ERR(dentry); + + inode = dentry->d_inode; + if (inode->i_op != &sysfs_dir_inode_operations) { + dput(dentry); return -EINVAL; + } i_op = kmalloc(sizeof(*i_op), GFP_KERNEL); if (!i_op) @@ -1223,17 +1235,23 @@ int sysfs_make_shadowed_dir(struct kobject *kobj, struct sysfs_dirent *sysfs_create_shadow_dir(struct kobject *kobj) { - struct dentry *dir = kobj->sd->s_dentry; - struct inode *inode = dir->d_inode; - struct dentry *parent = dir->d_parent; - struct sysfs_dirent *parent_sd = parent->d_fsdata; - struct dentry *shadow; + struct sysfs_dirent *parent_sd = kobj->sd->s_parent; + struct dentry *dir, *parent, *shadow; + struct inode *inode; struct sysfs_dirent *sd; struct sysfs_addrm_cxt acxt; + dir = sysfs_get_dentry(kobj->sd); + if (IS_ERR(dir)) { + sd = (void *)dir; + goto out; + } + parent = dir->d_parent; + + inode = dir->d_inode; sd = ERR_PTR(-EINVAL); if (!sysfs_is_shadowed_inode(inode)) - goto out; + goto out_dput; shadow = d_alloc(parent, &dir->d_name); if (!shadow) @@ -1258,12 +1276,15 @@ struct sysfs_dirent *sysfs_create_shadow_dir(struct kobject *kobj) dget(shadow); /* Extra count - pin the dentry in core */ -out: - return sd; -nomem: + goto out_dput; + + nomem: dput(shadow); sd = ERR_PTR(-ENOMEM); - goto out; + out_dput: + dput(dir); + out: + return sd; } /** diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 69bacf1db596..cc497994b2a8 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -362,43 +362,22 @@ static unsigned int sysfs_poll(struct file *filp, poll_table *wait) return POLLERR|POLLPRI; } - -static struct dentry *step_down(struct dentry *dir, const char * name) +void sysfs_notify(struct kobject *k, char *dir, char *attr) { - struct dentry * de; + struct sysfs_dirent *sd = k->sd; - if (dir == NULL || dir->d_inode == NULL) - return NULL; + mutex_lock(&sysfs_mutex); - mutex_lock(&dir->d_inode->i_mutex); - de = lookup_one_len(name, dir, strlen(name)); - mutex_unlock(&dir->d_inode->i_mutex); - dput(dir); - if (IS_ERR(de)) - return NULL; - if (de->d_inode == NULL) { - dput(de); - return NULL; - } - return de; -} - -void sysfs_notify(struct kobject * k, char *dir, char *attr) -{ - struct dentry *de = k->sd->s_dentry; - if (de) - dget(de); - if (de && dir) - de = step_down(de, dir); - if (de && attr) - de = step_down(de, attr); - if (de) { - struct sysfs_dirent * sd = de->d_fsdata; - if (sd) - atomic_inc(&sd->s_event); + if (sd && dir) + sd = sysfs_find_dirent(sd, dir); + if (sd && attr) + sd = sysfs_find_dirent(sd, attr); + if (sd) { + atomic_inc(&sd->s_event); wake_up_interruptible(&k->poll); - dput(de); } + + mutex_unlock(&sysfs_mutex); } EXPORT_SYMBOL_GPL(sysfs_notify); @@ -485,30 +464,31 @@ EXPORT_SYMBOL_GPL(sysfs_add_file_to_group); */ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) { - struct dentry *dir = kobj->sd->s_dentry; - struct dentry * victim; - int res = -ENOENT; + struct sysfs_dirent *victim_sd = NULL; + struct dentry *victim = NULL; + int rc; - mutex_lock(&dir->d_inode->i_mutex); - victim = lookup_one_len(attr->name, dir, strlen(attr->name)); - if (!IS_ERR(victim)) { - /* make sure dentry is really there */ - if (victim->d_inode && - (victim->d_parent->d_inode == dir->d_inode)) { - victim->d_inode->i_mtime = CURRENT_TIME; - fsnotify_modify(victim); - res = 0; - } else - d_drop(victim); - - /** - * Drop the reference acquired from lookup_one_len() above. - */ - dput(victim); + rc = -ENOENT; + victim_sd = sysfs_get_dirent(kobj->sd, attr->name); + if (!victim_sd) + goto out; + + victim = sysfs_get_dentry(victim_sd); + if (IS_ERR(victim)) { + rc = PTR_ERR(victim); + victim = NULL; + goto out; } - mutex_unlock(&dir->d_inode->i_mutex); - return res; + mutex_lock(&victim->d_inode->i_mutex); + victim->d_inode->i_mtime = CURRENT_TIME; + fsnotify_modify(victim); + mutex_unlock(&victim->d_inode->i_mutex); + rc = 0; + out: + dput(victim); + sysfs_put(victim_sd); + return rc; } @@ -521,30 +501,34 @@ int sysfs_update_file(struct kobject * kobj, const struct attribute * attr) */ int sysfs_chmod_file(struct kobject *kobj, struct attribute *attr, mode_t mode) { - struct dentry *dir = kobj->sd->s_dentry; - struct dentry *victim; + struct sysfs_dirent *victim_sd = NULL; + struct dentry *victim = NULL; struct inode * inode; struct iattr newattrs; - int res = -ENOENT; + int rc; - mutex_lock(&dir->d_inode->i_mutex); - victim = lookup_one_len(attr->name, dir, strlen(attr->name)); - if (!IS_ERR(victim)) { - if (victim->d_inode && - (victim->d_parent->d_inode == dir->d_inode)) { - inode = victim->d_inode; - mutex_lock(&inode->i_mutex); - newattrs.ia_mode = (mode & S_IALLUGO) | - (inode->i_mode & ~S_IALLUGO); - newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; - res = notify_change(victim, &newattrs); - mutex_unlock(&inode->i_mutex); - } - dput(victim); + rc = -ENOENT; + victim_sd = sysfs_get_dirent(kobj->sd, attr->name); + if (!victim_sd) + goto out; + + victim = sysfs_get_dentry(victim_sd); + if (IS_ERR(victim)) { + rc = PTR_ERR(victim); + victim = NULL; + goto out; } - mutex_unlock(&dir->d_inode->i_mutex); - return res; + inode = victim->d_inode; + mutex_lock(&inode->i_mutex); + newattrs.ia_mode = (mode & S_IALLUGO) | (inode->i_mode & ~S_IALLUGO); + newattrs.ia_valid = ATTR_MODE | ATTR_CTIME; + rc = notify_change(victim, &newattrs); + mutex_unlock(&inode->i_mutex); + out: + dput(victim); + sysfs_put(victim_sd); + return rc; } EXPORT_SYMBOL_GPL(sysfs_chmod_file); diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 078537e5d696..402cc356203c 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -24,7 +24,7 @@ static const struct super_operations sysfs_ops = { .drop_inode = sysfs_delete_inode, }; -static struct sysfs_dirent sysfs_root = { +struct sysfs_dirent sysfs_root = { .s_count = ATOMIC_INIT(1), .s_flags = SYSFS_ROOT, .s_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO, diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 72530dc666fd..6a37f2386a8d 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -52,6 +52,7 @@ struct sysfs_addrm_cxt { }; extern struct vfsmount * sysfs_mount; +extern struct sysfs_dirent sysfs_root; extern struct kmem_cache *sysfs_dir_cachep; extern struct dentry *sysfs_get_dentry(struct sysfs_dirent *sd); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 4c43030fae5d..2f58ca1af770 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -81,7 +81,6 @@ struct sysfs_ops { #define SYSFS_KOBJ_ATTR 0x0004 #define SYSFS_KOBJ_BIN_ATTR 0x0008 #define SYSFS_KOBJ_LINK 0x0020 -#define SYSFS_NOT_PINNED (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR | SYSFS_KOBJ_LINK) #define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) #define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK From 91a6902958f052358899f58683d44e36228d85c2 Mon Sep 17 00:00:00 2001 From: Zhang Rui Date: Sat, 9 Jun 2007 13:57:22 +0800 Subject: [PATCH 61/61] sysfs: add parameter "struct bin_attribute *" in .read/.write methods for sysfs binary attributes Well, first of all, I don't want to change so many files either. What I do: Adding a new parameter "struct bin_attribute *" in the .read/.write methods for the sysfs binary attributes. In fact, only the four lines change in fs/sysfs/bin.c and include/linux/sysfs.h do the real work. But I have to update all the files that use binary attributes to make them compatible with the new .read and .write methods. I'm not sure if I missed any. :( Why I do this: For a sysfs attribute, we can get a pointer pointing to the struct attribute in the .show/.store method, while we can't do this for the binary attributes. I don't know why this is different, but this does make it not so handy to use the binary attributes as the regular ones. So I think this patch is reasonable. :) Who benefits from it: The patch that exposes ACPI tables in sysfs requires such an improvement. All the table binary attributes share the same .read method. Parameter "struct bin_attribute *" is used to get the table signature and instance number which are used to distinguish different ACPI table binary attributes. Without this parameter, we need to offer different .read methods for different ACPI table binary attributes. This is impossible as there are various ACPI tables on different platforms, and we don't know what they are until they are loaded. Signed-off-by: Zhang Rui Signed-off-by: Greg Kroah-Hartman --- .../firmware_sample_firmware_class.c | 2 + drivers/base/firmware_class.c | 4 +- drivers/firmware/dcdbas.c | 10 ++-- drivers/firmware/dell_rbu.c | 25 ++++++---- drivers/i2c/chips/eeprom.c | 3 +- drivers/i2c/chips/max6875.c | 5 +- drivers/pci/hotplug/acpiphp_ibm.c | 6 ++- drivers/pci/pci-sysfs.c | 18 ++++--- drivers/pcmcia/socket_sysfs.c | 8 ++- drivers/rapidio/rio-sysfs.c | 6 ++- drivers/rtc/rtc-ds1553.c | 10 ++-- drivers/rtc/rtc-ds1742.c | 10 ++-- drivers/s390/cio/chp.c | 10 ++-- drivers/scsi/arcmsr/arcmsr_attr.c | 15 +++--- drivers/scsi/ipr.c | 18 ++++--- drivers/scsi/libsas/sas_expander.c | 16 +++--- drivers/scsi/lpfc/lpfc_attr.c | 12 +++-- drivers/scsi/qla2xxx/qla_attr.c | 50 +++++++++++-------- drivers/spi/at25.c | 6 ++- drivers/video/aty/radeon_base.c | 8 ++- drivers/w1/slaves/w1_ds2433.c | 10 ++-- drivers/w1/slaves/w1_therm.c | 7 ++- drivers/w1/w1.c | 12 +++-- drivers/zorro/zorro-sysfs.c | 5 +- fs/sysfs/bin.c | 4 +- include/linux/sysfs.h | 6 ++- net/bridge/br_sysfs_br.c | 5 +- 27 files changed, 185 insertions(+), 106 deletions(-) diff --git a/Documentation/firmware_class/firmware_sample_firmware_class.c b/Documentation/firmware_class/firmware_sample_firmware_class.c index 4994f1f28f8c..fba943aacf93 100644 --- a/Documentation/firmware_class/firmware_sample_firmware_class.c +++ b/Documentation/firmware_class/firmware_sample_firmware_class.c @@ -78,6 +78,7 @@ static CLASS_DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store); static ssize_t firmware_data_read(struct kobject *kobj, + struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) { struct class_device *class_dev = to_class_dev(kobj); @@ -88,6 +89,7 @@ static ssize_t firmware_data_read(struct kobject *kobj, return count; } static ssize_t firmware_data_write(struct kobject *kobj, + struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) { struct class_device *class_dev = to_class_dev(kobj); diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 0e511485d2e6..53f0ee6f3016 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -175,7 +175,7 @@ static ssize_t firmware_loading_store(struct device *dev, static DEVICE_ATTR(loading, 0644, firmware_loading_show, firmware_loading_store); static ssize_t -firmware_data_read(struct kobject *kobj, +firmware_data_read(struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) { struct device *dev = to_dev(kobj); @@ -240,7 +240,7 @@ fw_realloc_buffer(struct firmware_priv *fw_priv, int min_size) * the driver as a firmware image. **/ static ssize_t -firmware_data_write(struct kobject *kobj, +firmware_data_write(struct kobject *kobj, struct bin_attribute *bin_attr, char *buffer, loff_t offset, size_t count) { struct device *dev = to_dev(kobj); diff --git a/drivers/firmware/dcdbas.c b/drivers/firmware/dcdbas.c index 1865b56fb141..18cdcb3ae1ca 100644 --- a/drivers/firmware/dcdbas.c +++ b/drivers/firmware/dcdbas.c @@ -149,8 +149,9 @@ static ssize_t smi_data_buf_size_store(struct device *dev, return count; } -static ssize_t smi_data_read(struct kobject *kobj, char *buf, loff_t pos, - size_t count) +static ssize_t smi_data_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t count) { size_t max_read; ssize_t ret; @@ -170,8 +171,9 @@ out: return ret; } -static ssize_t smi_data_write(struct kobject *kobj, char *buf, loff_t pos, - size_t count) +static ssize_t smi_data_write(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t count) { ssize_t ret; diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c index f8afecb7d0cf..477a3d0e3caf 100644 --- a/drivers/firmware/dell_rbu.c +++ b/drivers/firmware/dell_rbu.c @@ -543,8 +543,9 @@ static ssize_t read_rbu_mono_data(char *buffer, loff_t pos, size_t count) return ret_count; } -static ssize_t read_rbu_data(struct kobject *kobj, char *buffer, - loff_t pos, size_t count) +static ssize_t read_rbu_data(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) { ssize_t ret_count = 0; @@ -591,8 +592,9 @@ static void callbackfn_rbu(const struct firmware *fw, void *context) spin_unlock(&rbu_data.lock); } -static ssize_t read_rbu_image_type(struct kobject *kobj, char *buffer, - loff_t pos, size_t count) +static ssize_t read_rbu_image_type(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) { int size = 0; if (!pos) @@ -600,8 +602,9 @@ static ssize_t read_rbu_image_type(struct kobject *kobj, char *buffer, return size; } -static ssize_t write_rbu_image_type(struct kobject *kobj, char *buffer, - loff_t pos, size_t count) +static ssize_t write_rbu_image_type(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) { int rc = count; int req_firm_rc = 0; @@ -660,8 +663,9 @@ static ssize_t write_rbu_image_type(struct kobject *kobj, char *buffer, return rc; } -static ssize_t read_rbu_packet_size(struct kobject *kobj, char *buffer, - loff_t pos, size_t count) +static ssize_t read_rbu_packet_size(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) { int size = 0; if (!pos) { @@ -672,8 +676,9 @@ static ssize_t read_rbu_packet_size(struct kobject *kobj, char *buffer, return size; } -static ssize_t write_rbu_packet_size(struct kobject *kobj, char *buffer, - loff_t pos, size_t count) +static ssize_t write_rbu_packet_size(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t count) { unsigned long temp; spin_lock(&rbu_data.lock); diff --git a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c index 5990dd5fc773..332816431105 100644 --- a/drivers/i2c/chips/eeprom.c +++ b/drivers/i2c/chips/eeprom.c @@ -110,7 +110,8 @@ exit: mutex_unlock(&data->update_lock); } -static ssize_t eeprom_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +static ssize_t eeprom_read(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj)); struct eeprom_data *data = i2c_get_clientdata(client); diff --git a/drivers/i2c/chips/max6875.c b/drivers/i2c/chips/max6875.c index 1405ce5b236c..4e238c0a7ca3 100644 --- a/drivers/i2c/chips/max6875.c +++ b/drivers/i2c/chips/max6875.c @@ -125,8 +125,9 @@ exit_up: mutex_unlock(&data->update_lock); } -static ssize_t max6875_read(struct kobject *kobj, char *buf, loff_t off, - size_t count) +static ssize_t max6875_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct i2c_client *client = kobj_to_i2c_client(kobj); struct max6875_data *data = i2c_get_clientdata(client); diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index 74556ec31a5b..70db38c0ced9 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -106,7 +106,8 @@ static int ibm_get_attention_status(struct hotplug_slot *slot, u8 *status); static void ibm_handle_events(acpi_handle handle, u32 event, void *context); static int ibm_get_table_from_acpi(char **bufp); static ssize_t ibm_read_apci_table(struct kobject *kobj, - char *buffer, loff_t pos, size_t size); + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t size); static acpi_status __init ibm_find_acpi_device(acpi_handle handle, u32 lvl, void *context, void **rv); static int __init ibm_acpiphp_init(void); @@ -357,7 +358,8 @@ read_table_done: * our solution is to only allow reading the table in all at once **/ static ssize_t ibm_read_apci_table(struct kobject *kobj, - char *buffer, loff_t pos, size_t size) + struct bin_attribute *bin_attr, + char *buffer, loff_t pos, size_t size) { int bytes_read = -EINVAL; char *table = NULL; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d448f8df8613..6543cbe83be5 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -213,7 +213,8 @@ struct device_attribute pci_dev_attrs[] = { }; static ssize_t -pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count) +pci_read_config(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj)); unsigned int size = 64; @@ -285,7 +286,8 @@ pci_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count) } static ssize_t -pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count) +pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj)); unsigned int size = count; @@ -352,7 +354,8 @@ pci_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count) * callback routine (pci_legacy_read). */ ssize_t -pci_read_legacy_io(struct kobject *kobj, char *buf, loff_t off, size_t count) +pci_read_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct pci_bus *bus = to_pci_bus(container_of(kobj, struct class_device, @@ -376,7 +379,8 @@ pci_read_legacy_io(struct kobject *kobj, char *buf, loff_t off, size_t count) * callback routine (pci_legacy_write). */ ssize_t -pci_write_legacy_io(struct kobject *kobj, char *buf, loff_t off, size_t count) +pci_write_legacy_io(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct pci_bus *bus = to_pci_bus(container_of(kobj, struct class_device, @@ -528,7 +532,8 @@ static inline void pci_remove_resource_files(struct pci_dev *dev) { return; } * writing anything except 0 enables it */ static ssize_t -pci_write_rom(struct kobject *kobj, char *buf, loff_t off, size_t count) +pci_write_rom(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj)); @@ -551,7 +556,8 @@ pci_write_rom(struct kobject *kobj, char *buf, loff_t off, size_t count) * device corresponding to @kobj. */ static ssize_t -pci_read_rom(struct kobject *kobj, char *buf, loff_t off, size_t count) +pci_read_rom(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct pci_dev *pdev = to_pci_dev(container_of(kobj, struct device, kobj)); void __iomem *rom; diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c index dbfbe65779e5..b4409002b7f8 100644 --- a/drivers/pcmcia/socket_sysfs.c +++ b/drivers/pcmcia/socket_sysfs.c @@ -283,7 +283,9 @@ static ssize_t pccard_extract_cis(struct pcmcia_socket *s, char *buf, loff_t off return (ret); } -static ssize_t pccard_show_cis(struct kobject *kobj, char *buf, loff_t off, size_t count) +static ssize_t pccard_show_cis(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { unsigned int size = 0x200; @@ -311,7 +313,9 @@ static ssize_t pccard_show_cis(struct kobject *kobj, char *buf, loff_t off, size return (count); } -static ssize_t pccard_store_cis(struct kobject *kobj, char *buf, loff_t off, size_t count) +static ssize_t pccard_store_cis(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct pcmcia_socket *s = to_socket(container_of(kobj, struct device, kobj)); cisdump_t *cis; diff --git a/drivers/rapidio/rio-sysfs.c b/drivers/rapidio/rio-sysfs.c index a3972b9f96e6..659e31164cf0 100644 --- a/drivers/rapidio/rio-sysfs.c +++ b/drivers/rapidio/rio-sysfs.c @@ -67,7 +67,8 @@ struct device_attribute rio_dev_attrs[] = { }; static ssize_t -rio_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count) +rio_read_config(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct rio_dev *dev = to_rio_dev(container_of(kobj, struct device, kobj)); @@ -137,7 +138,8 @@ rio_read_config(struct kobject *kobj, char *buf, loff_t off, size_t count) } static ssize_t -rio_write_config(struct kobject *kobj, char *buf, loff_t off, size_t count) +rio_write_config(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct rio_dev *dev = to_rio_dev(container_of(kobj, struct device, kobj)); diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c index b024cfb558f4..f98a83a11aae 100644 --- a/drivers/rtc/rtc-ds1553.c +++ b/drivers/rtc/rtc-ds1553.c @@ -258,8 +258,9 @@ static const struct rtc_class_ops ds1553_rtc_ops = { .ioctl = ds1553_rtc_ioctl, }; -static ssize_t ds1553_nvram_read(struct kobject *kobj, char *buf, - loff_t pos, size_t size) +static ssize_t ds1553_nvram_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t size) { struct platform_device *pdev = to_platform_device(container_of(kobj, struct device, kobj)); @@ -272,8 +273,9 @@ static ssize_t ds1553_nvram_read(struct kobject *kobj, char *buf, return count; } -static ssize_t ds1553_nvram_write(struct kobject *kobj, char *buf, - loff_t pos, size_t size) +static ssize_t ds1553_nvram_write(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t size) { struct platform_device *pdev = to_platform_device(container_of(kobj, struct device, kobj)); diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c index 1638acdbc913..d1778ae8bca5 100644 --- a/drivers/rtc/rtc-ds1742.c +++ b/drivers/rtc/rtc-ds1742.c @@ -127,8 +127,9 @@ static const struct rtc_class_ops ds1742_rtc_ops = { .set_time = ds1742_rtc_set_time, }; -static ssize_t ds1742_nvram_read(struct kobject *kobj, char *buf, - loff_t pos, size_t size) +static ssize_t ds1742_nvram_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t size) { struct platform_device *pdev = to_platform_device(container_of(kobj, struct device, kobj)); @@ -141,8 +142,9 @@ static ssize_t ds1742_nvram_read(struct kobject *kobj, char *buf, return count; } -static ssize_t ds1742_nvram_write(struct kobject *kobj, char *buf, - loff_t pos, size_t size) +static ssize_t ds1742_nvram_write(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t pos, size_t size) { struct platform_device *pdev = to_platform_device(container_of(kobj, struct device, kobj)); diff --git a/drivers/s390/cio/chp.c b/drivers/s390/cio/chp.c index 96a8a72a6083..b57d93d986c0 100644 --- a/drivers/s390/cio/chp.c +++ b/drivers/s390/cio/chp.c @@ -141,8 +141,9 @@ static int s390_vary_chpid(struct chp_id chpid, int on) /* * Channel measurement related functions */ -static ssize_t chp_measurement_chars_read(struct kobject *kobj, char *buf, - loff_t off, size_t count) +static ssize_t chp_measurement_chars_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct channel_path *chp; unsigned int size; @@ -192,8 +193,9 @@ static void chp_measurement_copy_block(struct cmg_entry *buf, } while (reference_buf.values[0] != buf->values[0]); } -static ssize_t chp_measurement_read(struct kobject *kobj, char *buf, - loff_t off, size_t count) +static ssize_t chp_measurement_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct channel_path *chp; struct channel_subsystem *css; diff --git a/drivers/scsi/arcmsr/arcmsr_attr.c b/drivers/scsi/arcmsr/arcmsr_attr.c index 8908228bc134..06c0dce3b839 100644 --- a/drivers/scsi/arcmsr/arcmsr_attr.c +++ b/drivers/scsi/arcmsr/arcmsr_attr.c @@ -59,8 +59,9 @@ struct class_device_attribute *arcmsr_host_attrs[]; static ssize_t -arcmsr_sysfs_iop_message_read(struct kobject *kobj, char *buf, loff_t off, - size_t count) +arcmsr_sysfs_iop_message_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct class_device *cdev = container_of(kobj,struct class_device,kobj); struct Scsi_Host *host = class_to_shost(cdev); @@ -105,8 +106,9 @@ arcmsr_sysfs_iop_message_read(struct kobject *kobj, char *buf, loff_t off, } static ssize_t -arcmsr_sysfs_iop_message_write(struct kobject *kobj, char *buf, loff_t off, - size_t count) +arcmsr_sysfs_iop_message_write(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct class_device *cdev = container_of(kobj,struct class_device,kobj); struct Scsi_Host *host = class_to_shost(cdev); @@ -152,8 +154,9 @@ arcmsr_sysfs_iop_message_write(struct kobject *kobj, char *buf, loff_t off, } static ssize_t -arcmsr_sysfs_iop_message_clear(struct kobject *kobj, char *buf, loff_t off, - size_t count) +arcmsr_sysfs_iop_message_clear(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct class_device *cdev = container_of(kobj,struct class_device,kobj); struct Scsi_Host *host = class_to_shost(cdev); diff --git a/drivers/scsi/ipr.c b/drivers/scsi/ipr.c index fa6ff295e568..4a3083ea59d5 100644 --- a/drivers/scsi/ipr.c +++ b/drivers/scsi/ipr.c @@ -2465,6 +2465,7 @@ restart: /** * ipr_read_trace - Dump the adapter trace * @kobj: kobject struct + * @bin_attr: bin_attribute struct * @buf: buffer * @off: offset * @count: buffer size @@ -2472,8 +2473,9 @@ restart: * Return value: * number of bytes printed to buffer **/ -static ssize_t ipr_read_trace(struct kobject *kobj, char *buf, - loff_t off, size_t count) +static ssize_t ipr_read_trace(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct class_device *cdev = container_of(kobj,struct class_device,kobj); struct Scsi_Host *shost = class_to_shost(cdev); @@ -3166,6 +3168,7 @@ static struct class_device_attribute *ipr_ioa_attrs[] = { /** * ipr_read_dump - Dump the adapter * @kobj: kobject struct + * @bin_attr: bin_attribute struct * @buf: buffer * @off: offset * @count: buffer size @@ -3173,8 +3176,9 @@ static struct class_device_attribute *ipr_ioa_attrs[] = { * Return value: * number of bytes printed to buffer **/ -static ssize_t ipr_read_dump(struct kobject *kobj, char *buf, - loff_t off, size_t count) +static ssize_t ipr_read_dump(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct class_device *cdev = container_of(kobj,struct class_device,kobj); struct Scsi_Host *shost = class_to_shost(cdev); @@ -3327,6 +3331,7 @@ static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) /** * ipr_write_dump - Setup dump state of adapter * @kobj: kobject struct + * @bin_attr: bin_attribute struct * @buf: buffer * @off: offset * @count: buffer size @@ -3334,8 +3339,9 @@ static int ipr_free_dump(struct ipr_ioa_cfg *ioa_cfg) * Return value: * number of bytes printed to buffer **/ -static ssize_t ipr_write_dump(struct kobject *kobj, char *buf, - loff_t off, size_t count) +static ssize_t ipr_write_dump(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct class_device *cdev = container_of(kobj,struct class_device,kobj); struct Scsi_Host *shost = class_to_shost(cdev); diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 578ed79f4148..23e90c5f8f35 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -38,8 +38,10 @@ static int sas_disable_routing(struct domain_device *dev, u8 *sas_addr); #if 0 /* FIXME: smp needs to migrate into the sas class */ -static ssize_t smp_portal_read(struct kobject *, char *, loff_t, size_t); -static ssize_t smp_portal_write(struct kobject *, char *, loff_t, size_t); +static ssize_t smp_portal_read(struct kobject *, struct bin_attribute *, + char *, loff_t, size_t); +static ssize_t smp_portal_write(struct kobject *, struct bin_attribute *, + char *, loff_t, size_t); #endif /* ---------- SMP task management ---------- */ @@ -1845,8 +1847,9 @@ out: #if 0 /* ---------- SMP portal ---------- */ -static ssize_t smp_portal_write(struct kobject *kobj, char *buf, loff_t offs, - size_t size) +static ssize_t smp_portal_write(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t offs, size_t size) { struct domain_device *dev = to_dom_device(kobj); struct expander_device *ex = &dev->ex_dev; @@ -1872,8 +1875,9 @@ static ssize_t smp_portal_write(struct kobject *kobj, char *buf, loff_t offs, return size; } -static ssize_t smp_portal_read(struct kobject *kobj, char *buf, loff_t offs, - size_t size) +static ssize_t smp_portal_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t offs, size_t size) { struct domain_device *dev = to_dom_device(kobj); struct expander_device *ex = &dev->ex_dev; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index f81fe501a4a1..5dfda9778c80 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -1133,7 +1133,8 @@ struct class_device_attribute *lpfc_host_attrs[] = { }; static ssize_t -sysfs_ctlreg_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +sysfs_ctlreg_write(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { size_t buf_off; struct Scsi_Host *host = class_to_shost(container_of(kobj, @@ -1165,7 +1166,8 @@ sysfs_ctlreg_write(struct kobject *kobj, char *buf, loff_t off, size_t count) } static ssize_t -sysfs_ctlreg_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +sysfs_ctlreg_read(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { size_t buf_off; uint32_t * tmp_ptr; @@ -1221,7 +1223,8 @@ sysfs_mbox_idle (struct lpfc_hba * phba) } static ssize_t -sysfs_mbox_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +sysfs_mbox_write(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct Scsi_Host * host = class_to_shost(container_of(kobj, struct class_device, kobj)); @@ -1273,7 +1276,8 @@ sysfs_mbox_write(struct kobject *kobj, char *buf, loff_t off, size_t count) } static ssize_t -sysfs_mbox_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +sysfs_mbox_read(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct Scsi_Host *host = class_to_shost(container_of(kobj, struct class_device, diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 96587253bfa9..942db9de785e 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -11,8 +11,9 @@ /* SYSFS attributes --------------------------------------------------------- */ static ssize_t -qla2x00_sysfs_read_fw_dump(struct kobject *kobj, char *buf, loff_t off, - size_t count) +qla2x00_sysfs_read_fw_dump(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, struct device, kobj))); @@ -31,8 +32,9 @@ qla2x00_sysfs_read_fw_dump(struct kobject *kobj, char *buf, loff_t off, } static ssize_t -qla2x00_sysfs_write_fw_dump(struct kobject *kobj, char *buf, loff_t off, - size_t count) +qla2x00_sysfs_write_fw_dump(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, struct device, kobj))); @@ -80,8 +82,9 @@ static struct bin_attribute sysfs_fw_dump_attr = { }; static ssize_t -qla2x00_sysfs_read_nvram(struct kobject *kobj, char *buf, loff_t off, - size_t count) +qla2x00_sysfs_read_nvram(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, struct device, kobj))); @@ -100,8 +103,9 @@ qla2x00_sysfs_read_nvram(struct kobject *kobj, char *buf, loff_t off, } static ssize_t -qla2x00_sysfs_write_nvram(struct kobject *kobj, char *buf, loff_t off, - size_t count) +qla2x00_sysfs_write_nvram(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, struct device, kobj))); @@ -155,8 +159,9 @@ static struct bin_attribute sysfs_nvram_attr = { }; static ssize_t -qla2x00_sysfs_read_optrom(struct kobject *kobj, char *buf, loff_t off, - size_t count) +qla2x00_sysfs_read_optrom(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, struct device, kobj))); @@ -174,8 +179,9 @@ qla2x00_sysfs_read_optrom(struct kobject *kobj, char *buf, loff_t off, } static ssize_t -qla2x00_sysfs_write_optrom(struct kobject *kobj, char *buf, loff_t off, - size_t count) +qla2x00_sysfs_write_optrom(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, struct device, kobj))); @@ -203,8 +209,9 @@ static struct bin_attribute sysfs_optrom_attr = { }; static ssize_t -qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj, char *buf, loff_t off, - size_t count) +qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, struct device, kobj))); @@ -282,8 +289,9 @@ static struct bin_attribute sysfs_optrom_ctl_attr = { }; static ssize_t -qla2x00_sysfs_read_vpd(struct kobject *kobj, char *buf, loff_t off, - size_t count) +qla2x00_sysfs_read_vpd(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, struct device, kobj))); @@ -301,8 +309,9 @@ qla2x00_sysfs_read_vpd(struct kobject *kobj, char *buf, loff_t off, } static ssize_t -qla2x00_sysfs_write_vpd(struct kobject *kobj, char *buf, loff_t off, - size_t count) +qla2x00_sysfs_write_vpd(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, struct device, kobj))); @@ -330,8 +339,9 @@ static struct bin_attribute sysfs_vpd_attr = { }; static ssize_t -qla2x00_sysfs_read_sfp(struct kobject *kobj, char *buf, loff_t off, - size_t count) +qla2x00_sysfs_read_sfp(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, struct device, kobj))); diff --git a/drivers/spi/at25.c b/drivers/spi/at25.c index fde1dededba3..e007833cca59 100644 --- a/drivers/spi/at25.c +++ b/drivers/spi/at25.c @@ -111,7 +111,8 @@ at25_ee_read( } static ssize_t -at25_bin_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +at25_bin_read(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct device *dev; struct at25_data *at25; @@ -236,7 +237,8 @@ at25_ee_write(struct at25_data *at25, char *buf, loff_t off, size_t count) } static ssize_t -at25_bin_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +at25_bin_write(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct device *dev; struct at25_data *at25; diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c index 3b3c6571f583..2349e71b0083 100644 --- a/drivers/video/aty/radeon_base.c +++ b/drivers/video/aty/radeon_base.c @@ -2102,7 +2102,9 @@ static ssize_t radeon_show_one_edid(char *buf, loff_t off, size_t count, const u } -static ssize_t radeon_show_edid1(struct kobject *kobj, char *buf, loff_t off, size_t count) +static ssize_t radeon_show_edid1(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); struct pci_dev *pdev = to_pci_dev(dev); @@ -2113,7 +2115,9 @@ static ssize_t radeon_show_edid1(struct kobject *kobj, char *buf, loff_t off, si } -static ssize_t radeon_show_edid2(struct kobject *kobj, char *buf, loff_t off, size_t count) +static ssize_t radeon_show_edid2(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct device *dev = container_of(kobj, struct device, kobj); struct pci_dev *pdev = to_pci_dev(dev); diff --git a/drivers/w1/slaves/w1_ds2433.c b/drivers/w1/slaves/w1_ds2433.c index 4e13aa71adea..cab56005dd49 100644 --- a/drivers/w1/slaves/w1_ds2433.c +++ b/drivers/w1/slaves/w1_ds2433.c @@ -91,8 +91,9 @@ static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data, } #endif /* CONFIG_W1_SLAVE_DS2433_CRC */ -static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off, - size_t count) +static ssize_t w1_f23_read_bin(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); #ifdef CONFIG_W1_SLAVE_DS2433_CRC @@ -199,8 +200,9 @@ static int w1_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data) return 0; } -static ssize_t w1_f23_write_bin(struct kobject *kobj, char *buf, loff_t off, - size_t count) +static ssize_t w1_f23_write_bin(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); int addr, len, idx; diff --git a/drivers/w1/slaves/w1_therm.c b/drivers/w1/slaves/w1_therm.c index 8ba4e572e09c..4318935678c5 100644 --- a/drivers/w1/slaves/w1_therm.c +++ b/drivers/w1/slaves/w1_therm.c @@ -42,7 +42,8 @@ static u8 bad_roms[][9] = { {} }; -static ssize_t w1_therm_read_bin(struct kobject *, char *, loff_t, size_t); +static ssize_t w1_therm_read_bin(struct kobject *, struct bin_attribute *, + char *, loff_t, size_t); static struct bin_attribute w1_therm_bin_attr = { .attr = { @@ -158,7 +159,9 @@ static int w1_therm_check_rom(u8 rom[9]) return 0; } -static ssize_t w1_therm_read_bin(struct kobject *kobj, char *buf, loff_t off, size_t count) +static ssize_t w1_therm_read_bin(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); struct w1_master *dev = sl->master; diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c index 1838cb29b646..f5c5b760ed7b 100644 --- a/drivers/w1/w1.c +++ b/drivers/w1/w1.c @@ -105,7 +105,9 @@ static ssize_t w1_slave_read_name(struct device *dev, struct device_attribute *a return sprintf(buf, "%s\n", sl->name); } -static ssize_t w1_slave_read_id(struct kobject *kobj, char *buf, loff_t off, size_t count) +static ssize_t w1_slave_read_id(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -135,7 +137,9 @@ static struct bin_attribute w1_slave_attr_bin_id = { /* Default family */ -static ssize_t w1_default_write(struct kobject *kobj, char *buf, loff_t off, size_t count) +static ssize_t w1_default_write(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); @@ -152,7 +156,9 @@ out_up: return count; } -static ssize_t w1_default_read(struct kobject *kobj, char *buf, loff_t off, size_t count) +static ssize_t w1_default_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct w1_slave *sl = kobj_to_w1_slave(kobj); diff --git a/drivers/zorro/zorro-sysfs.c b/drivers/zorro/zorro-sysfs.c index 7e03cc68b182..9130f1c12c26 100644 --- a/drivers/zorro/zorro-sysfs.c +++ b/drivers/zorro/zorro-sysfs.c @@ -49,8 +49,9 @@ static ssize_t zorro_show_resource(struct device *dev, struct device_attribute * static DEVICE_ATTR(resource, S_IRUGO, zorro_show_resource, NULL); -static ssize_t zorro_read_config(struct kobject *kobj, char *buf, loff_t off, - size_t count) +static ssize_t zorro_read_config(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct zorro_dev *z = to_zorro_dev(container_of(kobj, struct device, kobj)); diff --git a/fs/sysfs/bin.c b/fs/sysfs/bin.c index 55796bdacd3d..135353f8a296 100644 --- a/fs/sysfs/bin.c +++ b/fs/sysfs/bin.c @@ -40,7 +40,7 @@ fill_read(struct dentry *dentry, char *buffer, loff_t off, size_t count) rc = -EIO; if (attr->read) - rc = attr->read(kobj, buffer, off, count); + rc = attr->read(kobj, attr, buffer, off, count); sysfs_put_active_two(attr_sd); @@ -97,7 +97,7 @@ flush_write(struct dentry *dentry, char *buffer, loff_t offset, size_t count) rc = -EIO; if (attr->write) - rc = attr->write(kobj, buffer, offset, count); + rc = attr->write(kobj, attr, buffer, offset, count); sysfs_put_active_two(attr_sd); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 2f58ca1af770..be8228e50a27 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -64,8 +64,10 @@ struct bin_attribute { struct attribute attr; size_t size; void *private; - ssize_t (*read)(struct kobject *, char *, loff_t, size_t); - ssize_t (*write)(struct kobject *, char *, loff_t, size_t); + ssize_t (*read)(struct kobject *, struct bin_attribute *, + char *, loff_t, size_t); + ssize_t (*write)(struct kobject *, struct bin_attribute *, + char *, loff_t, size_t); int (*mmap)(struct kobject *, struct bin_attribute *attr, struct vm_area_struct *vma); }; diff --git a/net/bridge/br_sysfs_br.c b/net/bridge/br_sysfs_br.c index 31ace23a0914..4f42263e0a8a 100644 --- a/net/bridge/br_sysfs_br.c +++ b/net/bridge/br_sysfs_br.c @@ -360,8 +360,9 @@ static struct attribute_group bridge_group = { * * Returns the number of bytes read. */ -static ssize_t brforward_read(struct kobject *kobj, char *buf, - loff_t off, size_t count) +static ssize_t brforward_read(struct kobject *kobj, + struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) { struct device *dev = to_dev(kobj); struct net_bridge *br = to_bridge(dev);