From c84a3b27798dfce928b867fa1c9f3c3fd66f0a31 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 23 Nov 2013 18:01:46 -0500 Subject: sysfs: drop kobj_ns_type handling, take #2 The way namespace tags are implemented in sysfs is more complicated than necessary. As each tag is a pointer value and required to be non-NULL under a namespace enabled parent, there's no need to record separately what type each tag is. If multiple namespace types are needed, which currently aren't, we can simply compare the tag to a set of allowed tags in the superblock assuming that the tags, being pointers, won't have the same value across multiple types. This patch rips out kobj_ns_type handling from sysfs. sysfs now has an enable switch to turn on namespace under a node. If enabled, all children are required to have non-NULL namespace tags and filtered against the super_block's tag. kobject namespace determination is now performed in lib/kobject.c::create_dir() making sysfs_read_ns_type() unnecessary. The sanity checks are also moved. create_dir() is restructured to ease such addition. This removes most kobject namespace knowledge from sysfs proper which will enable proper separation and layering of sysfs. This is the second try. The first one was cb26a311578e ("sysfs: drop kobj_ns_type handling") which tried to automatically enable namespace if there are children with non-NULL namespace tags; however, it was broken for symlinks as they should inherit the target's tag iff namespace is enabled in the parent. This led to namespace filtering enabled incorrectly for wireless net class devices through phy80211 symlinks and thus network configuration failure. a1212d278c05 ("Revert "sysfs: drop kobj_ns_type handling"") reverted the commit. This shouldn't introduce any behavior changes, for real. v2: Dummy implementation of sysfs_enable_ns() for !CONFIG_SYSFS was missing and caused build failure. Reported by kbuild test robot. Signed-off-by: Tejun Heo Reported-by: Linus Torvalds Cc: Eric W. Biederman Cc: Kay Sievers Cc: Greg Kroah-Hartman Cc: kbuild test robot Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 92 ++++++++++++++++++++------------------------------- fs/sysfs/mount.c | 24 ++++---------- fs/sysfs/symlink.c | 26 ++++----------- fs/sysfs/sysfs.h | 25 ++++---------- include/linux/sysfs.h | 6 ++++ lib/kobject.c | 27 ++++++++++++--- 6 files changed, 83 insertions(+), 117 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 5e73d66..b3cf61d 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -279,7 +279,6 @@ static int sysfs_dentry_delete(const struct dentry *dentry) static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) { struct sysfs_dirent *sd; - int type; if (flags & LOOKUP_RCU) return -ECHILD; @@ -300,13 +299,9 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) goto out_bad; /* The sysfs dirent has been moved to a different namespace */ - type = KOBJ_NS_TYPE_NONE; - if (sd->s_parent) { - type = sysfs_ns_type(sd->s_parent); - if (type != KOBJ_NS_TYPE_NONE && - sysfs_info(dentry->d_sb)->ns[type] != sd->s_ns) - goto out_bad; - } + if (sd->s_parent && (sd->s_parent->s_flags & SYSFS_FLAG_NS) && + sysfs_info(dentry->d_sb)->ns != sd->s_ns) + goto out_bad; mutex_unlock(&sysfs_mutex); out_valid: @@ -423,13 +418,14 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd) { + bool has_ns = parent_sd->s_flags & SYSFS_FLAG_NS; struct sysfs_inode_attrs *ps_iattr; int ret; - if (!!sysfs_ns_type(parent_sd) != !!sd->s_ns) { + if (has_ns != (bool)sd->s_ns) { WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", - sysfs_ns_type(parent_sd) ? "required" : "invalid", - parent_sd->s_name, sd->s_name); + has_ns ? "required" : "invalid", + parent_sd->s_name, sd->s_name); return -EINVAL; } @@ -610,12 +606,13 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, const void *ns) { struct rb_node *node = parent_sd->s_dir.children.rb_node; + bool has_ns = parent_sd->s_flags & SYSFS_FLAG_NS; unsigned int hash; - if (!!sysfs_ns_type(parent_sd) != !!ns) { + if (has_ns != (bool)ns) { WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", - sysfs_ns_type(parent_sd) ? "required" : "invalid", - parent_sd->s_name, name); + has_ns ? "required" : "invalid", + parent_sd->s_name, name); return NULL; } @@ -667,7 +664,6 @@ struct sysfs_dirent *sysfs_get_dirent_ns(struct sysfs_dirent *parent_sd, EXPORT_SYMBOL_GPL(sysfs_get_dirent_ns); static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, - enum kobj_ns_type type, const char *name, const void *ns, struct sysfs_dirent **p_sd) { @@ -681,7 +677,6 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, if (!sd) return -ENOMEM; - sd->s_flags |= (type << SYSFS_NS_TYPE_SHIFT); sd->s_ns = ns; sd->s_dir.kobj = kobj; @@ -701,33 +696,7 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, int sysfs_create_subdir(struct kobject *kobj, const char *name, struct sysfs_dirent **p_sd) { - return create_dir(kobj, kobj->sd, - KOBJ_NS_TYPE_NONE, name, NULL, p_sd); -} - -/** - * sysfs_read_ns_type: return associated ns_type - * @kobj: the kobject being queried - * - * Each kobject can be tagged with exactly one namespace type - * (i.e. network or user). Return the ns_type associated with - * this object if any - */ -static enum kobj_ns_type sysfs_read_ns_type(struct kobject *kobj) -{ - const struct kobj_ns_type_operations *ops; - enum kobj_ns_type type; - - ops = kobj_child_ns_ops(kobj); - if (!ops) - return KOBJ_NS_TYPE_NONE; - - type = ops->type; - BUG_ON(type <= KOBJ_NS_TYPE_NONE); - BUG_ON(type >= KOBJ_NS_TYPES); - BUG_ON(!kobj_ns_type_registered(type)); - - return type; + return create_dir(kobj, kobj->sd, name, NULL, p_sd); } /** @@ -737,7 +706,6 @@ static enum kobj_ns_type sysfs_read_ns_type(struct kobject *kobj) */ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) { - enum kobj_ns_type type; struct sysfs_dirent *parent_sd, *sd; int error = 0; @@ -751,9 +719,7 @@ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) if (!parent_sd) return -ENOENT; - type = sysfs_read_ns_type(kobj); - - error = create_dir(kobj, parent_sd, type, kobject_name(kobj), ns, &sd); + error = create_dir(kobj, parent_sd, kobject_name(kobj), ns, &sd); if (!error) kobj->sd = sd; return error; @@ -767,13 +733,12 @@ static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, struct sysfs_dirent *parent_sd = parent->d_fsdata; struct sysfs_dirent *sd; struct inode *inode; - enum kobj_ns_type type; - const void *ns; + const void *ns = NULL; mutex_lock(&sysfs_mutex); - type = sysfs_ns_type(parent_sd); - ns = sysfs_info(dir->i_sb)->ns[type]; + if (parent_sd->s_flags & SYSFS_FLAG_NS) + ns = sysfs_info(dir->i_sb)->ns; sd = sysfs_find_dirent(parent_sd, dentry->d_name.name, ns); @@ -1029,6 +994,21 @@ int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, return sysfs_rename(sd, new_parent_sd, sd->s_name, new_ns); } +/** + * sysfs_enable_ns - enable namespace under a directory + * @sd: directory of interest, should be empty + * + * This is to be called right after @sd is created to enable namespace + * under it. All children of @sd must have non-NULL namespace tags and + * only the ones which match the super_block's tag will be visible. + */ +void sysfs_enable_ns(struct sysfs_dirent *sd) +{ + WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); + WARN_ON_ONCE(!RB_EMPTY_ROOT(&sd->s_dir.children)); + sd->s_flags |= SYSFS_FLAG_NS; +} + /* Relationship between s_mode and the DT_xxx types */ static inline unsigned char dt_type(struct sysfs_dirent *sd) { @@ -1096,15 +1076,15 @@ static int sysfs_readdir(struct file *file, struct dir_context *ctx) struct dentry *dentry = file->f_path.dentry; struct sysfs_dirent *parent_sd = dentry->d_fsdata; struct sysfs_dirent *pos = file->private_data; - enum kobj_ns_type type; - const void *ns; - - type = sysfs_ns_type(parent_sd); - ns = sysfs_info(dentry->d_sb)->ns[type]; + const void *ns = NULL; if (!dir_emit_dots(file, ctx)) return 0; mutex_lock(&sysfs_mutex); + + if (parent_sd->s_flags & SYSFS_FLAG_NS) + ns = sysfs_info(dentry->d_sb)->ns; + for (pos = sysfs_dir_pos(ns, parent_sd, ctx->pos, pos); pos; pos = sysfs_dir_next_pos(ns, parent_sd, ctx->pos, pos)) { diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 834ec2c..8c24bce 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -36,7 +36,7 @@ static const struct super_operations sysfs_ops = { struct sysfs_dirent sysfs_root = { .s_name = "", .s_count = ATOMIC_INIT(1), - .s_flags = SYSFS_DIR | (KOBJ_NS_TYPE_NONE << SYSFS_NS_TYPE_SHIFT), + .s_flags = SYSFS_DIR, .s_mode = S_IFDIR | S_IRUGO | S_IXUGO, .s_ino = 1, }; @@ -77,14 +77,8 @@ static int sysfs_test_super(struct super_block *sb, void *data) { struct sysfs_super_info *sb_info = sysfs_info(sb); struct sysfs_super_info *info = data; - enum kobj_ns_type type; - int found = 1; - for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) { - if (sb_info->ns[type] != info->ns[type]) - found = 0; - } - return found; + return sb_info->ns == info->ns; } static int sysfs_set_super(struct super_block *sb, void *data) @@ -98,9 +92,7 @@ static int sysfs_set_super(struct super_block *sb, void *data) static void free_sysfs_super_info(struct sysfs_super_info *info) { - int type; - for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) - kobj_ns_drop(type, info->ns[type]); + kobj_ns_drop(KOBJ_NS_TYPE_NET, info->ns); kfree(info); } @@ -108,7 +100,6 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { struct sysfs_super_info *info; - enum kobj_ns_type type; struct super_block *sb; int error; @@ -116,18 +107,15 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type)) return ERR_PTR(-EPERM); - for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) { - if (!kobj_ns_current_may_mount(type)) - return ERR_PTR(-EPERM); - } + if (!kobj_ns_current_may_mount(KOBJ_NS_TYPE_NET)) + return ERR_PTR(-EPERM); } info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return ERR_PTR(-ENOMEM); - for (type = KOBJ_NS_TYPE_NONE; type < KOBJ_NS_TYPES; type++) - info->ns[type] = kobj_ns_grab_current(type); + info->ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET); sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info); if (IS_ERR(sb) || sb->s_fs_info != info) diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 3ae3f1b..c660363 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -28,7 +28,6 @@ static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, struct sysfs_dirent *target_sd = NULL; struct sysfs_dirent *sd = NULL; struct sysfs_addrm_cxt acxt; - enum kobj_ns_type ns_type; int error; BUG_ON(!name || !parent_sd); @@ -52,29 +51,16 @@ static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, if (!sd) goto out_put; - ns_type = sysfs_ns_type(parent_sd); - if (ns_type) + if (parent_sd->s_flags & SYSFS_FLAG_NS) sd->s_ns = target_sd->s_ns; sd->s_symlink.target_sd = target_sd; target_sd = NULL; /* reference is now owned by the symlink */ sysfs_addrm_start(&acxt); - /* Symlinks must be between directories with the same ns_type */ - if (!ns_type || - (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent))) { - if (warn) - error = sysfs_add_one(&acxt, sd, parent_sd); - else - error = __sysfs_add_one(&acxt, sd, parent_sd); - } else { - error = -EINVAL; - WARN(1, KERN_WARNING - "sysfs: symlink across ns_types %s/%s -> %s/%s\n", - parent_sd->s_name, - sd->s_name, - sd->s_symlink.target_sd->s_parent->s_name, - sd->s_symlink.target_sd->s_name); - } + if (warn) + error = sysfs_add_one(&acxt, sd, parent_sd); + else + error = __sysfs_add_one(&acxt, sd, parent_sd); sysfs_addrm_finish(&acxt); if (error) @@ -164,7 +150,7 @@ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, * sysfs_remove_dir() for details. */ spin_lock(&sysfs_symlink_target_lock); - if (targ->sd && sysfs_ns_type(kobj->sd)) + if (targ->sd && (kobj->sd->s_flags & SYSFS_FLAG_NS)) ns = targ->sd->s_ns; spin_unlock(&sysfs_symlink_target_lock); sysfs_hash_and_remove(kobj->sd, name, ns); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 0af09fb..e116c21 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -90,11 +90,8 @@ struct sysfs_dirent { #define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) #define SYSFS_ACTIVE_REF (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR) -/* identify any namespace tag on sysfs_dirents */ -#define SYSFS_NS_TYPE_MASK 0xf00 -#define SYSFS_NS_TYPE_SHIFT 8 - -#define SYSFS_FLAG_MASK ~(SYSFS_NS_TYPE_MASK|SYSFS_TYPE_MASK) +#define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK +#define SYSFS_FLAG_NS 0x01000 #define SYSFS_FLAG_REMOVED 0x02000 static inline unsigned int sysfs_type(struct sysfs_dirent *sd) @@ -102,15 +99,6 @@ static inline unsigned int sysfs_type(struct sysfs_dirent *sd) return sd->s_flags & SYSFS_TYPE_MASK; } -/* - * Return any namespace tags on this dirent. - * enum kobj_ns_type is defined in linux/kobject.h - */ -static inline enum kobj_ns_type sysfs_ns_type(struct sysfs_dirent *sd) -{ - return (sd->s_flags & SYSFS_NS_TYPE_MASK) >> SYSFS_NS_TYPE_SHIFT; -} - #ifdef CONFIG_DEBUG_LOCK_ALLOC #define sysfs_dirent_init_lockdep(sd) \ @@ -155,12 +143,13 @@ struct sysfs_addrm_cxt { */ /* - * Each sb is associated with a set of namespace tags (i.e. - * the network namespace of the task which mounted this sysfs - * instance). + * Each sb is associated with one namespace tag, currently the network + * namespace of the task which mounted this sysfs instance. If multiple + * tags become necessary, make the following an array and compare + * sysfs_dirent tag against every entry. */ struct sysfs_super_info { - void *ns[KOBJ_NS_TYPES]; + void *ns; }; #define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info)) extern struct sysfs_dirent sysfs_root; diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 6695040..362a34d 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -220,6 +220,8 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *target, void sysfs_delete_link(struct kobject *dir, struct kobject *targ, const char *name); +void sysfs_enable_ns(struct sysfs_dirent *sd); + int __must_check sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp); int __must_check sysfs_create_groups(struct kobject *kobj, @@ -353,6 +355,10 @@ static inline void sysfs_delete_link(struct kobject *k, struct kobject *t, { } +static inline void sysfs_enable_ns(struct sysfs_dirent *sd) +{ +} + static inline int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) { diff --git a/lib/kobject.c b/lib/kobject.c index 5b4b888..16e9335 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -65,13 +65,17 @@ static int populate_dir(struct kobject *kobj) static int create_dir(struct kobject *kobj) { + const struct kobj_ns_type_operations *ops; int error; error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj)); - if (!error) { - error = populate_dir(kobj); - if (error) - sysfs_remove_dir(kobj); + if (error) + return error; + + error = populate_dir(kobj); + if (error) { + sysfs_remove_dir(kobj); + return error; } /* @@ -80,7 +84,20 @@ static int create_dir(struct kobject *kobj) */ sysfs_get(kobj->sd); - return error; + /* + * If @kobj has ns_ops, its children need to be filtered based on + * their namespace tags. Enable namespace support on @kobj->sd. + */ + ops = kobj_child_ns_ops(kobj); + if (ops) { + BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE); + BUG_ON(ops->type >= KOBJ_NS_TYPES); + BUG_ON(!kobj_ns_type_registered(ops->type)); + + sysfs_enable_ns(kobj->sd); + } + + return 0; } static int get_kobj_path_length(struct kobject *kobj) -- cgit v1.1 From ae2108ad32f5ca55e9895d5597e6552be1607569 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 23 Nov 2013 17:21:47 -0500 Subject: sysfs: make __sysfs_add_one() fail if the parent isn't a directory Currently the kobject based interface guarantees that a parent sysfs_dirent is always a directory; however, the planned kernfs interface will be directly based on sysfs_dirents and the caller may specify non-directory node as the parent. Add an explicit check in __sysfs_add_one() so that such attempts fail with -EINVAL. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index b3cf61d..98701c0 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -429,6 +429,9 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, return -EINVAL; } + if (sysfs_type(parent_sd) != SYSFS_DIR) + return -EINVAL; + sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); sd->s_parent = sysfs_get(parent_sd); -- cgit v1.1 From b8441ed279bff09a0a5ddeacf8f4087d2fb424ca Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sun, 24 Nov 2013 09:54:58 -0500 Subject: sysfs, kernfs: add skeletons for kernfs Core sysfs implementation will be separated into kernfs so that it can be used by other non-kobject users. This patch creates fs/kernfs/ directory and makes boilerplate changes. kernfs interface will be directly based on sysfs_dirent and its forward declaration is moved to include/linux/kernfs.h which is included from include/linux/sysfs.h. sysfs core implementation will be gradually separated out and moved to kernfs. This patch doesn't introduce any functional changes. v2: mount.c added. Signed-off-by: Tejun Heo Cc: linux-fsdevel@vger.kernel.org Cc: Christoph Hellwig Signed-off-by: Greg Kroah-Hartman --- fs/Makefile | 2 +- fs/kernfs/Makefile | 5 +++++ fs/kernfs/dir.c | 9 +++++++++ fs/kernfs/file.c | 9 +++++++++ fs/kernfs/inode.c | 9 +++++++++ fs/kernfs/mount.c | 9 +++++++++ fs/kernfs/symlink.c | 9 +++++++++ include/linux/kernfs.h | 12 ++++++++++++ include/linux/sysfs.h | 3 +-- 9 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 fs/kernfs/Makefile create mode 100644 fs/kernfs/dir.c create mode 100644 fs/kernfs/file.c create mode 100644 fs/kernfs/inode.c create mode 100644 fs/kernfs/mount.c create mode 100644 fs/kernfs/symlink.c create mode 100644 include/linux/kernfs.h diff --git a/fs/Makefile b/fs/Makefile index 4fe6df3..39a824f 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -53,7 +53,7 @@ obj-$(CONFIG_FHANDLE) += fhandle.o obj-y += quota/ obj-$(CONFIG_PROC_FS) += proc/ -obj-$(CONFIG_SYSFS) += sysfs/ +obj-$(CONFIG_SYSFS) += sysfs/ kernfs/ obj-$(CONFIG_CONFIGFS_FS) += configfs/ obj-y += devpts/ diff --git a/fs/kernfs/Makefile b/fs/kernfs/Makefile new file mode 100644 index 0000000..674337c --- /dev/null +++ b/fs/kernfs/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for the kernfs pseudo filesystem +# + +obj-y := mount.o inode.o dir.o file.o symlink.o diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c new file mode 100644 index 0000000..1061602 --- /dev/null +++ b/fs/kernfs/dir.c @@ -0,0 +1,9 @@ +/* + * fs/kernfs/dir.c - kernfs directory implementation + * + * Copyright (c) 2001-3 Patrick Mochel + * Copyright (c) 2007 SUSE Linux Products GmbH + * Copyright (c) 2007, 2013 Tejun Heo + * + * This file is released under the GPLv2. + */ diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c new file mode 100644 index 0000000..90b1e88 --- /dev/null +++ b/fs/kernfs/file.c @@ -0,0 +1,9 @@ +/* + * fs/kernfs/file.c - kernfs file implementation + * + * Copyright (c) 2001-3 Patrick Mochel + * Copyright (c) 2007 SUSE Linux Products GmbH + * Copyright (c) 2007, 2013 Tejun Heo + * + * This file is released under the GPLv2. + */ diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c new file mode 100644 index 0000000..86bfeea --- /dev/null +++ b/fs/kernfs/inode.c @@ -0,0 +1,9 @@ +/* + * fs/kernfs/inode.c - kernfs inode implementation + * + * Copyright (c) 2001-3 Patrick Mochel + * Copyright (c) 2007 SUSE Linux Products GmbH + * Copyright (c) 2007, 2013 Tejun Heo + * + * This file is released under the GPLv2. + */ diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c new file mode 100644 index 0000000..872e262 --- /dev/null +++ b/fs/kernfs/mount.c @@ -0,0 +1,9 @@ +/* + * fs/kernfs/mount.c - kernfs mount implementation + * + * Copyright (c) 2001-3 Patrick Mochel + * Copyright (c) 2007 SUSE Linux Products GmbH + * Copyright (c) 2007, 2013 Tejun Heo + * + * This file is released under the GPLv2. + */ diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c new file mode 100644 index 0000000..2578715 --- /dev/null +++ b/fs/kernfs/symlink.c @@ -0,0 +1,9 @@ +/* + * fs/kernfs/symlink.c - kernfs symlink implementation + * + * Copyright (c) 2001-3 Patrick Mochel + * Copyright (c) 2007 SUSE Linux Products GmbH + * Copyright (c) 2007, 2013 Tejun Heo + * + * This file is released under the GPLv2. + */ diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h new file mode 100644 index 0000000..254b9e8 --- /dev/null +++ b/include/linux/kernfs.h @@ -0,0 +1,12 @@ +/* + * kernfs.h - pseudo filesystem decoupled from vfs locking + * + * This file is released under the GPLv2. + */ + +#ifndef __LINUX_KERNFS_H +#define __LINUX_KERNFS_H + +struct sysfs_dirent; + +#endif /* __LINUX_KERNFS_H */ diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 362a34d..e17381a 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -12,6 +12,7 @@ #ifndef _SYSFS_H_ #define _SYSFS_H_ +#include #include #include #include @@ -175,8 +176,6 @@ struct sysfs_ops { ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t); }; -struct sysfs_dirent; - #ifdef CONFIG_SYSFS int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), -- cgit v1.1 From 879f40d193bb3c6c13930e88e3e9d5d7baf84d19 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 23 Nov 2013 17:21:49 -0500 Subject: sysfs, kernfs: introduce kernfs_remove[_by_name[_ns]]() Introduce kernfs removal interfaces - kernfs_remove() and kernfs_remove_by_name[_ns](). These are just renames of sysfs_remove() and sysfs_hash_and_remove(). No functional changes. v2: Dummy kernfs_remove_by_name_ns() for !CONFIG_SYSFS updated to return -ENOSYS instead of 0. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 20 ++++++++++---------- fs/sysfs/file.c | 6 +++--- fs/sysfs/group.c | 15 +++++++-------- fs/sysfs/symlink.c | 4 ++-- fs/sysfs/sysfs.h | 3 --- include/linux/kernfs.h | 24 ++++++++++++++++++++++++ 6 files changed, 46 insertions(+), 26 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 98701c0..edbde4e 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -829,8 +829,8 @@ static struct sysfs_dirent *sysfs_next_descendant_post(struct sysfs_dirent *pos, return pos->s_parent; } -static void __sysfs_remove(struct sysfs_addrm_cxt *acxt, - struct sysfs_dirent *sd) +static void __kernfs_remove(struct sysfs_addrm_cxt *acxt, + struct sysfs_dirent *sd) { struct sysfs_dirent *pos, *next; @@ -849,22 +849,22 @@ static void __sysfs_remove(struct sysfs_addrm_cxt *acxt, } /** - * sysfs_remove - remove a sysfs_dirent recursively + * kernfs_remove - remove a sysfs_dirent recursively * @sd: the sysfs_dirent to remove * * Remove @sd along with all its subdirectories and files. */ -void sysfs_remove(struct sysfs_dirent *sd) +void kernfs_remove(struct sysfs_dirent *sd) { struct sysfs_addrm_cxt acxt; sysfs_addrm_start(&acxt); - __sysfs_remove(&acxt, sd); + __kernfs_remove(&acxt, sd); sysfs_addrm_finish(&acxt); } /** - * sysfs_hash_and_remove - find a sysfs_dirent by name and remove it + * kernfs_remove_by_name_ns - find a sysfs_dirent by name and remove it * @dir_sd: parent of the target * @name: name of the sysfs_dirent to remove * @ns: namespace tag of the sysfs_dirent to remove @@ -872,8 +872,8 @@ void sysfs_remove(struct sysfs_dirent *sd) * Look for the sysfs_dirent with @name and @ns under @dir_sd and remove * it. Returns 0 on success, -ENOENT if such entry doesn't exist. */ -int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name, - const void *ns) +int kernfs_remove_by_name_ns(struct sysfs_dirent *dir_sd, const char *name, + const void *ns) { struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; @@ -888,7 +888,7 @@ int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name, sd = sysfs_find_dirent(dir_sd, name, ns); if (sd) - __sysfs_remove(&acxt, sd); + __kernfs_remove(&acxt, sd); sysfs_addrm_finish(&acxt); @@ -928,7 +928,7 @@ void sysfs_remove_dir(struct kobject *kobj) if (sd) { WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); - sysfs_remove(sd); + kernfs_remove(sd); } } diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 79b5da2..5664410 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -952,7 +952,7 @@ void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, { struct sysfs_dirent *dir_sd = kobj->sd; - sysfs_hash_and_remove(dir_sd, attr->name, ns); + kernfs_remove_by_name_ns(dir_sd, attr->name, ns); } EXPORT_SYMBOL_GPL(sysfs_remove_file_ns); @@ -980,7 +980,7 @@ void sysfs_remove_file_from_group(struct kobject *kobj, else dir_sd = sysfs_get(kobj->sd); if (dir_sd) { - sysfs_hash_and_remove(dir_sd, attr->name, NULL); + kernfs_remove_by_name(dir_sd, attr->name); sysfs_put(dir_sd); } } @@ -1008,7 +1008,7 @@ EXPORT_SYMBOL_GPL(sysfs_create_bin_file); void sysfs_remove_bin_file(struct kobject *kobj, const struct bin_attribute *attr) { - sysfs_hash_and_remove(kobj->sd, attr->attr.name, NULL); + kernfs_remove_by_name(kobj->sd, attr->attr.name); } EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 1898a10..4bd9973 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -26,7 +26,7 @@ static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, if (grp->attrs) for (attr = grp->attrs; *attr; attr++) - sysfs_hash_and_remove(dir_sd, (*attr)->name, NULL); + kernfs_remove_by_name(dir_sd, (*attr)->name); if (grp->bin_attrs) for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) sysfs_remove_bin_file(kobj, *bin_attr); @@ -49,8 +49,7 @@ static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, * re-adding (if required) the file. */ if (update) - sysfs_hash_and_remove(dir_sd, (*attr)->name, - NULL); + kernfs_remove_by_name(dir_sd, (*attr)->name); if (grp->is_visible) { mode = grp->is_visible(kobj, *attr, i); if (!mode) @@ -111,7 +110,7 @@ static int internal_create_group(struct kobject *kobj, int update, error = create_files(sd, kobj, grp, update); if (error) { if (grp->name) - sysfs_remove(sd); + kernfs_remove(sd); } sysfs_put(sd); return error; @@ -219,7 +218,7 @@ void sysfs_remove_group(struct kobject *kobj, remove_files(sd, kobj, grp); if (grp->name) - sysfs_remove(sd); + kernfs_remove(sd); sysfs_put(sd); } @@ -270,7 +269,7 @@ int sysfs_merge_group(struct kobject *kobj, error = sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR); if (error) { while (--i >= 0) - sysfs_hash_and_remove(dir_sd, (*--attr)->name, NULL); + kernfs_remove_by_name(dir_sd, (*--attr)->name); } sysfs_put(dir_sd); @@ -292,7 +291,7 @@ void sysfs_unmerge_group(struct kobject *kobj, dir_sd = sysfs_get_dirent(kobj->sd, grp->name); if (dir_sd) { for (attr = grp->attrs; *attr; ++attr) - sysfs_hash_and_remove(dir_sd, (*attr)->name, NULL); + kernfs_remove_by_name(dir_sd, (*attr)->name); sysfs_put(dir_sd); } } @@ -335,7 +334,7 @@ void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, dir_sd = sysfs_get_dirent(kobj->sd, group_name); if (dir_sd) { - sysfs_hash_and_remove(dir_sd, link_name, NULL); + kernfs_remove_by_name(dir_sd, link_name); sysfs_put(dir_sd); } } diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index c660363..71583fc 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -153,7 +153,7 @@ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, if (targ->sd && (kobj->sd->s_flags & SYSFS_FLAG_NS)) ns = targ->sd->s_ns; spin_unlock(&sysfs_symlink_target_lock); - sysfs_hash_and_remove(kobj->sd, name, ns); + kernfs_remove_by_name_ns(kobj->sd, name, ns); } /** @@ -170,7 +170,7 @@ void sysfs_remove_link(struct kobject *kobj, const char *name) else parent_sd = kobj->sd; - sysfs_hash_and_remove(parent_sd, name, NULL); + kernfs_remove_by_name(parent_sd, name); } EXPORT_SYMBOL_GPL(sysfs_remove_link); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index e116c21..97625b1 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -173,9 +173,6 @@ int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd); int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd); -void sysfs_remove(struct sysfs_dirent *sd); -int sysfs_hash_and_remove(struct sysfs_dirent *dir_sd, const char *name, - const void *ns); void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 254b9e8..83e151a 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -7,6 +7,30 @@ #ifndef __LINUX_KERNFS_H #define __LINUX_KERNFS_H +#include + struct sysfs_dirent; +#ifdef CONFIG_SYSFS + +void kernfs_remove(struct sysfs_dirent *sd); +int kernfs_remove_by_name_ns(struct sysfs_dirent *parent, const char *name, + const void *ns); + +#else /* CONFIG_SYSFS */ + +static inline void kernfs_remove(struct sysfs_dirent *sd) { } + +static inline int kernfs_remove_by_name_ns(struct sysfs_dirent *parent, + const char *name, const void *ns) +{ return -ENOSYS; } + +#endif /* CONFIG_SYSFS */ + +static inline int kernfs_remove_by_name(struct sysfs_dirent *parent, + const char *name) +{ + return kernfs_remove_by_name_ns(parent, name, NULL); +} + #endif /* __LINUX_KERNFS_H */ -- cgit v1.1 From 5d0e26bb59a680a5d97db5b6629941603e8de229 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 23 Nov 2013 17:21:50 -0500 Subject: sysfs, kernfs: introduce kernfs_create_link() Separate out kernfs symlink interface - kernfs_create_link() - which takes and returns sysfs_dirents, from sysfs_do_create_link_sd(). sysfs_do_create_link_sd() now just determines the parent and target sysfs_dirents and invokes the new interface and handles dup warning. This patch doesn't introduce behavior changes. v2: Dummy implementation for !CONFIG_SYSFS updated to return -ENOSYS. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/symlink.c | 76 ++++++++++++++++++++++++++++++-------------------- include/linux/kernfs.h | 9 ++++++ 2 files changed, 55 insertions(+), 30 deletions(-) diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 71583fc..41138e9 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -21,14 +21,48 @@ #include "sysfs.h" +/** + * kernfs_create_link - create a symlink + * @parent: directory to create the symlink in + * @name: name of the symlink + * @target: target node for the symlink to point to + * + * Returns the created node on success, ERR_PTR() value on error. + */ +struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, + const char *name, + struct sysfs_dirent *target) +{ + struct sysfs_dirent *sd; + struct sysfs_addrm_cxt acxt; + int error; + + sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); + if (!sd) + return ERR_PTR(-ENOMEM); + + if (parent->s_flags & SYSFS_FLAG_NS) + sd->s_ns = target->s_ns; + sd->s_symlink.target_sd = target; + sysfs_get(target); /* ref owned by symlink */ + + sysfs_addrm_start(&acxt); + error = __sysfs_add_one(&acxt, sd, parent); + sysfs_addrm_finish(&acxt); + + if (!error) + return sd; + + sysfs_put(sd); + return ERR_PTR(error); +} + + static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, struct kobject *target, const char *name, int warn) { - struct sysfs_dirent *target_sd = NULL; - struct sysfs_dirent *sd = NULL; - struct sysfs_addrm_cxt acxt; - int error; + struct sysfs_dirent *sd, *target_sd = NULL; BUG_ON(!name || !parent_sd); @@ -42,36 +76,18 @@ static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, target_sd = sysfs_get(target->sd); spin_unlock(&sysfs_symlink_target_lock); - error = -ENOENT; if (!target_sd) - goto out_put; + return -ENOENT; - error = -ENOMEM; - sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); - if (!sd) - goto out_put; - - if (parent_sd->s_flags & SYSFS_FLAG_NS) - sd->s_ns = target_sd->s_ns; - sd->s_symlink.target_sd = target_sd; - target_sd = NULL; /* reference is now owned by the symlink */ - - sysfs_addrm_start(&acxt); - if (warn) - error = sysfs_add_one(&acxt, sd, parent_sd); - else - error = __sysfs_add_one(&acxt, sd, parent_sd); - sysfs_addrm_finish(&acxt); - - if (error) - goto out_put; + sd = kernfs_create_link(parent_sd, name, target_sd); + sysfs_put(target_sd); - return 0; + if (!IS_ERR(sd)) + return 0; - out_put: - sysfs_put(target_sd); - sysfs_put(sd); - return error; + if (warn && PTR_ERR(sd) == -EEXIST) + sysfs_warn_dup(parent_sd, name); + return PTR_ERR(sd); } /** diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 83e151a..fe6290d 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -8,17 +8,26 @@ #define __LINUX_KERNFS_H #include +#include struct sysfs_dirent; #ifdef CONFIG_SYSFS +struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, + const char *name, + struct sysfs_dirent *target); void kernfs_remove(struct sysfs_dirent *sd); int kernfs_remove_by_name_ns(struct sysfs_dirent *parent, const char *name, const void *ns); #else /* CONFIG_SYSFS */ +static inline struct sysfs_dirent * +kernfs_create_link(struct sysfs_dirent *parent, const char *name, + struct sysfs_dirent *target) +{ return ERR_PTR(-ENOSYS); } + static inline void kernfs_remove(struct sysfs_dirent *sd) { } static inline int kernfs_remove_by_name_ns(struct sysfs_dirent *parent, -- cgit v1.1 From 890ece160c6465b49c42975d529c3481d89da8f5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 23 Nov 2013 17:21:51 -0500 Subject: sysfs, kernfs: introduce kernfs_rename[_ns]() Introduce kernfs rename interface, krenfs_rename[_ns](). This is just rename of sysfs_rename(). No functional changes. Function comment is added to kernfs_rename_ns() and @new_parent_sd is renamed to @new_parent for consistency with other kernfs interfaces. v2: Dummy implementation for !CONFIG_SYSFS updated to return -ENOSYS. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 23 +++++++++++++++-------- fs/sysfs/symlink.c | 2 +- fs/sysfs/sysfs.h | 3 --- include/linux/kernfs.h | 7 +++++++ 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index edbde4e..5ba8966 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -932,20 +932,27 @@ void sysfs_remove_dir(struct kobject *kobj) } } -int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd, - const char *new_name, const void *new_ns) +/** + * kernfs_rename_ns - move and rename a kernfs_node + * @sd: target node + * @new_parent: new parent to put @sd under + * @new_name: new name + * @new_ns: new namespace tag + */ +int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, + const char *new_name, const void *new_ns) { int error; mutex_lock(&sysfs_mutex); error = 0; - if ((sd->s_parent == new_parent_sd) && (sd->s_ns == new_ns) && + if ((sd->s_parent == new_parent) && (sd->s_ns == new_ns) && (strcmp(sd->s_name, new_name) == 0)) goto out; /* nothing to rename */ error = -EEXIST; - if (sysfs_find_dirent(new_parent_sd, new_name, new_ns)) + if (sysfs_find_dirent(new_parent, new_name, new_ns)) goto out; /* rename sysfs_dirent */ @@ -963,11 +970,11 @@ int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd, * Move to the appropriate place in the appropriate directories rbtree. */ sysfs_unlink_sibling(sd); - sysfs_get(new_parent_sd); + sysfs_get(new_parent); sysfs_put(sd->s_parent); sd->s_ns = new_ns; sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); - sd->s_parent = new_parent_sd; + sd->s_parent = new_parent; sysfs_link_sibling(sd); error = 0; @@ -981,7 +988,7 @@ int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, { struct sysfs_dirent *parent_sd = kobj->sd->s_parent; - return sysfs_rename(kobj->sd, parent_sd, new_name, new_ns); + return kernfs_rename_ns(kobj->sd, parent_sd, new_name, new_ns); } int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, @@ -994,7 +1001,7 @@ int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, new_parent_sd = new_parent_kobj && new_parent_kobj->sd ? new_parent_kobj->sd : &sysfs_root; - return sysfs_rename(sd, new_parent_sd, sd->s_name, new_ns); + return kernfs_rename_ns(sd, new_parent_sd, sd->s_name, new_ns); } /** diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 41138e9..0922c53 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -226,7 +226,7 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, if (sd->s_symlink.target_sd->s_dir.kobj != targ) goto out; - result = sysfs_rename(sd, parent_sd, new, new_ns); + result = kernfs_rename_ns(sd, parent_sd, new, new_ns); out: sysfs_put(sd); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 97625b1..8b3fc21 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -185,9 +185,6 @@ void release_sysfs_dirent(struct sysfs_dirent *sd); int sysfs_create_subdir(struct kobject *kobj, const char *name, struct sysfs_dirent **p_sd); -int sysfs_rename(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent_sd, - const char *new_name, const void *new_ns); - static inline struct sysfs_dirent *__sysfs_get(struct sysfs_dirent *sd) { if (sd) { diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index fe6290d..803d960 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -20,6 +20,8 @@ struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, void kernfs_remove(struct sysfs_dirent *sd); int kernfs_remove_by_name_ns(struct sysfs_dirent *parent, const char *name, const void *ns); +int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, + const char *new_name, const void *new_ns); #else /* CONFIG_SYSFS */ @@ -34,6 +36,11 @@ static inline int kernfs_remove_by_name_ns(struct sysfs_dirent *parent, const char *name, const void *ns) { return -ENOSYS; } +static inline int kernfs_rename_ns(struct sysfs_dirent *sd, + struct sysfs_dirent *new_parent, + const char *new_name, const void *new_ns) +{ return -ENOSYS; } + #endif /* CONFIG_SYSFS */ static inline int kernfs_remove_by_name(struct sysfs_dirent *parent, -- cgit v1.1 From 5d60418e54751c856f5aecc308620fde9572e481 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 23 Nov 2013 17:21:52 -0500 Subject: sysfs, kernfs: introduce kernfs_setattr() Introduce kernfs setattr interface - kernfs_setattr(). sysfs_sd_setattr() is renamed to __kernfs_setattr() and kernfs_setattr() is a simple wrapper around it with sysfs_mutex locking. sysfs_chmod_file() is updated to get an explicit ref on kobj->sd and then invoke kernfs_setattr() so that it doesn't have to use internal interface. This patch doesn't introduce any behavior differences. v2: Dummy implementation for !CONFIG_SYSFS updated to return -ENOSYS. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 13 +++++-------- fs/sysfs/inode.c | 21 +++++++++++++++++++-- fs/sysfs/sysfs.h | 1 - include/linux/kernfs.h | 8 ++++++++ 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 5664410..564abd2 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -922,19 +922,16 @@ int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, struct iattr newattrs; int rc; - mutex_lock(&sysfs_mutex); - - rc = -ENOENT; - sd = sysfs_find_dirent(kobj->sd, attr->name, NULL); + sd = sysfs_get_dirent(kobj->sd, attr->name); if (!sd) - goto out; + return -ENOENT; newattrs.ia_mode = (mode & S_IALLUGO) | (sd->s_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE; - rc = sysfs_sd_setattr(sd, &newattrs); - out: - mutex_unlock(&sysfs_mutex); + rc = kernfs_setattr(sd, &newattrs); + + sysfs_put(sd); return rc; } EXPORT_SYMBOL_GPL(sysfs_chmod_file); diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 1750f79..5f7e2af 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -67,7 +67,7 @@ static struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd) return attrs; } -int sysfs_sd_setattr(struct sysfs_dirent *sd, struct iattr *iattr) +static int __kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) { struct sysfs_inode_attrs *sd_attrs; struct iattr *iattrs; @@ -102,6 +102,23 @@ int sysfs_sd_setattr(struct sysfs_dirent *sd, struct iattr *iattr) return 0; } +/** + * kernfs_setattr - set iattr on a node + * @sd: target node + * @iattr: iattr to set + * + * Returns 0 on success, -errno on failure. + */ +int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) +{ + int ret; + + mutex_lock(&sysfs_mutex); + ret = __kernfs_setattr(sd, iattr); + mutex_unlock(&sysfs_mutex); + return ret; +} + int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) { struct inode *inode = dentry->d_inode; @@ -116,7 +133,7 @@ int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) if (error) goto out; - error = sysfs_sd_setattr(sd, iattr); + error = __kernfs_setattr(sd, iattr); if (error) goto out; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 8b3fc21..2abccfd 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -207,7 +207,6 @@ static inline void __sysfs_put(struct sysfs_dirent *sd) */ struct inode *sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd); void sysfs_evict_inode(struct inode *inode); -int sysfs_sd_setattr(struct sysfs_dirent *sd, struct iattr *iattr); int sysfs_permission(struct inode *inode, int mask); int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 803d960..8cb6738 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -10,6 +10,9 @@ #include #include +struct file; +struct iattr; + struct sysfs_dirent; #ifdef CONFIG_SYSFS @@ -22,6 +25,7 @@ int kernfs_remove_by_name_ns(struct sysfs_dirent *parent, const char *name, const void *ns); int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, const char *new_name, const void *new_ns); +int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr); #else /* CONFIG_SYSFS */ @@ -41,6 +45,10 @@ static inline int kernfs_rename_ns(struct sysfs_dirent *sd, const char *new_name, const void *new_ns) { return -ENOSYS; } +static inline int kernfs_setattr(struct sysfs_dirent *sd, + const struct iattr *iattr) +{ return -ENOSYS; } + #endif /* CONFIG_SYSFS */ static inline int kernfs_remove_by_name(struct sysfs_dirent *parent, -- cgit v1.1 From 7c6e2d362c19f01e6d6c8be59d83a89722032884 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:14 -0500 Subject: sysfs, kernfs: replace sysfs_dirent->s_dir.kobj and ->s_attr.[bin_]attr with ->priv A directory sysfs_dirent points to the associated kobj. A regular or bin file points to the associated [bin_]attribute. This patch replaces sysfs_dirent->s_dir.kobj and ->s_attr.[bin_]attr with void * ->priv. This is to prepare for kernfs interface so that sysfs can specify the private data in the same way for directories and files. This lower debuggability but not by much - the whole thing was overlaid in a union anyway. If debuggability becomes an issue, we can later add ->priv accessors which explicitly check for the sysfs_dirent type and performs casting. This patch doesn't introduce any behavior difference. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 2 +- fs/sysfs/file.c | 26 +++++++++++++------------- fs/sysfs/inode.c | 2 +- fs/sysfs/symlink.c | 2 +- fs/sysfs/sysfs.h | 13 +++++-------- 5 files changed, 21 insertions(+), 24 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 5ba8966..aeb08bd 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -681,7 +681,7 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, return -ENOMEM; sd->s_ns = ns; - sd->s_dir.kobj = kobj; + sd->priv = kobj; /* link in */ sysfs_addrm_start(&acxt); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 0f3f0a2..9b58d87 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -74,7 +74,7 @@ static struct sysfs_open_file *sysfs_of(struct file *file) */ static const struct sysfs_ops *sysfs_file_ops(struct sysfs_dirent *sd) { - struct kobject *kobj = sd->s_parent->s_dir.kobj; + struct kobject *kobj = sd->s_parent->priv; if (!sysfs_ignore_lockdep(sd)) lockdep_assert_held(sd); @@ -89,7 +89,7 @@ static const struct sysfs_ops *sysfs_file_ops(struct sysfs_dirent *sd) static int sysfs_seq_show(struct seq_file *sf, void *v) { struct sysfs_open_file *of = sf->private; - struct kobject *kobj = of->sd->s_parent->s_dir.kobj; + struct kobject *kobj = of->sd->s_parent->priv; const struct sysfs_ops *ops; char *buf; ssize_t count; @@ -120,7 +120,7 @@ static int sysfs_seq_show(struct seq_file *sf, void *v) */ ops = sysfs_file_ops(of->sd); if (ops->show) - count = ops->show(kobj, of->sd->s_attr.attr, buf); + count = ops->show(kobj, of->sd->priv, buf); else count = 0; @@ -154,8 +154,8 @@ static ssize_t sysfs_bin_read(struct file *file, char __user *userbuf, size_t bytes, loff_t *off) { struct sysfs_open_file *of = sysfs_of(file); - struct bin_attribute *battr = of->sd->s_attr.bin_attr; - struct kobject *kobj = of->sd->s_parent->s_dir.kobj; + struct bin_attribute *battr = of->sd->priv; + struct kobject *kobj = of->sd->s_parent->priv; loff_t size = file_inode(file)->i_size; int count = min_t(size_t, bytes, PAGE_SIZE); loff_t offs = *off; @@ -221,7 +221,7 @@ static ssize_t sysfs_bin_read(struct file *file, char __user *userbuf, static int flush_write_buffer(struct sysfs_open_file *of, char *buf, loff_t off, size_t count) { - struct kobject *kobj = of->sd->s_parent->s_dir.kobj; + struct kobject *kobj = of->sd->s_parent->priv; int rc = 0; /* @@ -236,7 +236,7 @@ static int flush_write_buffer(struct sysfs_open_file *of, char *buf, loff_t off, } if (sysfs_is_bin(of->sd)) { - struct bin_attribute *battr = of->sd->s_attr.bin_attr; + struct bin_attribute *battr = of->sd->priv; rc = -EIO; if (battr->write) @@ -245,7 +245,7 @@ static int flush_write_buffer(struct sysfs_open_file *of, char *buf, loff_t off, } else { const struct sysfs_ops *ops = sysfs_file_ops(of->sd); - rc = ops->store(kobj, of->sd->s_attr.attr, buf, count); + rc = ops->store(kobj, of->sd->priv, buf, count); } sysfs_put_active(of->sd); @@ -466,8 +466,8 @@ static const struct vm_operations_struct sysfs_bin_vm_ops = { static int sysfs_bin_mmap(struct file *file, struct vm_area_struct *vma) { struct sysfs_open_file *of = sysfs_of(file); - struct bin_attribute *battr = of->sd->s_attr.bin_attr; - struct kobject *kobj = of->sd->s_parent->s_dir.kobj; + struct bin_attribute *battr = of->sd->priv; + struct kobject *kobj = of->sd->s_parent->priv; int rc; mutex_lock(&of->mutex); @@ -607,7 +607,7 @@ static void sysfs_put_open_dirent(struct sysfs_dirent *sd, static int sysfs_open_file(struct inode *inode, struct file *file) { struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; - struct kobject *kobj = attr_sd->s_parent->s_dir.kobj; + struct kobject *kobj = attr_sd->s_parent->priv; struct sysfs_open_file *of; bool has_read, has_write, has_mmap; int error = -EACCES; @@ -617,7 +617,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file) return -ENODEV; if (sysfs_is_bin(attr_sd)) { - struct bin_attribute *battr = attr_sd->s_attr.bin_attr; + struct bin_attribute *battr = attr_sd->priv; has_read = battr->read || battr->mmap; has_write = battr->write || battr->mmap; @@ -848,7 +848,7 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, return -ENOMEM; sd->s_ns = ns; - sd->s_attr.attr = (void *)attr; + sd->priv = (void *)attr; sysfs_dirent_init_lockdep(sd); sysfs_addrm_start(&acxt); diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 5f7e2af..81cc858 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -275,7 +275,7 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) inode->i_fop = &sysfs_file_operations; break; case SYSFS_KOBJ_BIN_ATTR: - bin_attr = sd->s_attr.bin_attr; + bin_attr = sd->priv; inode->i_size = bin_attr->size; inode->i_fop = &sysfs_bin_operations; break; diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 0922c53..352fbbb 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -223,7 +223,7 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, result = -EINVAL; if (sysfs_type(sd) != SYSFS_KOBJ_LINK) goto out; - if (sd->s_symlink.target_sd->s_dir.kobj != targ) + if (sd->s_symlink.target_sd->priv != targ) goto out; result = kernfs_rename_ns(sd, parent_sd, new, new_ns); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 2abccfd..a6f3fa3 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -17,8 +17,6 @@ struct sysfs_open_dirent; /* type-specific structures for sysfs_dirent->s_* union members */ struct sysfs_elem_dir { - struct kobject *kobj; - unsigned long subdirs; /* children rbtree starts here and goes through sd->s_rb */ struct rb_root children; @@ -29,10 +27,6 @@ struct sysfs_elem_symlink { }; struct sysfs_elem_attr { - union { - struct attribute *attr; - struct bin_attribute *bin_attr; - }; struct sysfs_open_dirent *open; }; @@ -74,6 +68,8 @@ struct sysfs_dirent { struct sysfs_elem_attr s_attr; }; + void *priv; + unsigned short s_flags; umode_t s_mode; unsigned int s_ino; @@ -103,7 +99,7 @@ static inline unsigned int sysfs_type(struct sysfs_dirent *sd) #define sysfs_dirent_init_lockdep(sd) \ do { \ - struct attribute *attr = sd->s_attr.attr; \ + struct attribute *attr = sd->priv; \ struct lock_class_key *key = attr->key; \ if (!key) \ key = &attr->skey; \ @@ -114,10 +110,11 @@ do { \ /* Test for attributes that want to ignore lockdep for read-locking */ static inline bool sysfs_ignore_lockdep(struct sysfs_dirent *sd) { + struct attribute *attr = sd->priv; int type = sysfs_type(sd); return (type == SYSFS_KOBJ_ATTR || type == SYSFS_KOBJ_BIN_ATTR) && - sd->s_attr.attr->ignore_lockdep; + attr->ignore_lockdep; } #else -- cgit v1.1 From 93b2b8e4aa4317e3fe6414d117deb5f3c362e8bb Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:15 -0500 Subject: sysfs, kernfs: introduce kernfs_create_dir[_ns]() Introduce kernfs interface to manipulate a directory which takes and returns sysfs_dirents. create_dir() is renamed to kernfs_create_dir_ns() and its argumantes and return value are updated. create_dir() usages are replaced with kernfs_create_dir_ns() and sysfs_create_subdir() usages are replaced with kernfs_create_dir(). Dup warnings are handled explicitly by sysfs users of the kernfs interface. sysfs_enable_ns() is renamed to kernfs_enable_ns(). This patch doesn't introduce any behavior changes. v2: Dummy implementation for !CONFIG_SYSFS updated to return -ENOSYS. v3: kernfs_enable_ns() added. v4: Refreshed on top of "sysfs: drop kobj_ns_type handling, take #2" so that this patch removes sysfs_enable_ns(). Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 54 ++++++++++++++++++++++++++++---------------------- fs/sysfs/group.c | 9 ++++++--- fs/sysfs/sysfs.h | 3 --- include/linux/kernfs.h | 17 ++++++++++++++++ include/linux/sysfs.h | 6 ------ lib/kobject.c | 2 +- 6 files changed, 54 insertions(+), 37 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index aeb08bd..cfbf409 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -666,9 +666,18 @@ struct sysfs_dirent *sysfs_get_dirent_ns(struct sysfs_dirent *parent_sd, } EXPORT_SYMBOL_GPL(sysfs_get_dirent_ns); -static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, - const char *name, const void *ns, - struct sysfs_dirent **p_sd) +/** + * kernfs_create_dir_ns - create a directory + * @parent: parent in which to create a new directory + * @name: name of the new directory + * @priv: opaque data associated with the new directory + * @ns: optional namespace tag of the directory + * + * Returns the created node on success, ERR_PTR() value on failure. + */ +struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, + const char *name, void *priv, + const void *ns) { umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; struct sysfs_addrm_cxt acxt; @@ -678,28 +687,21 @@ static int create_dir(struct kobject *kobj, struct sysfs_dirent *parent_sd, /* allocate */ sd = sysfs_new_dirent(name, mode, SYSFS_DIR); if (!sd) - return -ENOMEM; + return ERR_PTR(-ENOMEM); sd->s_ns = ns; - sd->priv = kobj; + sd->priv = priv; /* link in */ sysfs_addrm_start(&acxt); - rc = sysfs_add_one(&acxt, sd, parent_sd); + rc = __sysfs_add_one(&acxt, sd, parent); sysfs_addrm_finish(&acxt); - if (rc == 0) - *p_sd = sd; - else - sysfs_put(sd); + if (!rc) + return sd; - return rc; -} - -int sysfs_create_subdir(struct kobject *kobj, const char *name, - struct sysfs_dirent **p_sd) -{ - return create_dir(kobj, kobj->sd, name, NULL, p_sd); + sysfs_put(sd); + return ERR_PTR(rc); } /** @@ -710,7 +712,6 @@ int sysfs_create_subdir(struct kobject *kobj, const char *name, int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) { struct sysfs_dirent *parent_sd, *sd; - int error = 0; BUG_ON(!kobj); @@ -722,10 +723,15 @@ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) if (!parent_sd) return -ENOENT; - error = create_dir(kobj, parent_sd, kobject_name(kobj), ns, &sd); - if (!error) - kobj->sd = sd; - return error; + sd = kernfs_create_dir_ns(parent_sd, kobject_name(kobj), kobj, ns); + if (IS_ERR(sd)) { + if (PTR_ERR(sd) == -EEXIST) + sysfs_warn_dup(parent_sd, kobject_name(kobj)); + return PTR_ERR(sd); + } + + kobj->sd = sd; + return 0; } static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, @@ -1005,14 +1011,14 @@ int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, } /** - * sysfs_enable_ns - enable namespace under a directory + * kernfs_enable_ns - enable namespace under a directory * @sd: directory of interest, should be empty * * This is to be called right after @sd is created to enable namespace * under it. All children of @sd must have non-NULL namespace tags and * only the ones which match the super_block's tag will be visible. */ -void sysfs_enable_ns(struct sysfs_dirent *sd) +void kernfs_enable_ns(struct sysfs_dirent *sd) { WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); WARN_ON_ONCE(!RB_EMPTY_ROOT(&sd->s_dir.children)); diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 4bd9973..065689d 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -101,9 +101,12 @@ static int internal_create_group(struct kobject *kobj, int update, return -EINVAL; } if (grp->name) { - error = sysfs_create_subdir(kobj, grp->name, &sd); - if (error) - return error; + sd = kernfs_create_dir(kobj->sd, grp->name, kobj); + if (IS_ERR(sd)) { + if (PTR_ERR(sd) == -EEXIST) + sysfs_warn_dup(kobj->sd, grp->name); + return PTR_ERR(sd); + } } else sd = kobj->sd; sysfs_get(sd); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index a6f3fa3..9ac234e 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -179,9 +179,6 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type); void release_sysfs_dirent(struct sysfs_dirent *sd); -int sysfs_create_subdir(struct kobject *kobj, const char *name, - struct sysfs_dirent **p_sd); - static inline struct sysfs_dirent *__sysfs_get(struct sysfs_dirent *sd) { if (sd) { diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 8cb6738..e8b73d4 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -17,6 +17,9 @@ struct sysfs_dirent; #ifdef CONFIG_SYSFS +struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, + const char *name, void *priv, + const void *ns); struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, const char *name, struct sysfs_dirent *target); @@ -25,11 +28,17 @@ int kernfs_remove_by_name_ns(struct sysfs_dirent *parent, const char *name, const void *ns); int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, const char *new_name, const void *new_ns); +void kernfs_enable_ns(struct sysfs_dirent *sd); int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr); #else /* CONFIG_SYSFS */ static inline struct sysfs_dirent * +kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, + const void *ns) +{ return ERR_PTR(-ENOSYS); } + +static inline struct sysfs_dirent * kernfs_create_link(struct sysfs_dirent *parent, const char *name, struct sysfs_dirent *target) { return ERR_PTR(-ENOSYS); } @@ -45,12 +54,20 @@ static inline int kernfs_rename_ns(struct sysfs_dirent *sd, const char *new_name, const void *new_ns) { return -ENOSYS; } +static inline void kernfs_enable_ns(struct sysfs_dirent *sd) { } + static inline int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) { return -ENOSYS; } #endif /* CONFIG_SYSFS */ +static inline struct sysfs_dirent * +kernfs_create_dir(struct sysfs_dirent *parent, const char *name, void *priv) +{ + return kernfs_create_dir_ns(parent, name, priv, NULL); +} + static inline int kernfs_remove_by_name(struct sysfs_dirent *parent, const char *name) { diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index e17381a..2bc735d 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -219,8 +219,6 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *target, void sysfs_delete_link(struct kobject *dir, struct kobject *targ, const char *name); -void sysfs_enable_ns(struct sysfs_dirent *sd); - int __must_check sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp); int __must_check sysfs_create_groups(struct kobject *kobj, @@ -354,10 +352,6 @@ static inline void sysfs_delete_link(struct kobject *k, struct kobject *t, { } -static inline void sysfs_enable_ns(struct sysfs_dirent *sd) -{ -} - static inline int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp) { diff --git a/lib/kobject.c b/lib/kobject.c index 16e9335..b8d848f 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -94,7 +94,7 @@ static int create_dir(struct kobject *kobj) BUG_ON(ops->type >= KOBJ_NS_TYPES); BUG_ON(!kobj_ns_type_registered(ops->type)); - sysfs_enable_ns(kobj->sd); + kernfs_enable_ns(kobj->sd); } return 0; -- cgit v1.1 From c2b19daf6760fae9d5db9e9d1683644728888293 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:16 -0500 Subject: sysfs, kernfs: prepare read path for kernfs We're in the process of separating out core sysfs functionality into kernfs which will deal with sysfs_dirents directly. This patch rearranges read path so that the kernfs and sysfs parts are separate. * Regular file read path is refactored such that kernfs_seq_start/next/stop/show() handle all the boilerplate work including locking and updating event count for poll, while sysfs_kf_seq_show() deals with interaction with kobj show method. * Bin file read path is refactored such that kernfs_file_direct_read() handles all the boilerplate work including buffer management and locking, while sysfs_kf_bin_read() deals with interaction with bin_attribute read method. kernfs_file_read() is added. It invokes either the seq_file or direct read path depending on the file type. This will eventually allow using the same file_operations for both file types, which is necessary to separate out kernfs. While this patch changes the order of some operations, it shouldn't change any visible behavior. v2: Dropped unnecessary zeroing of @count from sysfs_kf_seq_show(). Add comments explaining single_open() behavior. Both suggested by Pavel. v3: seq_stop() is called even after seq_start() failed. kernfs_seq_start() updated so that it doesn't unlock sysfs_open_file->mutex on failure so that kernfs_seq_stop() doesn't try to unlock an already unlocked mutex. Reported by Fengguang. Signed-off-by: Tejun Heo Cc: Pavel Machek Cc: Fengguang Wu Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 191 +++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 126 insertions(+), 65 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 9b58d87..b695b8b 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -86,13 +86,13 @@ static const struct sysfs_ops *sysfs_file_ops(struct sysfs_dirent *sd) * details like buffering and seeking. The following function pipes * sysfs_ops->show() result through seq_file. */ -static int sysfs_seq_show(struct seq_file *sf, void *v) +static int sysfs_kf_seq_show(struct seq_file *sf, void *v) { struct sysfs_open_file *of = sf->private; struct kobject *kobj = of->sd->s_parent->priv; - const struct sysfs_ops *ops; - char *buf; + const struct sysfs_ops *ops = sysfs_file_ops(of->sd); ssize_t count; + char *buf; /* acquire buffer and ensure that it's >= PAGE_SIZE */ count = seq_get_buf(sf, &buf); @@ -102,33 +102,14 @@ static int sysfs_seq_show(struct seq_file *sf, void *v) } /* - * Need @of->sd for attr and ops, its parent for kobj. @of->mutex - * nests outside active ref and is just to ensure that the ops - * aren't called concurrently for the same open file. + * Invoke show(). Control may reach here via seq file lseek even + * if @ops->show() isn't implemented. */ - mutex_lock(&of->mutex); - if (!sysfs_get_active(of->sd)) { - mutex_unlock(&of->mutex); - return -ENODEV; - } - - of->event = atomic_read(&of->sd->s_attr.open->event); - - /* - * Lookup @ops and invoke show(). Control may reach here via seq - * file lseek even if @ops->show() isn't implemented. - */ - ops = sysfs_file_ops(of->sd); - if (ops->show) + if (ops->show) { count = ops->show(kobj, of->sd->priv, buf); - else - count = 0; - - sysfs_put_active(of->sd); - mutex_unlock(&of->mutex); - - if (count < 0) - return count; + if (count < 0) + return count; + } /* * The code works fine with PAGE_SIZE return but it's likely to @@ -144,68 +125,146 @@ static int sysfs_seq_show(struct seq_file *sf, void *v) return 0; } -/* - * Read method for bin files. As reading a bin file can have side-effects, - * the exact offset and bytes specified in read(2) call should be passed to - * the read callback making it difficult to use seq_file. Implement - * simplistic custom buffering for bin files. - */ -static ssize_t sysfs_bin_read(struct file *file, char __user *userbuf, - size_t bytes, loff_t *off) +static ssize_t sysfs_kf_bin_read(struct sysfs_open_file *of, char *buf, + size_t count, loff_t pos) { - struct sysfs_open_file *of = sysfs_of(file); struct bin_attribute *battr = of->sd->priv; struct kobject *kobj = of->sd->s_parent->priv; - loff_t size = file_inode(file)->i_size; - int count = min_t(size_t, bytes, PAGE_SIZE); - loff_t offs = *off; - char *buf; + loff_t size = file_inode(of->file)->i_size; - if (!bytes) + if (!count) return 0; if (size) { - if (offs > size) + if (pos > size) return 0; - if (offs + count > size) - count = size - offs; + if (pos + count > size) + count = size - pos; } - buf = kmalloc(count, GFP_KERNEL); + if (!battr->read) + return -EIO; + + return battr->read(of->file, kobj, battr, buf, pos, count); +} + +static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) +{ + struct sysfs_open_file *of = sf->private; + + /* + * @of->mutex nests outside active ref and is just to ensure that + * the ops aren't called concurrently for the same open file. + */ + mutex_lock(&of->mutex); + if (!sysfs_get_active(of->sd)) + return ERR_PTR(-ENODEV); + + /* + * The same behavior and code as single_open(). Returns !NULL if + * pos is at the beginning; otherwise, NULL. + */ + return NULL + !*ppos; +} + +static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) +{ + /* + * The same behavior and code as single_open(), always terminate + * after the initial read. + */ + ++*ppos; + return NULL; +} + +static void kernfs_seq_stop(struct seq_file *sf, void *v) +{ + struct sysfs_open_file *of = sf->private; + + sysfs_put_active(of->sd); + mutex_unlock(&of->mutex); +} + +static int kernfs_seq_show(struct seq_file *sf, void *v) +{ + struct sysfs_open_file *of = sf->private; + + of->event = atomic_read(&of->sd->s_attr.open->event); + + return sysfs_kf_seq_show(sf, v); +} + +static const struct seq_operations kernfs_seq_ops = { + .start = kernfs_seq_start, + .next = kernfs_seq_next, + .stop = kernfs_seq_stop, + .show = kernfs_seq_show, +}; + +/* + * As reading a bin file can have side-effects, the exact offset and bytes + * specified in read(2) call should be passed to the read callback making + * it difficult to use seq_file. Implement simplistic custom buffering for + * bin files. + */ +static ssize_t kernfs_file_direct_read(struct sysfs_open_file *of, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + ssize_t len = min_t(size_t, count, PAGE_SIZE); + char *buf; + + buf = kmalloc(len, GFP_KERNEL); if (!buf) return -ENOMEM; - /* need of->sd for battr, its parent for kobj */ + /* + * @of->mutex nests outside active ref and is just to ensure that + * the ops aren't called concurrently for the same open file. + */ mutex_lock(&of->mutex); if (!sysfs_get_active(of->sd)) { - count = -ENODEV; + len = -ENODEV; mutex_unlock(&of->mutex); goto out_free; } - if (battr->read) - count = battr->read(file, kobj, battr, buf, offs, count); - else - count = -EIO; + len = sysfs_kf_bin_read(of, buf, len, *ppos); sysfs_put_active(of->sd); mutex_unlock(&of->mutex); - if (count < 0) + if (len < 0) goto out_free; - if (copy_to_user(userbuf, buf, count)) { - count = -EFAULT; + if (copy_to_user(user_buf, buf, len)) { + len = -EFAULT; goto out_free; } - pr_debug("offs = %lld, *off = %lld, count = %d\n", offs, *off, count); - - *off = offs + count; + *ppos += len; out_free: kfree(buf); - return count; + return len; +} + +/** + * kernfs_file_read - kernfs vfs read callback + * @file: file pointer + * @user_buf: data to write + * @count: number of bytes + * @ppos: starting offset + */ +static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct sysfs_open_file *of = sysfs_of(file); + + if (sysfs_is_bin(of->sd)) + return kernfs_file_direct_read(of, user_buf, count, ppos); + else + return seq_read(file, user_buf, count, ppos); } /** @@ -677,12 +736,14 @@ static int sysfs_open_file(struct inode *inode, struct file *file) * and readable regular files are the vast majority anyway. */ if (sysfs_is_bin(attr_sd)) - error = single_open(file, NULL, of); + error = seq_open(file, NULL); else - error = single_open(file, sysfs_seq_show, of); + error = seq_open(file, &kernfs_seq_ops); if (error) goto err_free; + ((struct seq_file *)file->private_data)->private = of; + /* seq_file clears PWRITE unconditionally, restore it if WRITE */ if (file->f_mode & FMODE_WRITE) file->f_mode |= FMODE_PWRITE; @@ -697,7 +758,7 @@ static int sysfs_open_file(struct inode *inode, struct file *file) return 0; err_close: - single_release(inode, file); + seq_release(inode, file); err_free: kfree(of); err_out: @@ -711,7 +772,7 @@ static int sysfs_release(struct inode *inode, struct file *filp) struct sysfs_open_file *of = sysfs_of(filp); sysfs_put_open_dirent(sd, of); - single_release(inode, filp); + seq_release(inode, filp); kfree(of); return 0; @@ -816,7 +877,7 @@ void sysfs_notify(struct kobject *k, const char *dir, const char *attr) EXPORT_SYMBOL_GPL(sysfs_notify); const struct file_operations sysfs_file_operations = { - .read = seq_read, + .read = kernfs_file_read, .write = sysfs_write_file, .llseek = generic_file_llseek, .open = sysfs_open_file, @@ -825,7 +886,7 @@ const struct file_operations sysfs_file_operations = { }; const struct file_operations sysfs_bin_operations = { - .read = sysfs_bin_read, + .read = kernfs_file_read, .write = sysfs_write_file, .llseek = generic_file_llseek, .mmap = sysfs_bin_mmap, -- cgit v1.1 From 50b38ca086e4d9920eede98b871b971e9958d70d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:17 -0500 Subject: sysfs, kernfs: prepare write path for kernfs We're in the process of separating out core sysfs functionality into kernfs which will deal with sysfs_dirents directly. This patch rearranges write path so that the kernfs and sysfs parts are separate. kernfs_file_write() handles all boilerplate work including buffer management and locking and invokes sysfs_kf_write() or sysfs_kf_bin_write() depending on the file type which deals with the interaction with kobj store or bin_attribute write method. While this patch changes the order of some operations, it shouldn't change any visible behavior. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 103 +++++++++++++++++++++++++++----------------------------- 1 file changed, 50 insertions(+), 53 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index b695b8b..2f849e8 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -267,61 +267,50 @@ static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, return seq_read(file, user_buf, count, ppos); } -/** - * flush_write_buffer - push buffer to kobject - * @of: open file - * @buf: data buffer for file - * @off: file offset to write to - * @count: number of bytes - * - * Get the correct pointers for the kobject and the attribute we're dealing - * with, then call the store() method for it with @buf. - */ -static int flush_write_buffer(struct sysfs_open_file *of, char *buf, loff_t off, - size_t count) +/* kernfs write callback for regular sysfs files */ +static ssize_t sysfs_kf_write(struct sysfs_open_file *of, char *buf, + size_t count, loff_t pos) { + const struct sysfs_ops *ops = sysfs_file_ops(of->sd); struct kobject *kobj = of->sd->s_parent->priv; - int rc = 0; - /* - * Need @of->sd for attr and ops, its parent for kobj. @of->mutex - * nests outside active ref and is just to ensure that the ops - * aren't called concurrently for the same open file. - */ - mutex_lock(&of->mutex); - if (!sysfs_get_active(of->sd)) { - mutex_unlock(&of->mutex); - return -ENODEV; - } + if (!count) + return 0; - if (sysfs_is_bin(of->sd)) { - struct bin_attribute *battr = of->sd->priv; + return ops->store(kobj, of->sd->priv, buf, count); +} - rc = -EIO; - if (battr->write) - rc = battr->write(of->file, kobj, battr, buf, off, - count); - } else { - const struct sysfs_ops *ops = sysfs_file_ops(of->sd); +/* kernfs write callback for bin sysfs files */ +static ssize_t sysfs_kf_bin_write(struct sysfs_open_file *of, char *buf, + size_t count, loff_t pos) +{ + struct bin_attribute *battr = of->sd->priv; + struct kobject *kobj = of->sd->s_parent->priv; + loff_t size = file_inode(of->file)->i_size; - rc = ops->store(kobj, of->sd->priv, buf, count); + if (size) { + if (size <= pos) + return 0; + count = min_t(ssize_t, count, size - pos); } + if (!count) + return 0; - sysfs_put_active(of->sd); - mutex_unlock(&of->mutex); + if (!battr->write) + return -EIO; - return rc; + return battr->write(of->file, kobj, battr, buf, pos, count); } /** - * sysfs_write_file - write an attribute + * kernfs_file_write - kernfs vfs write callback * @file: file pointer * @user_buf: data to write * @count: number of bytes * @ppos: starting offset * - * Copy data in from userland and pass it to the matching - * sysfs_ops->store() by invoking flush_write_buffer(). + * Copy data in from userland and pass it to the matching kernfs write + * operation. * * There is no easy way for us to know if userspace is only doing a partial * write, so we don't support them. We expect the entire buffer to come on @@ -329,23 +318,13 @@ static int flush_write_buffer(struct sysfs_open_file *of, char *buf, loff_t off, * modify only the the value you're changing, then write entire buffer * back. */ -static ssize_t sysfs_write_file(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) +static ssize_t kernfs_file_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) { struct sysfs_open_file *of = sysfs_of(file); ssize_t len = min_t(size_t, count, PAGE_SIZE); - loff_t size = file_inode(file)->i_size; char *buf; - if (sysfs_is_bin(of->sd) && size) { - if (size <= *ppos) - return 0; - len = min_t(ssize_t, len, size - *ppos); - } - - if (!len) - return 0; - buf = kmalloc(len + 1, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -356,7 +335,25 @@ static ssize_t sysfs_write_file(struct file *file, const char __user *user_buf, } buf[len] = '\0'; /* guarantee string termination */ - len = flush_write_buffer(of, buf, *ppos, len); + /* + * @of->mutex nests outside active ref and is just to ensure that + * the ops aren't called concurrently for the same open file. + */ + mutex_lock(&of->mutex); + if (!sysfs_get_active(of->sd)) { + mutex_unlock(&of->mutex); + len = -ENODEV; + goto out_free; + } + + if (sysfs_is_bin(of->sd)) + len = sysfs_kf_bin_write(of, buf, len, *ppos); + else + len = sysfs_kf_write(of, buf, len, *ppos); + + sysfs_put_active(of->sd); + mutex_unlock(&of->mutex); + if (len > 0) *ppos += len; out_free: @@ -878,7 +875,7 @@ EXPORT_SYMBOL_GPL(sysfs_notify); const struct file_operations sysfs_file_operations = { .read = kernfs_file_read, - .write = sysfs_write_file, + .write = kernfs_file_write, .llseek = generic_file_llseek, .open = sysfs_open_file, .release = sysfs_release, @@ -887,7 +884,7 @@ const struct file_operations sysfs_file_operations = { const struct file_operations sysfs_bin_operations = { .read = kernfs_file_read, - .write = sysfs_write_file, + .write = kernfs_file_write, .llseek = generic_file_llseek, .mmap = sysfs_bin_mmap, .open = sysfs_open_file, -- cgit v1.1 From fdbffaa478fc77e999cbe2ac0dcfbf609103e675 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:18 -0500 Subject: sysfs, kernfs: prepare mmap path for kernfs We're in the process of separating out core sysfs functionality into kernfs which will deal with sysfs_dirents directly. This patch rearranges mmap path so that the kernfs and sysfs parts are separate. sysfs_kf_bin_mmap() which handles the interaction with bin_attribute mmap method is factored out of sysfs_bin_mmap(), which is renamed to kernfs_file_mmap(). All vma ops are renamed accordingly. sysfs_bin_mmap() is updated such that it can be used for both file types. This will eventually allow using the same file_operations for both file types, which is necessary to separate out kernfs. This patch doesn't introduce any behavior changes. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 69 ++++++++++++++++++++++++++++++++------------------------- 1 file changed, 39 insertions(+), 30 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 2f849e8..2e24e89 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -361,7 +361,19 @@ out_free: return len; } -static void sysfs_bin_vma_open(struct vm_area_struct *vma) +static int sysfs_kf_bin_mmap(struct sysfs_open_file *of, + struct vm_area_struct *vma) +{ + struct bin_attribute *battr = of->sd->priv; + struct kobject *kobj = of->sd->s_parent->priv; + + if (!battr->mmap) + return -ENODEV; + + return battr->mmap(of->file, kobj, battr, vma); +} + +static void kernfs_vma_open(struct vm_area_struct *vma) { struct file *file = vma->vm_file; struct sysfs_open_file *of = sysfs_of(file); @@ -378,7 +390,7 @@ static void sysfs_bin_vma_open(struct vm_area_struct *vma) sysfs_put_active(of->sd); } -static int sysfs_bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +static int kernfs_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct file *file = vma->vm_file; struct sysfs_open_file *of = sysfs_of(file); @@ -398,8 +410,8 @@ static int sysfs_bin_fault(struct vm_area_struct *vma, struct vm_fault *vmf) return ret; } -static int sysfs_bin_page_mkwrite(struct vm_area_struct *vma, - struct vm_fault *vmf) +static int kernfs_vma_page_mkwrite(struct vm_area_struct *vma, + struct vm_fault *vmf) { struct file *file = vma->vm_file; struct sysfs_open_file *of = sysfs_of(file); @@ -421,8 +433,8 @@ static int sysfs_bin_page_mkwrite(struct vm_area_struct *vma, return ret; } -static int sysfs_bin_access(struct vm_area_struct *vma, unsigned long addr, - void *buf, int len, int write) +static int kernfs_vma_access(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write) { struct file *file = vma->vm_file; struct sysfs_open_file *of = sysfs_of(file); @@ -443,8 +455,8 @@ static int sysfs_bin_access(struct vm_area_struct *vma, unsigned long addr, } #ifdef CONFIG_NUMA -static int sysfs_bin_set_policy(struct vm_area_struct *vma, - struct mempolicy *new) +static int kernfs_vma_set_policy(struct vm_area_struct *vma, + struct mempolicy *new) { struct file *file = vma->vm_file; struct sysfs_open_file *of = sysfs_of(file); @@ -464,8 +476,8 @@ static int sysfs_bin_set_policy(struct vm_area_struct *vma, return ret; } -static struct mempolicy *sysfs_bin_get_policy(struct vm_area_struct *vma, - unsigned long addr) +static struct mempolicy *kernfs_vma_get_policy(struct vm_area_struct *vma, + unsigned long addr) { struct file *file = vma->vm_file; struct sysfs_open_file *of = sysfs_of(file); @@ -485,8 +497,9 @@ static struct mempolicy *sysfs_bin_get_policy(struct vm_area_struct *vma, return pol; } -static int sysfs_bin_migrate(struct vm_area_struct *vma, const nodemask_t *from, - const nodemask_t *to, unsigned long flags) +static int kernfs_vma_migrate(struct vm_area_struct *vma, + const nodemask_t *from, const nodemask_t *to, + unsigned long flags) { struct file *file = vma->vm_file; struct sysfs_open_file *of = sysfs_of(file); @@ -507,36 +520,31 @@ static int sysfs_bin_migrate(struct vm_area_struct *vma, const nodemask_t *from, } #endif -static const struct vm_operations_struct sysfs_bin_vm_ops = { - .open = sysfs_bin_vma_open, - .fault = sysfs_bin_fault, - .page_mkwrite = sysfs_bin_page_mkwrite, - .access = sysfs_bin_access, +static const struct vm_operations_struct kernfs_vm_ops = { + .open = kernfs_vma_open, + .fault = kernfs_vma_fault, + .page_mkwrite = kernfs_vma_page_mkwrite, + .access = kernfs_vma_access, #ifdef CONFIG_NUMA - .set_policy = sysfs_bin_set_policy, - .get_policy = sysfs_bin_get_policy, - .migrate = sysfs_bin_migrate, + .set_policy = kernfs_vma_set_policy, + .get_policy = kernfs_vma_get_policy, + .migrate = kernfs_vma_migrate, #endif }; -static int sysfs_bin_mmap(struct file *file, struct vm_area_struct *vma) +static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) { struct sysfs_open_file *of = sysfs_of(file); - struct bin_attribute *battr = of->sd->priv; - struct kobject *kobj = of->sd->s_parent->priv; int rc; mutex_lock(&of->mutex); - /* need of->sd for battr, its parent for kobj */ rc = -ENODEV; if (!sysfs_get_active(of->sd)) goto out_unlock; - if (!battr->mmap) - goto out_put; - - rc = battr->mmap(file, kobj, battr, vma); + if (sysfs_is_bin(of->sd)) + rc = sysfs_kf_bin_mmap(of, vma); if (rc) goto out_put; @@ -563,7 +571,7 @@ static int sysfs_bin_mmap(struct file *file, struct vm_area_struct *vma) rc = 0; of->mmapped = 1; of->vm_ops = vma->vm_ops; - vma->vm_ops = &sysfs_bin_vm_ops; + vma->vm_ops = &kernfs_vm_ops; out_put: sysfs_put_active(of->sd); out_unlock: @@ -877,6 +885,7 @@ const struct file_operations sysfs_file_operations = { .read = kernfs_file_read, .write = kernfs_file_write, .llseek = generic_file_llseek, + .mmap = kernfs_file_mmap, .open = sysfs_open_file, .release = sysfs_release, .poll = sysfs_poll, @@ -886,7 +895,7 @@ const struct file_operations sysfs_bin_operations = { .read = kernfs_file_read, .write = kernfs_file_write, .llseek = generic_file_llseek, - .mmap = sysfs_bin_mmap, + .mmap = kernfs_file_mmap, .open = sysfs_open_file, .release = sysfs_release, .poll = sysfs_poll, -- cgit v1.1 From c6fb449515f23edea828fb90a460d3622e261dba Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:19 -0500 Subject: sysfs, kernfs: prepare open, release, poll paths for kernfs We're in the process of separating out core sysfs functionality into kernfs which will deal with sysfs_dirents directly. This patch prepares the rest - open, release and poll. There isn't much to do. Just renaming is enough. As sysfs_file_operations and sysfs_bin_operations are identical now, use the same file_operations for both - kernfs_file_operations. This patch doesn't introduce any behavior changes. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 24 +++++++----------------- fs/sysfs/inode.c | 4 ++-- fs/sysfs/sysfs.h | 3 +-- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 2e24e89..a43df04 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -668,7 +668,7 @@ static void sysfs_put_open_dirent(struct sysfs_dirent *sd, kfree(od); } -static int sysfs_open_file(struct inode *inode, struct file *file) +static int kernfs_file_open(struct inode *inode, struct file *file) { struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; struct kobject *kobj = attr_sd->s_parent->priv; @@ -771,7 +771,7 @@ err_out: return error; } -static int sysfs_release(struct inode *inode, struct file *filp) +static int kernfs_file_release(struct inode *inode, struct file *filp) { struct sysfs_dirent *sd = filp->f_path.dentry->d_fsdata; struct sysfs_open_file *of = sysfs_of(filp); @@ -822,7 +822,7 @@ void sysfs_unmap_bin_file(struct sysfs_dirent *sd) * to see if it supports poll (Neither 'poll' nor 'select' return * an appropriate error code). When in doubt, set a suitable timeout value. */ -static unsigned int sysfs_poll(struct file *filp, poll_table *wait) +static unsigned int kernfs_file_poll(struct file *filp, poll_table *wait) { struct sysfs_open_file *of = sysfs_of(filp); struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; @@ -881,24 +881,14 @@ void sysfs_notify(struct kobject *k, const char *dir, const char *attr) } EXPORT_SYMBOL_GPL(sysfs_notify); -const struct file_operations sysfs_file_operations = { +const struct file_operations kernfs_file_operations = { .read = kernfs_file_read, .write = kernfs_file_write, .llseek = generic_file_llseek, .mmap = kernfs_file_mmap, - .open = sysfs_open_file, - .release = sysfs_release, - .poll = sysfs_poll, -}; - -const struct file_operations sysfs_bin_operations = { - .read = kernfs_file_read, - .write = kernfs_file_write, - .llseek = generic_file_llseek, - .mmap = kernfs_file_mmap, - .open = sysfs_open_file, - .release = sysfs_release, - .poll = sysfs_poll, + .open = kernfs_file_open, + .release = kernfs_file_release, + .poll = kernfs_file_poll, }; int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 81cc858..4c463da 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -272,12 +272,12 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) break; case SYSFS_KOBJ_ATTR: inode->i_size = PAGE_SIZE; - inode->i_fop = &sysfs_file_operations; + inode->i_fop = &kernfs_file_operations; break; case SYSFS_KOBJ_BIN_ATTR: bin_attr = sd->priv; inode->i_size = bin_attr->size; - inode->i_fop = &sysfs_bin_operations; + inode->i_fop = &kernfs_file_operations; break; case SYSFS_KOBJ_LINK: inode->i_op = &sysfs_symlink_inode_operations; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 9ac234e..619250d 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -212,8 +212,7 @@ int sysfs_inode_init(void); /* * file.c */ -extern const struct file_operations sysfs_file_operations; -extern const struct file_operations sysfs_bin_operations; +extern const struct file_operations kernfs_file_operations; int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, int type); -- cgit v1.1 From dd8a5b036b6e8d50854e130555f90f062c5eacec Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:20 -0500 Subject: sysfs, kernfs: move sysfs_open_file to include/linux/kernfs.h sysfs_open_file will be used as the primary handle for kernfs methods. Move its definition from fs/sysfs/file.c to include/linux/kernfs.h and mark the public and private fields. This is pure relocation. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 11 ----------- include/linux/kernfs.h | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index a43df04..acba583 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -47,17 +47,6 @@ struct sysfs_open_dirent { struct list_head files; /* goes through sysfs_open_file.list */ }; -struct sysfs_open_file { - struct sysfs_dirent *sd; - struct file *file; - struct mutex mutex; - int event; - struct list_head list; - - bool mmapped; - const struct vm_operations_struct *vm_ops; -}; - static bool sysfs_is_bin(struct sysfs_dirent *sd) { return sysfs_type(sd) == SYSFS_KOBJ_BIN_ATTR; diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index e8b73d4..b923052 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -9,12 +9,30 @@ #include #include +#include +#include struct file; struct iattr; +struct seq_file; +struct vm_area_struct; struct sysfs_dirent; +struct sysfs_open_file { + /* published fields */ + struct sysfs_dirent *sd; + struct file *file; + + /* private fields, do not use outside kernfs proper */ + struct mutex mutex; + int event; + struct list_head list; + + bool mmapped; + const struct vm_operations_struct *vm_ops; +}; + #ifdef CONFIG_SYSFS struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, -- cgit v1.1 From f6acf8bb6a40ba3bfcf542e4c4c9e8968c8cb57a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:21 -0500 Subject: sysfs, kernfs: introduce kernfs_ops We're in the process of separating out core sysfs functionality into kernfs which will deal with sysfs_dirents directly. This patch introduces kernfs_ops which hosts methods kernfs users implement and updates fs/sysfs/file.c such that sysfs_kf_*() functions populate kernfs_ops and kernfs_file_*() functions call the matching entries from kernfs_ops. kernfs_ops contains the following groups of methods. * seq_show() - for kernfs files which use seq_file for reads. * read() - for direct read implementations. Used iff seq_show() is not implemented. * write() - for writes. * mmap() - for mmaps. Notes: * sysfs_elem_attr->ops is added so that kernfs_ops can be accessed from sysfs_dirent. kernfs_ops() helper is added to verify locking and access the field. * SYSFS_FLAG_HAS_(SEQ_SHOW|MMAP) added. sd->s_attr->ops is accessible only while holding active_ref and there are cases where we want to take different actions depending on which ops are implemented. These two flags cache whether the two ops are implemented for those. * kernfs_file_*() no longer test sysfs type but chooses different behaviors depending on which methods in kernfs_ops are implemented. The conversions are trivial except for the open path. As kernfs_file_open() now decides whether to allow read/write accesses depending on the kernfs_ops implemented, the presence of methods in kobjs and attribute_bin should be propagated to kernfs_ops. sysfs_add_file_mode_ns() is updated so that it propagates presence / absence of the callbacks through _empty, _ro, _wo, _rw kernfs_ops. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 146 +++++++++++++++++++++++++++++++++++++------------ fs/sysfs/sysfs.h | 3 + include/linux/kernfs.h | 26 +++++++++ 3 files changed, 141 insertions(+), 34 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index acba583..cbebc33 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -58,6 +58,17 @@ static struct sysfs_open_file *sysfs_of(struct file *file) } /* + * Determine the kernfs_ops for the given sysfs_dirent. This function must + * be called while holding an active reference. + */ +static const struct kernfs_ops *kernfs_ops(struct sysfs_dirent *sd) +{ + if (!sysfs_ignore_lockdep(sd)) + lockdep_assert_held(sd); + return sd->s_attr.ops; +} + +/* * Determine ktype->sysfs_ops for the given sysfs_dirent. This function * must be called while holding an active reference. */ @@ -180,7 +191,7 @@ static int kernfs_seq_show(struct seq_file *sf, void *v) of->event = atomic_read(&of->sd->s_attr.open->event); - return sysfs_kf_seq_show(sf, v); + return of->sd->s_attr.ops->seq_show(sf, v); } static const struct seq_operations kernfs_seq_ops = { @@ -201,6 +212,7 @@ static ssize_t kernfs_file_direct_read(struct sysfs_open_file *of, loff_t *ppos) { ssize_t len = min_t(size_t, count, PAGE_SIZE); + const struct kernfs_ops *ops; char *buf; buf = kmalloc(len, GFP_KERNEL); @@ -218,7 +230,11 @@ static ssize_t kernfs_file_direct_read(struct sysfs_open_file *of, goto out_free; } - len = sysfs_kf_bin_read(of, buf, len, *ppos); + ops = kernfs_ops(of->sd); + if (ops->read) + len = ops->read(of, buf, len, *ppos); + else + len = -EINVAL; sysfs_put_active(of->sd); mutex_unlock(&of->mutex); @@ -250,10 +266,10 @@ static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, { struct sysfs_open_file *of = sysfs_of(file); - if (sysfs_is_bin(of->sd)) - return kernfs_file_direct_read(of, user_buf, count, ppos); - else + if (of->sd->s_flags & SYSFS_FLAG_HAS_SEQ_SHOW) return seq_read(file, user_buf, count, ppos); + else + return kernfs_file_direct_read(of, user_buf, count, ppos); } /* kernfs write callback for regular sysfs files */ @@ -312,6 +328,7 @@ static ssize_t kernfs_file_write(struct file *file, const char __user *user_buf, { struct sysfs_open_file *of = sysfs_of(file); ssize_t len = min_t(size_t, count, PAGE_SIZE); + const struct kernfs_ops *ops; char *buf; buf = kmalloc(len + 1, GFP_KERNEL); @@ -335,10 +352,11 @@ static ssize_t kernfs_file_write(struct file *file, const char __user *user_buf, goto out_free; } - if (sysfs_is_bin(of->sd)) - len = sysfs_kf_bin_write(of, buf, len, *ppos); + ops = kernfs_ops(of->sd); + if (ops->write) + len = ops->write(of, buf, len, *ppos); else - len = sysfs_kf_write(of, buf, len, *ppos); + len = -EINVAL; sysfs_put_active(of->sd); mutex_unlock(&of->mutex); @@ -524,6 +542,7 @@ static const struct vm_operations_struct kernfs_vm_ops = { static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) { struct sysfs_open_file *of = sysfs_of(file); + const struct kernfs_ops *ops; int rc; mutex_lock(&of->mutex); @@ -532,8 +551,9 @@ static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) if (!sysfs_get_active(of->sd)) goto out_unlock; - if (sysfs_is_bin(of->sd)) - rc = sysfs_kf_bin_mmap(of, vma); + ops = kernfs_ops(of->sd); + if (ops->mmap) + rc = ops->mmap(of, vma); if (rc) goto out_put; @@ -660,34 +680,19 @@ static void sysfs_put_open_dirent(struct sysfs_dirent *sd, static int kernfs_file_open(struct inode *inode, struct file *file) { struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; - struct kobject *kobj = attr_sd->s_parent->priv; + const struct kernfs_ops *ops; struct sysfs_open_file *of; bool has_read, has_write, has_mmap; int error = -EACCES; - /* need attr_sd for attr and ops, its parent for kobj */ if (!sysfs_get_active(attr_sd)) return -ENODEV; - if (sysfs_is_bin(attr_sd)) { - struct bin_attribute *battr = attr_sd->priv; + ops = kernfs_ops(attr_sd); - has_read = battr->read || battr->mmap; - has_write = battr->write || battr->mmap; - has_mmap = battr->mmap; - } else { - const struct sysfs_ops *ops = sysfs_file_ops(attr_sd); - - /* every kobject with an attribute needs a ktype assigned */ - if (WARN(!ops, KERN_ERR - "missing sysfs attribute operations for kobject: %s\n", - kobject_name(kobj))) - goto err_out; - - has_read = ops->show; - has_write = ops->store; - has_mmap = false; - } + has_read = ops->seq_show || ops->read || ops->mmap; + has_write = ops->write || ops->mmap; + has_mmap = ops->mmap; /* check perms and supported operations */ if ((file->f_mode & FMODE_WRITE) && @@ -729,10 +734,10 @@ static int kernfs_file_open(struct inode *inode, struct file *file) * seq_file or is not requested. This unifies private data access * and readable regular files are the vast majority anyway. */ - if (sysfs_is_bin(attr_sd)) - error = seq_open(file, NULL); - else + if (ops->seq_show) error = seq_open(file, &kernfs_seq_ops); + else + error = seq_open(file, NULL); if (error) goto err_free; @@ -777,7 +782,7 @@ void sysfs_unmap_bin_file(struct sysfs_dirent *sd) struct sysfs_open_dirent *od; struct sysfs_open_file *of; - if (!sysfs_is_bin(sd)) + if (!(sd->s_flags & SYSFS_FLAG_HAS_MMAP)) return; spin_lock_irq(&sysfs_open_dirent_lock); @@ -880,23 +885,96 @@ const struct file_operations kernfs_file_operations = { .poll = kernfs_file_poll, }; +static const struct kernfs_ops sysfs_file_kfops_empty = { +}; + +static const struct kernfs_ops sysfs_file_kfops_ro = { + .seq_show = sysfs_kf_seq_show, +}; + +static const struct kernfs_ops sysfs_file_kfops_wo = { + .write = sysfs_kf_write, +}; + +static const struct kernfs_ops sysfs_file_kfops_rw = { + .seq_show = sysfs_kf_seq_show, + .write = sysfs_kf_write, +}; + +static const struct kernfs_ops sysfs_bin_kfops_ro = { + .read = sysfs_kf_bin_read, +}; + +static const struct kernfs_ops sysfs_bin_kfops_wo = { + .write = sysfs_kf_bin_write, +}; + +static const struct kernfs_ops sysfs_bin_kfops_rw = { + .read = sysfs_kf_bin_read, + .write = sysfs_kf_bin_write, + .mmap = sysfs_kf_bin_mmap, +}; + int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, const struct attribute *attr, int type, umode_t amode, const void *ns) { umode_t mode = (amode & S_IALLUGO) | S_IFREG; + const struct kernfs_ops *ops; struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; int rc; + if (type == SYSFS_KOBJ_ATTR) { + struct kobject *kobj = dir_sd->priv; + const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops; + + /* every kobject with an attribute needs a ktype assigned */ + if (WARN(!sysfs_ops, KERN_ERR + "missing sysfs attribute operations for kobject: %s\n", + kobject_name(kobj))) + return -EINVAL; + + if (sysfs_ops->show && sysfs_ops->store) + ops = &sysfs_file_kfops_rw; + else if (sysfs_ops->show) + ops = &sysfs_file_kfops_ro; + else if (sysfs_ops->store) + ops = &sysfs_file_kfops_wo; + else + ops = &sysfs_file_kfops_empty; + } else { + struct bin_attribute *battr = (void *)attr; + + if ((battr->read && battr->write) || battr->mmap) + ops = &sysfs_bin_kfops_rw; + else if (battr->read) + ops = &sysfs_bin_kfops_ro; + else if (battr->write) + ops = &sysfs_bin_kfops_wo; + else + ops = &sysfs_file_kfops_empty; + } + sd = sysfs_new_dirent(attr->name, mode, type); if (!sd) return -ENOMEM; + sd->s_attr.ops = ops; sd->s_ns = ns; sd->priv = (void *)attr; sysfs_dirent_init_lockdep(sd); + /* + * sd->s_attr.ops is accesible only while holding active ref. We + * need to know whether some ops are implemented outside active + * ref. Cache their existence in flags. + */ + if (ops->seq_show) + sd->s_flags |= SYSFS_FLAG_HAS_SEQ_SHOW; + if (ops->mmap) + sd->s_flags |= SYSFS_FLAG_HAS_MMAP; + sysfs_addrm_start(&acxt); rc = sysfs_add_one(&acxt, sd, dir_sd); sysfs_addrm_finish(&acxt); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 619250d..c05e0dd 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -27,6 +27,7 @@ struct sysfs_elem_symlink { }; struct sysfs_elem_attr { + const struct kernfs_ops *ops; struct sysfs_open_dirent *open; }; @@ -89,6 +90,8 @@ struct sysfs_dirent { #define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK #define SYSFS_FLAG_NS 0x01000 #define SYSFS_FLAG_REMOVED 0x02000 +#define SYSFS_FLAG_HAS_SEQ_SHOW 0x04000 +#define SYSFS_FLAG_HAS_MMAP 0x08000 static inline unsigned int sysfs_type(struct sysfs_dirent *sd) { diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index b923052..97c6c0f 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -33,6 +33,32 @@ struct sysfs_open_file { const struct vm_operations_struct *vm_ops; }; +struct kernfs_ops { + /* + * Read is handled by either seq_file or raw_read(). + * + * If seq_show() is present, seq_file path is active. The behavior + * is equivalent to single_open(). @sf->private points to the + * associated sysfs_open_file. + * + * read() is bounced through kernel buffer and a read larger than + * PAGE_SIZE results in partial operation of PAGE_SIZE. + */ + int (*seq_show)(struct seq_file *sf, void *v); + + ssize_t (*read)(struct sysfs_open_file *of, char *buf, size_t bytes, + loff_t off); + + /* + * write() is bounced through kernel buffer and a write larger than + * PAGE_SIZE results in partial operation of PAGE_SIZE. + */ + ssize_t (*write)(struct sysfs_open_file *of, char *buf, size_t bytes, + loff_t off); + + int (*mmap)(struct sysfs_open_file *of, struct vm_area_struct *vma); +}; + #ifdef CONFIG_SYSFS struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, -- cgit v1.1 From 471bd7b78bd56c580e91e00a0f656ca922ab3b3c Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:22 -0500 Subject: sysfs, kernfs: add sysfs_dirent->s_attr.size sysfs sets the size of regular files unconditionally at PAGE_SIZE and takes the size of bin files from bin_attribute. The latter is a pretty bad interface which forces bin_attribute users to create a separate copy of bin_attribute for each instance of the file - e.g. pci resource files. Add sysfs_dirent->s_attr.size so that the size can be specified separately. This unifies inode init paths of ATTR and BIN_ATTR identical and allows for generic size handling for kernfs. Unfortunately, this grows the size of sysfs_dirent by sizeof(loff_t). Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 6 ++++++ fs/sysfs/inode.c | 8 +------- fs/sysfs/sysfs.h | 1 + 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index cbebc33..0b0cec8 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -923,6 +923,7 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, const struct kernfs_ops *ops; struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; + loff_t size; int rc; if (type == SYSFS_KOBJ_ATTR) { @@ -943,6 +944,8 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, ops = &sysfs_file_kfops_wo; else ops = &sysfs_file_kfops_empty; + + size = PAGE_SIZE; } else { struct bin_attribute *battr = (void *)attr; @@ -954,6 +957,8 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, ops = &sysfs_bin_kfops_wo; else ops = &sysfs_file_kfops_empty; + + size = battr->size; } sd = sysfs_new_dirent(attr->name, mode, type); @@ -961,6 +966,7 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, return -ENOMEM; sd->s_attr.ops = ops; + sd->s_attr.size = size; sd->s_ns = ns; sd->priv = (void *)attr; sysfs_dirent_init_lockdep(sd); diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 4c463da..037a892 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -254,8 +254,6 @@ int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) { - struct bin_attribute *bin_attr; - inode->i_private = sysfs_get(sd); inode->i_mapping->a_ops = &sysfs_aops; inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; @@ -271,12 +269,8 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) inode->i_fop = &sysfs_dir_operations; break; case SYSFS_KOBJ_ATTR: - inode->i_size = PAGE_SIZE; - inode->i_fop = &kernfs_file_operations; - break; case SYSFS_KOBJ_BIN_ATTR: - bin_attr = sd->priv; - inode->i_size = bin_attr->size; + inode->i_size = sd->s_attr.size; inode->i_fop = &kernfs_file_operations; break; case SYSFS_KOBJ_LINK: diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index c05e0dd..d40e85e 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -29,6 +29,7 @@ struct sysfs_elem_symlink { struct sysfs_elem_attr { const struct kernfs_ops *ops; struct sysfs_open_dirent *open; + loff_t size; }; struct sysfs_inode_attrs { -- cgit v1.1 From a7dc66dfb4c6d6c1d7c14d5106ce467f1dbd4eba Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:23 -0500 Subject: sysfs, kernfs: remove SYSFS_KOBJ_BIN_ATTR After kernfs_ops and sysfs_dirent->s_attr.size addition, the distinction between SYSFS_KOBJ_BIN_ATTR and SYSFS_KOBJ_ATTR is only necessary while creating files to decide which kernfs_ops to use. Afterwards, they behave exactly the same. This patch removes SYSFS_KOBJ_BIN_ATTR along with sysfs_is_bin(). sysfs_add_file[_mode_ns]() are updated to take bool @is_bin instead of @type. This patch doesn't introduce any behavior changes. This completely isolates the distinction between the two sysfs file types in the sysfs layer proper. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 23 ++++++++--------------- fs/sysfs/group.c | 5 ++--- fs/sysfs/inode.c | 1 - fs/sysfs/sysfs.h | 11 ++++------- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 0b0cec8..e2ce674 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -47,11 +47,6 @@ struct sysfs_open_dirent { struct list_head files; /* goes through sysfs_open_file.list */ }; -static bool sysfs_is_bin(struct sysfs_dirent *sd) -{ - return sysfs_type(sd) == SYSFS_KOBJ_BIN_ATTR; -} - static struct sysfs_open_file *sysfs_of(struct file *file) { return ((struct seq_file *)file->private_data)->private; @@ -916,7 +911,7 @@ static const struct kernfs_ops sysfs_bin_kfops_rw = { }; int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, - const struct attribute *attr, int type, + const struct attribute *attr, bool is_bin, umode_t amode, const void *ns) { umode_t mode = (amode & S_IALLUGO) | S_IFREG; @@ -926,7 +921,7 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, loff_t size; int rc; - if (type == SYSFS_KOBJ_ATTR) { + if (!is_bin) { struct kobject *kobj = dir_sd->priv; const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops; @@ -961,7 +956,7 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, size = battr->size; } - sd = sysfs_new_dirent(attr->name, mode, type); + sd = sysfs_new_dirent(attr->name, mode, SYSFS_KOBJ_ATTR); if (!sd) return -ENOMEM; @@ -991,11 +986,10 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, return rc; } - int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, - int type) + bool is_bin) { - return sysfs_add_file_mode_ns(dir_sd, attr, type, attr->mode, NULL); + return sysfs_add_file_mode_ns(dir_sd, attr, is_bin, attr->mode, NULL); } /** @@ -1009,8 +1003,7 @@ int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr, { BUG_ON(!kobj || !kobj->sd || !attr); - return sysfs_add_file_mode_ns(kobj->sd, attr, SYSFS_KOBJ_ATTR, - attr->mode, ns); + return sysfs_add_file_mode_ns(kobj->sd, attr, false, attr->mode, ns); } EXPORT_SYMBOL_GPL(sysfs_create_file_ns); @@ -1049,7 +1042,7 @@ int sysfs_add_file_to_group(struct kobject *kobj, if (!dir_sd) return -ENOENT; - error = sysfs_add_file(dir_sd, attr, SYSFS_KOBJ_ATTR); + error = sysfs_add_file(dir_sd, attr, false); sysfs_put(dir_sd); return error; @@ -1141,7 +1134,7 @@ int sysfs_create_bin_file(struct kobject *kobj, { BUG_ON(!kobj || !kobj->sd || !attr); - return sysfs_add_file(kobj->sd, &attr->attr, SYSFS_KOBJ_BIN_ATTR); + return sysfs_add_file(kobj->sd, &attr->attr, true); } EXPORT_SYMBOL_GPL(sysfs_create_bin_file); diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 065689d..9f65cd9 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -55,8 +55,7 @@ static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, if (!mode) continue; } - error = sysfs_add_file_mode_ns(dir_sd, *attr, - SYSFS_KOBJ_ATTR, + error = sysfs_add_file_mode_ns(dir_sd, *attr, false, (*attr)->mode | mode, NULL); if (unlikely(error)) @@ -269,7 +268,7 @@ int sysfs_merge_group(struct kobject *kobj, return -ENOENT; for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr)) - error = sysfs_add_file(dir_sd, *attr, SYSFS_KOBJ_ATTR); + error = sysfs_add_file(dir_sd, *attr, false); if (error) { while (--i >= 0) kernfs_remove_by_name(dir_sd, (*--attr)->name); diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index 037a892..b3c717a 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -269,7 +269,6 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) inode->i_fop = &sysfs_dir_operations; break; case SYSFS_KOBJ_ATTR: - case SYSFS_KOBJ_BIN_ATTR: inode->i_size = sd->s_attr.size; inode->i_fop = &kernfs_file_operations; break; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index d40e85e..28898fa 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -83,10 +83,9 @@ struct sysfs_dirent { #define SYSFS_TYPE_MASK 0x00ff #define SYSFS_DIR 0x0001 #define SYSFS_KOBJ_ATTR 0x0002 -#define SYSFS_KOBJ_BIN_ATTR 0x0004 #define SYSFS_KOBJ_LINK 0x0008 #define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) -#define SYSFS_ACTIVE_REF (SYSFS_KOBJ_ATTR | SYSFS_KOBJ_BIN_ATTR) +#define SYSFS_ACTIVE_REF SYSFS_KOBJ_ATTR #define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK #define SYSFS_FLAG_NS 0x01000 @@ -115,10 +114,8 @@ do { \ static inline bool sysfs_ignore_lockdep(struct sysfs_dirent *sd) { struct attribute *attr = sd->priv; - int type = sysfs_type(sd); - return (type == SYSFS_KOBJ_ATTR || type == SYSFS_KOBJ_BIN_ATTR) && - attr->ignore_lockdep; + return sysfs_type(sd) == SYSFS_KOBJ_ATTR && attr->ignore_lockdep; } #else @@ -219,10 +216,10 @@ int sysfs_inode_init(void); extern const struct file_operations kernfs_file_operations; int sysfs_add_file(struct sysfs_dirent *dir_sd, - const struct attribute *attr, int type); + const struct attribute *attr, bool is_bin); int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, - const struct attribute *attr, int type, + const struct attribute *attr, bool is_bin, umode_t amode, const void *ns); void sysfs_unmap_bin_file(struct sysfs_dirent *sd); -- cgit v1.1 From 496f73944a4a974f89d48920bf368aec8841b195 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:24 -0500 Subject: sysfs, kernfs: introduce kernfs_create_file[_ns]() Introduce kernfs interface to create a file which takes and returns sysfs_dirents. The actual file creation part is separated out from sysfs_add_file_mode_ns() into kernfs_create_file_ns(). The former now only decides the kernfs_ops to use and the file's size and invokes the latter. This patch doesn't introduce behavior changes. v2: Dummy implementation for !CONFIG_SYSFS updated to return -ENOSYS. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 53 +++++++++++++++++++++++++++++++++++++++----------- include/linux/kernfs.h | 18 +++++++++++++++++ 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index e2ce674..69cca0f 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -912,14 +912,11 @@ static const struct kernfs_ops sysfs_bin_kfops_rw = { int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, const struct attribute *attr, bool is_bin, - umode_t amode, const void *ns) + umode_t mode, const void *ns) { - umode_t mode = (amode & S_IALLUGO) | S_IFREG; const struct kernfs_ops *ops; - struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; loff_t size; - int rc; if (!is_bin) { struct kobject *kobj = dir_sd->priv; @@ -956,14 +953,47 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, size = battr->size; } - sd = sysfs_new_dirent(attr->name, mode, SYSFS_KOBJ_ATTR); + sd = kernfs_create_file_ns(dir_sd, attr->name, mode, size, + ops, (void *)attr, ns); + if (IS_ERR(sd)) { + if (PTR_ERR(sd) == -EEXIST) + sysfs_warn_dup(dir_sd, attr->name); + return PTR_ERR(sd); + } + return 0; +} + +/** + * kernfs_create_file_ns - create a file + * @parent: directory to create the file in + * @name: name of the file + * @mode: mode of the file + * @size: size of the file + * @ops: kernfs operations for the file + * @priv: private data for the file + * @ns: optional namespace tag of the file + * + * Returns the created node on success, ERR_PTR() value on error. + */ +struct sysfs_dirent *kernfs_create_file_ns(struct sysfs_dirent *parent, + const char *name, + umode_t mode, loff_t size, + const struct kernfs_ops *ops, + void *priv, const void *ns) +{ + struct sysfs_addrm_cxt acxt; + struct sysfs_dirent *sd; + int rc; + + sd = sysfs_new_dirent(name, (mode & S_IALLUGO) | S_IFREG, + SYSFS_KOBJ_ATTR); if (!sd) - return -ENOMEM; + return ERR_PTR(-ENOMEM); sd->s_attr.ops = ops; sd->s_attr.size = size; sd->s_ns = ns; - sd->priv = (void *)attr; + sd->priv = priv; sysfs_dirent_init_lockdep(sd); /* @@ -977,13 +1007,14 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, sd->s_flags |= SYSFS_FLAG_HAS_MMAP; sysfs_addrm_start(&acxt); - rc = sysfs_add_one(&acxt, sd, dir_sd); + rc = __sysfs_add_one(&acxt, sd, parent); sysfs_addrm_finish(&acxt); - if (rc) + if (rc) { sysfs_put(sd); - - return rc; + return ERR_PTR(rc); + } + return sd; } int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 97c6c0f..d0912cf 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -64,6 +64,11 @@ struct kernfs_ops { struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, const void *ns); +struct sysfs_dirent *kernfs_create_file_ns(struct sysfs_dirent *parent, + const char *name, + umode_t mode, loff_t size, + const struct kernfs_ops *ops, + void *priv, const void *ns); struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, const char *name, struct sysfs_dirent *target); @@ -83,6 +88,12 @@ kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, { return ERR_PTR(-ENOSYS); } static inline struct sysfs_dirent * +kernfs_create_file_ns(struct sysfs_dirent *parent, const char *name, + umode_t mode, loff_t size, const struct kernfs_ops *ops, + void *priv, const void *ns) +{ return ERR_PTR(-ENOSYS); } + +static inline struct sysfs_dirent * kernfs_create_link(struct sysfs_dirent *parent, const char *name, struct sysfs_dirent *target) { return ERR_PTR(-ENOSYS); } @@ -112,6 +123,13 @@ kernfs_create_dir(struct sysfs_dirent *parent, const char *name, void *priv) return kernfs_create_dir_ns(parent, name, priv, NULL); } +static inline struct sysfs_dirent * +kernfs_create_file(struct sysfs_dirent *parent, const char *name, umode_t mode, + loff_t size, const struct kernfs_ops *ops, void *priv) +{ + return kernfs_create_file_ns(parent, name, mode, size, ops, priv, NULL); +} + static inline int kernfs_remove_by_name(struct sysfs_dirent *parent, const char *name) { -- cgit v1.1 From 2d0cfbec2a95c16818960fda1dfa815fd1a62070 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:25 -0500 Subject: sysfs, kernfs: remove sysfs_add_one() sysfs_add_one() is a wrapper around __sysfs_add_one() which prints out duplicate name warning if __sysfs_add_one() fails with -EEXIST. The previous kernfs conversions moved all dup warnings to sysfs interface functions and sysfs_add_one() doesn't have any user left. Remove sysfs_add_one() and update __sysfs_add_one() to take its name. This patch doesn't make any functional changes. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 41 ++++------------------------------------- fs/sysfs/file.c | 2 +- fs/sysfs/symlink.c | 2 +- fs/sysfs/sysfs.h | 2 -- 4 files changed, 6 insertions(+), 41 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index cfbf409..e88e9a9 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -395,7 +395,7 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) } /** - * __sysfs_add_one - add sysfs_dirent to parent without warning + * sysfs_add_one - add sysfs_dirent to parent without warning * @acxt: addrm context to use * @sd: sysfs_dirent to be added * @parent_sd: the parent sysfs_dirent to add @sd to @@ -415,8 +415,8 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) * 0 on success, -EEXIST if entry with the given name already * exists. */ -int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, - struct sysfs_dirent *parent_sd) +int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, + struct sysfs_dirent *parent_sd) { bool has_ns = parent_sd->s_flags & SYSFS_FLAG_NS; struct sysfs_inode_attrs *ps_iattr; @@ -488,39 +488,6 @@ void sysfs_warn_dup(struct sysfs_dirent *parent, const char *name) } /** - * sysfs_add_one - add sysfs_dirent to parent - * @acxt: addrm context to use - * @sd: sysfs_dirent to be added - * @parent_sd: the parent sysfs_dirent to add @sd to - * - * Get @parent_sd and set @sd->s_parent to it and increment nlink of - * the parent inode if @sd is a directory and link into the children - * list of the parent. - * - * 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(). - * - * RETURNS: - * 0 on success, -EEXIST if entry with the given name already - * exists. - */ -int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, - struct sysfs_dirent *parent_sd) -{ - int ret; - - ret = __sysfs_add_one(acxt, sd, parent_sd); - - if (ret == -EEXIST) - sysfs_warn_dup(parent_sd, sd->s_name); - return ret; -} - -/** * sysfs_remove_one - remove sysfs_dirent from parent * @acxt: addrm context to use * @sd: sysfs_dirent to be removed @@ -694,7 +661,7 @@ struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, /* link in */ sysfs_addrm_start(&acxt); - rc = __sysfs_add_one(&acxt, sd, parent); + rc = sysfs_add_one(&acxt, sd, parent); sysfs_addrm_finish(&acxt); if (!rc) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 69cca0f..9852450 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -1007,7 +1007,7 @@ struct sysfs_dirent *kernfs_create_file_ns(struct sysfs_dirent *parent, sd->s_flags |= SYSFS_FLAG_HAS_MMAP; sysfs_addrm_start(&acxt); - rc = __sysfs_add_one(&acxt, sd, parent); + rc = sysfs_add_one(&acxt, sd, parent); sysfs_addrm_finish(&acxt); if (rc) { diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 352fbbb..76efeab 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -47,7 +47,7 @@ struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, sysfs_get(target); /* ref owned by symlink */ sysfs_addrm_start(&acxt); - error = __sysfs_add_one(&acxt, sd, parent); + error = sysfs_add_one(&acxt, sd, parent); sysfs_addrm_finish(&acxt); if (!error) diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 28898fa..a6542d2 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -167,8 +167,6 @@ struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd); void sysfs_put_active(struct sysfs_dirent *sd); void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt); void sysfs_warn_dup(struct sysfs_dirent *parent, const char *name); -int __sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, - struct sysfs_dirent *parent_sd); int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd); void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); -- cgit v1.1 From d19b9846df64d8845be682b6318bd1aee246cf60 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:26 -0500 Subject: sysfs, kernfs: add kernfs_ops->seq_{start|next|stop}() kernfs_ops currently only supports single_open() behavior which is pretty restrictive. Add optional callbacks ->seq_{start|next|stop}() which, when implemented, are invoked for seq_file traversal. This allows full seq_file functionality for kernfs users. This currently doesn't have any user and doesn't change any behavior. v2: Refreshed on top of the updated "sysfs, kernfs: prepare read path for kernfs". Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 39 ++++++++++++++++++++++++++++----------- include/linux/kernfs.h | 9 +++++++-- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 9852450..74e3478 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -146,6 +146,7 @@ static ssize_t sysfs_kf_bin_read(struct sysfs_open_file *of, char *buf, static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) { struct sysfs_open_file *of = sf->private; + const struct kernfs_ops *ops; /* * @of->mutex nests outside active ref and is just to ensure that @@ -155,26 +156,42 @@ static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) if (!sysfs_get_active(of->sd)) return ERR_PTR(-ENODEV); - /* - * The same behavior and code as single_open(). Returns !NULL if - * pos is at the beginning; otherwise, NULL. - */ - return NULL + !*ppos; + ops = kernfs_ops(of->sd); + if (ops->seq_start) { + return ops->seq_start(sf, ppos); + } else { + /* + * The same behavior and code as single_open(). Returns + * !NULL if pos is at the beginning; otherwise, NULL. + */ + return NULL + !*ppos; + } } static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) { - /* - * The same behavior and code as single_open(), always terminate - * after the initial read. - */ - ++*ppos; - return NULL; + struct sysfs_open_file *of = sf->private; + const struct kernfs_ops *ops = kernfs_ops(of->sd); + + if (ops->seq_next) { + return ops->seq_next(sf, v, ppos); + } else { + /* + * The same behavior and code as single_open(), always + * terminate after the initial read. + */ + ++*ppos; + return NULL; + } } static void kernfs_seq_stop(struct seq_file *sf, void *v) { struct sysfs_open_file *of = sf->private; + const struct kernfs_ops *ops = kernfs_ops(of->sd); + + if (ops->seq_stop) + ops->seq_stop(sf, v); sysfs_put_active(of->sd); mutex_unlock(&of->mutex); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index d0912cf..ba993eb 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -37,8 +37,9 @@ struct kernfs_ops { /* * Read is handled by either seq_file or raw_read(). * - * If seq_show() is present, seq_file path is active. The behavior - * is equivalent to single_open(). @sf->private points to the + * If seq_show() is present, seq_file path is active. Other seq + * operations are optional and if not implemented, the behavior is + * equivalent to single_open(). @sf->private points to the * associated sysfs_open_file. * * read() is bounced through kernel buffer and a read larger than @@ -46,6 +47,10 @@ struct kernfs_ops { */ int (*seq_show)(struct seq_file *sf, void *v); + void *(*seq_start)(struct seq_file *sf, loff_t *ppos); + void *(*seq_next)(struct seq_file *sf, void *v, loff_t *ppos); + void (*seq_stop)(struct seq_file *sf, void *v); + ssize_t (*read)(struct sysfs_open_file *of, char *buf, size_t bytes, loff_t off); -- cgit v1.1 From 024f647117d697165aaadf3f1af1343b7000149a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:27 -0500 Subject: sysfs, kernfs: introduce kernfs_notify() Introduce kernfs interface to wake up poll(2) which takes and returns sysfs_dirents. sysfs_notify_dirent() is renamed to kernfs_notify() and sysfs_notify() is updated so that it doesn't directly grab sysfs_mutex but acquires the target sysfs_dirents using sysfs_get_dirent(). sysfs_notify_dirent() is reimplemented as a dumb inline wrapper around kernfs_notify(). This patch doesn't introduce any behavior changes. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/file.c | 33 ++++++++++++++++++++++----------- include/linux/kernfs.h | 3 +++ include/linux/sysfs.h | 9 +++++---- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 74e3478..a68cbef 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -851,7 +851,13 @@ static unsigned int kernfs_file_poll(struct file *filp, poll_table *wait) return DEFAULT_POLLMASK|POLLERR|POLLPRI; } -void sysfs_notify_dirent(struct sysfs_dirent *sd) +/** + * kernfs_notify - notify a kernfs file + * @sd: file to notify + * + * Notify @sd such that poll(2) on @sd wakes up. + */ +void kernfs_notify(struct sysfs_dirent *sd) { struct sysfs_open_dirent *od; unsigned long flags; @@ -868,22 +874,27 @@ void sysfs_notify_dirent(struct sysfs_dirent *sd) spin_unlock_irqrestore(&sysfs_open_dirent_lock, flags); } -EXPORT_SYMBOL_GPL(sysfs_notify_dirent); +EXPORT_SYMBOL_GPL(kernfs_notify); void sysfs_notify(struct kobject *k, const char *dir, const char *attr) { - struct sysfs_dirent *sd = k->sd; - - mutex_lock(&sysfs_mutex); + struct sysfs_dirent *sd = k->sd, *tmp; if (sd && dir) - sd = sysfs_find_dirent(sd, dir, NULL); - if (sd && attr) - sd = sysfs_find_dirent(sd, attr, NULL); - if (sd) - sysfs_notify_dirent(sd); + sd = sysfs_get_dirent(sd, dir); + else + sysfs_get(sd); - mutex_unlock(&sysfs_mutex); + if (sd && attr) { + tmp = sysfs_get_dirent(sd, attr); + sysfs_put(sd); + sd = tmp; + } + + if (sd) { + kernfs_notify(sd); + sysfs_put(sd); + } } EXPORT_SYMBOL_GPL(sysfs_notify); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index ba993eb..f20796e 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -84,6 +84,7 @@ int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, const char *new_name, const void *new_ns); void kernfs_enable_ns(struct sysfs_dirent *sd); int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr); +void kernfs_notify(struct sysfs_dirent *sd); #else /* CONFIG_SYSFS */ @@ -120,6 +121,8 @@ static inline int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) { return -ENOSYS; } +static inline void kernfs_notify(struct sysfs_dirent *sd) { } + #endif /* CONFIG_SYSFS */ static inline struct sysfs_dirent * diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 2bc735d..0ab2b02 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -243,7 +243,6 @@ void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, const char *link_name); void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr); -void sysfs_notify_dirent(struct sysfs_dirent *sd); struct sysfs_dirent *sysfs_get_dirent_ns(struct sysfs_dirent *parent_sd, const unsigned char *name, const void *ns); @@ -418,9 +417,6 @@ static inline void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr) { } -static inline void sysfs_notify_dirent(struct sysfs_dirent *sd) -{ -} static inline struct sysfs_dirent * sysfs_get_dirent_ns(struct sysfs_dirent *parent_sd, const unsigned char *name, const void *ns) @@ -466,4 +462,9 @@ sysfs_get_dirent(struct sysfs_dirent *parent_sd, const unsigned char *name) return sysfs_get_dirent_ns(parent_sd, name, NULL); } +static inline void sysfs_notify_dirent(struct sysfs_dirent *sd) +{ + kernfs_notify(sd); +} + #endif /* _SYSFS_H_ */ -- cgit v1.1 From 2b25a62901a1af654c2604f19592b13742ad1601 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:28 -0500 Subject: sysfs, kernfs: reorganize SYSFS_* constants We want to add one more SYSFS_FLAG_* but we can't use the next higher bit, 0x10000, as the flag field is 16bits wide. The flags are currently arranged weirdly - 8 bits are set aside for the type flags when there are only three three used, the first flag starts at 0x1000 instead of 0x0100 and flag literals have 5 digits (20 bits) when only 4 digits can be used. Rearrange them so that type bits are only the lowest four, flags start at 0x0010 and similar flags are grouped. This patch doesn't cause any behavior difference. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/sysfs.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index a6542d2..c86456c 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -80,18 +80,18 @@ struct sysfs_dirent { #define SD_DEACTIVATED_BIAS INT_MIN -#define SYSFS_TYPE_MASK 0x00ff +#define SYSFS_TYPE_MASK 0x000f #define SYSFS_DIR 0x0001 #define SYSFS_KOBJ_ATTR 0x0002 -#define SYSFS_KOBJ_LINK 0x0008 +#define SYSFS_KOBJ_LINK 0x0004 #define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) #define SYSFS_ACTIVE_REF SYSFS_KOBJ_ATTR #define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK -#define SYSFS_FLAG_NS 0x01000 -#define SYSFS_FLAG_REMOVED 0x02000 -#define SYSFS_FLAG_HAS_SEQ_SHOW 0x04000 -#define SYSFS_FLAG_HAS_MMAP 0x08000 +#define SYSFS_FLAG_REMOVED 0x0010 +#define SYSFS_FLAG_NS 0x0020 +#define SYSFS_FLAG_HAS_SEQ_SHOW 0x0040 +#define SYSFS_FLAG_HAS_MMAP 0x0080 static inline unsigned int sysfs_type(struct sysfs_dirent *sd) { -- cgit v1.1 From 517e64f57883bd63c5a4ab8b3d0d3ed68c55d0cf Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:29 -0500 Subject: sysfs, kernfs: revamp sysfs_dirent active_ref lockdep annotation Currently, sysfs_dirent active_ref lockdep annotation uses attribute->[s]key as the lockdep key, which forces kernfs_create_file_ns() to assume that sysfs_dirent->priv is pointing to a struct attribute which may not be true for non-sysfs users. This patch restructures the lockdep annotation such that * kernfs_ops contains lockdep_key which is used by default for files created kernfs_create_file_ns(). * kernfs_create_file_ns_key() is introduced which takes an extra @key argument. The created file will use the specified key for active_ref lockdep annotation. If NULL is specified, lockdep for the file is disabled. * sysfs_add_file_mode_ns() is updated to use kernfs_create_file_ns_key() with the appropriate key from the attribute or NULL if ignore_lockdep is set. This makes the lockdep annotation properly contained in kernfs while allowing sysfs to cleanly keep its current behavior. This patch doesn't introduce any behavior differences. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 4 ++-- fs/sysfs/file.c | 35 ++++++++++++++++++++++++----------- fs/sysfs/sysfs.h | 32 +------------------------------- include/linux/kernfs.h | 37 +++++++++++++++++++++++++++++-------- 4 files changed, 56 insertions(+), 52 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index e88e9a9..8f2d577 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -150,7 +150,7 @@ struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) if (!atomic_inc_unless_negative(&sd->s_active)) return NULL; - if (likely(!sysfs_ignore_lockdep(sd))) + if (sd->s_flags & SYSFS_FLAG_LOCKDEP) rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_); return sd; } @@ -169,7 +169,7 @@ void sysfs_put_active(struct sysfs_dirent *sd) if (unlikely(!sd)) return; - if (likely(!sysfs_ignore_lockdep(sd))) + if (sd->s_flags & SYSFS_FLAG_LOCKDEP) rwsem_release(&sd->dep_map, 1, _RET_IP_); v = atomic_dec_return(&sd->s_active); if (likely(v != SD_DEACTIVATED_BIAS)) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index a68cbef..e4eca28 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -58,7 +58,7 @@ static struct sysfs_open_file *sysfs_of(struct file *file) */ static const struct kernfs_ops *kernfs_ops(struct sysfs_dirent *sd) { - if (!sysfs_ignore_lockdep(sd)) + if (sd->s_flags & SYSFS_FLAG_LOCKDEP) lockdep_assert_held(sd); return sd->s_attr.ops; } @@ -71,7 +71,7 @@ static const struct sysfs_ops *sysfs_file_ops(struct sysfs_dirent *sd) { struct kobject *kobj = sd->s_parent->priv; - if (!sysfs_ignore_lockdep(sd)) + if (sd->s_flags & SYSFS_FLAG_LOCKDEP) lockdep_assert_held(sd); return kobj->ktype ? kobj->ktype->sysfs_ops : NULL; } @@ -942,6 +942,7 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, const struct attribute *attr, bool is_bin, umode_t mode, const void *ns) { + struct lock_class_key *key = NULL; const struct kernfs_ops *ops; struct sysfs_dirent *sd; loff_t size; @@ -981,8 +982,12 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, size = battr->size; } - sd = kernfs_create_file_ns(dir_sd, attr->name, mode, size, - ops, (void *)attr, ns); +#ifdef CONFIG_DEBUG_LOCK_ALLOC + if (!attr->ignore_lockdep) + key = attr->key ?: (struct lock_class_key *)&attr->skey; +#endif + sd = kernfs_create_file_ns_key(dir_sd, attr->name, mode, size, + ops, (void *)attr, ns, key); if (IS_ERR(sd)) { if (PTR_ERR(sd) == -EEXIST) sysfs_warn_dup(dir_sd, attr->name); @@ -992,7 +997,7 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, } /** - * kernfs_create_file_ns - create a file + * kernfs_create_file_ns_key - create a file * @parent: directory to create the file in * @name: name of the file * @mode: mode of the file @@ -1000,14 +1005,16 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, * @ops: kernfs operations for the file * @priv: private data for the file * @ns: optional namespace tag of the file + * @key: lockdep key for the file's active_ref, %NULL to disable lockdep * * Returns the created node on success, ERR_PTR() value on error. */ -struct sysfs_dirent *kernfs_create_file_ns(struct sysfs_dirent *parent, - const char *name, - umode_t mode, loff_t size, - const struct kernfs_ops *ops, - void *priv, const void *ns) +struct sysfs_dirent *kernfs_create_file_ns_key(struct sysfs_dirent *parent, + const char *name, + umode_t mode, loff_t size, + const struct kernfs_ops *ops, + void *priv, const void *ns, + struct lock_class_key *key) { struct sysfs_addrm_cxt acxt; struct sysfs_dirent *sd; @@ -1022,7 +1029,13 @@ struct sysfs_dirent *kernfs_create_file_ns(struct sysfs_dirent *parent, sd->s_attr.size = size; sd->s_ns = ns; sd->priv = priv; - sysfs_dirent_init_lockdep(sd); + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + if (key) { + lockdep_init_map(&sd->dep_map, "s_active", key, 0); + sd->s_flags |= SYSFS_FLAG_LOCKDEP; + } +#endif /* * sd->s_attr.ops is accesible only while holding active ref. We diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index c86456c..e93f8b8 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -92,43 +92,13 @@ struct sysfs_dirent { #define SYSFS_FLAG_NS 0x0020 #define SYSFS_FLAG_HAS_SEQ_SHOW 0x0040 #define SYSFS_FLAG_HAS_MMAP 0x0080 +#define SYSFS_FLAG_LOCKDEP 0x0100 static inline unsigned int sysfs_type(struct sysfs_dirent *sd) { return sd->s_flags & SYSFS_TYPE_MASK; } -#ifdef CONFIG_DEBUG_LOCK_ALLOC - -#define sysfs_dirent_init_lockdep(sd) \ -do { \ - struct attribute *attr = sd->priv; \ - struct lock_class_key *key = attr->key; \ - if (!key) \ - key = &attr->skey; \ - \ - lockdep_init_map(&sd->dep_map, "s_active", key, 0); \ -} while (0) - -/* Test for attributes that want to ignore lockdep for read-locking */ -static inline bool sysfs_ignore_lockdep(struct sysfs_dirent *sd) -{ - struct attribute *attr = sd->priv; - - return sysfs_type(sd) == SYSFS_KOBJ_ATTR && attr->ignore_lockdep; -} - -#else - -#define sysfs_dirent_init_lockdep(sd) do {} while (0) - -static inline bool sysfs_ignore_lockdep(struct sysfs_dirent *sd) -{ - return true; -} - -#endif - /* * Context structure to be used while adding/removing nodes. */ diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index f20796e..105d09d 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -11,6 +11,7 @@ #include #include #include +#include struct file; struct iattr; @@ -62,6 +63,10 @@ struct kernfs_ops { loff_t off); int (*mmap)(struct sysfs_open_file *of, struct vm_area_struct *vma); + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lock_class_key lockdep_key; +#endif }; #ifdef CONFIG_SYSFS @@ -69,11 +74,12 @@ struct kernfs_ops { struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, const void *ns); -struct sysfs_dirent *kernfs_create_file_ns(struct sysfs_dirent *parent, - const char *name, - umode_t mode, loff_t size, - const struct kernfs_ops *ops, - void *priv, const void *ns); +struct sysfs_dirent *kernfs_create_file_ns_key(struct sysfs_dirent *parent, + const char *name, + umode_t mode, loff_t size, + const struct kernfs_ops *ops, + void *priv, const void *ns, + struct lock_class_key *key); struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, const char *name, struct sysfs_dirent *target); @@ -94,9 +100,10 @@ kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, { return ERR_PTR(-ENOSYS); } static inline struct sysfs_dirent * -kernfs_create_file_ns(struct sysfs_dirent *parent, const char *name, - umode_t mode, loff_t size, const struct kernfs_ops *ops, - void *priv, const void *ns) +kernfs_create_file_ns_key(struct sysfs_dirent *parent, const char *name, + umode_t mode, loff_t size, + const struct kernfs_ops *ops, void *priv, + const void *ns, struct lock_class_key *key) { return ERR_PTR(-ENOSYS); } static inline struct sysfs_dirent * @@ -132,6 +139,20 @@ kernfs_create_dir(struct sysfs_dirent *parent, const char *name, void *priv) } static inline struct sysfs_dirent * +kernfs_create_file_ns(struct sysfs_dirent *parent, const char *name, + umode_t mode, loff_t size, const struct kernfs_ops *ops, + void *priv, const void *ns) +{ + struct lock_class_key *key = NULL; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + key = (struct lock_class_key *)&ops->lockdep_key; +#endif + return kernfs_create_file_ns_key(parent, name, mode, size, ops, priv, + ns, key); +} + +static inline struct sysfs_dirent * kernfs_create_file(struct sysfs_dirent *parent, const char *name, umode_t mode, loff_t size, const struct kernfs_ops *ops, void *priv) { -- cgit v1.1 From ccf73cf336dc55bc52748205dee998d2fd4a8808 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:30 -0500 Subject: sysfs, kernfs: introduce kernfs[_find_and]_get() and kernfs_put() Introduce kernfs interface for finding, getting and putting sysfs_dirents. * sysfs_find_dirent() is renamed to kernfs_find_ns() and lockdep assertion for sysfs_mutex is added. * sysfs_get_dirent_ns() is renamed to kernfs_find_and_get(). * Macro inline dancing around __sysfs_get/put() are removed and kernfs_get/put() are made proper functions implemented in fs/sysfs/dir.c. While the conversions are mostly equivalent, there's one difference - kernfs_get() doesn't return the input param as its return value. This change is intentional. While passing through the input increases writability in some areas, it is unnecessary and has been shown to cause confusion regarding how the last ref is handled. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 117 ++++++++++++++++++++++++++++--------------------- fs/sysfs/file.c | 41 +++++++++-------- fs/sysfs/group.c | 30 +++++++------ fs/sysfs/inode.c | 5 ++- fs/sysfs/mount.c | 14 ------ fs/sysfs/symlink.c | 16 ++++--- fs/sysfs/sysfs.h | 22 ---------- include/linux/kernfs.h | 19 ++++++++ include/linux/sysfs.h | 35 ++++++--------- 9 files changed, 151 insertions(+), 148 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 8f2d577..0d806ef 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -240,10 +240,31 @@ static void sysfs_free_ino(unsigned int ino) spin_unlock(&sysfs_ino_lock); } -void release_sysfs_dirent(struct sysfs_dirent *sd) +/** + * kernfs_get - get a reference count on a sysfs_dirent + * @sd: the target sysfs_dirent + */ +void kernfs_get(struct sysfs_dirent *sd) +{ + if (sd) { + WARN_ON(!atomic_read(&sd->s_count)); + atomic_inc(&sd->s_count); + } +} +EXPORT_SYMBOL_GPL(kernfs_get); + +/** + * kernfs_put - put a reference count on a sysfs_dirent + * @sd: the target sysfs_dirent + * + * Put a reference count of @sd and destroy it if it reached zero. + */ +void kernfs_put(struct sysfs_dirent *sd) { struct sysfs_dirent *parent_sd; + if (!sd || !atomic_dec_and_test(&sd->s_count)) + return; repeat: /* Moving/renaming is always done while holding reference. * sd->s_parent won't change beneath us. @@ -255,7 +276,7 @@ void release_sysfs_dirent(struct sysfs_dirent *sd) parent_sd ? parent_sd->s_name : "", sd->s_name); if (sysfs_type(sd) == SYSFS_KOBJ_LINK) - sysfs_put(sd->s_symlink.target_sd); + kernfs_put(sd->s_symlink.target_sd); if (sysfs_type(sd) & SYSFS_COPY_NAME) kfree(sd->s_name); if (sd->s_iattr && sd->s_iattr->ia_secdata) @@ -269,6 +290,7 @@ void release_sysfs_dirent(struct sysfs_dirent *sd) if (sd && atomic_dec_and_test(&sd->s_count)) goto repeat; } +EXPORT_SYMBOL_GPL(kernfs_put); static int sysfs_dentry_delete(const struct dentry *dentry) { @@ -331,7 +353,7 @@ out_bad: static void sysfs_dentry_release(struct dentry *dentry) { - sysfs_put(dentry->d_fsdata); + kernfs_put(dentry->d_fsdata); } const struct dentry_operations sysfs_dentry_ops = { @@ -433,7 +455,8 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, return -EINVAL; sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); - sd->s_parent = sysfs_get(parent_sd); + sd->s_parent = parent_sd; + kernfs_get(parent_sd); ret = sysfs_link_sibling(sd); if (ret) @@ -553,36 +576,33 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) sysfs_deactivate(sd); sysfs_unmap_bin_file(sd); - sysfs_put(sd); + kernfs_put(sd); } } /** - * sysfs_find_dirent - find sysfs_dirent with the given name - * @parent_sd: sysfs_dirent to search under - * @name: name to look for - * @ns: the namespace tag to use - * - * Look for sysfs_dirent with name @name under @parent_sd. - * - * LOCKING: - * mutex_lock(sysfs_mutex) + * kernfs_find_ns - find sysfs_dirent with the given name + * @parent: sysfs_dirent to search under + * @name: name to look for + * @ns: the namespace tag to use * - * RETURNS: - * Pointer to sysfs_dirent if found, NULL if not. + * Look for sysfs_dirent with name @name under @parent. Returns pointer to + * the found sysfs_dirent on success, %NULL on failure. */ -struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, - const unsigned char *name, - const void *ns) +static struct sysfs_dirent *kernfs_find_ns(struct sysfs_dirent *parent, + const unsigned char *name, + const void *ns) { - struct rb_node *node = parent_sd->s_dir.children.rb_node; - bool has_ns = parent_sd->s_flags & SYSFS_FLAG_NS; + struct rb_node *node = parent->s_dir.children.rb_node; + bool has_ns = parent->s_flags & SYSFS_FLAG_NS; unsigned int hash; + lockdep_assert_held(&sysfs_mutex); + if (has_ns != (bool)ns) { WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", has_ns ? "required" : "invalid", - parent_sd->s_name, name); + parent->s_name, name); return NULL; } @@ -604,34 +624,28 @@ struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, } /** - * sysfs_get_dirent_ns - find and get sysfs_dirent with the given name - * @parent_sd: sysfs_dirent to search under - * @name: name to look for - * @ns: the namespace tag to use - * - * Look for sysfs_dirent with name @name under @parent_sd and get - * it if found. - * - * LOCKING: - * Kernel thread context (may sleep). Grabs sysfs_mutex. + * kernfs_find_and_get_ns - find and get sysfs_dirent with the given name + * @parent: sysfs_dirent to search under + * @name: name to look for + * @ns: the namespace tag to use * - * RETURNS: - * Pointer to sysfs_dirent if found, NULL if not. + * Look for sysfs_dirent with name @name under @parent and get a reference + * if found. This function may sleep and returns pointer to the found + * sysfs_dirent on success, %NULL on failure. */ -struct sysfs_dirent *sysfs_get_dirent_ns(struct sysfs_dirent *parent_sd, - const unsigned char *name, - const void *ns) +struct sysfs_dirent *kernfs_find_and_get_ns(struct sysfs_dirent *parent, + const char *name, const void *ns) { struct sysfs_dirent *sd; mutex_lock(&sysfs_mutex); - sd = sysfs_find_dirent(parent_sd, name, ns); - sysfs_get(sd); + sd = kernfs_find_ns(parent, name, ns); + kernfs_get(sd); mutex_unlock(&sysfs_mutex); return sd; } -EXPORT_SYMBOL_GPL(sysfs_get_dirent_ns); +EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); /** * kernfs_create_dir_ns - create a directory @@ -667,7 +681,7 @@ struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, if (!rc) return sd; - sysfs_put(sd); + kernfs_put(sd); return ERR_PTR(rc); } @@ -716,14 +730,15 @@ static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, if (parent_sd->s_flags & SYSFS_FLAG_NS) ns = sysfs_info(dir->i_sb)->ns; - sd = sysfs_find_dirent(parent_sd, dentry->d_name.name, ns); + sd = kernfs_find_ns(parent_sd, dentry->d_name.name, ns); /* no such entry */ if (!sd) { ret = ERR_PTR(-ENOENT); goto out_unlock; } - dentry->d_fsdata = sysfs_get(sd); + kernfs_get(sd); + dentry->d_fsdata = sd; /* attach dentry and inode */ inode = sysfs_get_inode(dir->i_sb, sd); @@ -859,7 +874,7 @@ int kernfs_remove_by_name_ns(struct sysfs_dirent *dir_sd, const char *name, sysfs_addrm_start(&acxt); - sd = sysfs_find_dirent(dir_sd, name, ns); + sd = kernfs_find_ns(dir_sd, name, ns); if (sd) __kernfs_remove(&acxt, sd); @@ -925,7 +940,7 @@ int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, goto out; /* nothing to rename */ error = -EEXIST; - if (sysfs_find_dirent(new_parent, new_name, new_ns)) + if (kernfs_find_ns(new_parent, new_name, new_ns)) goto out; /* rename sysfs_dirent */ @@ -943,8 +958,8 @@ int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, * Move to the appropriate place in the appropriate directories rbtree. */ sysfs_unlink_sibling(sd); - sysfs_get(new_parent); - sysfs_put(sd->s_parent); + kernfs_get(new_parent); + kernfs_put(sd->s_parent); sd->s_ns = new_ns; sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); sd->s_parent = new_parent; @@ -1000,7 +1015,7 @@ static inline unsigned char dt_type(struct sysfs_dirent *sd) static int sysfs_dir_release(struct inode *inode, struct file *filp) { - sysfs_put(filp->private_data); + kernfs_put(filp->private_data); return 0; } @@ -1011,7 +1026,7 @@ static struct sysfs_dirent *sysfs_dir_pos(const void *ns, int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && pos->s_parent == parent_sd && hash == pos->s_hash; - sysfs_put(pos); + kernfs_put(pos); if (!valid) pos = NULL; } @@ -1075,8 +1090,10 @@ static int sysfs_readdir(struct file *file, struct dir_context *ctx) unsigned int type = dt_type(pos); int len = strlen(name); ino_t ino = pos->s_ino; + ctx->pos = pos->s_hash; - file->private_data = sysfs_get(pos); + file->private_data = pos; + kernfs_get(pos); mutex_unlock(&sysfs_mutex); if (!dir_emit(ctx, name, len, ino, type)) diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index e4eca28..7f0a79f 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -881,19 +881,19 @@ void sysfs_notify(struct kobject *k, const char *dir, const char *attr) struct sysfs_dirent *sd = k->sd, *tmp; if (sd && dir) - sd = sysfs_get_dirent(sd, dir); + sd = kernfs_find_and_get(sd, dir); else - sysfs_get(sd); + kernfs_get(sd); if (sd && attr) { - tmp = sysfs_get_dirent(sd, attr); - sysfs_put(sd); + tmp = kernfs_find_and_get(sd, attr); + kernfs_put(sd); sd = tmp; } if (sd) { kernfs_notify(sd); - sysfs_put(sd); + kernfs_put(sd); } } EXPORT_SYMBOL_GPL(sysfs_notify); @@ -1052,7 +1052,7 @@ struct sysfs_dirent *kernfs_create_file_ns_key(struct sysfs_dirent *parent, sysfs_addrm_finish(&acxt); if (rc) { - sysfs_put(sd); + kernfs_put(sd); return ERR_PTR(rc); } return sd; @@ -1106,16 +1106,18 @@ int sysfs_add_file_to_group(struct kobject *kobj, struct sysfs_dirent *dir_sd; int error; - if (group) - dir_sd = sysfs_get_dirent(kobj->sd, group); - else - dir_sd = sysfs_get(kobj->sd); + if (group) { + dir_sd = kernfs_find_and_get(kobj->sd, group); + } else { + dir_sd = kobj->sd; + kernfs_get(dir_sd); + } if (!dir_sd) return -ENOENT; error = sysfs_add_file(dir_sd, attr, false); - sysfs_put(dir_sd); + kernfs_put(dir_sd); return error; } @@ -1135,7 +1137,7 @@ int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, struct iattr newattrs; int rc; - sd = sysfs_get_dirent(kobj->sd, attr->name); + sd = kernfs_find_and_get(kobj->sd, attr->name); if (!sd) return -ENOENT; @@ -1144,7 +1146,7 @@ int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, rc = kernfs_setattr(sd, &newattrs); - sysfs_put(sd); + kernfs_put(sd); return rc; } EXPORT_SYMBOL_GPL(sysfs_chmod_file); @@ -1185,13 +1187,16 @@ void sysfs_remove_file_from_group(struct kobject *kobj, { struct sysfs_dirent *dir_sd; - if (group) - dir_sd = sysfs_get_dirent(kobj->sd, group); - else - dir_sd = sysfs_get(kobj->sd); + if (group) { + dir_sd = kernfs_find_and_get(kobj->sd, group); + } else { + dir_sd = kobj->sd; + kernfs_get(dir_sd); + } + if (dir_sd) { kernfs_remove_by_name(dir_sd, attr->name); - sysfs_put(dir_sd); + kernfs_put(dir_sd); } } EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 9f65cd9..7177532 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -108,13 +108,13 @@ static int internal_create_group(struct kobject *kobj, int update, } } else sd = kobj->sd; - sysfs_get(sd); + kernfs_get(sd); error = create_files(sd, kobj, grp, update); if (error) { if (grp->name) kernfs_remove(sd); } - sysfs_put(sd); + kernfs_put(sd); return error; } @@ -208,21 +208,23 @@ void sysfs_remove_group(struct kobject *kobj, struct sysfs_dirent *sd; if (grp->name) { - sd = sysfs_get_dirent(dir_sd, grp->name); + sd = kernfs_find_and_get(dir_sd, grp->name); if (!sd) { WARN(!sd, KERN_WARNING "sysfs group %p not found for kobject '%s'\n", grp, kobject_name(kobj)); return; } - } else - sd = sysfs_get(dir_sd); + } else { + sd = dir_sd; + kernfs_get(sd); + } remove_files(sd, kobj, grp); if (grp->name) kernfs_remove(sd); - sysfs_put(sd); + kernfs_put(sd); } EXPORT_SYMBOL_GPL(sysfs_remove_group); @@ -263,7 +265,7 @@ int sysfs_merge_group(struct kobject *kobj, struct attribute *const *attr; int i; - dir_sd = sysfs_get_dirent(kobj->sd, grp->name); + dir_sd = kernfs_find_and_get(kobj->sd, grp->name); if (!dir_sd) return -ENOENT; @@ -273,7 +275,7 @@ int sysfs_merge_group(struct kobject *kobj, while (--i >= 0) kernfs_remove_by_name(dir_sd, (*--attr)->name); } - sysfs_put(dir_sd); + kernfs_put(dir_sd); return error; } @@ -290,11 +292,11 @@ void sysfs_unmerge_group(struct kobject *kobj, struct sysfs_dirent *dir_sd; struct attribute *const *attr; - dir_sd = sysfs_get_dirent(kobj->sd, grp->name); + dir_sd = kernfs_find_and_get(kobj->sd, grp->name); if (dir_sd) { for (attr = grp->attrs; *attr; ++attr) kernfs_remove_by_name(dir_sd, (*attr)->name); - sysfs_put(dir_sd); + kernfs_put(dir_sd); } } EXPORT_SYMBOL_GPL(sysfs_unmerge_group); @@ -312,12 +314,12 @@ int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name, struct sysfs_dirent *dir_sd; int error = 0; - dir_sd = sysfs_get_dirent(kobj->sd, group_name); + dir_sd = kernfs_find_and_get(kobj->sd, group_name); if (!dir_sd) return -ENOENT; error = sysfs_create_link_sd(dir_sd, target, link_name); - sysfs_put(dir_sd); + kernfs_put(dir_sd); return error; } @@ -334,10 +336,10 @@ void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, { struct sysfs_dirent *dir_sd; - dir_sd = sysfs_get_dirent(kobj->sd, group_name); + dir_sd = kernfs_find_and_get(kobj->sd, group_name); if (dir_sd) { kernfs_remove_by_name(dir_sd, link_name); - sysfs_put(dir_sd); + kernfs_put(dir_sd); } } EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group); diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c index b3c717a..bfe4478 100644 --- a/fs/sysfs/inode.c +++ b/fs/sysfs/inode.c @@ -254,7 +254,8 @@ int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) { - inode->i_private = sysfs_get(sd); + kernfs_get(sd); + inode->i_private = sd; inode->i_mapping->a_ops = &sysfs_aops; inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; inode->i_op = &sysfs_inode_operations; @@ -321,7 +322,7 @@ void sysfs_evict_inode(struct inode *inode) truncate_inode_pages(&inode->i_data, 0); clear_inode(inode); - sysfs_put(sd); + kernfs_put(sd); } int sysfs_permission(struct inode *inode, int mask) diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 8c24bce..852d115 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -184,17 +184,3 @@ out_err: sysfs_dir_cachep = NULL; goto out; } - -#undef sysfs_get -struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd) -{ - return __sysfs_get(sd); -} -EXPORT_SYMBOL_GPL(sysfs_get); - -#undef sysfs_put -void sysfs_put(struct sysfs_dirent *sd) -{ - __sysfs_put(sd); -} -EXPORT_SYMBOL_GPL(sysfs_put); diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 76efeab..b137aa3 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -44,7 +44,7 @@ struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, if (parent->s_flags & SYSFS_FLAG_NS) sd->s_ns = target->s_ns; sd->s_symlink.target_sd = target; - sysfs_get(target); /* ref owned by symlink */ + kernfs_get(target); /* ref owned by symlink */ sysfs_addrm_start(&acxt); error = sysfs_add_one(&acxt, sd, parent); @@ -53,7 +53,7 @@ struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, if (!error) return sd; - sysfs_put(sd); + kernfs_put(sd); return ERR_PTR(error); } @@ -72,15 +72,17 @@ static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, * sysfs_remove_dir() for details. */ spin_lock(&sysfs_symlink_target_lock); - if (target->sd) - target_sd = sysfs_get(target->sd); + if (target->sd) { + target_sd = target->sd; + kernfs_get(target_sd); + } spin_unlock(&sysfs_symlink_target_lock); if (!target_sd) return -ENOENT; sd = kernfs_create_link(parent_sd, name, target_sd); - sysfs_put(target_sd); + kernfs_put(target_sd); if (!IS_ERR(sd)) return 0; @@ -216,7 +218,7 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, old_ns = targ->sd->s_ns; result = -ENOENT; - sd = sysfs_get_dirent_ns(parent_sd, old, old_ns); + sd = kernfs_find_and_get_ns(parent_sd, old, old_ns); if (!sd) goto out; @@ -229,7 +231,7 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, result = kernfs_rename_ns(sd, parent_sd, new, new_ns); out: - sysfs_put(sd); + kernfs_put(sd); return result; } EXPORT_SYMBOL_GPL(sysfs_rename_link_ns); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index e93f8b8..85315e2 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -141,30 +141,8 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd); void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); -struct sysfs_dirent *sysfs_find_dirent(struct sysfs_dirent *parent_sd, - const unsigned char *name, - const void *ns); struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type); -void release_sysfs_dirent(struct sysfs_dirent *sd); - -static inline struct sysfs_dirent *__sysfs_get(struct sysfs_dirent *sd) -{ - if (sd) { - WARN_ON(!atomic_read(&sd->s_count)); - atomic_inc(&sd->s_count); - } - return sd; -} -#define sysfs_get(sd) __sysfs_get(sd) - -static inline void __sysfs_put(struct sysfs_dirent *sd) -{ - if (sd && atomic_dec_and_test(&sd->s_count)) - release_sysfs_dirent(sd); -} -#define sysfs_put(sd) __sysfs_put(sd) - /* * inode.c */ diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 105d09d..fd8f574 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -71,6 +71,11 @@ struct kernfs_ops { #ifdef CONFIG_SYSFS +struct sysfs_dirent *kernfs_find_and_get_ns(struct sysfs_dirent *parent, + const char *name, const void *ns); +void kernfs_get(struct sysfs_dirent *sd); +void kernfs_put(struct sysfs_dirent *sd); + struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, const void *ns); @@ -95,6 +100,14 @@ void kernfs_notify(struct sysfs_dirent *sd); #else /* CONFIG_SYSFS */ static inline struct sysfs_dirent * +kernfs_find_and_get_ns(struct sysfs_dirent *parent, const char *name, + const void *ns) +{ return NULL; } + +static inline void kernfs_get(struct sysfs_dirent *sd) { } +static inline void kernfs_put(struct sysfs_dirent *sd) { } + +static inline struct sysfs_dirent * kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, const void *ns) { return ERR_PTR(-ENOSYS); } @@ -133,6 +146,12 @@ static inline void kernfs_notify(struct sysfs_dirent *sd) { } #endif /* CONFIG_SYSFS */ static inline struct sysfs_dirent * +kernfs_find_and_get(struct sysfs_dirent *sd, const char *name) +{ + return kernfs_find_and_get_ns(sd, name, NULL); +} + +static inline struct sysfs_dirent * kernfs_create_dir(struct sysfs_dirent *parent, const char *name, void *priv) { return kernfs_create_dir_ns(parent, name, priv, NULL); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 0ab2b02..cd8f90b 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -243,11 +243,6 @@ void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, const char *link_name); void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr); -struct sysfs_dirent *sysfs_get_dirent_ns(struct sysfs_dirent *parent_sd, - const unsigned char *name, - const void *ns); -struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd); -void sysfs_put(struct sysfs_dirent *sd); int __must_check sysfs_init(void); @@ -417,19 +412,6 @@ static inline void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr) { } -static inline struct sysfs_dirent * -sysfs_get_dirent_ns(struct sysfs_dirent *parent_sd, const unsigned char *name, - const void *ns) -{ - return NULL; -} -static inline struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd) -{ - return NULL; -} -static inline void sysfs_put(struct sysfs_dirent *sd) -{ -} static inline int __must_check sysfs_init(void) { @@ -456,15 +438,26 @@ static inline int sysfs_rename_link(struct kobject *kobj, struct kobject *target return sysfs_rename_link_ns(kobj, target, old_name, new_name, NULL); } +static inline void sysfs_notify_dirent(struct sysfs_dirent *sd) +{ + kernfs_notify(sd); +} + static inline struct sysfs_dirent * sysfs_get_dirent(struct sysfs_dirent *parent_sd, const unsigned char *name) { - return sysfs_get_dirent_ns(parent_sd, name, NULL); + return kernfs_find_and_get(parent_sd, name); } -static inline void sysfs_notify_dirent(struct sysfs_dirent *sd) +static inline struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd) { - kernfs_notify(sd); + kernfs_get(sd); + return sd; +} + +static inline void sysfs_put(struct sysfs_dirent *sd) +{ + kernfs_put(sd); } #endif /* _SYSFS_H_ */ -- cgit v1.1 From ae6621b0716852146e4655fef7f74a181faa6c81 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:31 -0500 Subject: sysfs, kernfs: move internal decls to fs/kernfs/kernfs-internal.h Move data structure, constant and basic accessor declarations from fs/sysfs/sysfs.h to fs/kernfs/kernfs-internal.h. The two files currently include each other. Once kernfs / sysfs separation is complete, the cross inclusions will be removed. Inclusion protectors are added to fs/sysfs/sysfs.h to allow cross-inclusion. This patch doesn't introduce any functional changes. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/kernfs-internal.h | 115 ++++++++++++++++++++++++++++++++++++++++++++ fs/sysfs/sysfs.h | 102 +++------------------------------------ 2 files changed, 121 insertions(+), 96 deletions(-) create mode 100644 fs/kernfs/kernfs-internal.h diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h new file mode 100644 index 0000000..5a2c3a1 --- /dev/null +++ b/fs/kernfs/kernfs-internal.h @@ -0,0 +1,115 @@ +/* + * fs/kernfs/kernfs-internal.h - kernfs internal header file + * + * Copyright (c) 2001-3 Patrick Mochel + * Copyright (c) 2007 SUSE Linux Products GmbH + * Copyright (c) 2007, 2013 Tejun Heo + * + * This file is released under the GPLv2. + */ + +#ifndef __KERNFS_INTERNAL_H +#define __KERNFS_INTERNAL_H + +#include +#include +#include + +#include + +struct sysfs_open_dirent; + +/* type-specific structures for sysfs_dirent->s_* union members */ +struct sysfs_elem_dir { + unsigned long subdirs; + /* children rbtree starts here and goes through sd->s_rb */ + struct rb_root children; +}; + +struct sysfs_elem_symlink { + struct sysfs_dirent *target_sd; +}; + +struct sysfs_elem_attr { + const struct kernfs_ops *ops; + struct sysfs_open_dirent *open; + loff_t size; +}; + +struct sysfs_inode_attrs { + struct iattr ia_iattr; + void *ia_secdata; + u32 ia_secdata_len; +}; + +/* + * sysfs_dirent - the building block of sysfs hierarchy. Each and + * every sysfs node is represented by single sysfs_dirent. + * + * 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; + atomic_t s_active; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif + struct sysfs_dirent *s_parent; + const char *s_name; + + struct rb_node s_rb; + + union { + struct completion *completion; + struct sysfs_dirent *removed_list; + } u; + + const void *s_ns; /* namespace tag */ + unsigned int s_hash; /* ns + name hash */ + union { + struct sysfs_elem_dir s_dir; + struct sysfs_elem_symlink s_symlink; + struct sysfs_elem_attr s_attr; + }; + + void *priv; + + unsigned short s_flags; + umode_t s_mode; + unsigned int s_ino; + struct sysfs_inode_attrs *s_iattr; +}; + +#define SD_DEACTIVATED_BIAS INT_MIN + +#define SYSFS_TYPE_MASK 0x000f +#define SYSFS_DIR 0x0001 +#define SYSFS_KOBJ_ATTR 0x0002 +#define SYSFS_KOBJ_LINK 0x0004 +#define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) +#define SYSFS_ACTIVE_REF SYSFS_KOBJ_ATTR + +#define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK +#define SYSFS_FLAG_REMOVED 0x0010 +#define SYSFS_FLAG_NS 0x0020 +#define SYSFS_FLAG_HAS_SEQ_SHOW 0x0040 +#define SYSFS_FLAG_HAS_MMAP 0x0080 +#define SYSFS_FLAG_LOCKDEP 0x0100 + +static inline unsigned int sysfs_type(struct sysfs_dirent *sd) +{ + return sd->s_flags & SYSFS_TYPE_MASK; +} + +/* + * Context structure to be used while adding/removing nodes. + */ +struct sysfs_addrm_cxt { + struct sysfs_dirent *removed; +}; + +#include "../sysfs/sysfs.h" + +#endif /* __KERNFS_INTERNAL_H */ diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 85315e2..f8c936f 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -8,103 +8,11 @@ * This file is released under the GPLv2. */ -#include -#include -#include -#include +#ifndef __SYSFS_INTERNAL_H +#define __SYSFS_INTERNAL_H -struct sysfs_open_dirent; - -/* type-specific structures for sysfs_dirent->s_* union members */ -struct sysfs_elem_dir { - unsigned long subdirs; - /* children rbtree starts here and goes through sd->s_rb */ - struct rb_root children; -}; - -struct sysfs_elem_symlink { - struct sysfs_dirent *target_sd; -}; - -struct sysfs_elem_attr { - const struct kernfs_ops *ops; - struct sysfs_open_dirent *open; - loff_t size; -}; - -struct sysfs_inode_attrs { - struct iattr ia_iattr; - void *ia_secdata; - u32 ia_secdata_len; -}; - -/* - * sysfs_dirent - the building block of sysfs hierarchy. Each and - * every sysfs node is represented by single sysfs_dirent. - * - * 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; - atomic_t s_active; -#ifdef CONFIG_DEBUG_LOCK_ALLOC - struct lockdep_map dep_map; -#endif - struct sysfs_dirent *s_parent; - const char *s_name; - - struct rb_node s_rb; - - union { - struct completion *completion; - struct sysfs_dirent *removed_list; - } u; - - const void *s_ns; /* namespace tag */ - unsigned int s_hash; /* ns + name hash */ - union { - struct sysfs_elem_dir s_dir; - struct sysfs_elem_symlink s_symlink; - struct sysfs_elem_attr s_attr; - }; - - void *priv; - - unsigned short s_flags; - umode_t s_mode; - unsigned int s_ino; - struct sysfs_inode_attrs *s_iattr; -}; - -#define SD_DEACTIVATED_BIAS INT_MIN - -#define SYSFS_TYPE_MASK 0x000f -#define SYSFS_DIR 0x0001 -#define SYSFS_KOBJ_ATTR 0x0002 -#define SYSFS_KOBJ_LINK 0x0004 -#define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) -#define SYSFS_ACTIVE_REF SYSFS_KOBJ_ATTR - -#define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK -#define SYSFS_FLAG_REMOVED 0x0010 -#define SYSFS_FLAG_NS 0x0020 -#define SYSFS_FLAG_HAS_SEQ_SHOW 0x0040 -#define SYSFS_FLAG_HAS_MMAP 0x0080 -#define SYSFS_FLAG_LOCKDEP 0x0100 - -static inline unsigned int sysfs_type(struct sysfs_dirent *sd) -{ - return sd->s_flags & SYSFS_TYPE_MASK; -} - -/* - * Context structure to be used while adding/removing nodes. - */ -struct sysfs_addrm_cxt { - struct sysfs_dirent *removed; -}; +#include "../kernfs/kernfs-internal.h" +#include /* * mount.c @@ -175,3 +83,5 @@ void sysfs_unmap_bin_file(struct sysfs_dirent *sd); extern const struct inode_operations sysfs_symlink_inode_operations; int sysfs_create_link_sd(struct sysfs_dirent *sd, struct kobject *target, const char *name); + +#endif /* __SYSFS_INTERNAL_H */ -- cgit v1.1 From ffed24e22845a3da0ae01095ae3f11c8d16e889d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:32 -0500 Subject: sysfs, kernfs: move inode code to fs/kernfs/inode.c There's nothing sysfs-specific in fs/sysfs/inode.c. Move everything in it to fs/kernfs/inode.c. The respective declarations in fs/sysfs/sysfs.h are moved to fs/kernfs/kernfs-internal.h. This is pure relocation. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/inode.c | 327 ++++++++++++++++++++++++++++++++++++++++++ fs/kernfs/kernfs-internal.h | 13 ++ fs/sysfs/Makefile | 2 +- fs/sysfs/inode.c | 342 -------------------------------------------- fs/sysfs/sysfs.h | 13 -- 5 files changed, 341 insertions(+), 356 deletions(-) delete mode 100644 fs/sysfs/inode.c diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index 86bfeea..9d4fab4 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -7,3 +7,330 @@ * * This file is released under the GPLv2. */ + +#include +#include +#include +#include +#include +#include +#include + +#include "kernfs-internal.h" + +static const struct address_space_operations sysfs_aops = { + .readpage = simple_readpage, + .write_begin = simple_write_begin, + .write_end = simple_write_end, +}; + +static struct backing_dev_info sysfs_backing_dev_info = { + .name = "sysfs", + .ra_pages = 0, /* No readahead */ + .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK, +}; + +static const struct inode_operations sysfs_inode_operations = { + .permission = sysfs_permission, + .setattr = sysfs_setattr, + .getattr = sysfs_getattr, + .setxattr = sysfs_setxattr, +}; + +int __init sysfs_inode_init(void) +{ + return bdi_init(&sysfs_backing_dev_info); +} + +static struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd) +{ + struct sysfs_inode_attrs *attrs; + struct iattr *iattrs; + + attrs = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL); + if (!attrs) + return NULL; + iattrs = &attrs->ia_iattr; + + /* assign default attributes */ + iattrs->ia_mode = sd->s_mode; + iattrs->ia_uid = GLOBAL_ROOT_UID; + iattrs->ia_gid = GLOBAL_ROOT_GID; + iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME; + + return attrs; +} + +static int __kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) +{ + struct sysfs_inode_attrs *sd_attrs; + struct iattr *iattrs; + unsigned int ia_valid = iattr->ia_valid; + + sd_attrs = sd->s_iattr; + + if (!sd_attrs) { + /* setting attributes for the first time, allocate now */ + sd_attrs = sysfs_init_inode_attrs(sd); + if (!sd_attrs) + return -ENOMEM; + sd->s_iattr = sd_attrs; + } + /* attributes were changed at least once in past */ + iattrs = &sd_attrs->ia_iattr; + + if (ia_valid & ATTR_UID) + iattrs->ia_uid = iattr->ia_uid; + if (ia_valid & ATTR_GID) + iattrs->ia_gid = iattr->ia_gid; + if (ia_valid & ATTR_ATIME) + iattrs->ia_atime = iattr->ia_atime; + if (ia_valid & ATTR_MTIME) + iattrs->ia_mtime = iattr->ia_mtime; + if (ia_valid & ATTR_CTIME) + iattrs->ia_ctime = iattr->ia_ctime; + if (ia_valid & ATTR_MODE) { + umode_t mode = iattr->ia_mode; + iattrs->ia_mode = sd->s_mode = mode; + } + return 0; +} + +/** + * kernfs_setattr - set iattr on a node + * @sd: target node + * @iattr: iattr to set + * + * Returns 0 on success, -errno on failure. + */ +int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) +{ + int ret; + + mutex_lock(&sysfs_mutex); + ret = __kernfs_setattr(sd, iattr); + mutex_unlock(&sysfs_mutex); + return ret; +} + +int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) +{ + struct inode *inode = dentry->d_inode; + struct sysfs_dirent *sd = dentry->d_fsdata; + int error; + + if (!sd) + return -EINVAL; + + mutex_lock(&sysfs_mutex); + error = inode_change_ok(inode, iattr); + if (error) + goto out; + + error = __kernfs_setattr(sd, iattr); + if (error) + goto out; + + /* this ignores size changes */ + setattr_copy(inode, iattr); + +out: + mutex_unlock(&sysfs_mutex); + return error; +} + +static int sysfs_sd_setsecdata(struct sysfs_dirent *sd, void **secdata, + u32 *secdata_len) +{ + struct sysfs_inode_attrs *iattrs; + void *old_secdata; + size_t old_secdata_len; + + if (!sd->s_iattr) { + sd->s_iattr = sysfs_init_inode_attrs(sd); + if (!sd->s_iattr) + return -ENOMEM; + } + + iattrs = sd->s_iattr; + old_secdata = iattrs->ia_secdata; + old_secdata_len = iattrs->ia_secdata_len; + + iattrs->ia_secdata = *secdata; + iattrs->ia_secdata_len = *secdata_len; + + *secdata = old_secdata; + *secdata_len = old_secdata_len; + return 0; +} + +int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, + size_t size, int flags) +{ + struct sysfs_dirent *sd = dentry->d_fsdata; + void *secdata; + int error; + u32 secdata_len = 0; + + if (!sd) + return -EINVAL; + + if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) { + const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; + error = security_inode_setsecurity(dentry->d_inode, suffix, + value, size, flags); + if (error) + goto out; + error = security_inode_getsecctx(dentry->d_inode, + &secdata, &secdata_len); + if (error) + goto out; + + mutex_lock(&sysfs_mutex); + error = sysfs_sd_setsecdata(sd, &secdata, &secdata_len); + mutex_unlock(&sysfs_mutex); + + if (secdata) + security_release_secctx(secdata, secdata_len); + } else + return -EINVAL; +out: + return error; +} + +static inline void set_default_inode_attr(struct inode *inode, umode_t mode) +{ + inode->i_mode = mode; + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; +} + +static inline void set_inode_attr(struct inode *inode, struct iattr *iattr) +{ + inode->i_uid = iattr->ia_uid; + inode->i_gid = iattr->ia_gid; + inode->i_atime = iattr->ia_atime; + inode->i_mtime = iattr->ia_mtime; + inode->i_ctime = iattr->ia_ctime; +} + +static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode) +{ + struct sysfs_inode_attrs *iattrs = sd->s_iattr; + + inode->i_mode = sd->s_mode; + if (iattrs) { + /* sysfs_dirent has non-default attributes + * get them from persistent copy in sysfs_dirent + */ + set_inode_attr(inode, &iattrs->ia_iattr); + security_inode_notifysecctx(inode, + iattrs->ia_secdata, + iattrs->ia_secdata_len); + } + + if (sysfs_type(sd) == SYSFS_DIR) + set_nlink(inode, sd->s_dir.subdirs + 2); +} + +int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) +{ + struct sysfs_dirent *sd = dentry->d_fsdata; + struct inode *inode = dentry->d_inode; + + mutex_lock(&sysfs_mutex); + sysfs_refresh_inode(sd, inode); + mutex_unlock(&sysfs_mutex); + + generic_fillattr(inode, stat); + return 0; +} + +static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) +{ + kernfs_get(sd); + inode->i_private = sd; + inode->i_mapping->a_ops = &sysfs_aops; + inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; + inode->i_op = &sysfs_inode_operations; + + set_default_inode_attr(inode, sd->s_mode); + sysfs_refresh_inode(sd, inode); + + /* 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; + break; + case SYSFS_KOBJ_ATTR: + inode->i_size = sd->s_attr.size; + inode->i_fop = &kernfs_file_operations; + break; + case SYSFS_KOBJ_LINK: + inode->i_op = &sysfs_symlink_inode_operations; + break; + default: + BUG(); + } + + unlock_new_inode(inode); +} + +/** + * sysfs_get_inode - get inode for sysfs_dirent + * @sb: super block + * @sd: sysfs_dirent to allocate inode for + * + * 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). + * + * RETURNS: + * Pointer to allocated inode on success, NULL on failure. + */ +struct inode *sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd) +{ + struct inode *inode; + + inode = iget_locked(sb, sd->s_ino); + if (inode && (inode->i_state & I_NEW)) + sysfs_init_inode(sd, inode); + + return inode; +} + +/* + * The sysfs_dirent serves as both an inode and a directory entry for sysfs. + * To prevent the sysfs inode numbers from being freed prematurely we take a + * reference to sysfs_dirent from the sysfs inode. A + * super_operations.evict_inode() implementation is needed to drop that + * reference upon inode destruction. + */ +void sysfs_evict_inode(struct inode *inode) +{ + struct sysfs_dirent *sd = inode->i_private; + + truncate_inode_pages(&inode->i_data, 0); + clear_inode(inode); + kernfs_put(sd); +} + +int sysfs_permission(struct inode *inode, int mask) +{ + struct sysfs_dirent *sd; + + if (mask & MAY_NOT_BLOCK) + return -ECHILD; + + sd = inode->i_private; + + mutex_lock(&sysfs_mutex); + sysfs_refresh_inode(sd, inode); + mutex_unlock(&sysfs_mutex); + + return generic_permission(inode, mask); +} diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 5a2c3a1..933ac8d 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -112,4 +112,17 @@ struct sysfs_addrm_cxt { #include "../sysfs/sysfs.h" +/* + * inode.c + */ +struct inode *sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd); +void sysfs_evict_inode(struct inode *inode); +int sysfs_permission(struct inode *inode, int mask); +int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); +int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat); +int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, + size_t size, int flags); +int sysfs_inode_init(void); + #endif /* __KERNFS_INTERNAL_H */ diff --git a/fs/sysfs/Makefile b/fs/sysfs/Makefile index 8876ac1..6eff6e1 100644 --- a/fs/sysfs/Makefile +++ b/fs/sysfs/Makefile @@ -2,4 +2,4 @@ # Makefile for the sysfs virtual filesystem # -obj-y := inode.o file.o dir.o symlink.o mount.o group.o +obj-y := file.o dir.o symlink.o mount.o group.o diff --git a/fs/sysfs/inode.c b/fs/sysfs/inode.c deleted file mode 100644 index bfe4478..0000000 --- a/fs/sysfs/inode.c +++ /dev/null @@ -1,342 +0,0 @@ -/* - * fs/sysfs/inode.c - basic sysfs inode and dentry operations - * - * Copyright (c) 2001-3 Patrick Mochel - * Copyright (c) 2007 SUSE Linux Products GmbH - * Copyright (c) 2007 Tejun Heo - * - * This file is released under the GPLv2. - * - * Please see Documentation/filesystems/sysfs.txt for more information. - */ - -#undef DEBUG - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "sysfs.h" - -static const struct address_space_operations sysfs_aops = { - .readpage = simple_readpage, - .write_begin = simple_write_begin, - .write_end = simple_write_end, -}; - -static struct backing_dev_info sysfs_backing_dev_info = { - .name = "sysfs", - .ra_pages = 0, /* No readahead */ - .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK, -}; - -static const struct inode_operations sysfs_inode_operations = { - .permission = sysfs_permission, - .setattr = sysfs_setattr, - .getattr = sysfs_getattr, - .setxattr = sysfs_setxattr, -}; - -int __init sysfs_inode_init(void) -{ - return bdi_init(&sysfs_backing_dev_info); -} - -static struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd) -{ - struct sysfs_inode_attrs *attrs; - struct iattr *iattrs; - - attrs = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL); - if (!attrs) - return NULL; - iattrs = &attrs->ia_iattr; - - /* assign default attributes */ - iattrs->ia_mode = sd->s_mode; - iattrs->ia_uid = GLOBAL_ROOT_UID; - iattrs->ia_gid = GLOBAL_ROOT_GID; - iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME; - - return attrs; -} - -static int __kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) -{ - struct sysfs_inode_attrs *sd_attrs; - struct iattr *iattrs; - unsigned int ia_valid = iattr->ia_valid; - - sd_attrs = sd->s_iattr; - - if (!sd_attrs) { - /* setting attributes for the first time, allocate now */ - sd_attrs = sysfs_init_inode_attrs(sd); - if (!sd_attrs) - return -ENOMEM; - sd->s_iattr = sd_attrs; - } - /* attributes were changed at least once in past */ - iattrs = &sd_attrs->ia_iattr; - - if (ia_valid & ATTR_UID) - iattrs->ia_uid = iattr->ia_uid; - if (ia_valid & ATTR_GID) - iattrs->ia_gid = iattr->ia_gid; - if (ia_valid & ATTR_ATIME) - iattrs->ia_atime = iattr->ia_atime; - if (ia_valid & ATTR_MTIME) - iattrs->ia_mtime = iattr->ia_mtime; - if (ia_valid & ATTR_CTIME) - iattrs->ia_ctime = iattr->ia_ctime; - if (ia_valid & ATTR_MODE) { - umode_t mode = iattr->ia_mode; - iattrs->ia_mode = sd->s_mode = mode; - } - return 0; -} - -/** - * kernfs_setattr - set iattr on a node - * @sd: target node - * @iattr: iattr to set - * - * Returns 0 on success, -errno on failure. - */ -int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) -{ - int ret; - - mutex_lock(&sysfs_mutex); - ret = __kernfs_setattr(sd, iattr); - mutex_unlock(&sysfs_mutex); - return ret; -} - -int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) -{ - struct inode *inode = dentry->d_inode; - struct sysfs_dirent *sd = dentry->d_fsdata; - int error; - - if (!sd) - return -EINVAL; - - mutex_lock(&sysfs_mutex); - error = inode_change_ok(inode, iattr); - if (error) - goto out; - - error = __kernfs_setattr(sd, iattr); - if (error) - goto out; - - /* this ignores size changes */ - setattr_copy(inode, iattr); - -out: - mutex_unlock(&sysfs_mutex); - return error; -} - -static int sysfs_sd_setsecdata(struct sysfs_dirent *sd, void **secdata, - u32 *secdata_len) -{ - struct sysfs_inode_attrs *iattrs; - void *old_secdata; - size_t old_secdata_len; - - if (!sd->s_iattr) { - sd->s_iattr = sysfs_init_inode_attrs(sd); - if (!sd->s_iattr) - return -ENOMEM; - } - - iattrs = sd->s_iattr; - old_secdata = iattrs->ia_secdata; - old_secdata_len = iattrs->ia_secdata_len; - - iattrs->ia_secdata = *secdata; - iattrs->ia_secdata_len = *secdata_len; - - *secdata = old_secdata; - *secdata_len = old_secdata_len; - return 0; -} - -int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, - size_t size, int flags) -{ - struct sysfs_dirent *sd = dentry->d_fsdata; - void *secdata; - int error; - u32 secdata_len = 0; - - if (!sd) - return -EINVAL; - - if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) { - const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; - error = security_inode_setsecurity(dentry->d_inode, suffix, - value, size, flags); - if (error) - goto out; - error = security_inode_getsecctx(dentry->d_inode, - &secdata, &secdata_len); - if (error) - goto out; - - mutex_lock(&sysfs_mutex); - error = sysfs_sd_setsecdata(sd, &secdata, &secdata_len); - mutex_unlock(&sysfs_mutex); - - if (secdata) - security_release_secctx(secdata, secdata_len); - } else - return -EINVAL; -out: - return error; -} - -static inline void set_default_inode_attr(struct inode *inode, umode_t mode) -{ - inode->i_mode = mode; - inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; -} - -static inline void set_inode_attr(struct inode *inode, struct iattr *iattr) -{ - inode->i_uid = iattr->ia_uid; - inode->i_gid = iattr->ia_gid; - inode->i_atime = iattr->ia_atime; - inode->i_mtime = iattr->ia_mtime; - inode->i_ctime = iattr->ia_ctime; -} - -static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode) -{ - struct sysfs_inode_attrs *iattrs = sd->s_iattr; - - inode->i_mode = sd->s_mode; - if (iattrs) { - /* sysfs_dirent has non-default attributes - * get them from persistent copy in sysfs_dirent - */ - set_inode_attr(inode, &iattrs->ia_iattr); - security_inode_notifysecctx(inode, - iattrs->ia_secdata, - iattrs->ia_secdata_len); - } - - if (sysfs_type(sd) == SYSFS_DIR) - set_nlink(inode, sd->s_dir.subdirs + 2); -} - -int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) -{ - struct sysfs_dirent *sd = dentry->d_fsdata; - struct inode *inode = dentry->d_inode; - - mutex_lock(&sysfs_mutex); - sysfs_refresh_inode(sd, inode); - mutex_unlock(&sysfs_mutex); - - generic_fillattr(inode, stat); - return 0; -} - -static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) -{ - kernfs_get(sd); - inode->i_private = sd; - inode->i_mapping->a_ops = &sysfs_aops; - inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; - inode->i_op = &sysfs_inode_operations; - - set_default_inode_attr(inode, sd->s_mode); - sysfs_refresh_inode(sd, inode); - - /* 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; - break; - case SYSFS_KOBJ_ATTR: - inode->i_size = sd->s_attr.size; - inode->i_fop = &kernfs_file_operations; - break; - case SYSFS_KOBJ_LINK: - inode->i_op = &sysfs_symlink_inode_operations; - break; - default: - BUG(); - } - - unlock_new_inode(inode); -} - -/** - * sysfs_get_inode - get inode for sysfs_dirent - * @sb: super block - * @sd: sysfs_dirent to allocate inode for - * - * 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). - * - * RETURNS: - * Pointer to allocated inode on success, NULL on failure. - */ -struct inode *sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd) -{ - struct inode *inode; - - inode = iget_locked(sb, sd->s_ino); - if (inode && (inode->i_state & I_NEW)) - sysfs_init_inode(sd, inode); - - return inode; -} - -/* - * The sysfs_dirent serves as both an inode and a directory entry for sysfs. - * To prevent the sysfs inode numbers from being freed prematurely we take a - * reference to sysfs_dirent from the sysfs inode. A - * super_operations.evict_inode() implementation is needed to drop that - * reference upon inode destruction. - */ -void sysfs_evict_inode(struct inode *inode) -{ - struct sysfs_dirent *sd = inode->i_private; - - truncate_inode_pages(&inode->i_data, 0); - clear_inode(inode); - kernfs_put(sd); -} - -int sysfs_permission(struct inode *inode, int mask) -{ - struct sysfs_dirent *sd; - - if (mask & MAY_NOT_BLOCK) - return -ECHILD; - - sd = inode->i_private; - - mutex_lock(&sysfs_mutex); - sysfs_refresh_inode(sd, inode); - mutex_unlock(&sysfs_mutex); - - return generic_permission(inode, mask); -} diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index f8c936f..93c1910 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -52,19 +52,6 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type); /* - * inode.c - */ -struct inode *sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd); -void sysfs_evict_inode(struct inode *inode); -int sysfs_permission(struct inode *inode, int mask); -int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); -int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat); -int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, - size_t size, int flags); -int sysfs_inode_init(void); - -/* * file.c */ extern const struct file_operations kernfs_file_operations; -- cgit v1.1 From fd7b9f7b9776b11df629e9dd3865320bf57ce588 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:33 -0500 Subject: sysfs, kernfs: move dir core code to fs/kernfs/dir.c Move core dir code to fs/kernfs/dir.c. fs/sysfs/dir.c now only contains sysfs_warn_dup() and sysfs wrappers around kernfs interfaces. The respective declarations in fs/sysfs/sysfs.h are moved to fs/kernfs/kernfs-internal.h. This is pure relocation. v2: sysfs_symlink_target_lock was mistakenly relocated to kernfs. It should remain with sysfs. Fixed. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 988 ++++++++++++++++++++++++++++++++++++++++++++ fs/kernfs/kernfs-internal.h | 17 + fs/sysfs/dir.c | 986 ------------------------------------------- fs/sysfs/sysfs.h | 13 - 4 files changed, 1005 insertions(+), 999 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 1061602..a4ca4de 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -7,3 +7,991 @@ * * This file is released under the GPLv2. */ + +#include +#include +#include +#include +#include +#include + +#include "kernfs-internal.h" + +DEFINE_MUTEX(sysfs_mutex); + +#define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb) + +static DEFINE_SPINLOCK(sysfs_ino_lock); +static DEFINE_IDA(sysfs_ino_ida); + +/** + * sysfs_name_hash + * @name: Null terminated string to hash + * @ns: Namespace tag to hash + * + * Returns 31 bit hash of ns + name (so it fits in an off_t ) + */ +static unsigned int sysfs_name_hash(const char *name, const void *ns) +{ + unsigned long hash = init_name_hash(); + unsigned int len = strlen(name); + while (len--) + hash = partial_name_hash(*name++, hash); + hash = (end_name_hash(hash) ^ hash_ptr((void *)ns, 31)); + hash &= 0x7fffffffU; + /* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */ + if (hash < 1) + hash += 2; + if (hash >= INT_MAX) + hash = INT_MAX - 1; + return hash; +} + +static int sysfs_name_compare(unsigned int hash, const char *name, + const void *ns, const struct sysfs_dirent *sd) +{ + if (hash != sd->s_hash) + return hash - sd->s_hash; + if (ns != sd->s_ns) + return ns - sd->s_ns; + return strcmp(name, sd->s_name); +} + +static int sysfs_sd_compare(const struct sysfs_dirent *left, + const struct sysfs_dirent *right) +{ + return sysfs_name_compare(left->s_hash, left->s_name, left->s_ns, + right); +} + +/** + * sysfs_link_sibling - link sysfs_dirent into sibling rbtree + * @sd: sysfs_dirent of interest + * + * Link @sd into its sibling rbtree which starts from + * sd->s_parent->s_dir.children. + * + * Locking: + * mutex_lock(sysfs_mutex) + * + * RETURNS: + * 0 on susccess -EEXIST on failure. + */ +static int sysfs_link_sibling(struct sysfs_dirent *sd) +{ + struct rb_node **node = &sd->s_parent->s_dir.children.rb_node; + struct rb_node *parent = NULL; + + if (sysfs_type(sd) == SYSFS_DIR) + sd->s_parent->s_dir.subdirs++; + + while (*node) { + struct sysfs_dirent *pos; + int result; + + pos = to_sysfs_dirent(*node); + parent = *node; + result = sysfs_sd_compare(sd, pos); + if (result < 0) + node = &pos->s_rb.rb_left; + else if (result > 0) + node = &pos->s_rb.rb_right; + else + return -EEXIST; + } + /* add new node and rebalance the tree */ + rb_link_node(&sd->s_rb, parent, node); + rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children); + return 0; +} + +/** + * sysfs_unlink_sibling - unlink sysfs_dirent from sibling rbtree + * @sd: sysfs_dirent of interest + * + * Unlink @sd from its sibling rbtree which starts from + * sd->s_parent->s_dir.children. + * + * Locking: + * mutex_lock(sysfs_mutex) + */ +static void sysfs_unlink_sibling(struct sysfs_dirent *sd) +{ + if (sysfs_type(sd) == SYSFS_DIR) + sd->s_parent->s_dir.subdirs--; + + rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children); +} + +/** + * 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 (unlikely(!sd)) + return NULL; + + if (!atomic_inc_unless_negative(&sd->s_active)) + return NULL; + + if (sd->s_flags & SYSFS_FLAG_LOCKDEP) + rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_); + 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) +{ + int v; + + if (unlikely(!sd)) + return; + + if (sd->s_flags & SYSFS_FLAG_LOCKDEP) + rwsem_release(&sd->dep_map, 1, _RET_IP_); + 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->u.completion. + */ + complete(sd->u.completion); +} + +/** + * sysfs_deactivate - deactivate sysfs_dirent + * @sd: sysfs_dirent to deactivate + * + * Deny new active references and drain existing ones. + */ +static void sysfs_deactivate(struct sysfs_dirent *sd) +{ + DECLARE_COMPLETION_ONSTACK(wait); + int v; + + BUG_ON(!(sd->s_flags & SYSFS_FLAG_REMOVED)); + + if (!(sysfs_type(sd) & SYSFS_ACTIVE_REF)) + return; + + sd->u.completion = (void *)&wait; + + rwsem_acquire(&sd->dep_map, 0, 0, _RET_IP_); + /* atomic_add_return() is a mb(), put_active() will always see + * the updated sd->u.completion. + */ + v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active); + + if (v != SD_DEACTIVATED_BIAS) { + lock_contended(&sd->dep_map, _RET_IP_); + wait_for_completion(&wait); + } + + lock_acquired(&sd->dep_map, _RET_IP_); + rwsem_release(&sd->dep_map, 1, _RET_IP_); +} + +static int sysfs_alloc_ino(unsigned int *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(unsigned int ino) +{ + spin_lock(&sysfs_ino_lock); + ida_remove(&sysfs_ino_ida, ino); + spin_unlock(&sysfs_ino_lock); +} + +/** + * kernfs_get - get a reference count on a sysfs_dirent + * @sd: the target sysfs_dirent + */ +void kernfs_get(struct sysfs_dirent *sd) +{ + if (sd) { + WARN_ON(!atomic_read(&sd->s_count)); + atomic_inc(&sd->s_count); + } +} +EXPORT_SYMBOL_GPL(kernfs_get); + +/** + * kernfs_put - put a reference count on a sysfs_dirent + * @sd: the target sysfs_dirent + * + * Put a reference count of @sd and destroy it if it reached zero. + */ +void kernfs_put(struct sysfs_dirent *sd) +{ + struct sysfs_dirent *parent_sd; + + if (!sd || !atomic_dec_and_test(&sd->s_count)) + return; + repeat: + /* Moving/renaming is always done while holding reference. + * sd->s_parent won't change beneath us. + */ + parent_sd = sd->s_parent; + + WARN(!(sd->s_flags & SYSFS_FLAG_REMOVED), + "sysfs: free using entry: %s/%s\n", + parent_sd ? parent_sd->s_name : "", sd->s_name); + + if (sysfs_type(sd) == SYSFS_KOBJ_LINK) + kernfs_put(sd->s_symlink.target_sd); + if (sysfs_type(sd) & SYSFS_COPY_NAME) + kfree(sd->s_name); + if (sd->s_iattr && sd->s_iattr->ia_secdata) + security_release_secctx(sd->s_iattr->ia_secdata, + sd->s_iattr->ia_secdata_len); + 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; +} +EXPORT_SYMBOL_GPL(kernfs_put); + +static int sysfs_dentry_delete(const struct dentry *dentry) +{ + struct sysfs_dirent *sd = dentry->d_fsdata; + return !(sd && !(sd->s_flags & SYSFS_FLAG_REMOVED)); +} + +static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) +{ + struct sysfs_dirent *sd; + + if (flags & LOOKUP_RCU) + return -ECHILD; + + sd = dentry->d_fsdata; + mutex_lock(&sysfs_mutex); + + /* The sysfs dirent has been deleted */ + if (sd->s_flags & SYSFS_FLAG_REMOVED) + goto out_bad; + + /* The sysfs dirent has been moved? */ + if (dentry->d_parent->d_fsdata != sd->s_parent) + goto out_bad; + + /* The sysfs dirent has been renamed */ + if (strcmp(dentry->d_name.name, sd->s_name) != 0) + goto out_bad; + + /* The sysfs dirent has been moved to a different namespace */ + if (sd->s_parent && (sd->s_parent->s_flags & SYSFS_FLAG_NS) && + sysfs_info(dentry->d_sb)->ns != sd->s_ns) + goto out_bad; + + mutex_unlock(&sysfs_mutex); +out_valid: + return 1; +out_bad: + /* Remove the dentry from the dcache hashes. + * If this is a deleted dentry we use d_drop instead of d_delete + * so sysfs doesn't need to cope with negative dentries. + * + * If this is a dentry that has simply been renamed we + * use d_drop to remove it from the dcache lookup on its + * old parent. If this dentry persists later when a lookup + * is performed at its new name the dentry will be readded + * to the dcache hashes. + */ + mutex_unlock(&sysfs_mutex); + + /* If we have submounts we must allow the vfs caches + * to lie about the state of the filesystem to prevent + * leaks and other nasty things. + */ + if (check_submounts_and_drop(dentry) != 0) + goto out_valid; + + return 0; +} + +static void sysfs_dentry_release(struct dentry *dentry) +{ + kernfs_put(dentry->d_fsdata); +} + +const struct dentry_operations sysfs_dentry_ops = { + .d_revalidate = sysfs_dentry_revalidate, + .d_delete = sysfs_dentry_delete, + .d_release = sysfs_dentry_release, +}; + +struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) +{ + char *dup_name = NULL; + struct sysfs_dirent *sd; + + if (type & SYSFS_COPY_NAME) { + name = dup_name = kstrdup(name, GFP_KERNEL); + if (!name) + return NULL; + } + + sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); + if (!sd) + goto err_out1; + + if (sysfs_alloc_ino(&sd->s_ino)) + goto err_out2; + + atomic_set(&sd->s_count, 1); + atomic_set(&sd->s_active, 0); + + sd->s_name = name; + sd->s_mode = mode; + sd->s_flags = type | SYSFS_FLAG_REMOVED; + + return sd; + + err_out2: + kmem_cache_free(sysfs_dir_cachep, sd); + err_out1: + kfree(dup_name); + return NULL; +} + +/** + * sysfs_addrm_start - prepare for sysfs_dirent add/remove + * @acxt: pointer to sysfs_addrm_cxt to be used + * + * This function is called when the caller is about to add or remove + * sysfs_dirent. This function acquires sysfs_mutex. @acxt is used + * to keep and pass context to other addrm functions. + * + * LOCKING: + * Kernel thread context (may sleep). sysfs_mutex is locked on + * return. + */ +void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) + __acquires(sysfs_mutex) +{ + memset(acxt, 0, sizeof(*acxt)); + + mutex_lock(&sysfs_mutex); +} + +/** + * sysfs_add_one - add sysfs_dirent to parent without warning + * @acxt: addrm context to use + * @sd: sysfs_dirent to be added + * @parent_sd: the parent sysfs_dirent to add @sd to + * + * Get @parent_sd and set @sd->s_parent to it and increment nlink of + * the parent inode if @sd is a directory and link into the children + * list of the parent. + * + * 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(). + * + * RETURNS: + * 0 on success, -EEXIST if entry with the given name already + * exists. + */ +int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, + struct sysfs_dirent *parent_sd) +{ + bool has_ns = parent_sd->s_flags & SYSFS_FLAG_NS; + struct sysfs_inode_attrs *ps_iattr; + int ret; + + if (has_ns != (bool)sd->s_ns) { + WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", + has_ns ? "required" : "invalid", + parent_sd->s_name, sd->s_name); + return -EINVAL; + } + + if (sysfs_type(parent_sd) != SYSFS_DIR) + return -EINVAL; + + sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); + sd->s_parent = parent_sd; + kernfs_get(parent_sd); + + ret = sysfs_link_sibling(sd); + if (ret) + return ret; + + /* Update timestamps on the parent */ + ps_iattr = parent_sd->s_iattr; + if (ps_iattr) { + struct iattr *ps_iattrs = &ps_iattr->ia_iattr; + ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; + } + + /* Mark the entry added into directory tree */ + sd->s_flags &= ~SYSFS_FLAG_REMOVED; + + return 0; +} + +/** + * sysfs_remove_one - remove sysfs_dirent from parent + * @acxt: addrm context to use + * @sd: sysfs_dirent to be removed + * + * Mark @sd removed and drop nlink of parent inode if @sd is a + * directory. @sd is unlinked from 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(). + */ +static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, + struct sysfs_dirent *sd) +{ + struct sysfs_inode_attrs *ps_iattr; + + /* + * Removal can be called multiple times on the same node. Only the + * first invocation is effective and puts the base ref. + */ + if (sd->s_flags & SYSFS_FLAG_REMOVED) + return; + + sysfs_unlink_sibling(sd); + + /* Update timestamps on the parent */ + ps_iattr = sd->s_parent->s_iattr; + if (ps_iattr) { + struct iattr *ps_iattrs = &ps_iattr->ia_iattr; + ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; + } + + sd->s_flags |= SYSFS_FLAG_REMOVED; + sd->u.removed_list = acxt->removed; + acxt->removed = sd; +} + +/** + * 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. + * + * LOCKING: + * sysfs_mutex is released. + */ +void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) + __releases(sysfs_mutex) +{ + /* release resources acquired by sysfs_addrm_start() */ + mutex_unlock(&sysfs_mutex); + + /* kill removed sysfs_dirents */ + while (acxt->removed) { + struct sysfs_dirent *sd = acxt->removed; + + acxt->removed = sd->u.removed_list; + + sysfs_deactivate(sd); + sysfs_unmap_bin_file(sd); + kernfs_put(sd); + } +} + +/** + * kernfs_find_ns - find sysfs_dirent with the given name + * @parent: sysfs_dirent to search under + * @name: name to look for + * @ns: the namespace tag to use + * + * Look for sysfs_dirent with name @name under @parent. Returns pointer to + * the found sysfs_dirent on success, %NULL on failure. + */ +static struct sysfs_dirent *kernfs_find_ns(struct sysfs_dirent *parent, + const unsigned char *name, + const void *ns) +{ + struct rb_node *node = parent->s_dir.children.rb_node; + bool has_ns = parent->s_flags & SYSFS_FLAG_NS; + unsigned int hash; + + lockdep_assert_held(&sysfs_mutex); + + if (has_ns != (bool)ns) { + WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", + has_ns ? "required" : "invalid", + parent->s_name, name); + return NULL; + } + + hash = sysfs_name_hash(name, ns); + while (node) { + struct sysfs_dirent *sd; + int result; + + sd = to_sysfs_dirent(node); + result = sysfs_name_compare(hash, name, ns, sd); + if (result < 0) + node = node->rb_left; + else if (result > 0) + node = node->rb_right; + else + return sd; + } + return NULL; +} + +/** + * kernfs_find_and_get_ns - find and get sysfs_dirent with the given name + * @parent: sysfs_dirent to search under + * @name: name to look for + * @ns: the namespace tag to use + * + * Look for sysfs_dirent with name @name under @parent and get a reference + * if found. This function may sleep and returns pointer to the found + * sysfs_dirent on success, %NULL on failure. + */ +struct sysfs_dirent *kernfs_find_and_get_ns(struct sysfs_dirent *parent, + const char *name, const void *ns) +{ + struct sysfs_dirent *sd; + + mutex_lock(&sysfs_mutex); + sd = kernfs_find_ns(parent, name, ns); + kernfs_get(sd); + mutex_unlock(&sysfs_mutex); + + return sd; +} +EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); + +/** + * kernfs_create_dir_ns - create a directory + * @parent: parent in which to create a new directory + * @name: name of the new directory + * @priv: opaque data associated with the new directory + * @ns: optional namespace tag of the directory + * + * Returns the created node on success, ERR_PTR() value on failure. + */ +struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, + const char *name, void *priv, + const void *ns) +{ + umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; + struct sysfs_addrm_cxt acxt; + struct sysfs_dirent *sd; + int rc; + + /* allocate */ + sd = sysfs_new_dirent(name, mode, SYSFS_DIR); + if (!sd) + return ERR_PTR(-ENOMEM); + + sd->s_ns = ns; + sd->priv = priv; + + /* link in */ + sysfs_addrm_start(&acxt); + rc = sysfs_add_one(&acxt, sd, parent); + sysfs_addrm_finish(&acxt); + + if (!rc) + return sd; + + kernfs_put(sd); + return ERR_PTR(rc); +} + +static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, + unsigned int flags) +{ + struct dentry *ret = NULL; + struct dentry *parent = dentry->d_parent; + struct sysfs_dirent *parent_sd = parent->d_fsdata; + struct sysfs_dirent *sd; + struct inode *inode; + const void *ns = NULL; + + mutex_lock(&sysfs_mutex); + + if (parent_sd->s_flags & SYSFS_FLAG_NS) + ns = sysfs_info(dir->i_sb)->ns; + + sd = kernfs_find_ns(parent_sd, dentry->d_name.name, ns); + + /* no such entry */ + if (!sd) { + ret = ERR_PTR(-ENOENT); + goto out_unlock; + } + kernfs_get(sd); + dentry->d_fsdata = sd; + + /* attach dentry and inode */ + inode = sysfs_get_inode(dir->i_sb, sd); + if (!inode) { + ret = ERR_PTR(-ENOMEM); + goto out_unlock; + } + + /* instantiate and hash dentry */ + ret = d_materialise_unique(dentry, inode); + out_unlock: + mutex_unlock(&sysfs_mutex); + return ret; +} + +const struct inode_operations sysfs_dir_inode_operations = { + .lookup = sysfs_lookup, + .permission = sysfs_permission, + .setattr = sysfs_setattr, + .getattr = sysfs_getattr, + .setxattr = sysfs_setxattr, +}; + +static struct sysfs_dirent *sysfs_leftmost_descendant(struct sysfs_dirent *pos) +{ + struct sysfs_dirent *last; + + while (true) { + struct rb_node *rbn; + + last = pos; + + if (sysfs_type(pos) != SYSFS_DIR) + break; + + rbn = rb_first(&pos->s_dir.children); + if (!rbn) + break; + + pos = to_sysfs_dirent(rbn); + } + + return last; +} + +/** + * sysfs_next_descendant_post - find the next descendant for post-order walk + * @pos: the current position (%NULL to initiate traversal) + * @root: sysfs_dirent whose descendants to walk + * + * Find the next descendant to visit for post-order traversal of @root's + * descendants. @root is included in the iteration and the last node to be + * visited. + */ +static struct sysfs_dirent *sysfs_next_descendant_post(struct sysfs_dirent *pos, + struct sysfs_dirent *root) +{ + struct rb_node *rbn; + + lockdep_assert_held(&sysfs_mutex); + + /* if first iteration, visit leftmost descendant which may be root */ + if (!pos) + return sysfs_leftmost_descendant(root); + + /* if we visited @root, we're done */ + if (pos == root) + return NULL; + + /* if there's an unvisited sibling, visit its leftmost descendant */ + rbn = rb_next(&pos->s_rb); + if (rbn) + return sysfs_leftmost_descendant(to_sysfs_dirent(rbn)); + + /* no sibling left, visit parent */ + return pos->s_parent; +} + +static void __kernfs_remove(struct sysfs_addrm_cxt *acxt, + struct sysfs_dirent *sd) +{ + struct sysfs_dirent *pos, *next; + + if (!sd) + return; + + pr_debug("sysfs %s: removing\n", sd->s_name); + + next = NULL; + do { + pos = next; + next = sysfs_next_descendant_post(pos, sd); + if (pos) + sysfs_remove_one(acxt, pos); + } while (next); +} + +/** + * kernfs_remove - remove a sysfs_dirent recursively + * @sd: the sysfs_dirent to remove + * + * Remove @sd along with all its subdirectories and files. + */ +void kernfs_remove(struct sysfs_dirent *sd) +{ + struct sysfs_addrm_cxt acxt; + + sysfs_addrm_start(&acxt); + __kernfs_remove(&acxt, sd); + sysfs_addrm_finish(&acxt); +} + +/** + * kernfs_remove_by_name_ns - find a sysfs_dirent by name and remove it + * @dir_sd: parent of the target + * @name: name of the sysfs_dirent to remove + * @ns: namespace tag of the sysfs_dirent to remove + * + * Look for the sysfs_dirent with @name and @ns under @dir_sd and remove + * it. Returns 0 on success, -ENOENT if such entry doesn't exist. + */ +int kernfs_remove_by_name_ns(struct sysfs_dirent *dir_sd, const char *name, + const void *ns) +{ + struct sysfs_addrm_cxt acxt; + struct sysfs_dirent *sd; + + if (!dir_sd) { + WARN(1, KERN_WARNING "sysfs: can not remove '%s', no directory\n", + name); + return -ENOENT; + } + + sysfs_addrm_start(&acxt); + + sd = kernfs_find_ns(dir_sd, name, ns); + if (sd) + __kernfs_remove(&acxt, sd); + + sysfs_addrm_finish(&acxt); + + if (sd) + return 0; + else + return -ENOENT; +} + +/** + * kernfs_rename_ns - move and rename a kernfs_node + * @sd: target node + * @new_parent: new parent to put @sd under + * @new_name: new name + * @new_ns: new namespace tag + */ +int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, + const char *new_name, const void *new_ns) +{ + int error; + + mutex_lock(&sysfs_mutex); + + error = 0; + if ((sd->s_parent == new_parent) && (sd->s_ns == new_ns) && + (strcmp(sd->s_name, new_name) == 0)) + goto out; /* nothing to rename */ + + error = -EEXIST; + if (kernfs_find_ns(new_parent, new_name, new_ns)) + goto out; + + /* rename sysfs_dirent */ + if (strcmp(sd->s_name, new_name) != 0) { + error = -ENOMEM; + new_name = kstrdup(new_name, GFP_KERNEL); + if (!new_name) + goto out; + + kfree(sd->s_name); + sd->s_name = new_name; + } + + /* + * Move to the appropriate place in the appropriate directories rbtree. + */ + sysfs_unlink_sibling(sd); + kernfs_get(new_parent); + kernfs_put(sd->s_parent); + sd->s_ns = new_ns; + sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); + sd->s_parent = new_parent; + sysfs_link_sibling(sd); + + error = 0; + out: + mutex_unlock(&sysfs_mutex); + return error; +} + +/** + * kernfs_enable_ns - enable namespace under a directory + * @sd: directory of interest, should be empty + * + * This is to be called right after @sd is created to enable namespace + * under it. All children of @sd must have non-NULL namespace tags and + * only the ones which match the super_block's tag will be visible. + */ +void kernfs_enable_ns(struct sysfs_dirent *sd) +{ + WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); + WARN_ON_ONCE(!RB_EMPTY_ROOT(&sd->s_dir.children)); + sd->s_flags |= SYSFS_FLAG_NS; +} + +/* Relationship between s_mode and the DT_xxx types */ +static inline unsigned char dt_type(struct sysfs_dirent *sd) +{ + return (sd->s_mode >> 12) & 15; +} + +static int sysfs_dir_release(struct inode *inode, struct file *filp) +{ + kernfs_put(filp->private_data); + return 0; +} + +static struct sysfs_dirent *sysfs_dir_pos(const void *ns, + struct sysfs_dirent *parent_sd, loff_t hash, struct sysfs_dirent *pos) +{ + if (pos) { + int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && + pos->s_parent == parent_sd && + hash == pos->s_hash; + kernfs_put(pos); + if (!valid) + pos = NULL; + } + if (!pos && (hash > 1) && (hash < INT_MAX)) { + struct rb_node *node = parent_sd->s_dir.children.rb_node; + while (node) { + pos = to_sysfs_dirent(node); + + if (hash < pos->s_hash) + node = node->rb_left; + else if (hash > pos->s_hash) + node = node->rb_right; + else + break; + } + } + /* Skip over entries in the wrong namespace */ + while (pos && pos->s_ns != ns) { + struct rb_node *node = rb_next(&pos->s_rb); + if (!node) + pos = NULL; + else + pos = to_sysfs_dirent(node); + } + return pos; +} + +static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns, + struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos) +{ + pos = sysfs_dir_pos(ns, parent_sd, ino, pos); + if (pos) + do { + struct rb_node *node = rb_next(&pos->s_rb); + if (!node) + pos = NULL; + else + pos = to_sysfs_dirent(node); + } while (pos && pos->s_ns != ns); + return pos; +} + +static int sysfs_readdir(struct file *file, struct dir_context *ctx) +{ + struct dentry *dentry = file->f_path.dentry; + struct sysfs_dirent *parent_sd = dentry->d_fsdata; + struct sysfs_dirent *pos = file->private_data; + const void *ns = NULL; + + if (!dir_emit_dots(file, ctx)) + return 0; + mutex_lock(&sysfs_mutex); + + if (parent_sd->s_flags & SYSFS_FLAG_NS) + ns = sysfs_info(dentry->d_sb)->ns; + + for (pos = sysfs_dir_pos(ns, parent_sd, ctx->pos, pos); + pos; + pos = sysfs_dir_next_pos(ns, parent_sd, ctx->pos, pos)) { + const char *name = pos->s_name; + unsigned int type = dt_type(pos); + int len = strlen(name); + ino_t ino = pos->s_ino; + + ctx->pos = pos->s_hash; + file->private_data = pos; + kernfs_get(pos); + + mutex_unlock(&sysfs_mutex); + if (!dir_emit(ctx, name, len, ino, type)) + return 0; + mutex_lock(&sysfs_mutex); + } + mutex_unlock(&sysfs_mutex); + file->private_data = NULL; + ctx->pos = INT_MAX; + return 0; +} + +static loff_t sysfs_dir_llseek(struct file *file, loff_t offset, int whence) +{ + struct inode *inode = file_inode(file); + loff_t ret; + + mutex_lock(&inode->i_mutex); + ret = generic_file_llseek(file, offset, whence); + mutex_unlock(&inode->i_mutex); + + return ret; +} + +const struct file_operations sysfs_dir_operations = { + .read = generic_read_dir, + .iterate = sysfs_readdir, + .release = sysfs_dir_release, + .llseek = sysfs_dir_llseek, +}; diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 933ac8d..31f0dbe 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -125,4 +126,20 @@ int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); int sysfs_inode_init(void); +/* + * dir.c + */ +extern struct mutex sysfs_mutex; +extern const struct dentry_operations sysfs_dentry_ops; +extern const struct file_operations sysfs_dir_operations; +extern const struct inode_operations sysfs_dir_inode_operations; + +struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd); +void sysfs_put_active(struct sysfs_dirent *sd); +void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt); +int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, + struct sysfs_dirent *parent_sd); +void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); +struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type); + #endif /* __KERNFS_INTERNAL_H */ diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 0d806ef..e5c4e71 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -13,468 +13,12 @@ #undef DEBUG #include -#include -#include #include -#include -#include -#include -#include #include -#include -#include #include "sysfs.h" -DEFINE_MUTEX(sysfs_mutex); DEFINE_SPINLOCK(sysfs_symlink_target_lock); -#define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb) - -static DEFINE_SPINLOCK(sysfs_ino_lock); -static DEFINE_IDA(sysfs_ino_ida); - -/** - * sysfs_name_hash - * @name: Null terminated string to hash - * @ns: Namespace tag to hash - * - * Returns 31 bit hash of ns + name (so it fits in an off_t ) - */ -static unsigned int sysfs_name_hash(const char *name, const void *ns) -{ - unsigned long hash = init_name_hash(); - unsigned int len = strlen(name); - while (len--) - hash = partial_name_hash(*name++, hash); - hash = (end_name_hash(hash) ^ hash_ptr((void *)ns, 31)); - hash &= 0x7fffffffU; - /* Reserve hash numbers 0, 1 and INT_MAX for magic directory entries */ - if (hash < 1) - hash += 2; - if (hash >= INT_MAX) - hash = INT_MAX - 1; - return hash; -} - -static int sysfs_name_compare(unsigned int hash, const char *name, - const void *ns, const struct sysfs_dirent *sd) -{ - if (hash != sd->s_hash) - return hash - sd->s_hash; - if (ns != sd->s_ns) - return ns - sd->s_ns; - return strcmp(name, sd->s_name); -} - -static int sysfs_sd_compare(const struct sysfs_dirent *left, - const struct sysfs_dirent *right) -{ - return sysfs_name_compare(left->s_hash, left->s_name, left->s_ns, - right); -} - -/** - * sysfs_link_sibling - link sysfs_dirent into sibling rbtree - * @sd: sysfs_dirent of interest - * - * Link @sd into its sibling rbtree which starts from - * sd->s_parent->s_dir.children. - * - * Locking: - * mutex_lock(sysfs_mutex) - * - * RETURNS: - * 0 on susccess -EEXIST on failure. - */ -static int sysfs_link_sibling(struct sysfs_dirent *sd) -{ - struct rb_node **node = &sd->s_parent->s_dir.children.rb_node; - struct rb_node *parent = NULL; - - if (sysfs_type(sd) == SYSFS_DIR) - sd->s_parent->s_dir.subdirs++; - - while (*node) { - struct sysfs_dirent *pos; - int result; - - pos = to_sysfs_dirent(*node); - parent = *node; - result = sysfs_sd_compare(sd, pos); - if (result < 0) - node = &pos->s_rb.rb_left; - else if (result > 0) - node = &pos->s_rb.rb_right; - else - return -EEXIST; - } - /* add new node and rebalance the tree */ - rb_link_node(&sd->s_rb, parent, node); - rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children); - return 0; -} - -/** - * sysfs_unlink_sibling - unlink sysfs_dirent from sibling rbtree - * @sd: sysfs_dirent of interest - * - * Unlink @sd from its sibling rbtree which starts from - * sd->s_parent->s_dir.children. - * - * Locking: - * mutex_lock(sysfs_mutex) - */ -static void sysfs_unlink_sibling(struct sysfs_dirent *sd) -{ - if (sysfs_type(sd) == SYSFS_DIR) - sd->s_parent->s_dir.subdirs--; - - rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children); -} - -/** - * 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 (unlikely(!sd)) - return NULL; - - if (!atomic_inc_unless_negative(&sd->s_active)) - return NULL; - - if (sd->s_flags & SYSFS_FLAG_LOCKDEP) - rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_); - 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) -{ - int v; - - if (unlikely(!sd)) - return; - - if (sd->s_flags & SYSFS_FLAG_LOCKDEP) - rwsem_release(&sd->dep_map, 1, _RET_IP_); - 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->u.completion. - */ - complete(sd->u.completion); -} - -/** - * sysfs_deactivate - deactivate sysfs_dirent - * @sd: sysfs_dirent to deactivate - * - * Deny new active references and drain existing ones. - */ -static void sysfs_deactivate(struct sysfs_dirent *sd) -{ - DECLARE_COMPLETION_ONSTACK(wait); - int v; - - BUG_ON(!(sd->s_flags & SYSFS_FLAG_REMOVED)); - - if (!(sysfs_type(sd) & SYSFS_ACTIVE_REF)) - return; - - sd->u.completion = (void *)&wait; - - rwsem_acquire(&sd->dep_map, 0, 0, _RET_IP_); - /* atomic_add_return() is a mb(), put_active() will always see - * the updated sd->u.completion. - */ - v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active); - - if (v != SD_DEACTIVATED_BIAS) { - lock_contended(&sd->dep_map, _RET_IP_); - wait_for_completion(&wait); - } - - lock_acquired(&sd->dep_map, _RET_IP_); - rwsem_release(&sd->dep_map, 1, _RET_IP_); -} - -static int sysfs_alloc_ino(unsigned int *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(unsigned int ino) -{ - spin_lock(&sysfs_ino_lock); - ida_remove(&sysfs_ino_ida, ino); - spin_unlock(&sysfs_ino_lock); -} - -/** - * kernfs_get - get a reference count on a sysfs_dirent - * @sd: the target sysfs_dirent - */ -void kernfs_get(struct sysfs_dirent *sd) -{ - if (sd) { - WARN_ON(!atomic_read(&sd->s_count)); - atomic_inc(&sd->s_count); - } -} -EXPORT_SYMBOL_GPL(kernfs_get); - -/** - * kernfs_put - put a reference count on a sysfs_dirent - * @sd: the target sysfs_dirent - * - * Put a reference count of @sd and destroy it if it reached zero. - */ -void kernfs_put(struct sysfs_dirent *sd) -{ - struct sysfs_dirent *parent_sd; - - if (!sd || !atomic_dec_and_test(&sd->s_count)) - return; - repeat: - /* Moving/renaming is always done while holding reference. - * sd->s_parent won't change beneath us. - */ - parent_sd = sd->s_parent; - - WARN(!(sd->s_flags & SYSFS_FLAG_REMOVED), - "sysfs: free using entry: %s/%s\n", - parent_sd ? parent_sd->s_name : "", sd->s_name); - - if (sysfs_type(sd) == SYSFS_KOBJ_LINK) - kernfs_put(sd->s_symlink.target_sd); - if (sysfs_type(sd) & SYSFS_COPY_NAME) - kfree(sd->s_name); - if (sd->s_iattr && sd->s_iattr->ia_secdata) - security_release_secctx(sd->s_iattr->ia_secdata, - sd->s_iattr->ia_secdata_len); - 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; -} -EXPORT_SYMBOL_GPL(kernfs_put); - -static int sysfs_dentry_delete(const struct dentry *dentry) -{ - struct sysfs_dirent *sd = dentry->d_fsdata; - return !(sd && !(sd->s_flags & SYSFS_FLAG_REMOVED)); -} - -static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) -{ - struct sysfs_dirent *sd; - - if (flags & LOOKUP_RCU) - return -ECHILD; - - sd = dentry->d_fsdata; - mutex_lock(&sysfs_mutex); - - /* The sysfs dirent has been deleted */ - if (sd->s_flags & SYSFS_FLAG_REMOVED) - goto out_bad; - - /* The sysfs dirent has been moved? */ - if (dentry->d_parent->d_fsdata != sd->s_parent) - goto out_bad; - - /* The sysfs dirent has been renamed */ - if (strcmp(dentry->d_name.name, sd->s_name) != 0) - goto out_bad; - - /* The sysfs dirent has been moved to a different namespace */ - if (sd->s_parent && (sd->s_parent->s_flags & SYSFS_FLAG_NS) && - sysfs_info(dentry->d_sb)->ns != sd->s_ns) - goto out_bad; - - mutex_unlock(&sysfs_mutex); -out_valid: - return 1; -out_bad: - /* Remove the dentry from the dcache hashes. - * If this is a deleted dentry we use d_drop instead of d_delete - * so sysfs doesn't need to cope with negative dentries. - * - * If this is a dentry that has simply been renamed we - * use d_drop to remove it from the dcache lookup on its - * old parent. If this dentry persists later when a lookup - * is performed at its new name the dentry will be readded - * to the dcache hashes. - */ - mutex_unlock(&sysfs_mutex); - - /* If we have submounts we must allow the vfs caches - * to lie about the state of the filesystem to prevent - * leaks and other nasty things. - */ - if (check_submounts_and_drop(dentry) != 0) - goto out_valid; - - return 0; -} - -static void sysfs_dentry_release(struct dentry *dentry) -{ - kernfs_put(dentry->d_fsdata); -} - -const struct dentry_operations sysfs_dentry_ops = { - .d_revalidate = sysfs_dentry_revalidate, - .d_delete = sysfs_dentry_delete, - .d_release = sysfs_dentry_release, -}; - -struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) -{ - char *dup_name = NULL; - struct sysfs_dirent *sd; - - if (type & SYSFS_COPY_NAME) { - name = dup_name = kstrdup(name, GFP_KERNEL); - if (!name) - return NULL; - } - - sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); - if (!sd) - goto err_out1; - - if (sysfs_alloc_ino(&sd->s_ino)) - goto err_out2; - - atomic_set(&sd->s_count, 1); - atomic_set(&sd->s_active, 0); - - sd->s_name = name; - sd->s_mode = mode; - sd->s_flags = type | SYSFS_FLAG_REMOVED; - - return sd; - - err_out2: - kmem_cache_free(sysfs_dir_cachep, sd); - err_out1: - kfree(dup_name); - return NULL; -} - -/** - * sysfs_addrm_start - prepare for sysfs_dirent add/remove - * @acxt: pointer to sysfs_addrm_cxt to be used - * - * This function is called when the caller is about to add or remove - * sysfs_dirent. This function acquires sysfs_mutex. @acxt is used - * to keep and pass context to other addrm functions. - * - * LOCKING: - * Kernel thread context (may sleep). sysfs_mutex is locked on - * return. - */ -void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) - __acquires(sysfs_mutex) -{ - memset(acxt, 0, sizeof(*acxt)); - - mutex_lock(&sysfs_mutex); -} - -/** - * sysfs_add_one - add sysfs_dirent to parent without warning - * @acxt: addrm context to use - * @sd: sysfs_dirent to be added - * @parent_sd: the parent sysfs_dirent to add @sd to - * - * Get @parent_sd and set @sd->s_parent to it and increment nlink of - * the parent inode if @sd is a directory and link into the children - * list of the parent. - * - * 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(). - * - * RETURNS: - * 0 on success, -EEXIST if entry with the given name already - * exists. - */ -int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, - struct sysfs_dirent *parent_sd) -{ - bool has_ns = parent_sd->s_flags & SYSFS_FLAG_NS; - struct sysfs_inode_attrs *ps_iattr; - int ret; - - if (has_ns != (bool)sd->s_ns) { - WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", - has_ns ? "required" : "invalid", - parent_sd->s_name, sd->s_name); - return -EINVAL; - } - - if (sysfs_type(parent_sd) != SYSFS_DIR) - return -EINVAL; - - sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); - sd->s_parent = parent_sd; - kernfs_get(parent_sd); - - ret = sysfs_link_sibling(sd); - if (ret) - return ret; - - /* Update timestamps on the parent */ - ps_iattr = parent_sd->s_iattr; - if (ps_iattr) { - struct iattr *ps_iattrs = &ps_iattr->ia_iattr; - ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; - } - - /* Mark the entry added into directory tree */ - sd->s_flags &= ~SYSFS_FLAG_REMOVED; - - return 0; -} - /** * sysfs_pathname - return full path to sysfs dirent * @sd: sysfs_dirent whose path we want @@ -511,181 +55,6 @@ void sysfs_warn_dup(struct sysfs_dirent *parent, const char *name) } /** - * sysfs_remove_one - remove sysfs_dirent from parent - * @acxt: addrm context to use - * @sd: sysfs_dirent to be removed - * - * Mark @sd removed and drop nlink of parent inode if @sd is a - * directory. @sd is unlinked from 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(). - */ -static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, - struct sysfs_dirent *sd) -{ - struct sysfs_inode_attrs *ps_iattr; - - /* - * Removal can be called multiple times on the same node. Only the - * first invocation is effective and puts the base ref. - */ - if (sd->s_flags & SYSFS_FLAG_REMOVED) - return; - - sysfs_unlink_sibling(sd); - - /* Update timestamps on the parent */ - ps_iattr = sd->s_parent->s_iattr; - if (ps_iattr) { - struct iattr *ps_iattrs = &ps_iattr->ia_iattr; - ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; - } - - sd->s_flags |= SYSFS_FLAG_REMOVED; - sd->u.removed_list = acxt->removed; - acxt->removed = sd; -} - -/** - * 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. - * - * LOCKING: - * sysfs_mutex is released. - */ -void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) - __releases(sysfs_mutex) -{ - /* release resources acquired by sysfs_addrm_start() */ - mutex_unlock(&sysfs_mutex); - - /* kill removed sysfs_dirents */ - while (acxt->removed) { - struct sysfs_dirent *sd = acxt->removed; - - acxt->removed = sd->u.removed_list; - - sysfs_deactivate(sd); - sysfs_unmap_bin_file(sd); - kernfs_put(sd); - } -} - -/** - * kernfs_find_ns - find sysfs_dirent with the given name - * @parent: sysfs_dirent to search under - * @name: name to look for - * @ns: the namespace tag to use - * - * Look for sysfs_dirent with name @name under @parent. Returns pointer to - * the found sysfs_dirent on success, %NULL on failure. - */ -static struct sysfs_dirent *kernfs_find_ns(struct sysfs_dirent *parent, - const unsigned char *name, - const void *ns) -{ - struct rb_node *node = parent->s_dir.children.rb_node; - bool has_ns = parent->s_flags & SYSFS_FLAG_NS; - unsigned int hash; - - lockdep_assert_held(&sysfs_mutex); - - if (has_ns != (bool)ns) { - WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", - has_ns ? "required" : "invalid", - parent->s_name, name); - return NULL; - } - - hash = sysfs_name_hash(name, ns); - while (node) { - struct sysfs_dirent *sd; - int result; - - sd = to_sysfs_dirent(node); - result = sysfs_name_compare(hash, name, ns, sd); - if (result < 0) - node = node->rb_left; - else if (result > 0) - node = node->rb_right; - else - return sd; - } - return NULL; -} - -/** - * kernfs_find_and_get_ns - find and get sysfs_dirent with the given name - * @parent: sysfs_dirent to search under - * @name: name to look for - * @ns: the namespace tag to use - * - * Look for sysfs_dirent with name @name under @parent and get a reference - * if found. This function may sleep and returns pointer to the found - * sysfs_dirent on success, %NULL on failure. - */ -struct sysfs_dirent *kernfs_find_and_get_ns(struct sysfs_dirent *parent, - const char *name, const void *ns) -{ - struct sysfs_dirent *sd; - - mutex_lock(&sysfs_mutex); - sd = kernfs_find_ns(parent, name, ns); - kernfs_get(sd); - mutex_unlock(&sysfs_mutex); - - return sd; -} -EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); - -/** - * kernfs_create_dir_ns - create a directory - * @parent: parent in which to create a new directory - * @name: name of the new directory - * @priv: opaque data associated with the new directory - * @ns: optional namespace tag of the directory - * - * Returns the created node on success, ERR_PTR() value on failure. - */ -struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, - const char *name, void *priv, - const void *ns) -{ - umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; - struct sysfs_addrm_cxt acxt; - struct sysfs_dirent *sd; - int rc; - - /* allocate */ - sd = sysfs_new_dirent(name, mode, SYSFS_DIR); - if (!sd) - return ERR_PTR(-ENOMEM); - - sd->s_ns = ns; - sd->priv = priv; - - /* link in */ - sysfs_addrm_start(&acxt); - rc = sysfs_add_one(&acxt, sd, parent); - sysfs_addrm_finish(&acxt); - - if (!rc) - return sd; - - kernfs_put(sd); - return ERR_PTR(rc); -} - -/** * sysfs_create_dir_ns - create a directory for an object with a namespace tag * @kobj: object we're creating directory for * @ns: the namespace tag to use @@ -715,177 +84,6 @@ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) return 0; } -static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, - unsigned int flags) -{ - struct dentry *ret = NULL; - struct dentry *parent = dentry->d_parent; - struct sysfs_dirent *parent_sd = parent->d_fsdata; - struct sysfs_dirent *sd; - struct inode *inode; - const void *ns = NULL; - - mutex_lock(&sysfs_mutex); - - if (parent_sd->s_flags & SYSFS_FLAG_NS) - ns = sysfs_info(dir->i_sb)->ns; - - sd = kernfs_find_ns(parent_sd, dentry->d_name.name, ns); - - /* no such entry */ - if (!sd) { - ret = ERR_PTR(-ENOENT); - goto out_unlock; - } - kernfs_get(sd); - dentry->d_fsdata = sd; - - /* attach dentry and inode */ - inode = sysfs_get_inode(dir->i_sb, sd); - if (!inode) { - ret = ERR_PTR(-ENOMEM); - goto out_unlock; - } - - /* instantiate and hash dentry */ - ret = d_materialise_unique(dentry, inode); - out_unlock: - mutex_unlock(&sysfs_mutex); - return ret; -} - -const struct inode_operations sysfs_dir_inode_operations = { - .lookup = sysfs_lookup, - .permission = sysfs_permission, - .setattr = sysfs_setattr, - .getattr = sysfs_getattr, - .setxattr = sysfs_setxattr, -}; - -static struct sysfs_dirent *sysfs_leftmost_descendant(struct sysfs_dirent *pos) -{ - struct sysfs_dirent *last; - - while (true) { - struct rb_node *rbn; - - last = pos; - - if (sysfs_type(pos) != SYSFS_DIR) - break; - - rbn = rb_first(&pos->s_dir.children); - if (!rbn) - break; - - pos = to_sysfs_dirent(rbn); - } - - return last; -} - -/** - * sysfs_next_descendant_post - find the next descendant for post-order walk - * @pos: the current position (%NULL to initiate traversal) - * @root: sysfs_dirent whose descendants to walk - * - * Find the next descendant to visit for post-order traversal of @root's - * descendants. @root is included in the iteration and the last node to be - * visited. - */ -static struct sysfs_dirent *sysfs_next_descendant_post(struct sysfs_dirent *pos, - struct sysfs_dirent *root) -{ - struct rb_node *rbn; - - lockdep_assert_held(&sysfs_mutex); - - /* if first iteration, visit leftmost descendant which may be root */ - if (!pos) - return sysfs_leftmost_descendant(root); - - /* if we visited @root, we're done */ - if (pos == root) - return NULL; - - /* if there's an unvisited sibling, visit its leftmost descendant */ - rbn = rb_next(&pos->s_rb); - if (rbn) - return sysfs_leftmost_descendant(to_sysfs_dirent(rbn)); - - /* no sibling left, visit parent */ - return pos->s_parent; -} - -static void __kernfs_remove(struct sysfs_addrm_cxt *acxt, - struct sysfs_dirent *sd) -{ - struct sysfs_dirent *pos, *next; - - if (!sd) - return; - - pr_debug("sysfs %s: removing\n", sd->s_name); - - next = NULL; - do { - pos = next; - next = sysfs_next_descendant_post(pos, sd); - if (pos) - sysfs_remove_one(acxt, pos); - } while (next); -} - -/** - * kernfs_remove - remove a sysfs_dirent recursively - * @sd: the sysfs_dirent to remove - * - * Remove @sd along with all its subdirectories and files. - */ -void kernfs_remove(struct sysfs_dirent *sd) -{ - struct sysfs_addrm_cxt acxt; - - sysfs_addrm_start(&acxt); - __kernfs_remove(&acxt, sd); - sysfs_addrm_finish(&acxt); -} - -/** - * kernfs_remove_by_name_ns - find a sysfs_dirent by name and remove it - * @dir_sd: parent of the target - * @name: name of the sysfs_dirent to remove - * @ns: namespace tag of the sysfs_dirent to remove - * - * Look for the sysfs_dirent with @name and @ns under @dir_sd and remove - * it. Returns 0 on success, -ENOENT if such entry doesn't exist. - */ -int kernfs_remove_by_name_ns(struct sysfs_dirent *dir_sd, const char *name, - const void *ns) -{ - struct sysfs_addrm_cxt acxt; - struct sysfs_dirent *sd; - - if (!dir_sd) { - WARN(1, KERN_WARNING "sysfs: can not remove '%s', no directory\n", - name); - return -ENOENT; - } - - sysfs_addrm_start(&acxt); - - sd = kernfs_find_ns(dir_sd, name, ns); - if (sd) - __kernfs_remove(&acxt, sd); - - sysfs_addrm_finish(&acxt); - - if (sd) - return 0; - else - return -ENOENT; -} - /** * sysfs_remove_dir - remove an object's directory. * @kobj: object. @@ -920,57 +118,6 @@ void sysfs_remove_dir(struct kobject *kobj) } } -/** - * kernfs_rename_ns - move and rename a kernfs_node - * @sd: target node - * @new_parent: new parent to put @sd under - * @new_name: new name - * @new_ns: new namespace tag - */ -int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, - const char *new_name, const void *new_ns) -{ - int error; - - mutex_lock(&sysfs_mutex); - - error = 0; - if ((sd->s_parent == new_parent) && (sd->s_ns == new_ns) && - (strcmp(sd->s_name, new_name) == 0)) - goto out; /* nothing to rename */ - - error = -EEXIST; - if (kernfs_find_ns(new_parent, new_name, new_ns)) - goto out; - - /* rename sysfs_dirent */ - if (strcmp(sd->s_name, new_name) != 0) { - error = -ENOMEM; - new_name = kstrdup(new_name, GFP_KERNEL); - if (!new_name) - goto out; - - kfree(sd->s_name); - sd->s_name = new_name; - } - - /* - * Move to the appropriate place in the appropriate directories rbtree. - */ - sysfs_unlink_sibling(sd); - kernfs_get(new_parent); - kernfs_put(sd->s_parent); - sd->s_ns = new_ns; - sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); - sd->s_parent = new_parent; - sysfs_link_sibling(sd); - - error = 0; - out: - mutex_unlock(&sysfs_mutex); - return error; -} - int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, const void *new_ns) { @@ -991,136 +138,3 @@ int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, return kernfs_rename_ns(sd, new_parent_sd, sd->s_name, new_ns); } - -/** - * kernfs_enable_ns - enable namespace under a directory - * @sd: directory of interest, should be empty - * - * This is to be called right after @sd is created to enable namespace - * under it. All children of @sd must have non-NULL namespace tags and - * only the ones which match the super_block's tag will be visible. - */ -void kernfs_enable_ns(struct sysfs_dirent *sd) -{ - WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); - WARN_ON_ONCE(!RB_EMPTY_ROOT(&sd->s_dir.children)); - sd->s_flags |= SYSFS_FLAG_NS; -} - -/* Relationship between s_mode and the DT_xxx types */ -static inline unsigned char dt_type(struct sysfs_dirent *sd) -{ - return (sd->s_mode >> 12) & 15; -} - -static int sysfs_dir_release(struct inode *inode, struct file *filp) -{ - kernfs_put(filp->private_data); - return 0; -} - -static struct sysfs_dirent *sysfs_dir_pos(const void *ns, - struct sysfs_dirent *parent_sd, loff_t hash, struct sysfs_dirent *pos) -{ - if (pos) { - int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && - pos->s_parent == parent_sd && - hash == pos->s_hash; - kernfs_put(pos); - if (!valid) - pos = NULL; - } - if (!pos && (hash > 1) && (hash < INT_MAX)) { - struct rb_node *node = parent_sd->s_dir.children.rb_node; - while (node) { - pos = to_sysfs_dirent(node); - - if (hash < pos->s_hash) - node = node->rb_left; - else if (hash > pos->s_hash) - node = node->rb_right; - else - break; - } - } - /* Skip over entries in the wrong namespace */ - while (pos && pos->s_ns != ns) { - struct rb_node *node = rb_next(&pos->s_rb); - if (!node) - pos = NULL; - else - pos = to_sysfs_dirent(node); - } - return pos; -} - -static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns, - struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos) -{ - pos = sysfs_dir_pos(ns, parent_sd, ino, pos); - if (pos) - do { - struct rb_node *node = rb_next(&pos->s_rb); - if (!node) - pos = NULL; - else - pos = to_sysfs_dirent(node); - } while (pos && pos->s_ns != ns); - return pos; -} - -static int sysfs_readdir(struct file *file, struct dir_context *ctx) -{ - struct dentry *dentry = file->f_path.dentry; - struct sysfs_dirent *parent_sd = dentry->d_fsdata; - struct sysfs_dirent *pos = file->private_data; - const void *ns = NULL; - - if (!dir_emit_dots(file, ctx)) - return 0; - mutex_lock(&sysfs_mutex); - - if (parent_sd->s_flags & SYSFS_FLAG_NS) - ns = sysfs_info(dentry->d_sb)->ns; - - for (pos = sysfs_dir_pos(ns, parent_sd, ctx->pos, pos); - pos; - pos = sysfs_dir_next_pos(ns, parent_sd, ctx->pos, pos)) { - const char *name = pos->s_name; - unsigned int type = dt_type(pos); - int len = strlen(name); - ino_t ino = pos->s_ino; - - ctx->pos = pos->s_hash; - file->private_data = pos; - kernfs_get(pos); - - mutex_unlock(&sysfs_mutex); - if (!dir_emit(ctx, name, len, ino, type)) - return 0; - mutex_lock(&sysfs_mutex); - } - mutex_unlock(&sysfs_mutex); - file->private_data = NULL; - ctx->pos = INT_MAX; - return 0; -} - -static loff_t sysfs_dir_llseek(struct file *file, loff_t offset, int whence) -{ - struct inode *inode = file_inode(file); - loff_t ret; - - mutex_lock(&inode->i_mutex); - ret = generic_file_llseek(file, offset, whence); - mutex_unlock(&inode->i_mutex); - - return ret; -} - -const struct file_operations sysfs_dir_operations = { - .read = generic_read_dir, - .iterate = sysfs_readdir, - .release = sysfs_dir_release, - .llseek = sysfs_dir_llseek, -}; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 93c1910..972b4a4 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -34,22 +34,9 @@ extern struct kmem_cache *sysfs_dir_cachep; /* * dir.c */ -extern struct mutex sysfs_mutex; extern spinlock_t sysfs_symlink_target_lock; -extern const struct dentry_operations sysfs_dentry_ops; -extern const struct file_operations sysfs_dir_operations; -extern const struct inode_operations sysfs_dir_inode_operations; - -struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd); -void sysfs_put_active(struct sysfs_dirent *sd); -void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt); void sysfs_warn_dup(struct sysfs_dirent *parent, const char *name); -int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, - struct sysfs_dirent *parent_sd); -void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); - -struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type); /* * file.c -- cgit v1.1 From 414985ae23c031efbd6d16d484dea8b5de28b8f7 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:34 -0500 Subject: sysfs, kernfs: move file core code to fs/kernfs/file.c Move core file code to fs/kernfs/file.c. fs/sysfs/file.c now contains sysfs kernfs_ops callbacks, sysfs wrappers around kernfs interfaces, and sysfs_schedule_callback(). The respective declarations in fs/sysfs/sysfs.h are moved to fs/kernfs/kernfs-internal.h. This is pure relocation. v2: Refreshed on top of the v2 of "sysfs, kernfs: prepare read path for kernfs". v3: Refreshed on top of the v3 of "sysfs, kernfs: prepare read path for kernfs". Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/file.c | 805 ++++++++++++++++++++++++++++++++++++++++++++ fs/kernfs/kernfs-internal.h | 7 + fs/sysfs/file.c | 802 +------------------------------------------ fs/sysfs/sysfs.h | 4 - 4 files changed, 813 insertions(+), 805 deletions(-) diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 90b1e88..fa172e8 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -7,3 +7,808 @@ * * This file is released under the GPLv2. */ + +#include +#include +#include +#include +#include +#include +#include + +#include "kernfs-internal.h" + +/* + * There's one sysfs_open_file for each open file and one sysfs_open_dirent + * for each sysfs_dirent with one or more open files. + * + * sysfs_dirent->s_attr.open points to sysfs_open_dirent. s_attr.open is + * protected by sysfs_open_dirent_lock. + * + * filp->private_data points to seq_file whose ->private points to + * sysfs_open_file. sysfs_open_files are chained at + * sysfs_open_dirent->files, which is protected by sysfs_open_file_mutex. + */ +static DEFINE_SPINLOCK(sysfs_open_dirent_lock); +static DEFINE_MUTEX(sysfs_open_file_mutex); + +struct sysfs_open_dirent { + atomic_t refcnt; + atomic_t event; + wait_queue_head_t poll; + struct list_head files; /* goes through sysfs_open_file.list */ +}; + +static struct sysfs_open_file *sysfs_of(struct file *file) +{ + return ((struct seq_file *)file->private_data)->private; +} + +/* + * Determine the kernfs_ops for the given sysfs_dirent. This function must + * be called while holding an active reference. + */ +static const struct kernfs_ops *kernfs_ops(struct sysfs_dirent *sd) +{ + if (sd->s_flags & SYSFS_FLAG_LOCKDEP) + lockdep_assert_held(sd); + return sd->s_attr.ops; +} + +static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) +{ + struct sysfs_open_file *of = sf->private; + const struct kernfs_ops *ops; + + /* + * @of->mutex nests outside active ref and is just to ensure that + * the ops aren't called concurrently for the same open file. + */ + mutex_lock(&of->mutex); + if (!sysfs_get_active(of->sd)) + return ERR_PTR(-ENODEV); + + ops = kernfs_ops(of->sd); + if (ops->seq_start) { + return ops->seq_start(sf, ppos); + } else { + /* + * The same behavior and code as single_open(). Returns + * !NULL if pos is at the beginning; otherwise, NULL. + */ + return NULL + !*ppos; + } +} + +static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) +{ + struct sysfs_open_file *of = sf->private; + const struct kernfs_ops *ops = kernfs_ops(of->sd); + + if (ops->seq_next) { + return ops->seq_next(sf, v, ppos); + } else { + /* + * The same behavior and code as single_open(), always + * terminate after the initial read. + */ + ++*ppos; + return NULL; + } +} + +static void kernfs_seq_stop(struct seq_file *sf, void *v) +{ + struct sysfs_open_file *of = sf->private; + const struct kernfs_ops *ops = kernfs_ops(of->sd); + + if (ops->seq_stop) + ops->seq_stop(sf, v); + + sysfs_put_active(of->sd); + mutex_unlock(&of->mutex); +} + +static int kernfs_seq_show(struct seq_file *sf, void *v) +{ + struct sysfs_open_file *of = sf->private; + + of->event = atomic_read(&of->sd->s_attr.open->event); + + return of->sd->s_attr.ops->seq_show(sf, v); +} + +static const struct seq_operations kernfs_seq_ops = { + .start = kernfs_seq_start, + .next = kernfs_seq_next, + .stop = kernfs_seq_stop, + .show = kernfs_seq_show, +}; + +/* + * As reading a bin file can have side-effects, the exact offset and bytes + * specified in read(2) call should be passed to the read callback making + * it difficult to use seq_file. Implement simplistic custom buffering for + * bin files. + */ +static ssize_t kernfs_file_direct_read(struct sysfs_open_file *of, + char __user *user_buf, size_t count, + loff_t *ppos) +{ + ssize_t len = min_t(size_t, count, PAGE_SIZE); + const struct kernfs_ops *ops; + char *buf; + + buf = kmalloc(len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + /* + * @of->mutex nests outside active ref and is just to ensure that + * the ops aren't called concurrently for the same open file. + */ + mutex_lock(&of->mutex); + if (!sysfs_get_active(of->sd)) { + len = -ENODEV; + mutex_unlock(&of->mutex); + goto out_free; + } + + ops = kernfs_ops(of->sd); + if (ops->read) + len = ops->read(of, buf, len, *ppos); + else + len = -EINVAL; + + sysfs_put_active(of->sd); + mutex_unlock(&of->mutex); + + if (len < 0) + goto out_free; + + if (copy_to_user(user_buf, buf, len)) { + len = -EFAULT; + goto out_free; + } + + *ppos += len; + + out_free: + kfree(buf); + return len; +} + +/** + * kernfs_file_read - kernfs vfs read callback + * @file: file pointer + * @user_buf: data to write + * @count: number of bytes + * @ppos: starting offset + */ +static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct sysfs_open_file *of = sysfs_of(file); + + if (of->sd->s_flags & SYSFS_FLAG_HAS_SEQ_SHOW) + return seq_read(file, user_buf, count, ppos); + else + return kernfs_file_direct_read(of, user_buf, count, ppos); +} + +/** + * kernfs_file_write - kernfs vfs write callback + * @file: file pointer + * @user_buf: data to write + * @count: number of bytes + * @ppos: starting offset + * + * Copy data in from userland and pass it to the matching kernfs write + * operation. + * + * There is no easy way for us to know if userspace is only doing a partial + * write, so we don't support them. We expect the entire buffer to come on + * the first write. Hint: if you're writing a value, first read the file, + * modify only the the value you're changing, then write entire buffer + * back. + */ +static ssize_t kernfs_file_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct sysfs_open_file *of = sysfs_of(file); + ssize_t len = min_t(size_t, count, PAGE_SIZE); + const struct kernfs_ops *ops; + char *buf; + + buf = kmalloc(len + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + if (copy_from_user(buf, user_buf, len)) { + len = -EFAULT; + goto out_free; + } + buf[len] = '\0'; /* guarantee string termination */ + + /* + * @of->mutex nests outside active ref and is just to ensure that + * the ops aren't called concurrently for the same open file. + */ + mutex_lock(&of->mutex); + if (!sysfs_get_active(of->sd)) { + mutex_unlock(&of->mutex); + len = -ENODEV; + goto out_free; + } + + ops = kernfs_ops(of->sd); + if (ops->write) + len = ops->write(of, buf, len, *ppos); + else + len = -EINVAL; + + sysfs_put_active(of->sd); + mutex_unlock(&of->mutex); + + if (len > 0) + *ppos += len; +out_free: + kfree(buf); + return len; +} + +static void kernfs_vma_open(struct vm_area_struct *vma) +{ + struct file *file = vma->vm_file; + struct sysfs_open_file *of = sysfs_of(file); + + if (!of->vm_ops) + return; + + if (!sysfs_get_active(of->sd)) + return; + + if (of->vm_ops->open) + of->vm_ops->open(vma); + + sysfs_put_active(of->sd); +} + +static int kernfs_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct file *file = vma->vm_file; + struct sysfs_open_file *of = sysfs_of(file); + int ret; + + if (!of->vm_ops) + return VM_FAULT_SIGBUS; + + if (!sysfs_get_active(of->sd)) + return VM_FAULT_SIGBUS; + + ret = VM_FAULT_SIGBUS; + if (of->vm_ops->fault) + ret = of->vm_ops->fault(vma, vmf); + + sysfs_put_active(of->sd); + return ret; +} + +static int kernfs_vma_page_mkwrite(struct vm_area_struct *vma, + struct vm_fault *vmf) +{ + struct file *file = vma->vm_file; + struct sysfs_open_file *of = sysfs_of(file); + int ret; + + if (!of->vm_ops) + return VM_FAULT_SIGBUS; + + if (!sysfs_get_active(of->sd)) + return VM_FAULT_SIGBUS; + + ret = 0; + if (of->vm_ops->page_mkwrite) + ret = of->vm_ops->page_mkwrite(vma, vmf); + else + file_update_time(file); + + sysfs_put_active(of->sd); + return ret; +} + +static int kernfs_vma_access(struct vm_area_struct *vma, unsigned long addr, + void *buf, int len, int write) +{ + struct file *file = vma->vm_file; + struct sysfs_open_file *of = sysfs_of(file); + int ret; + + if (!of->vm_ops) + return -EINVAL; + + if (!sysfs_get_active(of->sd)) + return -EINVAL; + + ret = -EINVAL; + if (of->vm_ops->access) + ret = of->vm_ops->access(vma, addr, buf, len, write); + + sysfs_put_active(of->sd); + return ret; +} + +#ifdef CONFIG_NUMA +static int kernfs_vma_set_policy(struct vm_area_struct *vma, + struct mempolicy *new) +{ + struct file *file = vma->vm_file; + struct sysfs_open_file *of = sysfs_of(file); + int ret; + + if (!of->vm_ops) + return 0; + + if (!sysfs_get_active(of->sd)) + return -EINVAL; + + ret = 0; + if (of->vm_ops->set_policy) + ret = of->vm_ops->set_policy(vma, new); + + sysfs_put_active(of->sd); + return ret; +} + +static struct mempolicy *kernfs_vma_get_policy(struct vm_area_struct *vma, + unsigned long addr) +{ + struct file *file = vma->vm_file; + struct sysfs_open_file *of = sysfs_of(file); + struct mempolicy *pol; + + if (!of->vm_ops) + return vma->vm_policy; + + if (!sysfs_get_active(of->sd)) + return vma->vm_policy; + + pol = vma->vm_policy; + if (of->vm_ops->get_policy) + pol = of->vm_ops->get_policy(vma, addr); + + sysfs_put_active(of->sd); + return pol; +} + +static int kernfs_vma_migrate(struct vm_area_struct *vma, + const nodemask_t *from, const nodemask_t *to, + unsigned long flags) +{ + struct file *file = vma->vm_file; + struct sysfs_open_file *of = sysfs_of(file); + int ret; + + if (!of->vm_ops) + return 0; + + if (!sysfs_get_active(of->sd)) + return 0; + + ret = 0; + if (of->vm_ops->migrate) + ret = of->vm_ops->migrate(vma, from, to, flags); + + sysfs_put_active(of->sd); + return ret; +} +#endif + +static const struct vm_operations_struct kernfs_vm_ops = { + .open = kernfs_vma_open, + .fault = kernfs_vma_fault, + .page_mkwrite = kernfs_vma_page_mkwrite, + .access = kernfs_vma_access, +#ifdef CONFIG_NUMA + .set_policy = kernfs_vma_set_policy, + .get_policy = kernfs_vma_get_policy, + .migrate = kernfs_vma_migrate, +#endif +}; + +static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct sysfs_open_file *of = sysfs_of(file); + const struct kernfs_ops *ops; + int rc; + + mutex_lock(&of->mutex); + + rc = -ENODEV; + if (!sysfs_get_active(of->sd)) + goto out_unlock; + + ops = kernfs_ops(of->sd); + if (ops->mmap) + rc = ops->mmap(of, vma); + if (rc) + goto out_put; + + /* + * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup() + * to satisfy versions of X which crash if the mmap fails: that + * substitutes a new vm_file, and we don't then want bin_vm_ops. + */ + if (vma->vm_file != file) + goto out_put; + + rc = -EINVAL; + if (of->mmapped && of->vm_ops != vma->vm_ops) + goto out_put; + + /* + * It is not possible to successfully wrap close. + * So error if someone is trying to use close. + */ + rc = -EINVAL; + if (vma->vm_ops && vma->vm_ops->close) + goto out_put; + + rc = 0; + of->mmapped = 1; + of->vm_ops = vma->vm_ops; + vma->vm_ops = &kernfs_vm_ops; +out_put: + sysfs_put_active(of->sd); +out_unlock: + mutex_unlock(&of->mutex); + + return rc; +} + +/** + * sysfs_get_open_dirent - get or create sysfs_open_dirent + * @sd: target sysfs_dirent + * @of: sysfs_open_file for this instance of open + * + * If @sd->s_attr.open exists, increment its reference count; + * otherwise, create one. @of is chained to the files list. + * + * LOCKING: + * Kernel thread context (may sleep). + * + * RETURNS: + * 0 on success, -errno on failure. + */ +static int sysfs_get_open_dirent(struct sysfs_dirent *sd, + struct sysfs_open_file *of) +{ + struct sysfs_open_dirent *od, *new_od = NULL; + + retry: + mutex_lock(&sysfs_open_file_mutex); + spin_lock_irq(&sysfs_open_dirent_lock); + + if (!sd->s_attr.open && new_od) { + sd->s_attr.open = new_od; + new_od = NULL; + } + + od = sd->s_attr.open; + if (od) { + atomic_inc(&od->refcnt); + list_add_tail(&of->list, &od->files); + } + + spin_unlock_irq(&sysfs_open_dirent_lock); + mutex_unlock(&sysfs_open_file_mutex); + + if (od) { + kfree(new_od); + return 0; + } + + /* not there, initialize a new one and retry */ + new_od = kmalloc(sizeof(*new_od), GFP_KERNEL); + if (!new_od) + return -ENOMEM; + + atomic_set(&new_od->refcnt, 0); + atomic_set(&new_od->event, 1); + init_waitqueue_head(&new_od->poll); + INIT_LIST_HEAD(&new_od->files); + goto retry; +} + +/** + * sysfs_put_open_dirent - put sysfs_open_dirent + * @sd: target sysfs_dirent + * @of: associated sysfs_open_file + * + * Put @sd->s_attr.open and unlink @of from the files list. If + * reference count reaches zero, disassociate and free it. + * + * LOCKING: + * None. + */ +static void sysfs_put_open_dirent(struct sysfs_dirent *sd, + struct sysfs_open_file *of) +{ + struct sysfs_open_dirent *od = sd->s_attr.open; + unsigned long flags; + + mutex_lock(&sysfs_open_file_mutex); + spin_lock_irqsave(&sysfs_open_dirent_lock, flags); + + if (of) + list_del(&of->list); + + if (atomic_dec_and_test(&od->refcnt)) + sd->s_attr.open = NULL; + else + od = NULL; + + spin_unlock_irqrestore(&sysfs_open_dirent_lock, flags); + mutex_unlock(&sysfs_open_file_mutex); + + kfree(od); +} + +static int kernfs_file_open(struct inode *inode, struct file *file) +{ + struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; + const struct kernfs_ops *ops; + struct sysfs_open_file *of; + bool has_read, has_write, has_mmap; + int error = -EACCES; + + if (!sysfs_get_active(attr_sd)) + return -ENODEV; + + ops = kernfs_ops(attr_sd); + + has_read = ops->seq_show || ops->read || ops->mmap; + has_write = ops->write || ops->mmap; + has_mmap = ops->mmap; + + /* check perms and supported operations */ + if ((file->f_mode & FMODE_WRITE) && + (!(inode->i_mode & S_IWUGO) || !has_write)) + goto err_out; + + if ((file->f_mode & FMODE_READ) && + (!(inode->i_mode & S_IRUGO) || !has_read)) + goto err_out; + + /* allocate a sysfs_open_file for the file */ + error = -ENOMEM; + of = kzalloc(sizeof(struct sysfs_open_file), GFP_KERNEL); + if (!of) + goto err_out; + + /* + * The following is done to give a different lockdep key to + * @of->mutex for files which implement mmap. This is a rather + * crude way to avoid false positive lockdep warning around + * mm->mmap_sem - mmap nests @of->mutex under mm->mmap_sem and + * reading /sys/block/sda/trace/act_mask grabs sr_mutex, under + * which mm->mmap_sem nests, while holding @of->mutex. As each + * open file has a separate mutex, it's okay as long as those don't + * happen on the same file. At this point, we can't easily give + * each file a separate locking class. Let's differentiate on + * whether the file has mmap or not for now. + */ + if (has_mmap) + mutex_init(&of->mutex); + else + mutex_init(&of->mutex); + + of->sd = attr_sd; + of->file = file; + + /* + * Always instantiate seq_file even if read access doesn't use + * seq_file or is not requested. This unifies private data access + * and readable regular files are the vast majority anyway. + */ + if (ops->seq_show) + error = seq_open(file, &kernfs_seq_ops); + else + error = seq_open(file, NULL); + if (error) + goto err_free; + + ((struct seq_file *)file->private_data)->private = of; + + /* seq_file clears PWRITE unconditionally, restore it if WRITE */ + if (file->f_mode & FMODE_WRITE) + file->f_mode |= FMODE_PWRITE; + + /* make sure we have open dirent struct */ + error = sysfs_get_open_dirent(attr_sd, of); + if (error) + goto err_close; + + /* open succeeded, put active references */ + sysfs_put_active(attr_sd); + return 0; + +err_close: + seq_release(inode, file); +err_free: + kfree(of); +err_out: + sysfs_put_active(attr_sd); + return error; +} + +static int kernfs_file_release(struct inode *inode, struct file *filp) +{ + struct sysfs_dirent *sd = filp->f_path.dentry->d_fsdata; + struct sysfs_open_file *of = sysfs_of(filp); + + sysfs_put_open_dirent(sd, of); + seq_release(inode, filp); + kfree(of); + + return 0; +} + +void sysfs_unmap_bin_file(struct sysfs_dirent *sd) +{ + struct sysfs_open_dirent *od; + struct sysfs_open_file *of; + + if (!(sd->s_flags & SYSFS_FLAG_HAS_MMAP)) + return; + + spin_lock_irq(&sysfs_open_dirent_lock); + od = sd->s_attr.open; + if (od) + atomic_inc(&od->refcnt); + spin_unlock_irq(&sysfs_open_dirent_lock); + if (!od) + return; + + mutex_lock(&sysfs_open_file_mutex); + list_for_each_entry(of, &od->files, list) { + struct inode *inode = file_inode(of->file); + unmap_mapping_range(inode->i_mapping, 0, 0, 1); + } + mutex_unlock(&sysfs_open_file_mutex); + + sysfs_put_open_dirent(sd, NULL); +} + +/* Sysfs attribute files are pollable. The idea is that you read + * the content and then you use 'poll' or 'select' to wait for + * the content to change. When the content changes (assuming the + * manager for the kobject supports notification), poll will + * return POLLERR|POLLPRI, and select will return the fd whether + * it is waiting for read, write, or exceptions. + * Once poll/select indicates that the value has changed, you + * need to close and re-open the file, or seek to 0 and read again. + * Reminder: this only works for attributes which actively support + * it, and it is not possible to test an attribute from userspace + * to see if it supports poll (Neither 'poll' nor 'select' return + * an appropriate error code). When in doubt, set a suitable timeout value. + */ +static unsigned int kernfs_file_poll(struct file *filp, poll_table *wait) +{ + struct sysfs_open_file *of = sysfs_of(filp); + struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; + struct sysfs_open_dirent *od = attr_sd->s_attr.open; + + /* need parent for the kobj, grab both */ + if (!sysfs_get_active(attr_sd)) + goto trigger; + + poll_wait(filp, &od->poll, wait); + + sysfs_put_active(attr_sd); + + if (of->event != atomic_read(&od->event)) + goto trigger; + + return DEFAULT_POLLMASK; + + trigger: + return DEFAULT_POLLMASK|POLLERR|POLLPRI; +} + +/** + * kernfs_notify - notify a kernfs file + * @sd: file to notify + * + * Notify @sd such that poll(2) on @sd wakes up. + */ +void kernfs_notify(struct sysfs_dirent *sd) +{ + struct sysfs_open_dirent *od; + unsigned long flags; + + spin_lock_irqsave(&sysfs_open_dirent_lock, flags); + + if (!WARN_ON(sysfs_type(sd) != SYSFS_KOBJ_ATTR)) { + od = sd->s_attr.open; + if (od) { + atomic_inc(&od->event); + wake_up_interruptible(&od->poll); + } + } + + spin_unlock_irqrestore(&sysfs_open_dirent_lock, flags); +} +EXPORT_SYMBOL_GPL(kernfs_notify); + +const struct file_operations kernfs_file_operations = { + .read = kernfs_file_read, + .write = kernfs_file_write, + .llseek = generic_file_llseek, + .mmap = kernfs_file_mmap, + .open = kernfs_file_open, + .release = kernfs_file_release, + .poll = kernfs_file_poll, +}; + +/** + * kernfs_create_file_ns_key - create a file + * @parent: directory to create the file in + * @name: name of the file + * @mode: mode of the file + * @size: size of the file + * @ops: kernfs operations for the file + * @priv: private data for the file + * @ns: optional namespace tag of the file + * @key: lockdep key for the file's active_ref, %NULL to disable lockdep + * + * Returns the created node on success, ERR_PTR() value on error. + */ +struct sysfs_dirent *kernfs_create_file_ns_key(struct sysfs_dirent *parent, + const char *name, + umode_t mode, loff_t size, + const struct kernfs_ops *ops, + void *priv, const void *ns, + struct lock_class_key *key) +{ + struct sysfs_addrm_cxt acxt; + struct sysfs_dirent *sd; + int rc; + + sd = sysfs_new_dirent(name, (mode & S_IALLUGO) | S_IFREG, + SYSFS_KOBJ_ATTR); + if (!sd) + return ERR_PTR(-ENOMEM); + + sd->s_attr.ops = ops; + sd->s_attr.size = size; + sd->s_ns = ns; + sd->priv = priv; + +#ifdef CONFIG_DEBUG_LOCK_ALLOC + if (key) { + lockdep_init_map(&sd->dep_map, "s_active", key, 0); + sd->s_flags |= SYSFS_FLAG_LOCKDEP; + } +#endif + + /* + * sd->s_attr.ops is accesible only while holding active ref. We + * need to know whether some ops are implemented outside active + * ref. Cache their existence in flags. + */ + if (ops->seq_show) + sd->s_flags |= SYSFS_FLAG_HAS_SEQ_SHOW; + if (ops->mmap) + sd->s_flags |= SYSFS_FLAG_HAS_MMAP; + + sysfs_addrm_start(&acxt); + rc = sysfs_add_one(&acxt, sd, parent); + sysfs_addrm_finish(&acxt); + + if (rc) { + kernfs_put(sd); + return ERR_PTR(rc); + } + return sd; +} diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 31f0dbe..38e3a16 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -142,4 +142,11 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type); +/* + * file.c + */ +extern const struct file_operations kernfs_file_operations; + +void sysfs_unmap_bin_file(struct sysfs_dirent *sd); + #endif /* __KERNFS_INTERNAL_H */ diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 7f0a79f..ac77d2b 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -14,54 +14,12 @@ #include #include #include -#include -#include -#include #include #include -#include -#include #include -#include #include "sysfs.h" - -/* - * There's one sysfs_open_file for each open file and one sysfs_open_dirent - * for each sysfs_dirent with one or more open files. - * - * sysfs_dirent->s_attr.open points to sysfs_open_dirent. s_attr.open is - * protected by sysfs_open_dirent_lock. - * - * filp->private_data points to seq_file whose ->private points to - * sysfs_open_file. sysfs_open_files are chained at - * sysfs_open_dirent->files, which is protected by sysfs_open_file_mutex. - */ -static DEFINE_SPINLOCK(sysfs_open_dirent_lock); -static DEFINE_MUTEX(sysfs_open_file_mutex); - -struct sysfs_open_dirent { - atomic_t refcnt; - atomic_t event; - wait_queue_head_t poll; - struct list_head files; /* goes through sysfs_open_file.list */ -}; - -static struct sysfs_open_file *sysfs_of(struct file *file) -{ - return ((struct seq_file *)file->private_data)->private; -} - -/* - * Determine the kernfs_ops for the given sysfs_dirent. This function must - * be called while holding an active reference. - */ -static const struct kernfs_ops *kernfs_ops(struct sysfs_dirent *sd) -{ - if (sd->s_flags & SYSFS_FLAG_LOCKDEP) - lockdep_assert_held(sd); - return sd->s_attr.ops; -} +#include "../kernfs/kernfs-internal.h" /* * Determine ktype->sysfs_ops for the given sysfs_dirent. This function @@ -143,147 +101,6 @@ static ssize_t sysfs_kf_bin_read(struct sysfs_open_file *of, char *buf, return battr->read(of->file, kobj, battr, buf, pos, count); } -static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) -{ - struct sysfs_open_file *of = sf->private; - const struct kernfs_ops *ops; - - /* - * @of->mutex nests outside active ref and is just to ensure that - * the ops aren't called concurrently for the same open file. - */ - mutex_lock(&of->mutex); - if (!sysfs_get_active(of->sd)) - return ERR_PTR(-ENODEV); - - ops = kernfs_ops(of->sd); - if (ops->seq_start) { - return ops->seq_start(sf, ppos); - } else { - /* - * The same behavior and code as single_open(). Returns - * !NULL if pos is at the beginning; otherwise, NULL. - */ - return NULL + !*ppos; - } -} - -static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) -{ - struct sysfs_open_file *of = sf->private; - const struct kernfs_ops *ops = kernfs_ops(of->sd); - - if (ops->seq_next) { - return ops->seq_next(sf, v, ppos); - } else { - /* - * The same behavior and code as single_open(), always - * terminate after the initial read. - */ - ++*ppos; - return NULL; - } -} - -static void kernfs_seq_stop(struct seq_file *sf, void *v) -{ - struct sysfs_open_file *of = sf->private; - const struct kernfs_ops *ops = kernfs_ops(of->sd); - - if (ops->seq_stop) - ops->seq_stop(sf, v); - - sysfs_put_active(of->sd); - mutex_unlock(&of->mutex); -} - -static int kernfs_seq_show(struct seq_file *sf, void *v) -{ - struct sysfs_open_file *of = sf->private; - - of->event = atomic_read(&of->sd->s_attr.open->event); - - return of->sd->s_attr.ops->seq_show(sf, v); -} - -static const struct seq_operations kernfs_seq_ops = { - .start = kernfs_seq_start, - .next = kernfs_seq_next, - .stop = kernfs_seq_stop, - .show = kernfs_seq_show, -}; - -/* - * As reading a bin file can have side-effects, the exact offset and bytes - * specified in read(2) call should be passed to the read callback making - * it difficult to use seq_file. Implement simplistic custom buffering for - * bin files. - */ -static ssize_t kernfs_file_direct_read(struct sysfs_open_file *of, - char __user *user_buf, size_t count, - loff_t *ppos) -{ - ssize_t len = min_t(size_t, count, PAGE_SIZE); - const struct kernfs_ops *ops; - char *buf; - - buf = kmalloc(len, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - /* - * @of->mutex nests outside active ref and is just to ensure that - * the ops aren't called concurrently for the same open file. - */ - mutex_lock(&of->mutex); - if (!sysfs_get_active(of->sd)) { - len = -ENODEV; - mutex_unlock(&of->mutex); - goto out_free; - } - - ops = kernfs_ops(of->sd); - if (ops->read) - len = ops->read(of, buf, len, *ppos); - else - len = -EINVAL; - - sysfs_put_active(of->sd); - mutex_unlock(&of->mutex); - - if (len < 0) - goto out_free; - - if (copy_to_user(user_buf, buf, len)) { - len = -EFAULT; - goto out_free; - } - - *ppos += len; - - out_free: - kfree(buf); - return len; -} - -/** - * kernfs_file_read - kernfs vfs read callback - * @file: file pointer - * @user_buf: data to write - * @count: number of bytes - * @ppos: starting offset - */ -static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct sysfs_open_file *of = sysfs_of(file); - - if (of->sd->s_flags & SYSFS_FLAG_HAS_SEQ_SHOW) - return seq_read(file, user_buf, count, ppos); - else - return kernfs_file_direct_read(of, user_buf, count, ppos); -} - /* kernfs write callback for regular sysfs files */ static ssize_t sysfs_kf_write(struct sysfs_open_file *of, char *buf, size_t count, loff_t pos) @@ -319,67 +136,6 @@ static ssize_t sysfs_kf_bin_write(struct sysfs_open_file *of, char *buf, return battr->write(of->file, kobj, battr, buf, pos, count); } -/** - * kernfs_file_write - kernfs vfs write callback - * @file: file pointer - * @user_buf: data to write - * @count: number of bytes - * @ppos: starting offset - * - * Copy data in from userland and pass it to the matching kernfs write - * operation. - * - * There is no easy way for us to know if userspace is only doing a partial - * write, so we don't support them. We expect the entire buffer to come on - * the first write. Hint: if you're writing a value, first read the file, - * modify only the the value you're changing, then write entire buffer - * back. - */ -static ssize_t kernfs_file_write(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct sysfs_open_file *of = sysfs_of(file); - ssize_t len = min_t(size_t, count, PAGE_SIZE); - const struct kernfs_ops *ops; - char *buf; - - buf = kmalloc(len + 1, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - if (copy_from_user(buf, user_buf, len)) { - len = -EFAULT; - goto out_free; - } - buf[len] = '\0'; /* guarantee string termination */ - - /* - * @of->mutex nests outside active ref and is just to ensure that - * the ops aren't called concurrently for the same open file. - */ - mutex_lock(&of->mutex); - if (!sysfs_get_active(of->sd)) { - mutex_unlock(&of->mutex); - len = -ENODEV; - goto out_free; - } - - ops = kernfs_ops(of->sd); - if (ops->write) - len = ops->write(of, buf, len, *ppos); - else - len = -EINVAL; - - sysfs_put_active(of->sd); - mutex_unlock(&of->mutex); - - if (len > 0) - *ppos += len; -out_free: - kfree(buf); - return len; -} - static int sysfs_kf_bin_mmap(struct sysfs_open_file *of, struct vm_area_struct *vma) { @@ -392,490 +148,6 @@ static int sysfs_kf_bin_mmap(struct sysfs_open_file *of, return battr->mmap(of->file, kobj, battr, vma); } -static void kernfs_vma_open(struct vm_area_struct *vma) -{ - struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); - - if (!of->vm_ops) - return; - - if (!sysfs_get_active(of->sd)) - return; - - if (of->vm_ops->open) - of->vm_ops->open(vma); - - sysfs_put_active(of->sd); -} - -static int kernfs_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); - int ret; - - if (!of->vm_ops) - return VM_FAULT_SIGBUS; - - if (!sysfs_get_active(of->sd)) - return VM_FAULT_SIGBUS; - - ret = VM_FAULT_SIGBUS; - if (of->vm_ops->fault) - ret = of->vm_ops->fault(vma, vmf); - - sysfs_put_active(of->sd); - return ret; -} - -static int kernfs_vma_page_mkwrite(struct vm_area_struct *vma, - struct vm_fault *vmf) -{ - struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); - int ret; - - if (!of->vm_ops) - return VM_FAULT_SIGBUS; - - if (!sysfs_get_active(of->sd)) - return VM_FAULT_SIGBUS; - - ret = 0; - if (of->vm_ops->page_mkwrite) - ret = of->vm_ops->page_mkwrite(vma, vmf); - else - file_update_time(file); - - sysfs_put_active(of->sd); - return ret; -} - -static int kernfs_vma_access(struct vm_area_struct *vma, unsigned long addr, - void *buf, int len, int write) -{ - struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); - int ret; - - if (!of->vm_ops) - return -EINVAL; - - if (!sysfs_get_active(of->sd)) - return -EINVAL; - - ret = -EINVAL; - if (of->vm_ops->access) - ret = of->vm_ops->access(vma, addr, buf, len, write); - - sysfs_put_active(of->sd); - return ret; -} - -#ifdef CONFIG_NUMA -static int kernfs_vma_set_policy(struct vm_area_struct *vma, - struct mempolicy *new) -{ - struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); - int ret; - - if (!of->vm_ops) - return 0; - - if (!sysfs_get_active(of->sd)) - return -EINVAL; - - ret = 0; - if (of->vm_ops->set_policy) - ret = of->vm_ops->set_policy(vma, new); - - sysfs_put_active(of->sd); - return ret; -} - -static struct mempolicy *kernfs_vma_get_policy(struct vm_area_struct *vma, - unsigned long addr) -{ - struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); - struct mempolicy *pol; - - if (!of->vm_ops) - return vma->vm_policy; - - if (!sysfs_get_active(of->sd)) - return vma->vm_policy; - - pol = vma->vm_policy; - if (of->vm_ops->get_policy) - pol = of->vm_ops->get_policy(vma, addr); - - sysfs_put_active(of->sd); - return pol; -} - -static int kernfs_vma_migrate(struct vm_area_struct *vma, - const nodemask_t *from, const nodemask_t *to, - unsigned long flags) -{ - struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); - int ret; - - if (!of->vm_ops) - return 0; - - if (!sysfs_get_active(of->sd)) - return 0; - - ret = 0; - if (of->vm_ops->migrate) - ret = of->vm_ops->migrate(vma, from, to, flags); - - sysfs_put_active(of->sd); - return ret; -} -#endif - -static const struct vm_operations_struct kernfs_vm_ops = { - .open = kernfs_vma_open, - .fault = kernfs_vma_fault, - .page_mkwrite = kernfs_vma_page_mkwrite, - .access = kernfs_vma_access, -#ifdef CONFIG_NUMA - .set_policy = kernfs_vma_set_policy, - .get_policy = kernfs_vma_get_policy, - .migrate = kernfs_vma_migrate, -#endif -}; - -static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct sysfs_open_file *of = sysfs_of(file); - const struct kernfs_ops *ops; - int rc; - - mutex_lock(&of->mutex); - - rc = -ENODEV; - if (!sysfs_get_active(of->sd)) - goto out_unlock; - - ops = kernfs_ops(of->sd); - if (ops->mmap) - rc = ops->mmap(of, vma); - if (rc) - goto out_put; - - /* - * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup() - * to satisfy versions of X which crash if the mmap fails: that - * substitutes a new vm_file, and we don't then want bin_vm_ops. - */ - if (vma->vm_file != file) - goto out_put; - - rc = -EINVAL; - if (of->mmapped && of->vm_ops != vma->vm_ops) - goto out_put; - - /* - * It is not possible to successfully wrap close. - * So error if someone is trying to use close. - */ - rc = -EINVAL; - if (vma->vm_ops && vma->vm_ops->close) - goto out_put; - - rc = 0; - of->mmapped = 1; - of->vm_ops = vma->vm_ops; - vma->vm_ops = &kernfs_vm_ops; -out_put: - sysfs_put_active(of->sd); -out_unlock: - mutex_unlock(&of->mutex); - - return rc; -} - -/** - * sysfs_get_open_dirent - get or create sysfs_open_dirent - * @sd: target sysfs_dirent - * @of: sysfs_open_file for this instance of open - * - * If @sd->s_attr.open exists, increment its reference count; - * otherwise, create one. @of is chained to the files list. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * 0 on success, -errno on failure. - */ -static int sysfs_get_open_dirent(struct sysfs_dirent *sd, - struct sysfs_open_file *of) -{ - struct sysfs_open_dirent *od, *new_od = NULL; - - retry: - mutex_lock(&sysfs_open_file_mutex); - spin_lock_irq(&sysfs_open_dirent_lock); - - if (!sd->s_attr.open && new_od) { - sd->s_attr.open = new_od; - new_od = NULL; - } - - od = sd->s_attr.open; - if (od) { - atomic_inc(&od->refcnt); - list_add_tail(&of->list, &od->files); - } - - spin_unlock_irq(&sysfs_open_dirent_lock); - mutex_unlock(&sysfs_open_file_mutex); - - if (od) { - kfree(new_od); - return 0; - } - - /* not there, initialize a new one and retry */ - new_od = kmalloc(sizeof(*new_od), GFP_KERNEL); - if (!new_od) - return -ENOMEM; - - atomic_set(&new_od->refcnt, 0); - atomic_set(&new_od->event, 1); - init_waitqueue_head(&new_od->poll); - INIT_LIST_HEAD(&new_od->files); - goto retry; -} - -/** - * sysfs_put_open_dirent - put sysfs_open_dirent - * @sd: target sysfs_dirent - * @of: associated sysfs_open_file - * - * Put @sd->s_attr.open and unlink @of from the files list. If - * reference count reaches zero, disassociate and free it. - * - * LOCKING: - * None. - */ -static void sysfs_put_open_dirent(struct sysfs_dirent *sd, - struct sysfs_open_file *of) -{ - struct sysfs_open_dirent *od = sd->s_attr.open; - unsigned long flags; - - mutex_lock(&sysfs_open_file_mutex); - spin_lock_irqsave(&sysfs_open_dirent_lock, flags); - - if (of) - list_del(&of->list); - - if (atomic_dec_and_test(&od->refcnt)) - sd->s_attr.open = NULL; - else - od = NULL; - - spin_unlock_irqrestore(&sysfs_open_dirent_lock, flags); - mutex_unlock(&sysfs_open_file_mutex); - - kfree(od); -} - -static int kernfs_file_open(struct inode *inode, struct file *file) -{ - struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; - const struct kernfs_ops *ops; - struct sysfs_open_file *of; - bool has_read, has_write, has_mmap; - int error = -EACCES; - - if (!sysfs_get_active(attr_sd)) - return -ENODEV; - - ops = kernfs_ops(attr_sd); - - has_read = ops->seq_show || ops->read || ops->mmap; - has_write = ops->write || ops->mmap; - has_mmap = ops->mmap; - - /* check perms and supported operations */ - if ((file->f_mode & FMODE_WRITE) && - (!(inode->i_mode & S_IWUGO) || !has_write)) - goto err_out; - - if ((file->f_mode & FMODE_READ) && - (!(inode->i_mode & S_IRUGO) || !has_read)) - goto err_out; - - /* allocate a sysfs_open_file for the file */ - error = -ENOMEM; - of = kzalloc(sizeof(struct sysfs_open_file), GFP_KERNEL); - if (!of) - goto err_out; - - /* - * The following is done to give a different lockdep key to - * @of->mutex for files which implement mmap. This is a rather - * crude way to avoid false positive lockdep warning around - * mm->mmap_sem - mmap nests @of->mutex under mm->mmap_sem and - * reading /sys/block/sda/trace/act_mask grabs sr_mutex, under - * which mm->mmap_sem nests, while holding @of->mutex. As each - * open file has a separate mutex, it's okay as long as those don't - * happen on the same file. At this point, we can't easily give - * each file a separate locking class. Let's differentiate on - * whether the file has mmap or not for now. - */ - if (has_mmap) - mutex_init(&of->mutex); - else - mutex_init(&of->mutex); - - of->sd = attr_sd; - of->file = file; - - /* - * Always instantiate seq_file even if read access doesn't use - * seq_file or is not requested. This unifies private data access - * and readable regular files are the vast majority anyway. - */ - if (ops->seq_show) - error = seq_open(file, &kernfs_seq_ops); - else - error = seq_open(file, NULL); - if (error) - goto err_free; - - ((struct seq_file *)file->private_data)->private = of; - - /* seq_file clears PWRITE unconditionally, restore it if WRITE */ - if (file->f_mode & FMODE_WRITE) - file->f_mode |= FMODE_PWRITE; - - /* make sure we have open dirent struct */ - error = sysfs_get_open_dirent(attr_sd, of); - if (error) - goto err_close; - - /* open succeeded, put active references */ - sysfs_put_active(attr_sd); - return 0; - -err_close: - seq_release(inode, file); -err_free: - kfree(of); -err_out: - sysfs_put_active(attr_sd); - return error; -} - -static int kernfs_file_release(struct inode *inode, struct file *filp) -{ - struct sysfs_dirent *sd = filp->f_path.dentry->d_fsdata; - struct sysfs_open_file *of = sysfs_of(filp); - - sysfs_put_open_dirent(sd, of); - seq_release(inode, filp); - kfree(of); - - return 0; -} - -void sysfs_unmap_bin_file(struct sysfs_dirent *sd) -{ - struct sysfs_open_dirent *od; - struct sysfs_open_file *of; - - if (!(sd->s_flags & SYSFS_FLAG_HAS_MMAP)) - return; - - spin_lock_irq(&sysfs_open_dirent_lock); - od = sd->s_attr.open; - if (od) - atomic_inc(&od->refcnt); - spin_unlock_irq(&sysfs_open_dirent_lock); - if (!od) - return; - - mutex_lock(&sysfs_open_file_mutex); - list_for_each_entry(of, &od->files, list) { - struct inode *inode = file_inode(of->file); - unmap_mapping_range(inode->i_mapping, 0, 0, 1); - } - mutex_unlock(&sysfs_open_file_mutex); - - sysfs_put_open_dirent(sd, NULL); -} - -/* Sysfs attribute files are pollable. The idea is that you read - * the content and then you use 'poll' or 'select' to wait for - * the content to change. When the content changes (assuming the - * manager for the kobject supports notification), poll will - * return POLLERR|POLLPRI, and select will return the fd whether - * it is waiting for read, write, or exceptions. - * Once poll/select indicates that the value has changed, you - * need to close and re-open the file, or seek to 0 and read again. - * Reminder: this only works for attributes which actively support - * it, and it is not possible to test an attribute from userspace - * to see if it supports poll (Neither 'poll' nor 'select' return - * an appropriate error code). When in doubt, set a suitable timeout value. - */ -static unsigned int kernfs_file_poll(struct file *filp, poll_table *wait) -{ - struct sysfs_open_file *of = sysfs_of(filp); - struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; - struct sysfs_open_dirent *od = attr_sd->s_attr.open; - - /* need parent for the kobj, grab both */ - if (!sysfs_get_active(attr_sd)) - goto trigger; - - poll_wait(filp, &od->poll, wait); - - sysfs_put_active(attr_sd); - - if (of->event != atomic_read(&od->event)) - goto trigger; - - return DEFAULT_POLLMASK; - - trigger: - return DEFAULT_POLLMASK|POLLERR|POLLPRI; -} - -/** - * kernfs_notify - notify a kernfs file - * @sd: file to notify - * - * Notify @sd such that poll(2) on @sd wakes up. - */ -void kernfs_notify(struct sysfs_dirent *sd) -{ - struct sysfs_open_dirent *od; - unsigned long flags; - - spin_lock_irqsave(&sysfs_open_dirent_lock, flags); - - if (!WARN_ON(sysfs_type(sd) != SYSFS_KOBJ_ATTR)) { - od = sd->s_attr.open; - if (od) { - atomic_inc(&od->event); - wake_up_interruptible(&od->poll); - } - } - - spin_unlock_irqrestore(&sysfs_open_dirent_lock, flags); -} -EXPORT_SYMBOL_GPL(kernfs_notify); - void sysfs_notify(struct kobject *k, const char *dir, const char *attr) { struct sysfs_dirent *sd = k->sd, *tmp; @@ -898,16 +170,6 @@ void sysfs_notify(struct kobject *k, const char *dir, const char *attr) } EXPORT_SYMBOL_GPL(sysfs_notify); -const struct file_operations kernfs_file_operations = { - .read = kernfs_file_read, - .write = kernfs_file_write, - .llseek = generic_file_llseek, - .mmap = kernfs_file_mmap, - .open = kernfs_file_open, - .release = kernfs_file_release, - .poll = kernfs_file_poll, -}; - static const struct kernfs_ops sysfs_file_kfops_empty = { }; @@ -996,68 +258,6 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, return 0; } -/** - * kernfs_create_file_ns_key - create a file - * @parent: directory to create the file in - * @name: name of the file - * @mode: mode of the file - * @size: size of the file - * @ops: kernfs operations for the file - * @priv: private data for the file - * @ns: optional namespace tag of the file - * @key: lockdep key for the file's active_ref, %NULL to disable lockdep - * - * Returns the created node on success, ERR_PTR() value on error. - */ -struct sysfs_dirent *kernfs_create_file_ns_key(struct sysfs_dirent *parent, - const char *name, - umode_t mode, loff_t size, - const struct kernfs_ops *ops, - void *priv, const void *ns, - struct lock_class_key *key) -{ - struct sysfs_addrm_cxt acxt; - struct sysfs_dirent *sd; - int rc; - - sd = sysfs_new_dirent(name, (mode & S_IALLUGO) | S_IFREG, - SYSFS_KOBJ_ATTR); - if (!sd) - return ERR_PTR(-ENOMEM); - - sd->s_attr.ops = ops; - sd->s_attr.size = size; - sd->s_ns = ns; - sd->priv = priv; - -#ifdef CONFIG_DEBUG_LOCK_ALLOC - if (key) { - lockdep_init_map(&sd->dep_map, "s_active", key, 0); - sd->s_flags |= SYSFS_FLAG_LOCKDEP; - } -#endif - - /* - * sd->s_attr.ops is accesible only while holding active ref. We - * need to know whether some ops are implemented outside active - * ref. Cache their existence in flags. - */ - if (ops->seq_show) - sd->s_flags |= SYSFS_FLAG_HAS_SEQ_SHOW; - if (ops->mmap) - sd->s_flags |= SYSFS_FLAG_HAS_MMAP; - - sysfs_addrm_start(&acxt); - rc = sysfs_add_one(&acxt, sd, parent); - sysfs_addrm_finish(&acxt); - - if (rc) { - kernfs_put(sd); - return ERR_PTR(rc); - } - return sd; -} - int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, bool is_bin) { diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 972b4a4..4b8b60d 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -41,15 +41,11 @@ void sysfs_warn_dup(struct sysfs_dirent *parent, const char *name); /* * file.c */ -extern const struct file_operations kernfs_file_operations; - int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, bool is_bin); - int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, const struct attribute *attr, bool is_bin, umode_t amode, const void *ns); -void sysfs_unmap_bin_file(struct sysfs_dirent *sd); /* * symlink.c -- cgit v1.1 From 2072f1afddfe9fa00c1c0c79f8986707324ec65b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:35 -0500 Subject: sysfs, kernfs: move symlink core code to fs/kernfs/symlink.c Move core symlink code to fs/kernfs/symlink.c. fs/sysfs/symlink.c now only contains sysfs wrappers around kernfs interfaces. The respective declarations in fs/sysfs/sysfs.h are moved to fs/kernfs/kernfs-internal.h. This is pure relocation. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/kernfs-internal.h | 5 ++ fs/kernfs/symlink.c | 139 ++++++++++++++++++++++++++++++++++++++++++++ fs/sysfs/symlink.c | 137 ------------------------------------------- fs/sysfs/sysfs.h | 1 - 4 files changed, 144 insertions(+), 138 deletions(-) diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 38e3a16..62ae35f 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -149,4 +149,9 @@ extern const struct file_operations kernfs_file_operations; void sysfs_unmap_bin_file(struct sysfs_dirent *sd); +/* + * symlink.c + */ +extern const struct inode_operations sysfs_symlink_inode_operations; + #endif /* __KERNFS_INTERNAL_H */ diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 2578715..af3570b 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -7,3 +7,142 @@ * * This file is released under the GPLv2. */ + +#include +#include +#include + +#include "kernfs-internal.h" + +/** + * kernfs_create_link - create a symlink + * @parent: directory to create the symlink in + * @name: name of the symlink + * @target: target node for the symlink to point to + * + * Returns the created node on success, ERR_PTR() value on error. + */ +struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, + const char *name, + struct sysfs_dirent *target) +{ + struct sysfs_dirent *sd; + struct sysfs_addrm_cxt acxt; + int error; + + sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); + if (!sd) + return ERR_PTR(-ENOMEM); + + if (parent->s_flags & SYSFS_FLAG_NS) + sd->s_ns = target->s_ns; + sd->s_symlink.target_sd = target; + kernfs_get(target); /* ref owned by symlink */ + + sysfs_addrm_start(&acxt); + error = sysfs_add_one(&acxt, sd, parent); + sysfs_addrm_finish(&acxt); + + if (!error) + return sd; + + kernfs_put(sd); + return ERR_PTR(error); +} + +static int sysfs_get_target_path(struct sysfs_dirent *parent_sd, + struct sysfs_dirent *target_sd, char *path) +{ + struct sysfs_dirent *base, *sd; + char *s = path; + int len = 0; + + /* go up to the root, stop at the base */ + base = parent_sd; + while (base->s_parent) { + sd = target_sd->s_parent; + while (sd->s_parent && base != sd) + sd = sd->s_parent; + + if (base == sd) + break; + + strcpy(s, "../"); + s += 3; + base = base->s_parent; + } + + /* determine end of target string for reverse fillup */ + sd = target_sd; + while (sd->s_parent && sd != base) { + len += strlen(sd->s_name) + 1; + sd = sd->s_parent; + } + + /* check limits */ + if (len < 2) + return -EINVAL; + len--; + if ((s - path) + len > PATH_MAX) + return -ENAMETOOLONG; + + /* reverse fillup of target string from target to base */ + sd = target_sd; + while (sd->s_parent && sd != base) { + int slen = strlen(sd->s_name); + + len -= slen; + strncpy(s + len, sd->s_name, slen); + if (len) + s[--len] = '/'; + + sd = sd->s_parent; + } + + return 0; +} + +static int sysfs_getlink(struct dentry *dentry, char *path) +{ + struct sysfs_dirent *sd = dentry->d_fsdata; + struct sysfs_dirent *parent_sd = sd->s_parent; + struct sysfs_dirent *target_sd = sd->s_symlink.target_sd; + int error; + + mutex_lock(&sysfs_mutex); + error = sysfs_get_target_path(parent_sd, target_sd, path); + mutex_unlock(&sysfs_mutex); + + return error; +} + +static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + int error = -ENOMEM; + unsigned long page = get_zeroed_page(GFP_KERNEL); + if (page) { + error = sysfs_getlink(dentry, (char *) page); + if (error < 0) + free_page((unsigned long)page); + } + nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); + return NULL; +} + +static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, + void *cookie) +{ + char *page = nd_get_link(nd); + if (!IS_ERR(page)) + free_page((unsigned long)page); +} + +const struct inode_operations sysfs_symlink_inode_operations = { + .setxattr = sysfs_setxattr, + .readlink = generic_readlink, + .follow_link = sysfs_follow_link, + .put_link = sysfs_put_link, + .setattr = sysfs_setattr, + .getattr = sysfs_getattr, + .permission = sysfs_permission, +}; diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index b137aa3..6797c9c 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -11,53 +11,13 @@ */ #include -#include -#include #include #include -#include #include #include #include "sysfs.h" -/** - * kernfs_create_link - create a symlink - * @parent: directory to create the symlink in - * @name: name of the symlink - * @target: target node for the symlink to point to - * - * Returns the created node on success, ERR_PTR() value on error. - */ -struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, - const char *name, - struct sysfs_dirent *target) -{ - struct sysfs_dirent *sd; - struct sysfs_addrm_cxt acxt; - int error; - - sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); - if (!sd) - return ERR_PTR(-ENOMEM); - - if (parent->s_flags & SYSFS_FLAG_NS) - sd->s_ns = target->s_ns; - sd->s_symlink.target_sd = target; - kernfs_get(target); /* ref owned by symlink */ - - sysfs_addrm_start(&acxt); - error = sysfs_add_one(&acxt, sd, parent); - sysfs_addrm_finish(&acxt); - - if (!error) - return sd; - - kernfs_put(sd); - return ERR_PTR(error); -} - - static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, struct kobject *target, const char *name, int warn) @@ -235,100 +195,3 @@ out: return result; } EXPORT_SYMBOL_GPL(sysfs_rename_link_ns); - -static int sysfs_get_target_path(struct sysfs_dirent *parent_sd, - struct sysfs_dirent *target_sd, char *path) -{ - struct sysfs_dirent *base, *sd; - char *s = path; - int len = 0; - - /* go up to the root, stop at the base */ - base = parent_sd; - while (base->s_parent) { - sd = target_sd->s_parent; - while (sd->s_parent && base != sd) - sd = sd->s_parent; - - if (base == sd) - break; - - strcpy(s, "../"); - s += 3; - base = base->s_parent; - } - - /* determine end of target string for reverse fillup */ - sd = target_sd; - while (sd->s_parent && sd != base) { - len += strlen(sd->s_name) + 1; - sd = sd->s_parent; - } - - /* check limits */ - if (len < 2) - return -EINVAL; - len--; - if ((s - path) + len > PATH_MAX) - return -ENAMETOOLONG; - - /* reverse fillup of target string from target to base */ - sd = target_sd; - while (sd->s_parent && sd != base) { - int slen = strlen(sd->s_name); - - len -= slen; - strncpy(s + len, sd->s_name, slen); - if (len) - s[--len] = '/'; - - sd = sd->s_parent; - } - - return 0; -} - -static int sysfs_getlink(struct dentry *dentry, char *path) -{ - struct sysfs_dirent *sd = dentry->d_fsdata; - struct sysfs_dirent *parent_sd = sd->s_parent; - struct sysfs_dirent *target_sd = sd->s_symlink.target_sd; - int error; - - mutex_lock(&sysfs_mutex); - error = sysfs_get_target_path(parent_sd, target_sd, path); - mutex_unlock(&sysfs_mutex); - - return error; -} - -static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) -{ - int error = -ENOMEM; - unsigned long page = get_zeroed_page(GFP_KERNEL); - if (page) { - error = sysfs_getlink(dentry, (char *) page); - if (error < 0) - free_page((unsigned long)page); - } - nd_set_link(nd, error ? ERR_PTR(error) : (char *)page); - return NULL; -} - -static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) -{ - char *page = nd_get_link(nd); - if (!IS_ERR(page)) - free_page((unsigned long)page); -} - -const struct inode_operations sysfs_symlink_inode_operations = { - .setxattr = sysfs_setxattr, - .readlink = generic_readlink, - .follow_link = sysfs_follow_link, - .put_link = sysfs_put_link, - .setattr = sysfs_setattr, - .getattr = sysfs_getattr, - .permission = sysfs_permission, -}; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 4b8b60d..6d0dcea 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -50,7 +50,6 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, /* * symlink.c */ -extern const struct inode_operations sysfs_symlink_inode_operations; int sysfs_create_link_sd(struct sysfs_dirent *sd, struct kobject *target, const char *name); -- cgit v1.1 From ccc532dc12af9dd95dc1cd13e9a6d5f8f08c0842 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:36 -0500 Subject: sysfs, kernfs: drop unused params from sysfs_fill_super() sysfs_fill_super() takes three params - @sb, @data and @silent - but uses only @sb. Drop the latter two. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/mount.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 852d115..21070c2 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -41,7 +41,7 @@ struct sysfs_dirent sysfs_root = { .s_ino = 1, }; -static int sysfs_fill_super(struct super_block *sb, void *data, int silent) +static int sysfs_fill_super(struct super_block *sb) { struct inode *inode; struct dentry *root; @@ -123,7 +123,7 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, if (IS_ERR(sb)) return ERR_CAST(sb); if (!sb->s_root) { - error = sysfs_fill_super(sb, data, flags & MS_SILENT ? 1 : 0); + error = sysfs_fill_super(sb); if (error) { deactivate_locked_super(sb); return ERR_PTR(error); -- cgit v1.1 From 51a35e9fd0f229d2f84455ee7e85a5d30fa35594 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:37 -0500 Subject: sysfs, kernfs: make sysfs_super_info->ns const Add const qualifier to sysfs_super_info->ns so that it's consistent with other namespace tag usages in sysfs. Because kobject doesn't use const qualifier for namespace tags, this ends up requiring an explicit cast to drop const qualifier in free_sysfs_super_info(). Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/mount.c | 2 +- fs/sysfs/sysfs.h | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 21070c2..fcbe5e8 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -92,7 +92,7 @@ static int sysfs_set_super(struct super_block *sb, void *data) static void free_sysfs_super_info(struct sysfs_super_info *info) { - kobj_ns_drop(KOBJ_NS_TYPE_NET, info->ns); + kobj_ns_drop(KOBJ_NS_TYPE_NET, (void *)info->ns); kfree(info); } diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 6d0dcea..ce97907 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -18,14 +18,14 @@ * mount.c */ -/* - * Each sb is associated with one namespace tag, currently the network - * namespace of the task which mounted this sysfs instance. If multiple - * tags become necessary, make the following an array and compare - * sysfs_dirent tag against every entry. - */ struct sysfs_super_info { - void *ns; + /* + * Each sb is associated with one namespace tag, currently the network + * namespace of the task which mounted this sysfs instance. If multiple + * tags become necessary, make the following an array and compare + * sysfs_dirent tag against every entry. + */ + const void *ns; }; #define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info)) extern struct sysfs_dirent sysfs_root; -- cgit v1.1 From 9e30cc9595303b27b48be49b7bcd4d0679e34253 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:38 -0500 Subject: sysfs, kernfs: no need to kern_mount() sysfs from sysfs_init() It has been very long since sysfs depended on vfs to keep track of internal states and whether sysfs is mounted or not doesn't make any difference to sysfs's internal operation. In addition to init and filesystem type registration, sysfs_init() invokes kern_mount() to create in-kernel mount of sysfs. This internal mounting doesn't server any purpose anymore. Remove it. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/mount.c | 23 +++++++---------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index fcbe5e8..0c80f03 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -24,7 +24,6 @@ #include "sysfs.h" -static struct vfsmount *sysfs_mnt; struct kmem_cache *sysfs_dir_cachep; static const struct super_operations sysfs_ops = { @@ -153,34 +152,26 @@ static struct file_system_type sysfs_fs_type = { int __init sysfs_init(void) { - int err = -ENOMEM; + int err; sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache", sizeof(struct sysfs_dirent), 0, 0, NULL); if (!sysfs_dir_cachep) - goto out; + return -ENOMEM; err = sysfs_inode_init(); if (err) goto out_err; err = register_filesystem(&sysfs_fs_type); - if (!err) { - sysfs_mnt = kern_mount(&sysfs_fs_type); - if (IS_ERR(sysfs_mnt)) { - printk(KERN_ERR "sysfs: could not mount!\n"); - err = PTR_ERR(sysfs_mnt); - sysfs_mnt = NULL; - unregister_filesystem(&sysfs_fs_type); - goto out_err; - } - } else + if (err) goto out_err; -out: - return err; + + return 0; + out_err: kmem_cache_destroy(sysfs_dir_cachep); sysfs_dir_cachep = NULL; - goto out; + return err; } -- cgit v1.1 From 061447a496b915f1dc8f8c645c6825f856d2bbac Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:39 -0500 Subject: sysfs, kernfs: introduce sysfs_root_sd Currently, it's assumed that there's a single kernfs hierarchy in the system anchored at sysfs_root which is defined as a global struct. To allow other users of kernfs, this will be made dynamic. Introduce a new global variable sysfs_root_sd which points to &sysfs_root and convert all &sysfs_root users. This patch doesn't introduce any behavior difference. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/dir.c | 4 ++-- fs/sysfs/mount.c | 8 +++++--- fs/sysfs/symlink.c | 6 +++--- fs/sysfs/sysfs.h | 2 +- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index e5c4e71..2fea501 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -68,7 +68,7 @@ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) if (kobj->parent) parent_sd = kobj->parent->sd; else - parent_sd = &sysfs_root; + parent_sd = sysfs_root_sd; if (!parent_sd) return -ENOENT; @@ -134,7 +134,7 @@ int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, BUG_ON(!sd->s_parent); new_parent_sd = new_parent_kobj && new_parent_kobj->sd ? - new_parent_kobj->sd : &sysfs_root; + new_parent_kobj->sd : sysfs_root_sd; return kernfs_rename_ns(sd, new_parent_sd, sd->s_name, new_ns); } diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 0c80f03..7cbd1fc 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -32,7 +32,7 @@ static const struct super_operations sysfs_ops = { .evict_inode = sysfs_evict_inode, }; -struct sysfs_dirent sysfs_root = { +static struct sysfs_dirent sysfs_root = { .s_name = "", .s_count = ATOMIC_INIT(1), .s_flags = SYSFS_DIR, @@ -40,6 +40,8 @@ struct sysfs_dirent sysfs_root = { .s_ino = 1, }; +struct sysfs_dirent *sysfs_root_sd = &sysfs_root; + static int sysfs_fill_super(struct super_block *sb) { struct inode *inode; @@ -53,7 +55,7 @@ static int sysfs_fill_super(struct super_block *sb) /* get root inode, initialize and unlock it */ mutex_lock(&sysfs_mutex); - inode = sysfs_get_inode(sb, &sysfs_root); + inode = sysfs_get_inode(sb, sysfs_root_sd); mutex_unlock(&sysfs_mutex); if (!inode) { pr_debug("sysfs: could not get root inode\n"); @@ -66,7 +68,7 @@ static int sysfs_fill_super(struct super_block *sb) pr_debug("%s: could not get root dentry!\n", __func__); return -ENOMEM; } - root->d_fsdata = &sysfs_root; + root->d_fsdata = sysfs_root_sd; sb->s_root = root; sb->s_d_op = &sysfs_dentry_ops; return 0; diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 6797c9c..62f0e01 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -70,7 +70,7 @@ static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, struct sysfs_dirent *parent_sd = NULL; if (!kobj) - parent_sd = &sysfs_root; + parent_sd = sysfs_root_sd; else parent_sd = kobj->sd; @@ -144,7 +144,7 @@ void sysfs_remove_link(struct kobject *kobj, const char *name) struct sysfs_dirent *parent_sd = NULL; if (!kobj) - parent_sd = &sysfs_root; + parent_sd = sysfs_root_sd; else parent_sd = kobj->sd; @@ -170,7 +170,7 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, int result; if (!kobj) - parent_sd = &sysfs_root; + parent_sd = sysfs_root_sd; else parent_sd = kobj->sd; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index ce97907..2b217ce 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -28,7 +28,7 @@ struct sysfs_super_info { const void *ns; }; #define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info)) -extern struct sysfs_dirent sysfs_root; +extern struct sysfs_dirent *sysfs_root_sd; extern struct kmem_cache *sysfs_dir_cachep; /* -- cgit v1.1 From ba7443bc656e5236c316b2acacc8b551f872910f Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:40 -0500 Subject: sysfs, kernfs: implement kernfs_create/destroy_root() There currently is single kernfs hierarchy in the whole system which is used for sysfs. kernfs needs to support multiple hierarchies to allow other users. This patch introduces struct kernfs_root which serves as the root of each kernfs hierarchy and implements kernfs_create/destroy_root(). * Each kernfs_root is associated with a root sd (sysfs_dentry). The root is freed when the root sd is released and kernfs_destory_root() simply invokes kernfs_remove() on the root sd. sysfs_remove_one() is updated to handle release of the root sd. Note that ps_iattr update in sysfs_remove_one() is trivially updated for readability. * Root sd's are now dynamically allocated using sysfs_new_dirent(). Update sysfs_alloc_ino() so that it gives out ino from 1 so that the root sd still gets ino 1. * While kernfs currently only points to the root sd, it'll soon grow fields which are specific to each hierarchy. As determining a given sd's root will be necessary, sd->s_dir.root is added. This backlink fits better as a separate field in sd; however, sd->s_dir is inside union with space to spare, so use it to save space and provide kernfs_root() accessor to determine the root sd. * As hierarchies may be destroyed now, each mount needs to hold onto the hierarchy it's attached to. Update sysfs_fill_super() and sysfs_kill_sb() so that they get and put the kernfs_root respectively. * sysfs_root is replaced with kernfs_root which is dynamically created by invoking kernfs_create_root() from sysfs_init(). This patch doesn't introduce any visible behavior changes. v2: kernfs_create_root() forgot to set @sd->priv. Fixed. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 71 +++++++++++++++++++++++++++++++++++++++------ fs/kernfs/kernfs-internal.h | 20 +++++++++++++ fs/sysfs/mount.c | 29 +++++++++++------- include/linux/kernfs.h | 13 +++++++++ 4 files changed, 113 insertions(+), 20 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index a4ca4de..246740a 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -211,7 +211,7 @@ static int sysfs_alloc_ino(unsigned int *pino) retry: spin_lock(&sysfs_ino_lock); - rc = ida_get_new_above(&sysfs_ino_ida, 2, &ino); + rc = ida_get_new_above(&sysfs_ino_ida, 1, &ino); spin_unlock(&sysfs_ino_lock); if (rc == -EAGAIN) { @@ -253,9 +253,11 @@ EXPORT_SYMBOL_GPL(kernfs_get); void kernfs_put(struct sysfs_dirent *sd) { struct sysfs_dirent *parent_sd; + struct kernfs_root *root; if (!sd || !atomic_dec_and_test(&sd->s_count)) return; + root = kernfs_root(sd); repeat: /* Moving/renaming is always done while holding reference. * sd->s_parent won't change beneath us. @@ -278,8 +280,13 @@ void kernfs_put(struct sysfs_dirent *sd) kmem_cache_free(sysfs_dir_cachep, sd); sd = parent_sd; - if (sd && atomic_dec_and_test(&sd->s_count)) - goto repeat; + if (sd) { + if (atomic_dec_and_test(&sd->s_count)) + goto repeat; + } else { + /* just released the root sd, free @root too */ + kfree(root); + } } EXPORT_SYMBOL_GPL(kernfs_put); @@ -493,13 +500,15 @@ static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, if (sd->s_flags & SYSFS_FLAG_REMOVED) return; - sysfs_unlink_sibling(sd); + if (sd->s_parent) { + sysfs_unlink_sibling(sd); - /* Update timestamps on the parent */ - ps_iattr = sd->s_parent->s_iattr; - if (ps_iattr) { - struct iattr *ps_iattrs = &ps_iattr->ia_iattr; - ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; + /* Update timestamps on the parent */ + ps_iattr = sd->s_parent->s_iattr; + if (ps_iattr) { + ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; + ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; + } } sd->s_flags |= SYSFS_FLAG_REMOVED; @@ -604,6 +613,49 @@ struct sysfs_dirent *kernfs_find_and_get_ns(struct sysfs_dirent *parent, EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); /** + * kernfs_create_root - create a new kernfs hierarchy + * @priv: opaque data associated with the new directory + * + * Returns the root of the new hierarchy on success, ERR_PTR() value on + * failure. + */ +struct kernfs_root *kernfs_create_root(void *priv) +{ + struct kernfs_root *root; + struct sysfs_dirent *sd; + + root = kzalloc(sizeof(*root), GFP_KERNEL); + if (!root) + return ERR_PTR(-ENOMEM); + + sd = sysfs_new_dirent("", S_IFDIR | S_IRUGO | S_IXUGO, SYSFS_DIR); + if (!sd) { + kfree(root); + return ERR_PTR(-ENOMEM); + } + + sd->s_flags &= ~SYSFS_FLAG_REMOVED; + sd->priv = priv; + sd->s_dir.root = root; + + root->sd = sd; + + return root; +} + +/** + * kernfs_destroy_root - destroy a kernfs hierarchy + * @root: root of the hierarchy to destroy + * + * Destroy the hierarchy anchored at @root by removing all existing + * directories and destroying @root. + */ +void kernfs_destroy_root(struct kernfs_root *root) +{ + kernfs_remove(root->sd); /* will also free @root */ +} + +/** * kernfs_create_dir_ns - create a directory * @parent: parent in which to create a new directory * @name: name of the new directory @@ -626,6 +678,7 @@ struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, if (!sd) return ERR_PTR(-ENOMEM); + sd->s_dir.root = parent->s_dir.root; sd->s_ns = ns; sd->priv = priv; diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 62ae35f..7dfe062 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -25,6 +25,12 @@ struct sysfs_elem_dir { unsigned long subdirs; /* children rbtree starts here and goes through sd->s_rb */ struct rb_root children; + + /* + * The kernfs hierarchy this directory belongs to. This fits + * better directly in sysfs_dirent but is here to save space. + */ + struct kernfs_root *root; }; struct sysfs_elem_symlink { @@ -104,6 +110,20 @@ static inline unsigned int sysfs_type(struct sysfs_dirent *sd) return sd->s_flags & SYSFS_TYPE_MASK; } +/** + * kernfs_root - find out the kernfs_root a sysfs_dirent belongs to + * @sd: sysfs_dirent of interest + * + * Return the kernfs_root @sd belongs to. + */ +static inline struct kernfs_root *kernfs_root(struct sysfs_dirent *sd) +{ + /* if parent exists, it's always a dir; otherwise, @sd is a dir */ + if (sd->s_parent) + sd = sd->s_parent; + return sd->s_dir.root; +} + /* * Context structure to be used while adding/removing nodes. */ diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 7cbd1fc..0b5661b 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -32,15 +32,8 @@ static const struct super_operations sysfs_ops = { .evict_inode = sysfs_evict_inode, }; -static struct sysfs_dirent sysfs_root = { - .s_name = "", - .s_count = ATOMIC_INIT(1), - .s_flags = SYSFS_DIR, - .s_mode = S_IFDIR | S_IRUGO | S_IXUGO, - .s_ino = 1, -}; - -struct sysfs_dirent *sysfs_root_sd = &sysfs_root; +static struct kernfs_root *sysfs_root; +struct sysfs_dirent *sysfs_root_sd; static int sysfs_fill_super(struct super_block *sb) { @@ -68,6 +61,7 @@ static int sysfs_fill_super(struct super_block *sb) pr_debug("%s: could not get root dentry!\n", __func__); return -ENOMEM; } + kernfs_get(sysfs_root_sd); root->d_fsdata = sysfs_root_sd; sb->s_root = root; sb->s_d_op = &sysfs_dentry_ops; @@ -138,11 +132,15 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, static void sysfs_kill_sb(struct super_block *sb) { struct sysfs_super_info *info = sysfs_info(sb); - /* Remove the superblock from fs_supers/s_instances + struct sysfs_dirent *root_sd = sb->s_root->d_fsdata; + + /* + * Remove the superblock from fs_supers/s_instances * so we can't find it, before freeing sysfs_super_info. */ kill_anon_super(sb); free_sysfs_super_info(info); + kernfs_put(root_sd); } static struct file_system_type sysfs_fs_type = { @@ -166,12 +164,21 @@ int __init sysfs_init(void) if (err) goto out_err; + sysfs_root = kernfs_create_root(NULL); + if (IS_ERR(sysfs_root)) { + err = PTR_ERR(sysfs_root); + goto out_err; + } + sysfs_root_sd = sysfs_root->sd; + err = register_filesystem(&sysfs_fs_type); if (err) - goto out_err; + goto out_destroy_root; return 0; +out_destroy_root: + kernfs_destroy_root(sysfs_root); out_err: kmem_cache_destroy(sysfs_dir_cachep); sysfs_dir_cachep = NULL; diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index fd8f574..f75548b 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -20,6 +20,11 @@ struct vm_area_struct; struct sysfs_dirent; +struct kernfs_root { + /* published fields */ + struct sysfs_dirent *sd; +}; + struct sysfs_open_file { /* published fields */ struct sysfs_dirent *sd; @@ -76,6 +81,9 @@ struct sysfs_dirent *kernfs_find_and_get_ns(struct sysfs_dirent *parent, void kernfs_get(struct sysfs_dirent *sd); void kernfs_put(struct sysfs_dirent *sd); +struct kernfs_root *kernfs_create_root(void *priv); +void kernfs_destroy_root(struct kernfs_root *root); + struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, const void *ns); @@ -107,6 +115,11 @@ kernfs_find_and_get_ns(struct sysfs_dirent *parent, const char *name, static inline void kernfs_get(struct sysfs_dirent *sd) { } static inline void kernfs_put(struct sysfs_dirent *sd) { } +static inline struct kernfs_root *kernfs_create_root(void *priv) +{ return ERR_PTR(-ENOSYS); } + +static inline void kernfs_destroy_root(struct kernfs_root *root) { } + static inline struct sysfs_dirent * kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, const void *ns) -- cgit v1.1 From bc755553df9ab33f389c1a0a8bd0b4f4646e80ef Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:41 -0500 Subject: sysfs, kernfs: make inode number ida per kernfs_root kernfs is being updated to allow multiple sysfs_dirent hierarchies so that it can also be used by other users. Currently, inode number is allocated using a global ida, sysfs_ino_ida; however, inos for different hierarchies should be handled separately. This patch makes ino allocation per kernfs_root. sysfs_ino_ida is replaced by kernfs_root->ino_ida and sysfs_new_dirent() is updated to take @root and allocate ino from it. ida_simple_get/remove() are used instead of sysfs_ino_lock and sysfs_alloc/free_ino(). Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 47 +++++++++++++-------------------------------- fs/kernfs/file.c | 4 ++-- fs/kernfs/kernfs-internal.h | 3 ++- fs/kernfs/symlink.c | 3 ++- include/linux/kernfs.h | 4 ++++ 5 files changed, 23 insertions(+), 38 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 246740a..eaffa83 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -21,9 +21,6 @@ DEFINE_MUTEX(sysfs_mutex); #define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb) -static DEFINE_SPINLOCK(sysfs_ino_lock); -static DEFINE_IDA(sysfs_ino_ida); - /** * sysfs_name_hash * @name: Null terminated string to hash @@ -205,32 +202,6 @@ static void sysfs_deactivate(struct sysfs_dirent *sd) rwsem_release(&sd->dep_map, 1, _RET_IP_); } -static int sysfs_alloc_ino(unsigned int *pino) -{ - int ino, rc; - - retry: - spin_lock(&sysfs_ino_lock); - rc = ida_get_new_above(&sysfs_ino_ida, 1, &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(unsigned int ino) -{ - spin_lock(&sysfs_ino_lock); - ida_remove(&sysfs_ino_ida, ino); - spin_unlock(&sysfs_ino_lock); -} - /** * kernfs_get - get a reference count on a sysfs_dirent * @sd: the target sysfs_dirent @@ -276,7 +247,7 @@ void kernfs_put(struct sysfs_dirent *sd) security_release_secctx(sd->s_iattr->ia_secdata, sd->s_iattr->ia_secdata_len); kfree(sd->s_iattr); - sysfs_free_ino(sd->s_ino); + ida_simple_remove(&root->ino_ida, sd->s_ino); kmem_cache_free(sysfs_dir_cachep, sd); sd = parent_sd; @@ -285,6 +256,7 @@ void kernfs_put(struct sysfs_dirent *sd) goto repeat; } else { /* just released the root sd, free @root too */ + ida_destroy(&root->ino_ida); kfree(root); } } @@ -360,10 +332,12 @@ const struct dentry_operations sysfs_dentry_ops = { .d_release = sysfs_dentry_release, }; -struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) +struct sysfs_dirent *sysfs_new_dirent(struct kernfs_root *root, + const char *name, umode_t mode, int type) { char *dup_name = NULL; struct sysfs_dirent *sd; + int ret; if (type & SYSFS_COPY_NAME) { name = dup_name = kstrdup(name, GFP_KERNEL); @@ -375,8 +349,10 @@ struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) if (!sd) goto err_out1; - if (sysfs_alloc_ino(&sd->s_ino)) + ret = ida_simple_get(&root->ino_ida, 1, 0, GFP_KERNEL); + if (ret < 0) goto err_out2; + sd->s_ino = ret; atomic_set(&sd->s_count, 1); atomic_set(&sd->s_active, 0); @@ -628,8 +604,11 @@ struct kernfs_root *kernfs_create_root(void *priv) if (!root) return ERR_PTR(-ENOMEM); - sd = sysfs_new_dirent("", S_IFDIR | S_IRUGO | S_IXUGO, SYSFS_DIR); + ida_init(&root->ino_ida); + + sd = sysfs_new_dirent(root, "", S_IFDIR | S_IRUGO | S_IXUGO, SYSFS_DIR); if (!sd) { + ida_destroy(&root->ino_ida); kfree(root); return ERR_PTR(-ENOMEM); } @@ -674,7 +653,7 @@ struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, int rc; /* allocate */ - sd = sysfs_new_dirent(name, mode, SYSFS_DIR); + sd = sysfs_new_dirent(kernfs_root(parent), name, mode, SYSFS_DIR); if (!sd) return ERR_PTR(-ENOMEM); diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index fa172e8..990c97f 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -775,8 +775,8 @@ struct sysfs_dirent *kernfs_create_file_ns_key(struct sysfs_dirent *parent, struct sysfs_dirent *sd; int rc; - sd = sysfs_new_dirent(name, (mode & S_IALLUGO) | S_IFREG, - SYSFS_KOBJ_ATTR); + sd = sysfs_new_dirent(kernfs_root(parent), name, + (mode & S_IALLUGO) | S_IFREG, SYSFS_KOBJ_ATTR); if (!sd) return ERR_PTR(-ENOMEM); diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 7dfe062..466943d 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -160,7 +160,8 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt); int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd); void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); -struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type); +struct sysfs_dirent *sysfs_new_dirent(struct kernfs_root *root, + const char *name, umode_t mode, int type); /* * file.c diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index af3570b..004c164 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -30,7 +30,8 @@ struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, struct sysfs_addrm_cxt acxt; int error; - sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); + sd = sysfs_new_dirent(kernfs_root(parent), name, S_IFLNK|S_IRWXUGO, + SYSFS_KOBJ_LINK); if (!sd) return ERR_PTR(-ENOMEM); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index f75548b..fad8b98 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -11,6 +11,7 @@ #include #include #include +#include #include struct file; @@ -23,6 +24,9 @@ struct sysfs_dirent; struct kernfs_root { /* published fields */ struct sysfs_dirent *sd; + + /* private fields, do not use outside kernfs proper */ + struct ida ino_ida; }; struct sysfs_open_file { -- cgit v1.1 From df394fb56c64244b30b442e9e02de1a2d9c5a98b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:42 -0500 Subject: sysfs, kernfs: make super_blocks bind to different kernfs_roots kernfs is being updated to allow multiple sysfs_dirent hierarchies so that it can also be used by other users. Currently, sysfs super_blocks are always attached to one kernfs_root - sysfs_root - and distinguished only by their namespace tags. This patch adds sysfs_super_info->root and update sysfs_fill/test_super() so that super_blocks are identified by the combination of both the associated kernfs_root and namespace tag. This allows mounting different kernfs hierarchies. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/mount.c | 10 ++++++---- fs/sysfs/sysfs.h | 6 ++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 0b5661b..f143b20 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -37,6 +37,7 @@ struct sysfs_dirent *sysfs_root_sd; static int sysfs_fill_super(struct super_block *sb) { + struct sysfs_super_info *info = sysfs_info(sb); struct inode *inode; struct dentry *root; @@ -48,7 +49,7 @@ static int sysfs_fill_super(struct super_block *sb) /* get root inode, initialize and unlock it */ mutex_lock(&sysfs_mutex); - inode = sysfs_get_inode(sb, sysfs_root_sd); + inode = sysfs_get_inode(sb, info->root->sd); mutex_unlock(&sysfs_mutex); if (!inode) { pr_debug("sysfs: could not get root inode\n"); @@ -61,8 +62,8 @@ static int sysfs_fill_super(struct super_block *sb) pr_debug("%s: could not get root dentry!\n", __func__); return -ENOMEM; } - kernfs_get(sysfs_root_sd); - root->d_fsdata = sysfs_root_sd; + kernfs_get(info->root->sd); + root->d_fsdata = info->root->sd; sb->s_root = root; sb->s_d_op = &sysfs_dentry_ops; return 0; @@ -73,7 +74,7 @@ static int sysfs_test_super(struct super_block *sb, void *data) struct sysfs_super_info *sb_info = sysfs_info(sb); struct sysfs_super_info *info = data; - return sb_info->ns == info->ns; + return sb_info->root == info->root && sb_info->ns == info->ns; } static int sysfs_set_super(struct super_block *sb, void *data) @@ -110,6 +111,7 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, if (!info) return ERR_PTR(-ENOMEM); + info->root = sysfs_root; info->ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET); sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 2b217ce..93b4b68 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -20,6 +20,12 @@ struct sysfs_super_info { /* + * The root associated with this super_block. Each super_block is + * identified by the root and ns it's associated with. + */ + struct kernfs_root *root; + + /* * Each sb is associated with one namespace tag, currently the network * namespace of the task which mounted this sysfs instance. If multiple * tags become necessary, make the following an array and compare -- cgit v1.1 From 4b93dc9b1c684d0587fe44d36bbfdf45bd3bea9d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:43 -0500 Subject: sysfs, kernfs: prepare mount path for kernfs We're in the process of separating out core sysfs functionality into kernfs which will deal with sysfs_dirents directly. This patch rearranges mount path so that the kernfs and sysfs parts are separate. * As sysfs_super_info won't be visible outside kernfs proper, kernfs_super_ns() is added to allow kernfs users to access a super_block's namespace tag. * Generic mount operation is separated out into kernfs_mount_ns(). sysfs_mount() now just performs sysfs-specific permission check, acquires namespace tag, and invokes kernfs_mount_ns(). * Generic superblock release is separated out into kernfs_kill_sb() which can be used directly as file_system_type->kill_sb(). As sysfs needs to put the namespace tag, sysfs_kill_sb() wraps kernfs_kill_sb() with ns tag put. * sysfs_dir_cachep init and sysfs_inode_init() are separated out into kernfs_init(). kernfs_init() uses only small amount of memory and trying to handle and propagate kernfs_init() failure doesn't make much sense. Use SLAB_PANIC for sysfs_dir_cachep and make sysfs_inode_init() panic on failure. After this change, kernfs_init() should be called before sysfs_init(), fs/namespace.c::mnt_init() modified accordingly. Signed-off-by: Tejun Heo Cc: linux-fsdevel@vger.kernel.org Cc: Christoph Hellwig Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/inode.c | 5 ++- fs/kernfs/kernfs-internal.h | 2 +- fs/namespace.c | 2 + fs/sysfs/mount.c | 104 ++++++++++++++++++++++++++++++-------------- include/linux/kernfs.h | 28 ++++++++++++ 5 files changed, 106 insertions(+), 35 deletions(-) diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index 9d4fab4..b4cae6f 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -37,9 +37,10 @@ static const struct inode_operations sysfs_inode_operations = { .setxattr = sysfs_setxattr, }; -int __init sysfs_inode_init(void) +void __init sysfs_inode_init(void) { - return bdi_init(&sysfs_backing_dev_info); + if (bdi_init(&sysfs_backing_dev_info)) + panic("failed to init sysfs_backing_dev_info"); } static struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd) diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 466943d..0d94988 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -144,7 +144,7 @@ int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); -int sysfs_inode_init(void); +void sysfs_inode_init(void); /* * dir.c diff --git a/fs/namespace.c b/fs/namespace.c index ac2ce8a..a511ea0 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -2790,6 +2790,8 @@ void __init mnt_init(void) for (u = 0; u < HASH_SIZE; u++) INIT_LIST_HEAD(&mountpoint_hashtable[u]); + kernfs_init(); + err = sysfs_init(); if (err) printk(KERN_WARNING "%s: sysfs_init error: %d\n", diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index f143b20..5384732 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -86,18 +86,24 @@ static int sysfs_set_super(struct super_block *sb, void *data) return error; } -static void free_sysfs_super_info(struct sysfs_super_info *info) +/** + * kernfs_super_ns - determine the namespace tag of a kernfs super_block + * @sb: super_block of interest + * + * Return the namespace tag associated with kernfs super_block @sb. + */ +const void *kernfs_super_ns(struct super_block *sb) { - kobj_ns_drop(KOBJ_NS_TYPE_NET, (void *)info->ns); - kfree(info); + struct sysfs_super_info *info = sysfs_info(sb); + + return info->ns; } static struct dentry *sysfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { - struct sysfs_super_info *info; - struct super_block *sb; - int error; + struct dentry *root; + void *ns; if (!(flags & MS_KERNMOUNT)) { if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type)) @@ -107,16 +113,44 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, return ERR_PTR(-EPERM); } + ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET); + root = kernfs_mount_ns(fs_type, flags, sysfs_root, ns); + if (IS_ERR(root)) + kobj_ns_drop(KOBJ_NS_TYPE_NET, ns); + return root; +} + +/** + * kernfs_mount_ns - kernfs mount helper + * @fs_type: file_system_type of the fs being mounted + * @flags: mount flags specified for the mount + * @root: kernfs_root of the hierarchy being mounted + * @ns: optional namespace tag of the mount + * + * This is to be called from each kernfs user's file_system_type->mount() + * implementation, which should pass through the specified @fs_type and + * @flags, and specify the hierarchy and namespace tag to mount via @root + * and @ns, respectively. + * + * The return value can be passed to the vfs layer verbatim. + */ +struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, + struct kernfs_root *root, const void *ns) +{ + struct super_block *sb; + struct sysfs_super_info *info; + int error; + info = kzalloc(sizeof(*info), GFP_KERNEL); if (!info) return ERR_PTR(-ENOMEM); - info->root = sysfs_root; - info->ns = kobj_ns_grab_current(KOBJ_NS_TYPE_NET); + info->root = root; + info->ns = ns; sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info); if (IS_ERR(sb) || sb->s_fs_info != info) - free_sysfs_super_info(info); + kfree(info); if (IS_ERR(sb)) return ERR_CAST(sb); if (!sb->s_root) { @@ -133,6 +167,20 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, static void sysfs_kill_sb(struct super_block *sb) { + kernfs_kill_sb(sb); + kobj_ns_drop(KOBJ_NS_TYPE_NET, (void *)kernfs_super_ns(sb)); +} + +/** + * kernfs_kill_sb - kill_sb for kernfs + * @sb: super_block being killed + * + * This can be used directly for file_system_type->kill_sb(). If a kernfs + * user needs extra cleanup, it can implement its own kill_sb() and call + * this function at the end. + */ +void kernfs_kill_sb(struct super_block *sb) +{ struct sysfs_super_info *info = sysfs_info(sb); struct sysfs_dirent *root_sd = sb->s_root->d_fsdata; @@ -141,7 +189,7 @@ static void sysfs_kill_sb(struct super_block *sb) * so we can't find it, before freeing sysfs_super_info. */ kill_anon_super(sb); - free_sysfs_super_info(info); + kfree(info); kernfs_put(root_sd); } @@ -152,37 +200,29 @@ static struct file_system_type sysfs_fs_type = { .fs_flags = FS_USERNS_MOUNT, }; -int __init sysfs_init(void) +void __init kernfs_init(void) { - int err; - sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache", sizeof(struct sysfs_dirent), - 0, 0, NULL); - if (!sysfs_dir_cachep) - return -ENOMEM; + 0, SLAB_PANIC, NULL); + sysfs_inode_init(); +} - err = sysfs_inode_init(); - if (err) - goto out_err; +int __init sysfs_init(void) +{ + int err; sysfs_root = kernfs_create_root(NULL); - if (IS_ERR(sysfs_root)) { - err = PTR_ERR(sysfs_root); - goto out_err; - } + if (IS_ERR(sysfs_root)) + return PTR_ERR(sysfs_root); + sysfs_root_sd = sysfs_root->sd; err = register_filesystem(&sysfs_fs_type); - if (err) - goto out_destroy_root; + if (err) { + kernfs_destroy_root(sysfs_root); + return err; + } return 0; - -out_destroy_root: - kernfs_destroy_root(sysfs_root); -out_err: - kmem_cache_destroy(sysfs_dir_cachep); - sysfs_dir_cachep = NULL; - return err; } diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index fad8b98..75fcbe5 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -18,6 +18,8 @@ struct file; struct iattr; struct seq_file; struct vm_area_struct; +struct super_block; +struct file_system_type; struct sysfs_dirent; @@ -109,6 +111,13 @@ void kernfs_enable_ns(struct sysfs_dirent *sd); int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr); void kernfs_notify(struct sysfs_dirent *sd); +const void *kernfs_super_ns(struct super_block *sb); +struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, + struct kernfs_root *root, const void *ns); +void kernfs_kill_sb(struct super_block *sb); + +void kernfs_init(void); + #else /* CONFIG_SYSFS */ static inline struct sysfs_dirent * @@ -160,6 +169,18 @@ static inline int kernfs_setattr(struct sysfs_dirent *sd, static inline void kernfs_notify(struct sysfs_dirent *sd) { } +static inline const void *kernfs_super_ns(struct super_block *sb) +{ return NULL; } + +static inline struct dentry * +kernfs_mount_ns(struct file_system_type *fs_type, int flags, + struct kernfs_root *root, const void *ns) +{ return ERR_PTR(-ENOSYS); } + +static inline void kernfs_kill_sb(struct super_block *sb) { } + +static inline void kernfs_init(void) { } + #endif /* CONFIG_SYSFS */ static inline struct sysfs_dirent * @@ -201,4 +222,11 @@ static inline int kernfs_remove_by_name(struct sysfs_dirent *parent, return kernfs_remove_by_name_ns(parent, name, NULL); } +static inline struct dentry * +kernfs_mount(struct file_system_type *fs_type, int flags, + struct kernfs_root *root) +{ + return kernfs_mount_ns(fs_type, flags, root, NULL); +} + #endif /* __LINUX_KERNFS_H */ -- cgit v1.1 From fa736a951e456b996a76826ba78ff974414c3b55 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:44 -0500 Subject: sysfs, kernfs: move mount core code to fs/kernfs/mount.c Move core mount code to fs/kernfs/mount.c. The respective declarations in fs/sysfs/sysfs.h are moved to fs/kernfs/kernfs-internal.h. This is pure relocation. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/kernfs-internal.h | 22 +++++++ fs/kernfs/mount.c | 156 ++++++++++++++++++++++++++++++++++++++++++++ fs/sysfs/mount.c | 152 ------------------------------------------ fs/sysfs/sysfs.h | 18 ----- 4 files changed, 178 insertions(+), 170 deletions(-) diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 0d94988..ced0d6d 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -134,6 +134,28 @@ struct sysfs_addrm_cxt { #include "../sysfs/sysfs.h" /* + * mount.c + */ +struct sysfs_super_info { + /* + * The root associated with this super_block. Each super_block is + * identified by the root and ns it's associated with. + */ + struct kernfs_root *root; + + /* + * Each sb is associated with one namespace tag, currently the network + * namespace of the task which mounted this sysfs instance. If multiple + * tags become necessary, make the following an array and compare + * sysfs_dirent tag against every entry. + */ + const void *ns; +}; +#define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info)) + +extern struct kmem_cache *sysfs_dir_cachep; + +/* * inode.c */ struct inode *sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd); diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index 872e262..84c83e2 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -7,3 +7,159 @@ * * This file is released under the GPLv2. */ + +#include +#include +#include +#include +#include +#include + +#include "kernfs-internal.h" + +struct kmem_cache *sysfs_dir_cachep; + +static const struct super_operations sysfs_ops = { + .statfs = simple_statfs, + .drop_inode = generic_delete_inode, + .evict_inode = sysfs_evict_inode, +}; + +static int sysfs_fill_super(struct super_block *sb) +{ + struct sysfs_super_info *info = sysfs_info(sb); + struct inode *inode; + struct dentry *root; + + sb->s_blocksize = PAGE_CACHE_SIZE; + sb->s_blocksize_bits = PAGE_CACHE_SHIFT; + sb->s_magic = SYSFS_MAGIC; + sb->s_op = &sysfs_ops; + sb->s_time_gran = 1; + + /* get root inode, initialize and unlock it */ + mutex_lock(&sysfs_mutex); + inode = sysfs_get_inode(sb, info->root->sd); + mutex_unlock(&sysfs_mutex); + if (!inode) { + pr_debug("sysfs: could not get root inode\n"); + return -ENOMEM; + } + + /* instantiate and link root dentry */ + root = d_make_root(inode); + if (!root) { + pr_debug("%s: could not get root dentry!\n", __func__); + return -ENOMEM; + } + kernfs_get(info->root->sd); + root->d_fsdata = info->root->sd; + sb->s_root = root; + sb->s_d_op = &sysfs_dentry_ops; + return 0; +} + +static int sysfs_test_super(struct super_block *sb, void *data) +{ + struct sysfs_super_info *sb_info = sysfs_info(sb); + struct sysfs_super_info *info = data; + + return sb_info->root == info->root && sb_info->ns == info->ns; +} + +static int sysfs_set_super(struct super_block *sb, void *data) +{ + int error; + error = set_anon_super(sb, data); + if (!error) + sb->s_fs_info = data; + return error; +} + +/** + * kernfs_super_ns - determine the namespace tag of a kernfs super_block + * @sb: super_block of interest + * + * Return the namespace tag associated with kernfs super_block @sb. + */ +const void *kernfs_super_ns(struct super_block *sb) +{ + struct sysfs_super_info *info = sysfs_info(sb); + + return info->ns; +} + +/** + * kernfs_mount_ns - kernfs mount helper + * @fs_type: file_system_type of the fs being mounted + * @flags: mount flags specified for the mount + * @root: kernfs_root of the hierarchy being mounted + * @ns: optional namespace tag of the mount + * + * This is to be called from each kernfs user's file_system_type->mount() + * implementation, which should pass through the specified @fs_type and + * @flags, and specify the hierarchy and namespace tag to mount via @root + * and @ns, respectively. + * + * The return value can be passed to the vfs layer verbatim. + */ +struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, + struct kernfs_root *root, const void *ns) +{ + struct super_block *sb; + struct sysfs_super_info *info; + int error; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) + return ERR_PTR(-ENOMEM); + + info->root = root; + info->ns = ns; + + sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info); + if (IS_ERR(sb) || sb->s_fs_info != info) + kfree(info); + if (IS_ERR(sb)) + return ERR_CAST(sb); + if (!sb->s_root) { + error = sysfs_fill_super(sb); + if (error) { + deactivate_locked_super(sb); + return ERR_PTR(error); + } + sb->s_flags |= MS_ACTIVE; + } + + return dget(sb->s_root); +} + +/** + * kernfs_kill_sb - kill_sb for kernfs + * @sb: super_block being killed + * + * This can be used directly for file_system_type->kill_sb(). If a kernfs + * user needs extra cleanup, it can implement its own kill_sb() and call + * this function at the end. + */ +void kernfs_kill_sb(struct super_block *sb) +{ + struct sysfs_super_info *info = sysfs_info(sb); + struct sysfs_dirent *root_sd = sb->s_root->d_fsdata; + + /* + * Remove the superblock from fs_supers/s_instances + * so we can't find it, before freeing sysfs_super_info. + */ + kill_anon_super(sb); + kfree(info); + kernfs_put(root_sd); +} + +void __init kernfs_init(void) +{ + sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache", + sizeof(struct sysfs_dirent), + 0, SLAB_PANIC, NULL); + sysfs_inode_init(); +} diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 5384732..e7e3aa8 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -14,91 +14,14 @@ #include #include -#include #include -#include -#include -#include #include #include "sysfs.h" - -struct kmem_cache *sysfs_dir_cachep; - -static const struct super_operations sysfs_ops = { - .statfs = simple_statfs, - .drop_inode = generic_delete_inode, - .evict_inode = sysfs_evict_inode, -}; - static struct kernfs_root *sysfs_root; struct sysfs_dirent *sysfs_root_sd; -static int sysfs_fill_super(struct super_block *sb) -{ - struct sysfs_super_info *info = sysfs_info(sb); - struct inode *inode; - struct dentry *root; - - sb->s_blocksize = PAGE_CACHE_SIZE; - sb->s_blocksize_bits = PAGE_CACHE_SHIFT; - sb->s_magic = SYSFS_MAGIC; - sb->s_op = &sysfs_ops; - sb->s_time_gran = 1; - - /* get root inode, initialize and unlock it */ - mutex_lock(&sysfs_mutex); - inode = sysfs_get_inode(sb, info->root->sd); - mutex_unlock(&sysfs_mutex); - if (!inode) { - pr_debug("sysfs: could not get root inode\n"); - return -ENOMEM; - } - - /* instantiate and link root dentry */ - root = d_make_root(inode); - if (!root) { - pr_debug("%s: could not get root dentry!\n", __func__); - return -ENOMEM; - } - kernfs_get(info->root->sd); - root->d_fsdata = info->root->sd; - sb->s_root = root; - sb->s_d_op = &sysfs_dentry_ops; - return 0; -} - -static int sysfs_test_super(struct super_block *sb, void *data) -{ - struct sysfs_super_info *sb_info = sysfs_info(sb); - struct sysfs_super_info *info = data; - - return sb_info->root == info->root && sb_info->ns == info->ns; -} - -static int sysfs_set_super(struct super_block *sb, void *data) -{ - int error; - error = set_anon_super(sb, data); - if (!error) - sb->s_fs_info = data; - return error; -} - -/** - * kernfs_super_ns - determine the namespace tag of a kernfs super_block - * @sb: super_block of interest - * - * Return the namespace tag associated with kernfs super_block @sb. - */ -const void *kernfs_super_ns(struct super_block *sb) -{ - struct sysfs_super_info *info = sysfs_info(sb); - - return info->ns; -} - static struct dentry *sysfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) { @@ -120,79 +43,12 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, return root; } -/** - * kernfs_mount_ns - kernfs mount helper - * @fs_type: file_system_type of the fs being mounted - * @flags: mount flags specified for the mount - * @root: kernfs_root of the hierarchy being mounted - * @ns: optional namespace tag of the mount - * - * This is to be called from each kernfs user's file_system_type->mount() - * implementation, which should pass through the specified @fs_type and - * @flags, and specify the hierarchy and namespace tag to mount via @root - * and @ns, respectively. - * - * The return value can be passed to the vfs layer verbatim. - */ -struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, - struct kernfs_root *root, const void *ns) -{ - struct super_block *sb; - struct sysfs_super_info *info; - int error; - - info = kzalloc(sizeof(*info), GFP_KERNEL); - if (!info) - return ERR_PTR(-ENOMEM); - - info->root = root; - info->ns = ns; - - sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info); - if (IS_ERR(sb) || sb->s_fs_info != info) - kfree(info); - if (IS_ERR(sb)) - return ERR_CAST(sb); - if (!sb->s_root) { - error = sysfs_fill_super(sb); - if (error) { - deactivate_locked_super(sb); - return ERR_PTR(error); - } - sb->s_flags |= MS_ACTIVE; - } - - return dget(sb->s_root); -} - static void sysfs_kill_sb(struct super_block *sb) { kernfs_kill_sb(sb); kobj_ns_drop(KOBJ_NS_TYPE_NET, (void *)kernfs_super_ns(sb)); } -/** - * kernfs_kill_sb - kill_sb for kernfs - * @sb: super_block being killed - * - * This can be used directly for file_system_type->kill_sb(). If a kernfs - * user needs extra cleanup, it can implement its own kill_sb() and call - * this function at the end. - */ -void kernfs_kill_sb(struct super_block *sb) -{ - struct sysfs_super_info *info = sysfs_info(sb); - struct sysfs_dirent *root_sd = sb->s_root->d_fsdata; - - /* - * Remove the superblock from fs_supers/s_instances - * so we can't find it, before freeing sysfs_super_info. - */ - kill_anon_super(sb); - kfree(info); - kernfs_put(root_sd); -} - static struct file_system_type sysfs_fs_type = { .name = "sysfs", .mount = sysfs_mount, @@ -200,14 +56,6 @@ static struct file_system_type sysfs_fs_type = { .fs_flags = FS_USERNS_MOUNT, }; -void __init kernfs_init(void) -{ - sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache", - sizeof(struct sysfs_dirent), - 0, SLAB_PANIC, NULL); - sysfs_inode_init(); -} - int __init sysfs_init(void) { int err; diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 93b4b68..6a82311 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -17,25 +17,7 @@ /* * mount.c */ - -struct sysfs_super_info { - /* - * The root associated with this super_block. Each super_block is - * identified by the root and ns it's associated with. - */ - struct kernfs_root *root; - - /* - * Each sb is associated with one namespace tag, currently the network - * namespace of the task which mounted this sysfs instance. If multiple - * tags become necessary, make the following an array and compare - * sysfs_dirent tag against every entry. - */ - const void *ns; -}; -#define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info)) extern struct sysfs_dirent *sysfs_root_sd; -extern struct kmem_cache *sysfs_dir_cachep; /* * dir.c -- cgit v1.1 From cf9e5a73aaff0204801dd19cb4bd91d32f32026a Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 29 Nov 2013 17:18:32 -0500 Subject: sysfs, kernfs: make sysfs_dirent definition public sysfs_dirent includes some information which should be available to kernfs users - the type, flags, name and parent pointer. This patch moves sysfs_dirent definition from kernfs/kernfs-internal.h to include/linux/kernfs.h so that kernfs users can access them. The type part of flags is exported as enum kernfs_node_type, the flags kernfs_node_flag, sysfs_type() and kernfs_enable_ns() are moved to include/linux/kernfs.h and the former is updated to return the enum type. sysfs_dirent->s_parent and ->s_name are marked explicitly as public. This patch doesn't introduce any functional changes. v2: Flags exported too and kernfs_enable_ns() definition moved. v3: While moving kernfs_enable_ns() to include/linux/kernfs.h, v1 and v2 put the definition outside CONFIG_SYSFS replacing the dummy implementation with the actual implementation too. Unfortunately, this can lead to oops when !CONFIG_SYSFS because kernfs_enable_ns() may be called on a NULL @sd and now tries to dereference @sd instead of not doing anything. This issue was reported by Yuanhan Liu. Signed-off-by: Tejun Heo Reported-by: Yuanhan Liu Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 15 ------ fs/kernfs/kernfs-internal.h | 85 +------------------------------ include/linux/kernfs.h | 118 ++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 115 insertions(+), 103 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index eaffa83..7c5b517 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -895,21 +895,6 @@ int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, return error; } -/** - * kernfs_enable_ns - enable namespace under a directory - * @sd: directory of interest, should be empty - * - * This is to be called right after @sd is created to enable namespace - * under it. All children of @sd must have non-NULL namespace tags and - * only the ones which match the super_block's tag will be visible. - */ -void kernfs_enable_ns(struct sysfs_dirent *sd) -{ - WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); - WARN_ON_ONCE(!RB_EMPTY_ROOT(&sd->s_dir.children)); - sd->s_flags |= SYSFS_FLAG_NS; -} - /* Relationship between s_mode and the DT_xxx types */ static inline unsigned char dt_type(struct sysfs_dirent *sd) { diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index ced0d6d..f33a784 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -13,102 +13,19 @@ #include #include -#include #include #include -struct sysfs_open_dirent; - -/* type-specific structures for sysfs_dirent->s_* union members */ -struct sysfs_elem_dir { - unsigned long subdirs; - /* children rbtree starts here and goes through sd->s_rb */ - struct rb_root children; - - /* - * The kernfs hierarchy this directory belongs to. This fits - * better directly in sysfs_dirent but is here to save space. - */ - struct kernfs_root *root; -}; - -struct sysfs_elem_symlink { - struct sysfs_dirent *target_sd; -}; - -struct sysfs_elem_attr { - const struct kernfs_ops *ops; - struct sysfs_open_dirent *open; - loff_t size; -}; - struct sysfs_inode_attrs { struct iattr ia_iattr; void *ia_secdata; u32 ia_secdata_len; }; -/* - * sysfs_dirent - the building block of sysfs hierarchy. Each and - * every sysfs node is represented by single sysfs_dirent. - * - * 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; - atomic_t s_active; -#ifdef CONFIG_DEBUG_LOCK_ALLOC - struct lockdep_map dep_map; -#endif - struct sysfs_dirent *s_parent; - const char *s_name; - - struct rb_node s_rb; - - union { - struct completion *completion; - struct sysfs_dirent *removed_list; - } u; - - const void *s_ns; /* namespace tag */ - unsigned int s_hash; /* ns + name hash */ - union { - struct sysfs_elem_dir s_dir; - struct sysfs_elem_symlink s_symlink; - struct sysfs_elem_attr s_attr; - }; - - void *priv; - - unsigned short s_flags; - umode_t s_mode; - unsigned int s_ino; - struct sysfs_inode_attrs *s_iattr; -}; - #define SD_DEACTIVATED_BIAS INT_MIN -#define SYSFS_TYPE_MASK 0x000f -#define SYSFS_DIR 0x0001 -#define SYSFS_KOBJ_ATTR 0x0002 -#define SYSFS_KOBJ_LINK 0x0004 -#define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) -#define SYSFS_ACTIVE_REF SYSFS_KOBJ_ATTR - -#define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK -#define SYSFS_FLAG_REMOVED 0x0010 -#define SYSFS_FLAG_NS 0x0020 -#define SYSFS_FLAG_HAS_SEQ_SHOW 0x0040 -#define SYSFS_FLAG_HAS_MMAP 0x0080 -#define SYSFS_FLAG_LOCKDEP 0x0100 - -static inline unsigned int sysfs_type(struct sysfs_dirent *sd) -{ - return sd->s_flags & SYSFS_TYPE_MASK; -} +/* SYSFS_TYPE_MASK and types are defined in include/linux/kernfs.h */ /** * kernfs_root - find out the kernfs_root a sysfs_dirent belongs to diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 75fcbe5..faaf4f2 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -13,6 +13,9 @@ #include #include #include +#include +#include +#include struct file; struct iattr; @@ -21,7 +24,92 @@ struct vm_area_struct; struct super_block; struct file_system_type; -struct sysfs_dirent; +struct sysfs_open_dirent; +struct sysfs_inode_attrs; + +enum kernfs_node_type { + SYSFS_DIR = 0x0001, + SYSFS_KOBJ_ATTR = 0x0002, + SYSFS_KOBJ_LINK = 0x0004, +}; + +#define SYSFS_TYPE_MASK 0x000f +#define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) +#define SYSFS_ACTIVE_REF SYSFS_KOBJ_ATTR +#define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK + +enum kernfs_node_flag { + SYSFS_FLAG_REMOVED = 0x0010, + SYSFS_FLAG_NS = 0x0020, + SYSFS_FLAG_HAS_SEQ_SHOW = 0x0040, + SYSFS_FLAG_HAS_MMAP = 0x0080, + SYSFS_FLAG_LOCKDEP = 0x0100, +}; + +/* type-specific structures for sysfs_dirent->s_* union members */ +struct sysfs_elem_dir { + unsigned long subdirs; + /* children rbtree starts here and goes through sd->s_rb */ + struct rb_root children; + + /* + * The kernfs hierarchy this directory belongs to. This fits + * better directly in sysfs_dirent but is here to save space. + */ + struct kernfs_root *root; +}; + +struct sysfs_elem_symlink { + struct sysfs_dirent *target_sd; +}; + +struct sysfs_elem_attr { + const struct kernfs_ops *ops; + struct sysfs_open_dirent *open; + loff_t size; +}; + +/* + * sysfs_dirent - the building block of sysfs hierarchy. Each and every + * sysfs node is represented by single sysfs_dirent. Most fields are + * private to kernfs and shouldn't be accessed directly by kernfs users. + * + * 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; + atomic_t s_active; +#ifdef CONFIG_DEBUG_LOCK_ALLOC + struct lockdep_map dep_map; +#endif + /* the following two fields are published */ + struct sysfs_dirent *s_parent; + const char *s_name; + + struct rb_node s_rb; + + union { + struct completion *completion; + struct sysfs_dirent *removed_list; + } u; + + const void *s_ns; /* namespace tag */ + unsigned int s_hash; /* ns + name hash */ + union { + struct sysfs_elem_dir s_dir; + struct sysfs_elem_symlink s_symlink; + struct sysfs_elem_attr s_attr; + }; + + void *priv; + + unsigned short s_flags; + umode_t s_mode; + unsigned int s_ino; + struct sysfs_inode_attrs *s_iattr; +}; struct kernfs_root { /* published fields */ @@ -82,6 +170,26 @@ struct kernfs_ops { #ifdef CONFIG_SYSFS +static inline enum kernfs_node_type sysfs_type(struct sysfs_dirent *sd) +{ + return sd->s_flags & SYSFS_TYPE_MASK; +} + +/** + * kernfs_enable_ns - enable namespace under a directory + * @sd: directory of interest, should be empty + * + * This is to be called right after @sd is created to enable namespace + * under it. All children of @sd must have non-NULL namespace tags and + * only the ones which match the super_block's tag will be visible. + */ +static inline void kernfs_enable_ns(struct sysfs_dirent *sd) +{ + WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); + WARN_ON_ONCE(!RB_EMPTY_ROOT(&sd->s_dir.children)); + sd->s_flags |= SYSFS_FLAG_NS; +} + struct sysfs_dirent *kernfs_find_and_get_ns(struct sysfs_dirent *parent, const char *name, const void *ns); void kernfs_get(struct sysfs_dirent *sd); @@ -107,7 +215,6 @@ int kernfs_remove_by_name_ns(struct sysfs_dirent *parent, const char *name, const void *ns); int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, const char *new_name, const void *new_ns); -void kernfs_enable_ns(struct sysfs_dirent *sd); int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr); void kernfs_notify(struct sysfs_dirent *sd); @@ -120,6 +227,11 @@ void kernfs_init(void); #else /* CONFIG_SYSFS */ +static inline enum kernfs_node_type sysfs_type(struct sysfs_dirent *sd) +{ return 0; } /* whatever */ + +static inline void kernfs_enable_ns(struct sysfs_dirent *sd) { } + static inline struct sysfs_dirent * kernfs_find_and_get_ns(struct sysfs_dirent *parent, const char *name, const void *ns) @@ -161,8 +273,6 @@ static inline int kernfs_rename_ns(struct sysfs_dirent *sd, const char *new_name, const void *new_ns) { return -ENOSYS; } -static inline void kernfs_enable_ns(struct sysfs_dirent *sd) { } - static inline int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) { return -ENOSYS; } -- cgit v1.1 From ac9bba031001704a2339713cc12148857eccc5e5 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 29 Nov 2013 17:19:09 -0500 Subject: sysfs, kernfs: implement kernfs_ns_enabled() fs/sysfs/symlink.c::sysfs_delete_link() tests @sd->s_flags for SYSFS_FLAG_NS. Let's add kernfs_ns_enabled() so that sysfs doesn't have to test sysfs_dirent flag directly. This makes things tidier for kernfs proper too. This is purely cosmetic. v2: To avoid possible NULL deref, use noop dummy implementation which always returns false when !CONFIG_SYSFS. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 10 +++++----- fs/kernfs/symlink.c | 2 +- fs/sysfs/symlink.c | 2 +- include/linux/kernfs.h | 14 ++++++++++++++ 4 files changed, 21 insertions(+), 7 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 7c5b517..f51e062 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -291,7 +291,7 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) goto out_bad; /* The sysfs dirent has been moved to a different namespace */ - if (sd->s_parent && (sd->s_parent->s_flags & SYSFS_FLAG_NS) && + if (sd->s_parent && kernfs_ns_enabled(sd->s_parent) && sysfs_info(dentry->d_sb)->ns != sd->s_ns) goto out_bad; @@ -414,7 +414,7 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, struct sysfs_dirent *parent_sd) { - bool has_ns = parent_sd->s_flags & SYSFS_FLAG_NS; + bool has_ns = kernfs_ns_enabled(parent_sd); struct sysfs_inode_attrs *ps_iattr; int ret; @@ -535,7 +535,7 @@ static struct sysfs_dirent *kernfs_find_ns(struct sysfs_dirent *parent, const void *ns) { struct rb_node *node = parent->s_dir.children.rb_node; - bool has_ns = parent->s_flags & SYSFS_FLAG_NS; + bool has_ns = kernfs_ns_enabled(parent); unsigned int hash; lockdep_assert_held(&sysfs_mutex); @@ -685,7 +685,7 @@ static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, mutex_lock(&sysfs_mutex); - if (parent_sd->s_flags & SYSFS_FLAG_NS) + if (kernfs_ns_enabled(parent_sd)) ns = sysfs_info(dir->i_sb)->ns; sd = kernfs_find_ns(parent_sd, dentry->d_name.name, ns); @@ -968,7 +968,7 @@ static int sysfs_readdir(struct file *file, struct dir_context *ctx) return 0; mutex_lock(&sysfs_mutex); - if (parent_sd->s_flags & SYSFS_FLAG_NS) + if (kernfs_ns_enabled(parent_sd)) ns = sysfs_info(dentry->d_sb)->ns; for (pos = sysfs_dir_pos(ns, parent_sd, ctx->pos, pos); diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 004c164..12569a7 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -35,7 +35,7 @@ struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, if (!sd) return ERR_PTR(-ENOMEM); - if (parent->s_flags & SYSFS_FLAG_NS) + if (kernfs_ns_enabled(parent)) sd->s_ns = target->s_ns; sd->s_symlink.target_sd = target; kernfs_get(target); /* ref owned by symlink */ diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 62f0e01..1b8c9ed 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -128,7 +128,7 @@ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, * sysfs_remove_dir() for details. */ spin_lock(&sysfs_symlink_target_lock); - if (targ->sd && (kobj->sd->s_flags & SYSFS_FLAG_NS)) + if (targ->sd && kernfs_ns_enabled(kobj->sd)) ns = targ->sd->s_ns; spin_unlock(&sysfs_symlink_target_lock); kernfs_remove_by_name_ns(kobj->sd, name, ns); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index faaf4f2..d655413 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -190,6 +190,17 @@ static inline void kernfs_enable_ns(struct sysfs_dirent *sd) sd->s_flags |= SYSFS_FLAG_NS; } +/** + * kernfs_ns_enabled - test whether namespace is enabled + * @sd: the node to test + * + * Test whether namespace filtering is enabled for the children of @ns. + */ +static inline bool kernfs_ns_enabled(struct sysfs_dirent *sd) +{ + return sd->s_flags & SYSFS_FLAG_NS; +} + struct sysfs_dirent *kernfs_find_and_get_ns(struct sysfs_dirent *parent, const char *name, const void *ns); void kernfs_get(struct sysfs_dirent *sd); @@ -232,6 +243,9 @@ static inline enum kernfs_node_type sysfs_type(struct sysfs_dirent *sd) static inline void kernfs_enable_ns(struct sysfs_dirent *sd) { } +static inline bool kernfs_ns_enabled(struct sysfs_dirent *sd) +{ return false; } + static inline struct sysfs_dirent * kernfs_find_and_get_ns(struct sysfs_dirent *parent, const char *name, const void *ns) -- cgit v1.1 From bfc5c17337145955b31c22b96a6e07def048471b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 28 Nov 2013 14:54:47 -0500 Subject: sysfs, kernfs: remove cross inclusions of internal headers fs/kernfs/kernfs-internal.h needed to include fs/sysfs/sysfs.h because part of kernfs core implementation was living in sysfs. fs/sysfs/sysfs.h needed to include fs/kernfs/kernfs-internal.h because include/linux/kernfs.h didn't expose enough interface. The separation is complete and neither is true anymore. Remove the cross inclusion and make sysfs a proper user of kernfs. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/kernfs-internal.h | 2 -- fs/sysfs/sysfs.h | 1 - 2 files changed, 3 deletions(-) diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index f33a784..d1ff591 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -48,8 +48,6 @@ struct sysfs_addrm_cxt { struct sysfs_dirent *removed; }; -#include "../sysfs/sysfs.h" - /* * mount.c */ diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index 6a82311..c8e395b 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -11,7 +11,6 @@ #ifndef __SYSFS_INTERNAL_H #define __SYSFS_INTERNAL_H -#include "../kernfs/kernfs-internal.h" #include /* -- cgit v1.1 From e756bc5670d0f801ca43dc55b8eacde42a5b818b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 8 Oct 2013 14:20:24 -0600 Subject: kobject: fix kset sample error path Previously, example_init() leaked a kset if any of the object creations failed. This fixes the leak by calling kset_unregister() in the error path. Signed-off-by: Bjorn Helgaas Signed-off-by: Greg Kroah-Hartman --- samples/kobject/kset-example.c | 1 + 1 file changed, 1 insertion(+) diff --git a/samples/kobject/kset-example.c b/samples/kobject/kset-example.c index d0c687f..5dce351 100644 --- a/samples/kobject/kset-example.c +++ b/samples/kobject/kset-example.c @@ -262,6 +262,7 @@ baz_error: bar_error: destroy_foo_obj(foo_obj); foo_error: + kset_unregister(example_kset); return -EINVAL; } -- cgit v1.1 From 9a8049affd55a2c857a89faece27b878416fbf91 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 23 Nov 2013 17:40:01 -0500 Subject: kernfs: update sysfs_init_inode_attrs() sysfs_init_inode_attrs() is a bit clumsy to use requiring the caller to check whether @sd->s_iattr is already set or not. Rename it to sysfs_inode_attrs(), update it to check whether @sd->s_iattr is already initialized before trying to initialize it and return @sd->s_iattr. This simplifies the callers. While at it, * Rename struct sysfs_inode_attrs pointer variables to "attrs". As kernfs no longer deals with "struct attribute", this isn't confusing and makes it easier to distinguish from struct iattr pointers. * A new field will be added to sysfs_inode_attrs. Reindent in preparation. This patch doesn't introduce any behavior changes. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/inode.c | 60 ++++++++++++++++++++------------------------- fs/kernfs/kernfs-internal.h | 6 ++--- 2 files changed, 29 insertions(+), 37 deletions(-) diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index b4cae6f..a1f8382 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -43,15 +43,17 @@ void __init sysfs_inode_init(void) panic("failed to init sysfs_backing_dev_info"); } -static struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd) +static struct sysfs_inode_attrs *sysfs_inode_attrs(struct sysfs_dirent *sd) { - struct sysfs_inode_attrs *attrs; struct iattr *iattrs; - attrs = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL); - if (!attrs) + if (sd->s_iattr) + return sd->s_iattr; + + sd->s_iattr = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL); + if (!sd->s_iattr) return NULL; - iattrs = &attrs->ia_iattr; + iattrs = &sd->s_iattr->ia_iattr; /* assign default attributes */ iattrs->ia_mode = sd->s_mode; @@ -59,26 +61,20 @@ static struct sysfs_inode_attrs *sysfs_init_inode_attrs(struct sysfs_dirent *sd) iattrs->ia_gid = GLOBAL_ROOT_GID; iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME; - return attrs; + return sd->s_iattr; } static int __kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) { - struct sysfs_inode_attrs *sd_attrs; + struct sysfs_inode_attrs *attrs; struct iattr *iattrs; unsigned int ia_valid = iattr->ia_valid; - sd_attrs = sd->s_iattr; + attrs = sysfs_inode_attrs(sd); + if (!attrs) + return -ENOMEM; - if (!sd_attrs) { - /* setting attributes for the first time, allocate now */ - sd_attrs = sysfs_init_inode_attrs(sd); - if (!sd_attrs) - return -ENOMEM; - sd->s_iattr = sd_attrs; - } - /* attributes were changed at least once in past */ - iattrs = &sd_attrs->ia_iattr; + iattrs = &attrs->ia_iattr; if (ia_valid & ATTR_UID) iattrs->ia_uid = iattr->ia_uid; @@ -143,22 +139,19 @@ out: static int sysfs_sd_setsecdata(struct sysfs_dirent *sd, void **secdata, u32 *secdata_len) { - struct sysfs_inode_attrs *iattrs; + struct sysfs_inode_attrs *attrs; void *old_secdata; size_t old_secdata_len; - if (!sd->s_iattr) { - sd->s_iattr = sysfs_init_inode_attrs(sd); - if (!sd->s_iattr) - return -ENOMEM; - } + attrs = sysfs_inode_attrs(sd); + if (!attrs) + return -ENOMEM; - iattrs = sd->s_iattr; - old_secdata = iattrs->ia_secdata; - old_secdata_len = iattrs->ia_secdata_len; + old_secdata = attrs->ia_secdata; + old_secdata_len = attrs->ia_secdata_len; - iattrs->ia_secdata = *secdata; - iattrs->ia_secdata_len = *secdata_len; + attrs->ia_secdata = *secdata; + attrs->ia_secdata_len = *secdata_len; *secdata = old_secdata; *secdata_len = old_secdata_len; @@ -216,17 +209,16 @@ static inline void set_inode_attr(struct inode *inode, struct iattr *iattr) static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode) { - struct sysfs_inode_attrs *iattrs = sd->s_iattr; + struct sysfs_inode_attrs *attrs = sd->s_iattr; inode->i_mode = sd->s_mode; - if (iattrs) { + if (attrs) { /* sysfs_dirent has non-default attributes * get them from persistent copy in sysfs_dirent */ - set_inode_attr(inode, &iattrs->ia_iattr); - security_inode_notifysecctx(inode, - iattrs->ia_secdata, - iattrs->ia_secdata_len); + set_inode_attr(inode, &attrs->ia_iattr); + security_inode_notifysecctx(inode, attrs->ia_secdata, + attrs->ia_secdata_len); } if (sysfs_type(sd) == SYSFS_DIR) diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index d1ff591..f25b354 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -18,9 +18,9 @@ #include struct sysfs_inode_attrs { - struct iattr ia_iattr; - void *ia_secdata; - u32 ia_secdata_len; + struct iattr ia_iattr; + void *ia_secdata; + u32 ia_secdata_len; }; #define SD_DEACTIVATED_BIAS INT_MIN -- cgit v1.1 From 2322392b020badfe49730f1529b9c1a15248c387 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 23 Nov 2013 17:40:02 -0500 Subject: kernfs: implement "trusted.*" xattr support kernfs inherited "security.*" xattr support from sysfs. This patch extends xattr support to "trusted.*" using simple_xattr_*(). As trusted xattrs are restricted to CAP_SYS_ADMIN, simple_xattr_*() which uses kernel memory for storage shouldn't be problematic. Note that the existing "security.*" support doesn't implement get/remove/list and the this patch only implements those ops for "trusted.*". We probably want to extend those ops to include support for "security.*". This patch will allow using kernfs from cgroup which requires "trusted.*" xattr support. Signed-off-by: Tejun Heo Cc: David P. Quigley Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 12 ++++++--- fs/kernfs/inode.c | 63 +++++++++++++++++++++++++++++++++++++++------ fs/kernfs/kernfs-internal.h | 7 +++++ fs/kernfs/symlink.c | 3 +++ 4 files changed, 74 insertions(+), 11 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index f51e062..a441e3b 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -243,9 +243,12 @@ void kernfs_put(struct sysfs_dirent *sd) kernfs_put(sd->s_symlink.target_sd); if (sysfs_type(sd) & SYSFS_COPY_NAME) kfree(sd->s_name); - if (sd->s_iattr && sd->s_iattr->ia_secdata) - security_release_secctx(sd->s_iattr->ia_secdata, - sd->s_iattr->ia_secdata_len); + if (sd->s_iattr) { + if (sd->s_iattr->ia_secdata) + security_release_secctx(sd->s_iattr->ia_secdata, + sd->s_iattr->ia_secdata_len); + simple_xattrs_free(&sd->s_iattr->xattrs); + } kfree(sd->s_iattr); ida_simple_remove(&root->ino_ida, sd->s_ino); kmem_cache_free(sysfs_dir_cachep, sd); @@ -718,6 +721,9 @@ const struct inode_operations sysfs_dir_inode_operations = { .setattr = sysfs_setattr, .getattr = sysfs_getattr, .setxattr = sysfs_setxattr, + .removexattr = sysfs_removexattr, + .getxattr = sysfs_getxattr, + .listxattr = sysfs_listxattr, }; static struct sysfs_dirent *sysfs_leftmost_descendant(struct sysfs_dirent *pos) diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index a1f8382..18ad431 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -35,6 +35,9 @@ static const struct inode_operations sysfs_inode_operations = { .setattr = sysfs_setattr, .getattr = sysfs_getattr, .setxattr = sysfs_setxattr, + .removexattr = sysfs_removexattr, + .getxattr = sysfs_getxattr, + .listxattr = sysfs_listxattr, }; void __init sysfs_inode_init(void) @@ -61,6 +64,8 @@ static struct sysfs_inode_attrs *sysfs_inode_attrs(struct sysfs_dirent *sd) iattrs->ia_gid = GLOBAL_ROOT_GID; iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME; + simple_xattrs_init(&sd->s_iattr->xattrs); + return sd->s_iattr; } @@ -162,23 +167,25 @@ int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct sysfs_dirent *sd = dentry->d_fsdata; + struct sysfs_inode_attrs *attrs; void *secdata; int error; u32 secdata_len = 0; - if (!sd) - return -EINVAL; + attrs = sysfs_inode_attrs(sd); + if (!attrs) + return -ENOMEM; if (!strncmp(name, XATTR_SECURITY_PREFIX, XATTR_SECURITY_PREFIX_LEN)) { const char *suffix = name + XATTR_SECURITY_PREFIX_LEN; error = security_inode_setsecurity(dentry->d_inode, suffix, value, size, flags); if (error) - goto out; + return error; error = security_inode_getsecctx(dentry->d_inode, &secdata, &secdata_len); if (error) - goto out; + return error; mutex_lock(&sysfs_mutex); error = sysfs_sd_setsecdata(sd, &secdata, &secdata_len); @@ -186,10 +193,50 @@ int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, if (secdata) security_release_secctx(secdata, secdata_len); - } else - return -EINVAL; -out: - return error; + return error; + } else if (!strncmp(name, XATTR_TRUSTED_PREFIX, XATTR_TRUSTED_PREFIX_LEN)) { + return simple_xattr_set(&attrs->xattrs, name, value, size, + flags); + } + + return -EINVAL; +} + +int sysfs_removexattr(struct dentry *dentry, const char *name) +{ + struct sysfs_dirent *sd = dentry->d_fsdata; + struct sysfs_inode_attrs *attrs; + + attrs = sysfs_inode_attrs(sd); + if (!attrs) + return -ENOMEM; + + return simple_xattr_remove(&attrs->xattrs, name); +} + +ssize_t sysfs_getxattr(struct dentry *dentry, const char *name, void *buf, + size_t size) +{ + struct sysfs_dirent *sd = dentry->d_fsdata; + struct sysfs_inode_attrs *attrs; + + attrs = sysfs_inode_attrs(sd); + if (!attrs) + return -ENOMEM; + + return simple_xattr_get(&attrs->xattrs, name, buf, size); +} + +ssize_t sysfs_listxattr(struct dentry *dentry, char *buf, size_t size) +{ + struct sysfs_dirent *sd = dentry->d_fsdata; + struct sysfs_inode_attrs *attrs; + + attrs = sysfs_inode_attrs(sd); + if (!attrs) + return -ENOMEM; + + return simple_xattr_list(&attrs->xattrs, buf, size); } static inline void set_default_inode_attr(struct inode *inode, umode_t mode) diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index f25b354..910e485 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -14,6 +14,7 @@ #include #include #include +#include #include @@ -21,6 +22,8 @@ struct sysfs_inode_attrs { struct iattr ia_iattr; void *ia_secdata; u32 ia_secdata_len; + + struct simple_xattrs xattrs; }; #define SD_DEACTIVATED_BIAS INT_MIN @@ -81,6 +84,10 @@ int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat); int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags); +int sysfs_removexattr(struct dentry *dentry, const char *name); +ssize_t sysfs_getxattr(struct dentry *dentry, const char *name, void *buf, + size_t size); +ssize_t sysfs_listxattr(struct dentry *dentry, char *buf, size_t size); void sysfs_inode_init(void); /* diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 12569a7..adf2875 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -140,6 +140,9 @@ static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, const struct inode_operations sysfs_symlink_inode_operations = { .setxattr = sysfs_setxattr, + .removexattr = sysfs_removexattr, + .getxattr = sysfs_getxattr, + .listxattr = sysfs_listxattr, .readlink = generic_readlink, .follow_link = sysfs_follow_link, .put_link = sysfs_put_link, -- cgit v1.1 From 89c86a64cd056e283323710c9ddf6f7090a450c8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Dec 2013 17:37:51 -0700 Subject: kobject: delay kobject release for random time When CONFIG_DEBUG_KOBJECT_RELEASE=y, delay kobject release functions for a random time between 1 and 8 seconds, which effectively changes the order in which they're called. Signed-off-by: Bjorn Helgaas Signed-off-by: Greg Kroah-Hartman --- lib/kobject.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/kobject.c b/lib/kobject.c index b8d848f..1d110dc9 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -18,6 +18,7 @@ #include #include #include +#include /** * kobject_namespace - return @kobj's namespace tag @@ -642,10 +643,12 @@ static void kobject_release(struct kref *kref) { struct kobject *kobj = container_of(kref, struct kobject, kref); #ifdef CONFIG_DEBUG_KOBJECT_RELEASE - pr_info("kobject: '%s' (%p): %s, parent %p (delayed)\n", - kobject_name(kobj), kobj, __func__, kobj->parent); + unsigned long delay = HZ + HZ * (get_random_int() & 0x3); + pr_info("kobject: '%s' (%p): %s, parent %p (delayed %ld)\n", + kobject_name(kobj), kobj, __func__, kobj->parent, delay); INIT_DELAYED_WORK(&kobj->release, kobject_delayed_cleanup); - schedule_delayed_work(&kobj->release, HZ); + + schedule_delayed_work(&kobj->release, delay); #else kobject_cleanup(kobj); #endif -- cgit v1.1 From 35a5fe695b07ae899510ad76fdf0aeaef85fe951 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Dec 2013 17:38:00 -0700 Subject: kobject: remove kset from sysfs immediately in kset_unregister() There's no "unlink from sysfs" interface for ksets, so I think callers of kset_unregister() expect the kset to be removed from sysfs immediately, without waiting for the last reference to be released. This patch makes the sysfs removal happen immediately, so the caller may create a new kset with the same name as soon as kset_unregister() returns. Without this, every caller has to call "kobject_del(&kset->kobj)" first unless it knows it will never create a new kset with the same name. This sometimes shows up on module unload and reload, where the reload fails because it tries to create a kobject with the same name as one from the original load that still exists. CONFIG_DEBUG_KOBJECT_RELEASE=y makes this problem easier to hit. Signed-off-by: Bjorn Helgaas Signed-off-by: Greg Kroah-Hartman --- Documentation/kobject.txt | 5 ++++- lib/kobject.c | 1 + 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/kobject.txt b/Documentation/kobject.txt index c5182bb..f87241d 100644 --- a/Documentation/kobject.txt +++ b/Documentation/kobject.txt @@ -342,7 +342,10 @@ kset use: When you are finished with the kset, call: void kset_unregister(struct kset *kset); -to destroy it. +to destroy it. This removes the kset from sysfs and decrements its reference +count. When the reference count goes to zero, the kset will be released. +Because other references to the kset may still exist, the release may happen +after kset_unregister() returns. An example of using a kset can be seen in the samples/kobject/kset-example.c file in the kernel tree. diff --git a/lib/kobject.c b/lib/kobject.c index 1d110dc9..98b45bb 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -855,6 +855,7 @@ void kset_unregister(struct kset *k) { if (!k) return; + kobject_del(&k->kobj); kobject_put(&k->kobj); } -- cgit v1.1 From 21d71662f895462abaa3054d504af55a306f42ba Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Sat, 7 Dec 2013 14:07:36 +0800 Subject: sysfs, kernfs: remove duplicated include from file.c Remove duplicated include. Signed-off-by: Wei Yongjun Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/file.c | 1 - 1 file changed, 1 deletion(-) diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 990c97f..4a5863b 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -13,7 +13,6 @@ #include #include #include -#include #include #include "kernfs-internal.h" -- cgit v1.1 From 020d30f17f196dcbf0c2c68a874345e8885a3149 Mon Sep 17 00:00:00 2001 From: Maurizio Lombardi Date: Fri, 8 Nov 2013 15:28:25 +0100 Subject: kobject: fix memory leak in kobject_set_name_vargs If the call to kvasprintf fails then the old name of the object will be leaked, this patch fixes the bug by restoring the old name before returning ENOMEM. Signed-off-by: Maurizio Lombardi Signed-off-by: Greg Kroah-Hartman --- lib/kobject.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/kobject.c b/lib/kobject.c index 98b45bb..94b321f 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -265,8 +265,10 @@ int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, return 0; kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs); - if (!kobj->name) + if (!kobj->name) { + kobj->name = old_name; return -ENOMEM; + } /* ewww... some of these buggers have '/' in the name ... */ while ((s = strchr(kobj->name, '/'))) -- cgit v1.1 From bba3a87e982ad5992e776ca1fc409326915d6b44 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Dec 2013 15:38:16 +0100 Subject: firmware: Introduce request_firmware_direct() When CONFIG_FW_LOADER_USER_HELPER is set, request_firmware() falls back to the usermode helper for loading via udev when the direct loading fails. But the recent udev takes way too long timeout (60 seconds) for non-existing firmware. This is unacceptable for the drivers like microcode loader where they load firmwares optionally, i.e. it's no error even if no requested file exists. This patch provides a new helper function, request_firmware_direct(). It behaves as same as request_firmware() except for that it doesn't fall back to usermode helper but returns an error immediately if the f/w can't be loaded directly in kernel. Without CONFIG_FW_LOADER_USER_HELPER=y, request_firmware_direct() is just an alias of request_firmware(), due to obvious reason. Tested-by: Prarit Bhargava Acked-by: Ming Lei Acked-by: Borislav Petkov Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 41 ++++++++++++++++++++++++++++++++++------- include/linux/firmware.h | 7 +++++++ 2 files changed, 41 insertions(+), 7 deletions(-) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index eb8fb94..1af0364 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -1061,7 +1061,7 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device, /* called from request_firmware() and request_firmware_work_func() */ static int _request_firmware(const struct firmware **firmware_p, const char *name, - struct device *device, bool uevent, bool nowait) + struct device *device, bool uevent, bool nowait, bool fallback) { struct firmware *fw; long timeout; @@ -1095,11 +1095,14 @@ _request_firmware(const struct firmware **firmware_p, const char *name, ret = fw_get_filesystem_firmware(device, fw->priv); if (ret) { - dev_warn(device, "Direct firmware load failed with error %d\n", - ret); - dev_warn(device, "Falling back to user helper\n"); - ret = fw_load_from_user_helper(fw, name, device, + if (fallback) { + dev_warn(device, + "Direct firmware load failed with error %d\n", + ret); + dev_warn(device, "Falling back to user helper\n"); + ret = fw_load_from_user_helper(fw, name, device, uevent, nowait, timeout); + } } /* don't cache firmware handled without uevent */ @@ -1146,12 +1149,36 @@ request_firmware(const struct firmware **firmware_p, const char *name, /* Need to pin this module until return */ __module_get(THIS_MODULE); - ret = _request_firmware(firmware_p, name, device, true, false); + ret = _request_firmware(firmware_p, name, device, true, false, true); module_put(THIS_MODULE); return ret; } EXPORT_SYMBOL(request_firmware); +#ifdef CONFIG_FW_LOADER_USER_HELPER +/** + * request_firmware: - load firmware directly without usermode helper + * @firmware_p: pointer to firmware image + * @name: name of firmware file + * @device: device for which firmware is being loaded + * + * This function works pretty much like request_firmware(), but this doesn't + * fall back to usermode helper even if the firmware couldn't be loaded + * directly from fs. Hence it's useful for loading optional firmwares, which + * aren't always present, without extra long timeouts of udev. + **/ +int request_firmware_direct(const struct firmware **firmware_p, + const char *name, struct device *device) +{ + int ret; + __module_get(THIS_MODULE); + ret = _request_firmware(firmware_p, name, device, true, false, false); + module_put(THIS_MODULE); + return ret; +} +EXPORT_SYMBOL_GPL(request_firmware_direct); +#endif + /** * release_firmware: - release the resource associated with a firmware image * @fw: firmware resource to release @@ -1185,7 +1212,7 @@ static void request_firmware_work_func(struct work_struct *work) fw_work = container_of(work, struct firmware_work, work); _request_firmware(&fw, fw_work->name, fw_work->device, - fw_work->uevent, true); + fw_work->uevent, true, true); fw_work->cont(fw, fw_work->context); put_device(fw_work->device); /* taken in request_firmware_nowait() */ diff --git a/include/linux/firmware.h b/include/linux/firmware.h index e154c10..5952933 100644 --- a/include/linux/firmware.h +++ b/include/linux/firmware.h @@ -68,4 +68,11 @@ static inline void release_firmware(const struct firmware *fw) #endif +#ifdef CONFIG_FW_LOADER_USER_HELPER +int request_firmware_direct(const struct firmware **fw, const char *name, + struct device *device); +#else +#define request_firmware_direct request_firmware +#endif + #endif -- cgit v1.1 From 75da02b29fd99cb2d6ac822f2864de4d6e35b9fd Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Dec 2013 15:38:17 +0100 Subject: microcode: Use request_firmware_direct() Use the new helper, request_firmware_direct(), for avoiding the lengthy timeout of non-existing firmware loads. Especially the Intel microcode driver suffers from this problem because each CPU triggers the f/w loading, thus it ends up taking (literally) hours with many cores. Tested-by: Prarit Bhargava Acked-by: Borislav Petkov Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- arch/x86/kernel/microcode_amd.c | 2 +- arch/x86/kernel/microcode_intel.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/x86/kernel/microcode_amd.c b/arch/x86/kernel/microcode_amd.c index c3d4cc9..22b3a11 100644 --- a/arch/x86/kernel/microcode_amd.c +++ b/arch/x86/kernel/microcode_amd.c @@ -430,7 +430,7 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device, if (c->x86 >= 0x15) snprintf(fw_name, sizeof(fw_name), "amd-ucode/microcode_amd_fam%.2xh.bin", c->x86); - if (request_firmware(&fw, (const char *)fw_name, device)) { + if (request_firmware_direct(&fw, (const char *)fw_name, device)) { pr_debug("failed to load file %s\n", fw_name); goto out; } diff --git a/arch/x86/kernel/microcode_intel.c b/arch/x86/kernel/microcode_intel.c index 5fb2ceb..a276fa7 100644 --- a/arch/x86/kernel/microcode_intel.c +++ b/arch/x86/kernel/microcode_intel.c @@ -278,7 +278,7 @@ static enum ucode_state request_microcode_fw(int cpu, struct device *device, sprintf(name, "intel-ucode/%02x-%02x-%02x", c->x86, c->x86_model, c->x86_mask); - if (request_firmware(&firmware, name, device)) { + if (request_firmware_direct(&firmware, name, device)) { pr_debug("data file %s load failed\n", name); return UCODE_NFOUND; } -- cgit v1.1 From 14c4bae77c80bd37f19d95405d42bd0b1fd95add Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Dec 2013 15:38:18 +0100 Subject: firmware: Use bit flags instead of boolean combos More than two boolean arguments to a function are rather confusing and error-prone for callers. Let's make the behavior bit flags instead of triple combos. A nice suggestion by Borislav Petkov. Acked-by: Borislav Petkov Acked-by: Prarit Bhargava Acked-by: Ming Lei Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 51 ++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 1af0364..6579774 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -96,6 +96,11 @@ static inline long firmware_loading_timeout(void) return loading_timeout > 0 ? loading_timeout * HZ : MAX_SCHEDULE_TIMEOUT; } +/* firmware behavior options */ +#define FW_OPT_UEVENT (1U << 0) +#define FW_OPT_NOWAIT (1U << 1) +#define FW_OPT_FALLBACK (1U << 2) + struct firmware_cache { /* firmware_buf instance will be added into the below list */ spinlock_t lock; @@ -820,7 +825,7 @@ static void firmware_class_timeout_work(struct work_struct *work) static struct firmware_priv * fw_create_instance(struct firmware *firmware, const char *fw_name, - struct device *device, bool uevent, bool nowait) + struct device *device, unsigned int opt_flags) { struct firmware_priv *fw_priv; struct device *f_dev; @@ -832,7 +837,7 @@ fw_create_instance(struct firmware *firmware, const char *fw_name, goto exit; } - fw_priv->nowait = nowait; + fw_priv->nowait = !!(opt_flags & FW_OPT_NOWAIT); fw_priv->fw = firmware; INIT_DELAYED_WORK(&fw_priv->timeout_work, firmware_class_timeout_work); @@ -848,8 +853,8 @@ exit: } /* load a firmware via user helper */ -static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, - long timeout) +static int _request_firmware_load(struct firmware_priv *fw_priv, + unsigned int opt_flags, long timeout) { int retval = 0; struct device *f_dev = &fw_priv->dev; @@ -885,7 +890,7 @@ static int _request_firmware_load(struct firmware_priv *fw_priv, bool uevent, goto err_del_bin_attr; } - if (uevent) { + if (opt_flags & FW_OPT_UEVENT) { buf->need_uevent = true; dev_set_uevent_suppress(f_dev, false); dev_dbg(f_dev, "firmware: requesting %s\n", buf->fw_id); @@ -911,16 +916,16 @@ err_put_dev: static int fw_load_from_user_helper(struct firmware *firmware, const char *name, struct device *device, - bool uevent, bool nowait, long timeout) + unsigned int opt_flags, long timeout) { struct firmware_priv *fw_priv; - fw_priv = fw_create_instance(firmware, name, device, uevent, nowait); + fw_priv = fw_create_instance(firmware, name, device, opt_flags); if (IS_ERR(fw_priv)) return PTR_ERR(fw_priv); fw_priv->buf = firmware->priv; - return _request_firmware_load(fw_priv, uevent, timeout); + return _request_firmware_load(fw_priv, opt_flags, timeout); } #ifdef CONFIG_PM_SLEEP @@ -942,7 +947,7 @@ static void kill_requests_without_uevent(void) #else /* CONFIG_FW_LOADER_USER_HELPER */ static inline int fw_load_from_user_helper(struct firmware *firmware, const char *name, - struct device *device, bool uevent, bool nowait, + struct device *device, unsigned int opt_flags, long timeout) { return -ENOENT; @@ -1023,7 +1028,7 @@ _request_firmware_prepare(struct firmware **firmware_p, const char *name, } static int assign_firmware_buf(struct firmware *fw, struct device *device, - bool skip_cache) + unsigned int opt_flags) { struct firmware_buf *buf = fw->priv; @@ -1040,7 +1045,8 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device, * device may has been deleted already, but the problem * should be fixed in devres or driver core. */ - if (device && !skip_cache) + /* don't cache firmware handled without uevent */ + if (device && (opt_flags & FW_OPT_UEVENT)) fw_add_devm_name(device, buf->fw_id); /* @@ -1061,7 +1067,7 @@ static int assign_firmware_buf(struct firmware *fw, struct device *device, /* called from request_firmware() and request_firmware_work_func() */ static int _request_firmware(const struct firmware **firmware_p, const char *name, - struct device *device, bool uevent, bool nowait, bool fallback) + struct device *device, unsigned int opt_flags) { struct firmware *fw; long timeout; @@ -1076,7 +1082,7 @@ _request_firmware(const struct firmware **firmware_p, const char *name, ret = 0; timeout = firmware_loading_timeout(); - if (nowait) { + if (opt_flags & FW_OPT_NOWAIT) { timeout = usermodehelper_read_lock_wait(timeout); if (!timeout) { dev_dbg(device, "firmware: %s loading timed out\n", @@ -1095,19 +1101,18 @@ _request_firmware(const struct firmware **firmware_p, const char *name, ret = fw_get_filesystem_firmware(device, fw->priv); if (ret) { - if (fallback) { + if (opt_flags & FW_OPT_FALLBACK) { dev_warn(device, "Direct firmware load failed with error %d\n", ret); dev_warn(device, "Falling back to user helper\n"); ret = fw_load_from_user_helper(fw, name, device, - uevent, nowait, timeout); + opt_flags, timeout); } } - /* don't cache firmware handled without uevent */ if (!ret) - ret = assign_firmware_buf(fw, device, !uevent); + ret = assign_firmware_buf(fw, device, opt_flags); usermodehelper_read_unlock(); @@ -1149,7 +1154,8 @@ request_firmware(const struct firmware **firmware_p, const char *name, /* Need to pin this module until return */ __module_get(THIS_MODULE); - ret = _request_firmware(firmware_p, name, device, true, false, true); + ret = _request_firmware(firmware_p, name, device, + FW_OPT_UEVENT | FW_OPT_FALLBACK); module_put(THIS_MODULE); return ret; } @@ -1172,7 +1178,7 @@ int request_firmware_direct(const struct firmware **firmware_p, { int ret; __module_get(THIS_MODULE); - ret = _request_firmware(firmware_p, name, device, true, false, false); + ret = _request_firmware(firmware_p, name, device, FW_OPT_UEVENT); module_put(THIS_MODULE); return ret; } @@ -1201,7 +1207,7 @@ struct firmware_work { struct device *device; void *context; void (*cont)(const struct firmware *fw, void *context); - bool uevent; + unsigned int opt_flags; }; static void request_firmware_work_func(struct work_struct *work) @@ -1212,7 +1218,7 @@ static void request_firmware_work_func(struct work_struct *work) fw_work = container_of(work, struct firmware_work, work); _request_firmware(&fw, fw_work->name, fw_work->device, - fw_work->uevent, true, true); + fw_work->opt_flags); fw_work->cont(fw, fw_work->context); put_device(fw_work->device); /* taken in request_firmware_nowait() */ @@ -1260,7 +1266,8 @@ request_firmware_nowait( fw_work->device = device; fw_work->context = context; fw_work->cont = cont; - fw_work->uevent = uevent; + fw_work->opt_flags = FW_OPT_NOWAIT | FW_OPT_FALLBACK | + (uevent ? FW_OPT_UEVENT : 0); if (!try_module_get(module)) { kfree(fw_work); -- cgit v1.1 From 68aeeaaaf86b61c78dca09879ce262778a9080e4 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 2 Dec 2013 15:38:19 +0100 Subject: firmware: Suppress fallback warnings when CONFIG_FW_LOADER_USER_HELPER=n The commit [3e358ac2bb5b: firmware: Be a bit more verbose about direct firmware loading failure] introduced a new warning message about falling back to user helper, but this isn't true when CONFIG_FW_LOADER_USER_HELPER isn't set. In this patch, clear the FW_OPT_FALLBACK flag in the case without userhelper, so that the corresponding code will be disabled. Signed-off-by: Takashi Iwai Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 6579774..33b87bf 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -99,7 +99,11 @@ static inline long firmware_loading_timeout(void) /* firmware behavior options */ #define FW_OPT_UEVENT (1U << 0) #define FW_OPT_NOWAIT (1U << 1) +#ifdef CONFIG_FW_LOADER_USER_HELPER #define FW_OPT_FALLBACK (1U << 2) +#else +#define FW_OPT_FALLBACK 0 +#endif struct firmware_cache { /* firmware_buf instance will be added into the below list */ -- cgit v1.1 From a61aca2854eaecf2d1bffbaf1fc368fb1a20c850 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Dec 2013 16:46:28 -0700 Subject: firmware: dmi-sysfs: Don't remove dmi-sysfs "raw" file explicitly Removing the dmi-sysfs module causes the following warning: # modprobe -r dmi_sysfs WARNING: CPU: 11 PID: 6785 at fs/sysfs/inode.c:325 sysfs_hash_and_remove+0xa9/0xb0() sysfs: can not remove 'raw', no directory This is because putting the entry kobject, e.g., for "/sys/firmware/dmi/entries/19-0", removes the directory and all its contents. By the time dmi_sysfs_entry_release() runs, the "raw" file inside ".../19-0/" has already been removed. Therefore, we don't need to remove the "raw" bin file at all in dmi_sysfs_entry_release(). Signed-off-by: Bjorn Helgaas Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/dmi-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index eb26d62..66200ed 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -553,7 +553,7 @@ static const struct bin_attribute dmi_entry_raw_attr = { static void dmi_sysfs_entry_release(struct kobject *kobj) { struct dmi_sysfs_entry *entry = to_entry(kobj); - sysfs_remove_bin_file(&entry->kobj, &dmi_entry_raw_attr); + spin_lock(&entry_list_lock); list_del(&entry->list); spin_unlock(&entry_list_lock); -- cgit v1.1 From d0f80f9aadf60adc4caafed0d2b01e79a315ff80 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 5 Dec 2013 16:46:35 -0700 Subject: firmware: dmi-sysfs: Remove "dmi" directory on module exit With CONFIG_DEBUG_KOBJECT_RELEASE=y, removing and immediately reloading the dmi-sysfs module causes the following warning: sysfs: cannot create duplicate filename '/firmware/dmi' kobject_add_internal failed for dmi with -EEXIST, don't try to register things with the same name in the same directory. The "dmi" directory stays in sysfs until the dmi_kobj is released, and DEBUG_KOBJECT_RELEASE delays that. I don't think we can hit this problem in normal usage because dmi_kobj is static and nothing outside dmi-sysfs can get a reference to it, so the only way to delay the "dmi" release is with DEBUG_KOBJECT_RELEASE. Signed-off-by: Bjorn Helgaas Signed-off-by: Greg Kroah-Hartman --- drivers/firmware/dmi-sysfs.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/firmware/dmi-sysfs.c b/drivers/firmware/dmi-sysfs.c index 66200ed..e0f1cb3 100644 --- a/drivers/firmware/dmi-sysfs.c +++ b/drivers/firmware/dmi-sysfs.c @@ -685,6 +685,7 @@ static void __exit dmi_sysfs_exit(void) pr_debug("dmi-sysfs: unloading.\n"); cleanup_entry_list(); kset_unregister(dmi_kset); + kobject_del(dmi_kobj); kobject_put(dmi_kobj); } -- cgit v1.1 From bbc780f8bab52fef1784151d3c4982cb1143edd2 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Thu, 21 Nov 2013 20:15:48 +0100 Subject: driver core: fix device_create() error path We call put_device() in the error path, which is fine for dev==NULL. However, in case kobject_set_name_vargs() fails, we have dev!=NULL but device_initialized() wasn't called, yet. Fix this by splitting device_register() into explicit calls to device_add() and an early call to device_initialize(). Signed-off-by: David Herrmann Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 67b180d..aab43fb 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1603,6 +1603,7 @@ device_create_groups_vargs(struct class *class, struct device *parent, goto error; } + device_initialize(dev); dev->devt = devt; dev->class = class; dev->parent = parent; @@ -1614,7 +1615,7 @@ device_create_groups_vargs(struct class *class, struct device *parent, if (retval) goto error; - retval = device_register(dev); + retval = device_add(dev); if (retval) goto error; -- cgit v1.1 From 9b2db6e1894577d48f4e290381bac6e573593838 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 10 Dec 2013 09:29:17 -0500 Subject: sysfs: bail early from kernfs_file_mmap() to avoid spurious lockdep warning This is v3.14 fix for the same issue that a8b14744429f ("sysfs: give different locking key to regular and bin files") addresses for v3.13. Due to the extensive kernfs reorganization in v3.14 branch, the same fix couldn't be ported as-is. The v3.13 fix was ignored while merging it into v3.14 branch. 027a485d12e0 ("sysfs: use a separate locking class for open files depending on mmap") assigned different lockdep key to sysfs_open_file->mutex depending on whether the file implements mmap or not in an attempt to avoid spurious lockdep warning caused by merging of regular and bin file paths. While this restored some of the original behavior of using different locks (at least lockdep is concerned) for the different clases of files. The restoration wasn't full because now the lockdep key assignment depends on whether the file has mmap or not instead of whether it's a regular file or not. This means that bin files which don't implement mmap will get assigned the same lockdep class as regular files. This is problematic because file_operations for bin files still implements the mmap file operation and checking whether the sysfs file actually implements mmap happens in the file operation after grabbing @sysfs_open_file->mutex. We still end up adding locking dependency from mmap locking to sysfs_open_file->mutex to the regular file mutex which triggers spurious circular locking warning. For v3.13, a8b14744429f ("sysfs: give different locking key to regular and bin files") fixed it by giving sysfs_open_file->mutex different lockdep keys depending on whether the file is regular or bin instead of whether mmap exists or not; however, due to the way sysfs is now layered behind kernfs, this approach is no longer viable. kernfs can tell whether a sysfs node has mmap implemented or not but can't tell whether a bin file from a regular one. This patch updates kernfs such that kernfs_file_mmap() checks SYSFS_FLAG_HAS_MMAP and bail before grabbing sysfs_open_file->mutex so that it doesn't add spurious locking dependency from mmap to sysfs_open_file->mutex and changes sysfs so that it specifies kernfs_ops->mmap iff the sysfs file implements mmap. Combined, this ensures that sysfs_open_file->mutex is grabbed under mmap path iff the sysfs file actually implements mmap. As sysfs_open_file->mutex is already given a different lockdep key if mmap is implemented, this removes the spurious locking dependency. Signed-off-by: Tejun Heo Reported-by: Dave Jones Link: http://lkml.kernel.org/g/20131203184324.GA11320@redhat.com Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/file.c | 18 ++++++++++++++---- fs/sysfs/file.c | 12 ++++++++---- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 4a5863b..fa05315 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -421,6 +421,16 @@ static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) const struct kernfs_ops *ops; int rc; + /* + * mmap path and of->mutex are prone to triggering spurious lockdep + * warnings and we don't want to add spurious locking dependency + * between the two. Check whether mmap is actually implemented + * without grabbing @of->mutex by testing HAS_MMAP flag. See the + * comment in kernfs_file_open() for more details. + */ + if (!(of->sd->s_flags & SYSFS_FLAG_HAS_MMAP)) + return -ENODEV; + mutex_lock(&of->mutex); rc = -ENODEV; @@ -428,10 +438,7 @@ static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) goto out_unlock; ops = kernfs_ops(of->sd); - if (ops->mmap) - rc = ops->mmap(of, vma); - if (rc) - goto out_put; + rc = ops->mmap(of, vma); /* * PowerPC's pci_mmap of legacy_mem uses shmem_zero_setup() @@ -596,6 +603,9 @@ static int kernfs_file_open(struct inode *inode, struct file *file) * happen on the same file. At this point, we can't easily give * each file a separate locking class. Let's differentiate on * whether the file has mmap or not for now. + * + * Both paths of the branch look the same. They're supposed to + * look that way and give @of->mutex different static lockdep keys. */ if (has_mmap) mutex_init(&of->mutex); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index ac77d2b..a67d1c6 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -142,9 +142,6 @@ static int sysfs_kf_bin_mmap(struct sysfs_open_file *of, struct bin_attribute *battr = of->sd->priv; struct kobject *kobj = of->sd->s_parent->priv; - if (!battr->mmap) - return -ENODEV; - return battr->mmap(of->file, kobj, battr, vma); } @@ -197,6 +194,11 @@ static const struct kernfs_ops sysfs_bin_kfops_wo = { static const struct kernfs_ops sysfs_bin_kfops_rw = { .read = sysfs_kf_bin_read, .write = sysfs_kf_bin_write, +}; + +static const struct kernfs_ops sysfs_bin_kfops_mmap = { + .read = sysfs_kf_bin_read, + .write = sysfs_kf_bin_write, .mmap = sysfs_kf_bin_mmap, }; @@ -232,7 +234,9 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, } else { struct bin_attribute *battr = (void *)attr; - if ((battr->read && battr->write) || battr->mmap) + if (battr->mmap) + ops = &sysfs_bin_kfops_mmap; + else if (battr->read && battr->write) ops = &sysfs_bin_kfops_rw; else if (battr->read) ops = &sysfs_bin_kfops_ro; -- cgit v1.1 From a7560a0132cfc93b25d2df1d277a078a05220cf4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 10 Dec 2013 10:22:30 -0500 Subject: sysfs: fix use-after-free in sysfs_kill_sb() While restructuring the [u]mount path, 4b93dc9b1c68 ("sysfs, kernfs: prepare mount path for kernfs") incorrectly updated sysfs_kill_sb() so that it first kills super_block and then tries to dereference its namespace tag to drop it. Fix it by caching namespace tag before killing the superblock and then drop the cached namespace tag. Signed-off-by: Tejun Heo Reported-by: Yuanhan Liu Tested-by: Yuanhan Liu Tested-by: Vlastimil Babka Link: http://lkml.kernel.org/g/20131205031051.GC5135@yliu-dev.sh.intel.com Signed-off-by: Greg Kroah-Hartman --- fs/sysfs/mount.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index e7e3aa8..8d07527 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -45,8 +45,10 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type, static void sysfs_kill_sb(struct super_block *sb) { + void *ns = (void *)kernfs_super_ns(sb); + kernfs_kill_sb(sb); - kobj_ns_drop(KOBJ_NS_TYPE_NET, (void *)kernfs_super_ns(sb)); + kobj_ns_drop(KOBJ_NS_TYPE_NET, ns); } static struct file_system_type sysfs_fs_type = { -- cgit v1.1 From a8b1c0193602b7ecdeaa7aa8c15c9c3da33244c8 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 4 Dec 2013 13:22:37 +0100 Subject: Documentation: start documenting driver design patterns After realizing that we tend to tell developers the same thing over and over, let's attempt to document some commin design patterns used in the device drivers. The idea is that this can be extended so I just start out with two well-known design patterns. Cc: Rob Landley Cc: Mark Brown Cc: Arnd Bergmann Cc: Grant Likely Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-model/design-patterns.txt | 116 +++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 Documentation/driver-model/design-patterns.txt diff --git a/Documentation/driver-model/design-patterns.txt b/Documentation/driver-model/design-patterns.txt new file mode 100644 index 0000000..9ef8c16 --- /dev/null +++ b/Documentation/driver-model/design-patterns.txt @@ -0,0 +1,116 @@ + +Device Driver Design Patterns +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +This document describes a few common design patterns found in device drivers. +It is likely that subsystem maintainers will ask driver developers to +conform to these design patterns. + +1. State Container +2. container_of() + + +1. State Container +~~~~~~~~~~~~~~~~~~ + +While the kernel contains a few device drivers that assume that they will +only be probed() once on a certain system (singletons), it is custom to assume +that the device the driver binds to will appear in several instances. This +means that the probe() function and all callbacks need to be reentrant. + +The most common way to achieve this is to use the state container design +pattern. It usually has this form: + +struct foo { + spinlock_t lock; /* Example member */ + (...) +}; + +static int foo_probe(...) +{ + struct foo *foo; + + foo = devm_kzalloc(dev, sizeof(*foo), GFP_KERNEL); + if (!foo) + return -ENOMEM; + spin_lock_init(&foo->lock); + (...) +} + +This will create an instance of struct foo in memory every time probe() is +called. This is our state container for this instance of the device driver. +Of course it is then necessary to always pass this instance of the +state around to all functions that need access to the state and its members. + +For example, if the driver is registering an interrupt handler, you would +pass around a pointer to struct foo like this: + +static irqreturn_t foo_handler(int irq, void *arg) +{ + struct foo *foo = arg; + (...) +} + +static int foo_probe(...) +{ + struct foo *foo; + + (...) + ret = request_irq(irq, foo_handler, 0, "foo", foo); +} + +This way you always get a pointer back to the correct instance of foo in +your interrupt handler. + + +2. container_of() +~~~~~~~~~~~~~~~~~ + +Continuing on the above example we add a offloaded work: + +struct foo { + spinlock_t lock; + struct workqueue_struct *wq; + struct work_struct offload; + (...) +}; + +static void foo_work(struct work_struct *work) +{ + struct foo *foo = container_of(work, struct foo, offload); + + (...) +} + +static irqreturn_t foo_handler(int irq, void *arg) +{ + struct foo *foo = arg; + + queue_work(foo->wq, &foo->offload); + (...) +} + +static int foo_probe(...) +{ + struct foo *foo; + + foo->wq = create_singlethread_workqueue("foo-wq"); + INIT_WORK(&foo->offload, foo_work); + (...) +} + +The design pattern is the same for a a hrtimer or something similar that will +return a single argument which is a pointer to a struct member in the +callback. + +container_of() is a macro defined in + +What container_of() does is to obtain a pointer to the containing struct from +a pointer to a member by a simple subtraction using the offsetof() macro from +standard C, which allows something similar to object oriented behaviours. +Notice that the contained member must not be a pointer, but an actual member +for this to work. + +We can see here that we avoid having global pointers to our struct foo * +instance this way, while still keeping the number of parameters passed to the +work function to a single pointer. -- cgit v1.1 From 324a56e16e44baecac3ca799fd216154145c14bf Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 11 Dec 2013 14:11:53 -0500 Subject: kernfs: s/sysfs_dirent/kernfs_node/ and rename its friends accordingly kernfs has just been separated out from sysfs and we're already in full conflict mode. Nothing can make the situation any worse. Let's take the chance to name things properly. This patch performs the following renames. * s/sysfs_elem_dir/kernfs_elem_dir/ * s/sysfs_elem_symlink/kernfs_elem_symlink/ * s/sysfs_elem_attr/kernfs_elem_file/ * s/sysfs_dirent/kernfs_node/ * s/sd/kn/ in kernfs proper * s/parent_sd/parent/ * s/target_sd/target/ * s/dir_sd/parent/ * s/to_sysfs_dirent()/rb_to_kn()/ * misc renames of local vars when they conflict with the above Because md, mic and gpio dig into sysfs details, this patch ends up modifying them. All are sysfs_dirent renames and trivial. While we can avoid these by introducing a dummy wrapping struct sysfs_dirent around kernfs_node, given the limited usage outside kernfs and sysfs proper, I don't think such workaround is called for. This patch is strictly rename only and doesn't introduce any functional difference. - mic / gpio renames were missing. Spotted by kbuild test robot. Signed-off-by: Tejun Heo Cc: Neil Brown Cc: Linus Walleij Cc: Ashutosh Dixit Cc: kbuild test robot Signed-off-by: Greg Kroah-Hartman --- drivers/gpio/gpiolib.c | 4 +- drivers/md/bitmap.c | 2 +- drivers/md/bitmap.h | 2 +- drivers/md/md.h | 10 +- drivers/misc/mic/host/mic_device.h | 2 +- fs/kernfs/dir.c | 542 ++++++++++++++++++------------------- fs/kernfs/file.c | 186 ++++++------- fs/kernfs/inode.c | 123 ++++----- fs/kernfs/kernfs-internal.h | 40 +-- fs/kernfs/mount.c | 12 +- fs/kernfs/symlink.c | 64 ++--- fs/sysfs/dir.c | 66 ++--- fs/sysfs/file.c | 122 ++++----- fs/sysfs/group.c | 96 +++---- fs/sysfs/mount.c | 4 +- fs/sysfs/symlink.c | 72 ++--- fs/sysfs/sysfs.h | 10 +- include/linux/kernfs.h | 166 ++++++------ include/linux/kobject.h | 2 +- include/linux/sysfs.h | 20 +- lib/kobject.c | 2 +- 21 files changed, 774 insertions(+), 773 deletions(-) diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 85f772c..c8a7c81 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -393,7 +393,7 @@ static const DEVICE_ATTR(value, 0644, static irqreturn_t gpio_sysfs_irq(int irq, void *priv) { - struct sysfs_dirent *value_sd = priv; + struct kernfs_node *value_sd = priv; sysfs_notify_dirent(value_sd); return IRQ_HANDLED; @@ -402,7 +402,7 @@ static irqreturn_t gpio_sysfs_irq(int irq, void *priv) static int gpio_setup_irq(struct gpio_desc *desc, struct device *dev, unsigned long gpio_flags) { - struct sysfs_dirent *value_sd; + struct kernfs_node *value_sd; unsigned long irq_flags; int ret, irq, id; diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c index 12dc29b..4195a01 100644 --- a/drivers/md/bitmap.c +++ b/drivers/md/bitmap.c @@ -1635,7 +1635,7 @@ int bitmap_create(struct mddev *mddev) sector_t blocks = mddev->resync_max_sectors; struct file *file = mddev->bitmap_info.file; int err; - struct sysfs_dirent *bm = NULL; + struct kernfs_node *bm = NULL; BUILD_BUG_ON(sizeof(bitmap_super_t) != 256); diff --git a/drivers/md/bitmap.h b/drivers/md/bitmap.h index df4aeb6..30210b9 100644 --- a/drivers/md/bitmap.h +++ b/drivers/md/bitmap.h @@ -225,7 +225,7 @@ struct bitmap { wait_queue_head_t overflow_wait; wait_queue_head_t behind_wait; - struct sysfs_dirent *sysfs_can_clear; + struct kernfs_node *sysfs_can_clear; }; /* the bitmap API */ diff --git a/drivers/md/md.h b/drivers/md/md.h index 2f5cc8a..389a3c9 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -106,7 +106,7 @@ struct md_rdev { */ struct work_struct del_work; /* used for delayed sysfs removal */ - struct sysfs_dirent *sysfs_state; /* handle for 'state' + struct kernfs_node *sysfs_state; /* handle for 'state' * sysfs entry */ struct badblocks { @@ -376,10 +376,10 @@ struct mddev { sector_t resync_max; /* resync should pause * when it gets here */ - struct sysfs_dirent *sysfs_state; /* handle for 'array_state' + struct kernfs_node *sysfs_state; /* handle for 'array_state' * file in sysfs. */ - struct sysfs_dirent *sysfs_action; /* handle for 'sync_action' */ + struct kernfs_node *sysfs_action; /* handle for 'sync_action' */ struct work_struct del_work; /* used for delayed sysfs removal */ @@ -498,13 +498,13 @@ struct md_sysfs_entry { }; extern struct attribute_group md_bitmap_group; -static inline struct sysfs_dirent *sysfs_get_dirent_safe(struct sysfs_dirent *sd, char *name) +static inline struct kernfs_node *sysfs_get_dirent_safe(struct kernfs_node *sd, char *name) { if (sd) return sysfs_get_dirent(sd, name); return sd; } -static inline void sysfs_notify_dirent_safe(struct sysfs_dirent *sd) +static inline void sysfs_notify_dirent_safe(struct kernfs_node *sd) { if (sd) sysfs_notify_dirent(sd); diff --git a/drivers/misc/mic/host/mic_device.h b/drivers/misc/mic/host/mic_device.h index 3574cc3..538e3d3 100644 --- a/drivers/misc/mic/host/mic_device.h +++ b/drivers/misc/mic/host/mic_device.h @@ -112,7 +112,7 @@ struct mic_device { struct work_struct shutdown_work; u8 state; u8 shutdown_status; - struct sysfs_dirent *state_sysfs; + struct kernfs_node *state_sysfs; struct completion reset_wait; void *log_buf_addr; int *log_buf_len; diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index a441e3b..800ebf5 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -19,7 +19,7 @@ DEFINE_MUTEX(sysfs_mutex); -#define to_sysfs_dirent(X) rb_entry((X), struct sysfs_dirent, s_rb) +#define rb_to_kn(X) rb_entry((X), struct kernfs_node, s_rb) /** * sysfs_name_hash @@ -45,28 +45,28 @@ static unsigned int sysfs_name_hash(const char *name, const void *ns) } static int sysfs_name_compare(unsigned int hash, const char *name, - const void *ns, const struct sysfs_dirent *sd) + const void *ns, const struct kernfs_node *kn) { - if (hash != sd->s_hash) - return hash - sd->s_hash; - if (ns != sd->s_ns) - return ns - sd->s_ns; - return strcmp(name, sd->s_name); + if (hash != kn->s_hash) + return hash - kn->s_hash; + if (ns != kn->s_ns) + return ns - kn->s_ns; + return strcmp(name, kn->s_name); } -static int sysfs_sd_compare(const struct sysfs_dirent *left, - const struct sysfs_dirent *right) +static int sysfs_sd_compare(const struct kernfs_node *left, + const struct kernfs_node *right) { return sysfs_name_compare(left->s_hash, left->s_name, left->s_ns, right); } /** - * sysfs_link_sibling - link sysfs_dirent into sibling rbtree - * @sd: sysfs_dirent of interest + * sysfs_link_sibling - link kernfs_node into sibling rbtree + * @kn: kernfs_node of interest * - * Link @sd into its sibling rbtree which starts from - * sd->s_parent->s_dir.children. + * Link @kn into its sibling rbtree which starts from + * @kn->s_parent->s_dir.children. * * Locking: * mutex_lock(sysfs_mutex) @@ -74,21 +74,21 @@ static int sysfs_sd_compare(const struct sysfs_dirent *left, * RETURNS: * 0 on susccess -EEXIST on failure. */ -static int sysfs_link_sibling(struct sysfs_dirent *sd) +static int sysfs_link_sibling(struct kernfs_node *kn) { - struct rb_node **node = &sd->s_parent->s_dir.children.rb_node; + struct rb_node **node = &kn->s_parent->s_dir.children.rb_node; struct rb_node *parent = NULL; - if (sysfs_type(sd) == SYSFS_DIR) - sd->s_parent->s_dir.subdirs++; + if (sysfs_type(kn) == SYSFS_DIR) + kn->s_parent->s_dir.subdirs++; while (*node) { - struct sysfs_dirent *pos; + struct kernfs_node *pos; int result; - pos = to_sysfs_dirent(*node); + pos = rb_to_kn(*node); parent = *node; - result = sysfs_sd_compare(sd, pos); + result = sysfs_sd_compare(kn, pos); if (result < 0) node = &pos->s_rb.rb_left; else if (result > 0) @@ -97,168 +97,169 @@ static int sysfs_link_sibling(struct sysfs_dirent *sd) return -EEXIST; } /* add new node and rebalance the tree */ - rb_link_node(&sd->s_rb, parent, node); - rb_insert_color(&sd->s_rb, &sd->s_parent->s_dir.children); + rb_link_node(&kn->s_rb, parent, node); + rb_insert_color(&kn->s_rb, &kn->s_parent->s_dir.children); return 0; } /** - * sysfs_unlink_sibling - unlink sysfs_dirent from sibling rbtree - * @sd: sysfs_dirent of interest + * sysfs_unlink_sibling - unlink kernfs_node from sibling rbtree + * @kn: kernfs_node of interest * - * Unlink @sd from its sibling rbtree which starts from - * sd->s_parent->s_dir.children. + * Unlink @kn from its sibling rbtree which starts from + * kn->s_parent->s_dir.children. * * Locking: * mutex_lock(sysfs_mutex) */ -static void sysfs_unlink_sibling(struct sysfs_dirent *sd) +static void sysfs_unlink_sibling(struct kernfs_node *kn) { - if (sysfs_type(sd) == SYSFS_DIR) - sd->s_parent->s_dir.subdirs--; + if (sysfs_type(kn) == SYSFS_DIR) + kn->s_parent->s_dir.subdirs--; - rb_erase(&sd->s_rb, &sd->s_parent->s_dir.children); + rb_erase(&kn->s_rb, &kn->s_parent->s_dir.children); } /** - * sysfs_get_active - get an active reference to sysfs_dirent - * @sd: sysfs_dirent to get an active reference to + * sysfs_get_active - get an active reference to kernfs_node + * @kn: kernfs_node to get an active reference to * - * Get an active reference of @sd. This function is noop if @sd + * Get an active reference of @kn. This function is noop if @kn * is NULL. * * RETURNS: - * Pointer to @sd on success, NULL on failure. + * Pointer to @kn on success, NULL on failure. */ -struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd) +struct kernfs_node *sysfs_get_active(struct kernfs_node *kn) { - if (unlikely(!sd)) + if (unlikely(!kn)) return NULL; - if (!atomic_inc_unless_negative(&sd->s_active)) + if (!atomic_inc_unless_negative(&kn->s_active)) return NULL; - if (sd->s_flags & SYSFS_FLAG_LOCKDEP) - rwsem_acquire_read(&sd->dep_map, 0, 1, _RET_IP_); - return sd; + if (kn->s_flags & SYSFS_FLAG_LOCKDEP) + rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_); + return kn; } /** - * sysfs_put_active - put an active reference to sysfs_dirent - * @sd: sysfs_dirent to put an active reference to + * sysfs_put_active - put an active reference to kernfs_node + * @kn: kernfs_node to put an active reference to * - * Put an active reference to @sd. This function is noop if @sd + * Put an active reference to @kn. This function is noop if @kn * is NULL. */ -void sysfs_put_active(struct sysfs_dirent *sd) +void sysfs_put_active(struct kernfs_node *kn) { int v; - if (unlikely(!sd)) + if (unlikely(!kn)) return; - if (sd->s_flags & SYSFS_FLAG_LOCKDEP) - rwsem_release(&sd->dep_map, 1, _RET_IP_); - v = atomic_dec_return(&sd->s_active); + if (kn->s_flags & SYSFS_FLAG_LOCKDEP) + rwsem_release(&kn->dep_map, 1, _RET_IP_); + v = atomic_dec_return(&kn->s_active); if (likely(v != SD_DEACTIVATED_BIAS)) return; - /* atomic_dec_return() is a mb(), we'll always see the updated - * sd->u.completion. + /* + * atomic_dec_return() is a mb(), we'll always see the updated + * kn->u.completion. */ - complete(sd->u.completion); + complete(kn->u.completion); } /** - * sysfs_deactivate - deactivate sysfs_dirent - * @sd: sysfs_dirent to deactivate + * sysfs_deactivate - deactivate kernfs_node + * @kn: kernfs_node to deactivate * * Deny new active references and drain existing ones. */ -static void sysfs_deactivate(struct sysfs_dirent *sd) +static void sysfs_deactivate(struct kernfs_node *kn) { DECLARE_COMPLETION_ONSTACK(wait); int v; - BUG_ON(!(sd->s_flags & SYSFS_FLAG_REMOVED)); + BUG_ON(!(kn->s_flags & SYSFS_FLAG_REMOVED)); - if (!(sysfs_type(sd) & SYSFS_ACTIVE_REF)) + if (!(sysfs_type(kn) & SYSFS_ACTIVE_REF)) return; - sd->u.completion = (void *)&wait; + kn->u.completion = (void *)&wait; - rwsem_acquire(&sd->dep_map, 0, 0, _RET_IP_); + rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); /* atomic_add_return() is a mb(), put_active() will always see - * the updated sd->u.completion. + * the updated kn->u.completion. */ - v = atomic_add_return(SD_DEACTIVATED_BIAS, &sd->s_active); + v = atomic_add_return(SD_DEACTIVATED_BIAS, &kn->s_active); if (v != SD_DEACTIVATED_BIAS) { - lock_contended(&sd->dep_map, _RET_IP_); + lock_contended(&kn->dep_map, _RET_IP_); wait_for_completion(&wait); } - lock_acquired(&sd->dep_map, _RET_IP_); - rwsem_release(&sd->dep_map, 1, _RET_IP_); + lock_acquired(&kn->dep_map, _RET_IP_); + rwsem_release(&kn->dep_map, 1, _RET_IP_); } /** - * kernfs_get - get a reference count on a sysfs_dirent - * @sd: the target sysfs_dirent + * kernfs_get - get a reference count on a kernfs_node + * @kn: the target kernfs_node */ -void kernfs_get(struct sysfs_dirent *sd) +void kernfs_get(struct kernfs_node *kn) { - if (sd) { - WARN_ON(!atomic_read(&sd->s_count)); - atomic_inc(&sd->s_count); + if (kn) { + WARN_ON(!atomic_read(&kn->s_count)); + atomic_inc(&kn->s_count); } } EXPORT_SYMBOL_GPL(kernfs_get); /** - * kernfs_put - put a reference count on a sysfs_dirent - * @sd: the target sysfs_dirent + * kernfs_put - put a reference count on a kernfs_node + * @kn: the target kernfs_node * - * Put a reference count of @sd and destroy it if it reached zero. + * Put a reference count of @kn and destroy it if it reached zero. */ -void kernfs_put(struct sysfs_dirent *sd) +void kernfs_put(struct kernfs_node *kn) { - struct sysfs_dirent *parent_sd; + struct kernfs_node *parent; struct kernfs_root *root; - if (!sd || !atomic_dec_and_test(&sd->s_count)) + if (!kn || !atomic_dec_and_test(&kn->s_count)) return; - root = kernfs_root(sd); + root = kernfs_root(kn); repeat: /* Moving/renaming is always done while holding reference. - * sd->s_parent won't change beneath us. + * kn->s_parent won't change beneath us. */ - parent_sd = sd->s_parent; + parent = kn->s_parent; - WARN(!(sd->s_flags & SYSFS_FLAG_REMOVED), + WARN(!(kn->s_flags & SYSFS_FLAG_REMOVED), "sysfs: free using entry: %s/%s\n", - parent_sd ? parent_sd->s_name : "", sd->s_name); - - if (sysfs_type(sd) == SYSFS_KOBJ_LINK) - kernfs_put(sd->s_symlink.target_sd); - if (sysfs_type(sd) & SYSFS_COPY_NAME) - kfree(sd->s_name); - if (sd->s_iattr) { - if (sd->s_iattr->ia_secdata) - security_release_secctx(sd->s_iattr->ia_secdata, - sd->s_iattr->ia_secdata_len); - simple_xattrs_free(&sd->s_iattr->xattrs); + parent ? parent->s_name : "", kn->s_name); + + if (sysfs_type(kn) == SYSFS_KOBJ_LINK) + kernfs_put(kn->s_symlink.target_kn); + if (sysfs_type(kn) & SYSFS_COPY_NAME) + kfree(kn->s_name); + if (kn->s_iattr) { + if (kn->s_iattr->ia_secdata) + security_release_secctx(kn->s_iattr->ia_secdata, + kn->s_iattr->ia_secdata_len); + simple_xattrs_free(&kn->s_iattr->xattrs); } - kfree(sd->s_iattr); - ida_simple_remove(&root->ino_ida, sd->s_ino); - kmem_cache_free(sysfs_dir_cachep, sd); + kfree(kn->s_iattr); + ida_simple_remove(&root->ino_ida, kn->s_ino); + kmem_cache_free(sysfs_dir_cachep, kn); - sd = parent_sd; - if (sd) { - if (atomic_dec_and_test(&sd->s_count)) + kn = parent; + if (kn) { + if (atomic_dec_and_test(&kn->s_count)) goto repeat; } else { - /* just released the root sd, free @root too */ + /* just released the root kn, free @root too */ ida_destroy(&root->ino_ida); kfree(root); } @@ -267,35 +268,35 @@ EXPORT_SYMBOL_GPL(kernfs_put); static int sysfs_dentry_delete(const struct dentry *dentry) { - struct sysfs_dirent *sd = dentry->d_fsdata; - return !(sd && !(sd->s_flags & SYSFS_FLAG_REMOVED)); + struct kernfs_node *kn = dentry->d_fsdata; + return !(kn && !(kn->s_flags & SYSFS_FLAG_REMOVED)); } static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) { - struct sysfs_dirent *sd; + struct kernfs_node *kn; if (flags & LOOKUP_RCU) return -ECHILD; - sd = dentry->d_fsdata; + kn = dentry->d_fsdata; mutex_lock(&sysfs_mutex); /* The sysfs dirent has been deleted */ - if (sd->s_flags & SYSFS_FLAG_REMOVED) + if (kn->s_flags & SYSFS_FLAG_REMOVED) goto out_bad; /* The sysfs dirent has been moved? */ - if (dentry->d_parent->d_fsdata != sd->s_parent) + if (dentry->d_parent->d_fsdata != kn->s_parent) goto out_bad; /* The sysfs dirent has been renamed */ - if (strcmp(dentry->d_name.name, sd->s_name) != 0) + if (strcmp(dentry->d_name.name, kn->s_name) != 0) goto out_bad; /* The sysfs dirent has been moved to a different namespace */ - if (sd->s_parent && kernfs_ns_enabled(sd->s_parent) && - sysfs_info(dentry->d_sb)->ns != sd->s_ns) + if (kn->s_parent && kernfs_ns_enabled(kn->s_parent) && + sysfs_info(dentry->d_sb)->ns != kn->s_ns) goto out_bad; mutex_unlock(&sysfs_mutex); @@ -335,11 +336,11 @@ const struct dentry_operations sysfs_dentry_ops = { .d_release = sysfs_dentry_release, }; -struct sysfs_dirent *sysfs_new_dirent(struct kernfs_root *root, - const char *name, umode_t mode, int type) +struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, + const char *name, umode_t mode, int type) { char *dup_name = NULL; - struct sysfs_dirent *sd; + struct kernfs_node *kn; int ret; if (type & SYSFS_COPY_NAME) { @@ -348,38 +349,38 @@ struct sysfs_dirent *sysfs_new_dirent(struct kernfs_root *root, return NULL; } - sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); - if (!sd) + kn = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); + if (!kn) goto err_out1; ret = ida_simple_get(&root->ino_ida, 1, 0, GFP_KERNEL); if (ret < 0) goto err_out2; - sd->s_ino = ret; + kn->s_ino = ret; - atomic_set(&sd->s_count, 1); - atomic_set(&sd->s_active, 0); + atomic_set(&kn->s_count, 1); + atomic_set(&kn->s_active, 0); - sd->s_name = name; - sd->s_mode = mode; - sd->s_flags = type | SYSFS_FLAG_REMOVED; + kn->s_name = name; + kn->s_mode = mode; + kn->s_flags = type | SYSFS_FLAG_REMOVED; - return sd; + return kn; err_out2: - kmem_cache_free(sysfs_dir_cachep, sd); + kmem_cache_free(sysfs_dir_cachep, kn); err_out1: kfree(dup_name); return NULL; } /** - * sysfs_addrm_start - prepare for sysfs_dirent add/remove + * sysfs_addrm_start - prepare for kernfs_node add/remove * @acxt: pointer to sysfs_addrm_cxt to be used * * This function is called when the caller is about to add or remove - * sysfs_dirent. This function acquires sysfs_mutex. @acxt is used - * to keep and pass context to other addrm functions. + * kernfs_node. This function acquires sysfs_mutex. @acxt is used to + * keep and pass context to other addrm functions. * * LOCKING: * Kernel thread context (may sleep). sysfs_mutex is locked on @@ -394,13 +395,13 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) } /** - * sysfs_add_one - add sysfs_dirent to parent without warning + * sysfs_add_one - add kernfs_node to parent without warning * @acxt: addrm context to use - * @sd: sysfs_dirent to be added - * @parent_sd: the parent sysfs_dirent to add @sd to + * @kn: kernfs_node to be added + * @parent: the parent kernfs_node to add @kn to * - * Get @parent_sd and set @sd->s_parent to it and increment nlink of - * the parent inode if @sd is a directory and link into the children + * Get @parent and set @kn->s_parent to it and increment nlink of + * the parent inode if @kn is a directory and link into the children * list of the parent. * * This function should be called between calls to @@ -414,51 +415,51 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) * 0 on success, -EEXIST if entry with the given name already * exists. */ -int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, - struct sysfs_dirent *parent_sd) +int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct kernfs_node *kn, + struct kernfs_node *parent) { - bool has_ns = kernfs_ns_enabled(parent_sd); + bool has_ns = kernfs_ns_enabled(parent); struct sysfs_inode_attrs *ps_iattr; int ret; - if (has_ns != (bool)sd->s_ns) { + if (has_ns != (bool)kn->s_ns) { WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", has_ns ? "required" : "invalid", - parent_sd->s_name, sd->s_name); + parent->s_name, kn->s_name); return -EINVAL; } - if (sysfs_type(parent_sd) != SYSFS_DIR) + if (sysfs_type(parent) != SYSFS_DIR) return -EINVAL; - sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); - sd->s_parent = parent_sd; - kernfs_get(parent_sd); + kn->s_hash = sysfs_name_hash(kn->s_name, kn->s_ns); + kn->s_parent = parent; + kernfs_get(parent); - ret = sysfs_link_sibling(sd); + ret = sysfs_link_sibling(kn); if (ret) return ret; /* Update timestamps on the parent */ - ps_iattr = parent_sd->s_iattr; + ps_iattr = parent->s_iattr; if (ps_iattr) { struct iattr *ps_iattrs = &ps_iattr->ia_iattr; ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; } /* Mark the entry added into directory tree */ - sd->s_flags &= ~SYSFS_FLAG_REMOVED; + kn->s_flags &= ~SYSFS_FLAG_REMOVED; return 0; } /** - * sysfs_remove_one - remove sysfs_dirent from parent + * sysfs_remove_one - remove kernfs_node from parent * @acxt: addrm context to use - * @sd: sysfs_dirent to be removed + * @kn: kernfs_node to be removed * - * Mark @sd removed and drop nlink of parent inode if @sd is a - * directory. @sd is unlinked from the children list. + * Mark @kn removed and drop nlink of parent inode if @kn is a + * directory. @kn is unlinked from the children list. * * This function should be called between calls to * sysfs_addrm_start() and sysfs_addrm_finish() and should be @@ -468,7 +469,7 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, * Determined by sysfs_addrm_start(). */ static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, - struct sysfs_dirent *sd) + struct kernfs_node *kn) { struct sysfs_inode_attrs *ps_iattr; @@ -476,31 +477,31 @@ static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, * Removal can be called multiple times on the same node. Only the * first invocation is effective and puts the base ref. */ - if (sd->s_flags & SYSFS_FLAG_REMOVED) + if (kn->s_flags & SYSFS_FLAG_REMOVED) return; - if (sd->s_parent) { - sysfs_unlink_sibling(sd); + if (kn->s_parent) { + sysfs_unlink_sibling(kn); /* Update timestamps on the parent */ - ps_iattr = sd->s_parent->s_iattr; + ps_iattr = kn->s_parent->s_iattr; if (ps_iattr) { ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; } } - sd->s_flags |= SYSFS_FLAG_REMOVED; - sd->u.removed_list = acxt->removed; - acxt->removed = sd; + kn->s_flags |= SYSFS_FLAG_REMOVED; + kn->u.removed_list = acxt->removed; + acxt->removed = kn; } /** - * sysfs_addrm_finish - finish up sysfs_dirent add/remove + * sysfs_addrm_finish - finish up kernfs_node 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 + * Finish up kernfs_node add/remove. Resources acquired by + * sysfs_addrm_start() are released and removed kernfs_nodes are * cleaned up. * * LOCKING: @@ -512,30 +513,30 @@ void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) /* release resources acquired by sysfs_addrm_start() */ mutex_unlock(&sysfs_mutex); - /* kill removed sysfs_dirents */ + /* kill removed kernfs_nodes */ while (acxt->removed) { - struct sysfs_dirent *sd = acxt->removed; + struct kernfs_node *kn = acxt->removed; - acxt->removed = sd->u.removed_list; + acxt->removed = kn->u.removed_list; - sysfs_deactivate(sd); - sysfs_unmap_bin_file(sd); - kernfs_put(sd); + sysfs_deactivate(kn); + sysfs_unmap_bin_file(kn); + kernfs_put(kn); } } /** - * kernfs_find_ns - find sysfs_dirent with the given name - * @parent: sysfs_dirent to search under + * kernfs_find_ns - find kernfs_node with the given name + * @parent: kernfs_node to search under * @name: name to look for * @ns: the namespace tag to use * - * Look for sysfs_dirent with name @name under @parent. Returns pointer to - * the found sysfs_dirent on success, %NULL on failure. + * Look for kernfs_node with name @name under @parent. Returns pointer to + * the found kernfs_node on success, %NULL on failure. */ -static struct sysfs_dirent *kernfs_find_ns(struct sysfs_dirent *parent, - const unsigned char *name, - const void *ns) +static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent, + const unsigned char *name, + const void *ns) { struct rb_node *node = parent->s_dir.children.rb_node; bool has_ns = kernfs_ns_enabled(parent); @@ -552,42 +553,42 @@ static struct sysfs_dirent *kernfs_find_ns(struct sysfs_dirent *parent, hash = sysfs_name_hash(name, ns); while (node) { - struct sysfs_dirent *sd; + struct kernfs_node *kn; int result; - sd = to_sysfs_dirent(node); - result = sysfs_name_compare(hash, name, ns, sd); + kn = rb_to_kn(node); + result = sysfs_name_compare(hash, name, ns, kn); if (result < 0) node = node->rb_left; else if (result > 0) node = node->rb_right; else - return sd; + return kn; } return NULL; } /** - * kernfs_find_and_get_ns - find and get sysfs_dirent with the given name - * @parent: sysfs_dirent to search under + * kernfs_find_and_get_ns - find and get kernfs_node with the given name + * @parent: kernfs_node to search under * @name: name to look for * @ns: the namespace tag to use * - * Look for sysfs_dirent with name @name under @parent and get a reference + * Look for kernfs_node with name @name under @parent and get a reference * if found. This function may sleep and returns pointer to the found - * sysfs_dirent on success, %NULL on failure. + * kernfs_node on success, %NULL on failure. */ -struct sysfs_dirent *kernfs_find_and_get_ns(struct sysfs_dirent *parent, - const char *name, const void *ns) +struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, + const char *name, const void *ns) { - struct sysfs_dirent *sd; + struct kernfs_node *kn; mutex_lock(&sysfs_mutex); - sd = kernfs_find_ns(parent, name, ns); - kernfs_get(sd); + kn = kernfs_find_ns(parent, name, ns); + kernfs_get(kn); mutex_unlock(&sysfs_mutex); - return sd; + return kn; } EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); @@ -601,7 +602,7 @@ EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); struct kernfs_root *kernfs_create_root(void *priv) { struct kernfs_root *root; - struct sysfs_dirent *sd; + struct kernfs_node *kn; root = kzalloc(sizeof(*root), GFP_KERNEL); if (!root) @@ -609,18 +610,18 @@ struct kernfs_root *kernfs_create_root(void *priv) ida_init(&root->ino_ida); - sd = sysfs_new_dirent(root, "", S_IFDIR | S_IRUGO | S_IXUGO, SYSFS_DIR); - if (!sd) { + kn = sysfs_new_dirent(root, "", S_IFDIR | S_IRUGO | S_IXUGO, SYSFS_DIR); + if (!kn) { ida_destroy(&root->ino_ida); kfree(root); return ERR_PTR(-ENOMEM); } - sd->s_flags &= ~SYSFS_FLAG_REMOVED; - sd->priv = priv; - sd->s_dir.root = root; + kn->s_flags &= ~SYSFS_FLAG_REMOVED; + kn->priv = priv; + kn->s_dir.root = root; - root->sd = sd; + root->kn = kn; return root; } @@ -634,7 +635,7 @@ struct kernfs_root *kernfs_create_root(void *priv) */ void kernfs_destroy_root(struct kernfs_root *root) { - kernfs_remove(root->sd); /* will also free @root */ + kernfs_remove(root->kn); /* will also free @root */ } /** @@ -646,33 +647,33 @@ void kernfs_destroy_root(struct kernfs_root *root) * * Returns the created node on success, ERR_PTR() value on failure. */ -struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, - const char *name, void *priv, - const void *ns) +struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, + const char *name, void *priv, + const void *ns) { umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; struct sysfs_addrm_cxt acxt; - struct sysfs_dirent *sd; + struct kernfs_node *kn; int rc; /* allocate */ - sd = sysfs_new_dirent(kernfs_root(parent), name, mode, SYSFS_DIR); - if (!sd) + kn = sysfs_new_dirent(kernfs_root(parent), name, mode, SYSFS_DIR); + if (!kn) return ERR_PTR(-ENOMEM); - sd->s_dir.root = parent->s_dir.root; - sd->s_ns = ns; - sd->priv = priv; + kn->s_dir.root = parent->s_dir.root; + kn->s_ns = ns; + kn->priv = priv; /* link in */ sysfs_addrm_start(&acxt); - rc = sysfs_add_one(&acxt, sd, parent); + rc = sysfs_add_one(&acxt, kn, parent); sysfs_addrm_finish(&acxt); if (!rc) - return sd; + return kn; - kernfs_put(sd); + kernfs_put(kn); return ERR_PTR(rc); } @@ -680,29 +681,28 @@ static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct dentry *ret = NULL; - struct dentry *parent = dentry->d_parent; - struct sysfs_dirent *parent_sd = parent->d_fsdata; - struct sysfs_dirent *sd; + struct kernfs_node *parent = dentry->d_parent->d_fsdata; + struct kernfs_node *kn; struct inode *inode; const void *ns = NULL; mutex_lock(&sysfs_mutex); - if (kernfs_ns_enabled(parent_sd)) + if (kernfs_ns_enabled(parent)) ns = sysfs_info(dir->i_sb)->ns; - sd = kernfs_find_ns(parent_sd, dentry->d_name.name, ns); + kn = kernfs_find_ns(parent, dentry->d_name.name, ns); /* no such entry */ - if (!sd) { + if (!kn) { ret = ERR_PTR(-ENOENT); goto out_unlock; } - kernfs_get(sd); - dentry->d_fsdata = sd; + kernfs_get(kn); + dentry->d_fsdata = kn; /* attach dentry and inode */ - inode = sysfs_get_inode(dir->i_sb, sd); + inode = sysfs_get_inode(dir->i_sb, kn); if (!inode) { ret = ERR_PTR(-ENOMEM); goto out_unlock; @@ -726,9 +726,9 @@ const struct inode_operations sysfs_dir_inode_operations = { .listxattr = sysfs_listxattr, }; -static struct sysfs_dirent *sysfs_leftmost_descendant(struct sysfs_dirent *pos) +static struct kernfs_node *sysfs_leftmost_descendant(struct kernfs_node *pos) { - struct sysfs_dirent *last; + struct kernfs_node *last; while (true) { struct rb_node *rbn; @@ -742,7 +742,7 @@ static struct sysfs_dirent *sysfs_leftmost_descendant(struct sysfs_dirent *pos) if (!rbn) break; - pos = to_sysfs_dirent(rbn); + pos = rb_to_kn(rbn); } return last; @@ -751,14 +751,14 @@ static struct sysfs_dirent *sysfs_leftmost_descendant(struct sysfs_dirent *pos) /** * sysfs_next_descendant_post - find the next descendant for post-order walk * @pos: the current position (%NULL to initiate traversal) - * @root: sysfs_dirent whose descendants to walk + * @root: kernfs_node whose descendants to walk * * Find the next descendant to visit for post-order traversal of @root's * descendants. @root is included in the iteration and the last node to be * visited. */ -static struct sysfs_dirent *sysfs_next_descendant_post(struct sysfs_dirent *pos, - struct sysfs_dirent *root) +static struct kernfs_node *sysfs_next_descendant_post(struct kernfs_node *pos, + struct kernfs_node *root) { struct rb_node *rbn; @@ -775,62 +775,62 @@ static struct sysfs_dirent *sysfs_next_descendant_post(struct sysfs_dirent *pos, /* if there's an unvisited sibling, visit its leftmost descendant */ rbn = rb_next(&pos->s_rb); if (rbn) - return sysfs_leftmost_descendant(to_sysfs_dirent(rbn)); + return sysfs_leftmost_descendant(rb_to_kn(rbn)); /* no sibling left, visit parent */ return pos->s_parent; } static void __kernfs_remove(struct sysfs_addrm_cxt *acxt, - struct sysfs_dirent *sd) + struct kernfs_node *kn) { - struct sysfs_dirent *pos, *next; + struct kernfs_node *pos, *next; - if (!sd) + if (!kn) return; - pr_debug("sysfs %s: removing\n", sd->s_name); + pr_debug("sysfs %s: removing\n", kn->s_name); next = NULL; do { pos = next; - next = sysfs_next_descendant_post(pos, sd); + next = sysfs_next_descendant_post(pos, kn); if (pos) sysfs_remove_one(acxt, pos); } while (next); } /** - * kernfs_remove - remove a sysfs_dirent recursively - * @sd: the sysfs_dirent to remove + * kernfs_remove - remove a kernfs_node recursively + * @kn: the kernfs_node to remove * - * Remove @sd along with all its subdirectories and files. + * Remove @kn along with all its subdirectories and files. */ -void kernfs_remove(struct sysfs_dirent *sd) +void kernfs_remove(struct kernfs_node *kn) { struct sysfs_addrm_cxt acxt; sysfs_addrm_start(&acxt); - __kernfs_remove(&acxt, sd); + __kernfs_remove(&acxt, kn); sysfs_addrm_finish(&acxt); } /** - * kernfs_remove_by_name_ns - find a sysfs_dirent by name and remove it - * @dir_sd: parent of the target - * @name: name of the sysfs_dirent to remove - * @ns: namespace tag of the sysfs_dirent to remove + * kernfs_remove_by_name_ns - find a kernfs_node by name and remove it + * @parent: parent of the target + * @name: name of the kernfs_node to remove + * @ns: namespace tag of the kernfs_node to remove * - * Look for the sysfs_dirent with @name and @ns under @dir_sd and remove - * it. Returns 0 on success, -ENOENT if such entry doesn't exist. + * Look for the kernfs_node with @name and @ns under @parent and remove it. + * Returns 0 on success, -ENOENT if such entry doesn't exist. */ -int kernfs_remove_by_name_ns(struct sysfs_dirent *dir_sd, const char *name, +int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, const void *ns) { struct sysfs_addrm_cxt acxt; - struct sysfs_dirent *sd; + struct kernfs_node *kn; - if (!dir_sd) { + if (!parent) { WARN(1, KERN_WARNING "sysfs: can not remove '%s', no directory\n", name); return -ENOENT; @@ -838,13 +838,13 @@ int kernfs_remove_by_name_ns(struct sysfs_dirent *dir_sd, const char *name, sysfs_addrm_start(&acxt); - sd = kernfs_find_ns(dir_sd, name, ns); - if (sd) - __kernfs_remove(&acxt, sd); + kn = kernfs_find_ns(parent, name, ns); + if (kn) + __kernfs_remove(&acxt, kn); sysfs_addrm_finish(&acxt); - if (sd) + if (kn) return 0; else return -ENOENT; @@ -852,12 +852,12 @@ int kernfs_remove_by_name_ns(struct sysfs_dirent *dir_sd, const char *name, /** * kernfs_rename_ns - move and rename a kernfs_node - * @sd: target node + * @kn: target node * @new_parent: new parent to put @sd under * @new_name: new name * @new_ns: new namespace tag */ -int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, +int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, const char *new_name, const void *new_ns) { int error; @@ -865,35 +865,35 @@ int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, mutex_lock(&sysfs_mutex); error = 0; - if ((sd->s_parent == new_parent) && (sd->s_ns == new_ns) && - (strcmp(sd->s_name, new_name) == 0)) + if ((kn->s_parent == new_parent) && (kn->s_ns == new_ns) && + (strcmp(kn->s_name, new_name) == 0)) goto out; /* nothing to rename */ error = -EEXIST; if (kernfs_find_ns(new_parent, new_name, new_ns)) goto out; - /* rename sysfs_dirent */ - if (strcmp(sd->s_name, new_name) != 0) { + /* rename kernfs_node */ + if (strcmp(kn->s_name, new_name) != 0) { error = -ENOMEM; new_name = kstrdup(new_name, GFP_KERNEL); if (!new_name) goto out; - kfree(sd->s_name); - sd->s_name = new_name; + kfree(kn->s_name); + kn->s_name = new_name; } /* * Move to the appropriate place in the appropriate directories rbtree. */ - sysfs_unlink_sibling(sd); + sysfs_unlink_sibling(kn); kernfs_get(new_parent); - kernfs_put(sd->s_parent); - sd->s_ns = new_ns; - sd->s_hash = sysfs_name_hash(sd->s_name, sd->s_ns); - sd->s_parent = new_parent; - sysfs_link_sibling(sd); + kernfs_put(kn->s_parent); + kn->s_ns = new_ns; + kn->s_hash = sysfs_name_hash(kn->s_name, kn->s_ns); + kn->s_parent = new_parent; + sysfs_link_sibling(kn); error = 0; out: @@ -902,9 +902,9 @@ int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, } /* Relationship between s_mode and the DT_xxx types */ -static inline unsigned char dt_type(struct sysfs_dirent *sd) +static inline unsigned char dt_type(struct kernfs_node *kn) { - return (sd->s_mode >> 12) & 15; + return (kn->s_mode >> 12) & 15; } static int sysfs_dir_release(struct inode *inode, struct file *filp) @@ -913,21 +913,21 @@ static int sysfs_dir_release(struct inode *inode, struct file *filp) return 0; } -static struct sysfs_dirent *sysfs_dir_pos(const void *ns, - struct sysfs_dirent *parent_sd, loff_t hash, struct sysfs_dirent *pos) +static struct kernfs_node *sysfs_dir_pos(const void *ns, + struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) { if (pos) { int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && - pos->s_parent == parent_sd && + pos->s_parent == parent && hash == pos->s_hash; kernfs_put(pos); if (!valid) pos = NULL; } if (!pos && (hash > 1) && (hash < INT_MAX)) { - struct rb_node *node = parent_sd->s_dir.children.rb_node; + struct rb_node *node = parent->s_dir.children.rb_node; while (node) { - pos = to_sysfs_dirent(node); + pos = rb_to_kn(node); if (hash < pos->s_hash) node = node->rb_left; @@ -943,22 +943,22 @@ static struct sysfs_dirent *sysfs_dir_pos(const void *ns, if (!node) pos = NULL; else - pos = to_sysfs_dirent(node); + pos = rb_to_kn(node); } return pos; } -static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns, - struct sysfs_dirent *parent_sd, ino_t ino, struct sysfs_dirent *pos) +static struct kernfs_node *sysfs_dir_next_pos(const void *ns, + struct kernfs_node *parent, ino_t ino, struct kernfs_node *pos) { - pos = sysfs_dir_pos(ns, parent_sd, ino, pos); + pos = sysfs_dir_pos(ns, parent, ino, pos); if (pos) do { struct rb_node *node = rb_next(&pos->s_rb); if (!node) pos = NULL; else - pos = to_sysfs_dirent(node); + pos = rb_to_kn(node); } while (pos && pos->s_ns != ns); return pos; } @@ -966,20 +966,20 @@ static struct sysfs_dirent *sysfs_dir_next_pos(const void *ns, static int sysfs_readdir(struct file *file, struct dir_context *ctx) { struct dentry *dentry = file->f_path.dentry; - struct sysfs_dirent *parent_sd = dentry->d_fsdata; - struct sysfs_dirent *pos = file->private_data; + struct kernfs_node *parent = dentry->d_fsdata; + struct kernfs_node *pos = file->private_data; const void *ns = NULL; if (!dir_emit_dots(file, ctx)) return 0; mutex_lock(&sysfs_mutex); - if (kernfs_ns_enabled(parent_sd)) + if (kernfs_ns_enabled(parent)) ns = sysfs_info(dentry->d_sb)->ns; - for (pos = sysfs_dir_pos(ns, parent_sd, ctx->pos, pos); + for (pos = sysfs_dir_pos(ns, parent, ctx->pos, pos); pos; - pos = sysfs_dir_next_pos(ns, parent_sd, ctx->pos, pos)) { + pos = sysfs_dir_next_pos(ns, parent, ctx->pos, pos)) { const char *name = pos->s_name; unsigned int type = dt_type(pos); int len = strlen(name); diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index fa05315..1bf07de 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -19,9 +19,9 @@ /* * There's one sysfs_open_file for each open file and one sysfs_open_dirent - * for each sysfs_dirent with one or more open files. + * for each kernfs_node with one or more open files. * - * sysfs_dirent->s_attr.open points to sysfs_open_dirent. s_attr.open is + * kernfs_node->s_attr.open points to sysfs_open_dirent. s_attr.open is * protected by sysfs_open_dirent_lock. * * filp->private_data points to seq_file whose ->private points to @@ -44,14 +44,14 @@ static struct sysfs_open_file *sysfs_of(struct file *file) } /* - * Determine the kernfs_ops for the given sysfs_dirent. This function must + * Determine the kernfs_ops for the given kernfs_node. This function must * be called while holding an active reference. */ -static const struct kernfs_ops *kernfs_ops(struct sysfs_dirent *sd) +static const struct kernfs_ops *kernfs_ops(struct kernfs_node *kn) { - if (sd->s_flags & SYSFS_FLAG_LOCKDEP) - lockdep_assert_held(sd); - return sd->s_attr.ops; + if (kn->s_flags & SYSFS_FLAG_LOCKDEP) + lockdep_assert_held(kn); + return kn->s_attr.ops; } static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) @@ -64,10 +64,10 @@ static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) * the ops aren't called concurrently for the same open file. */ mutex_lock(&of->mutex); - if (!sysfs_get_active(of->sd)) + if (!sysfs_get_active(of->kn)) return ERR_PTR(-ENODEV); - ops = kernfs_ops(of->sd); + ops = kernfs_ops(of->kn); if (ops->seq_start) { return ops->seq_start(sf, ppos); } else { @@ -82,7 +82,7 @@ static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) { struct sysfs_open_file *of = sf->private; - const struct kernfs_ops *ops = kernfs_ops(of->sd); + const struct kernfs_ops *ops = kernfs_ops(of->kn); if (ops->seq_next) { return ops->seq_next(sf, v, ppos); @@ -99,12 +99,12 @@ static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) static void kernfs_seq_stop(struct seq_file *sf, void *v) { struct sysfs_open_file *of = sf->private; - const struct kernfs_ops *ops = kernfs_ops(of->sd); + const struct kernfs_ops *ops = kernfs_ops(of->kn); if (ops->seq_stop) ops->seq_stop(sf, v); - sysfs_put_active(of->sd); + sysfs_put_active(of->kn); mutex_unlock(&of->mutex); } @@ -112,9 +112,9 @@ static int kernfs_seq_show(struct seq_file *sf, void *v) { struct sysfs_open_file *of = sf->private; - of->event = atomic_read(&of->sd->s_attr.open->event); + of->event = atomic_read(&of->kn->s_attr.open->event); - return of->sd->s_attr.ops->seq_show(sf, v); + return of->kn->s_attr.ops->seq_show(sf, v); } static const struct seq_operations kernfs_seq_ops = { @@ -147,19 +147,19 @@ static ssize_t kernfs_file_direct_read(struct sysfs_open_file *of, * the ops aren't called concurrently for the same open file. */ mutex_lock(&of->mutex); - if (!sysfs_get_active(of->sd)) { + if (!sysfs_get_active(of->kn)) { len = -ENODEV; mutex_unlock(&of->mutex); goto out_free; } - ops = kernfs_ops(of->sd); + ops = kernfs_ops(of->kn); if (ops->read) len = ops->read(of, buf, len, *ppos); else len = -EINVAL; - sysfs_put_active(of->sd); + sysfs_put_active(of->kn); mutex_unlock(&of->mutex); if (len < 0) @@ -189,7 +189,7 @@ static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, { struct sysfs_open_file *of = sysfs_of(file); - if (of->sd->s_flags & SYSFS_FLAG_HAS_SEQ_SHOW) + if (of->kn->s_flags & SYSFS_FLAG_HAS_SEQ_SHOW) return seq_read(file, user_buf, count, ppos); else return kernfs_file_direct_read(of, user_buf, count, ppos); @@ -234,19 +234,19 @@ static ssize_t kernfs_file_write(struct file *file, const char __user *user_buf, * the ops aren't called concurrently for the same open file. */ mutex_lock(&of->mutex); - if (!sysfs_get_active(of->sd)) { + if (!sysfs_get_active(of->kn)) { mutex_unlock(&of->mutex); len = -ENODEV; goto out_free; } - ops = kernfs_ops(of->sd); + ops = kernfs_ops(of->kn); if (ops->write) len = ops->write(of, buf, len, *ppos); else len = -EINVAL; - sysfs_put_active(of->sd); + sysfs_put_active(of->kn); mutex_unlock(&of->mutex); if (len > 0) @@ -264,13 +264,13 @@ static void kernfs_vma_open(struct vm_area_struct *vma) if (!of->vm_ops) return; - if (!sysfs_get_active(of->sd)) + if (!sysfs_get_active(of->kn)) return; if (of->vm_ops->open) of->vm_ops->open(vma); - sysfs_put_active(of->sd); + sysfs_put_active(of->kn); } static int kernfs_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) @@ -282,14 +282,14 @@ static int kernfs_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) if (!of->vm_ops) return VM_FAULT_SIGBUS; - if (!sysfs_get_active(of->sd)) + if (!sysfs_get_active(of->kn)) return VM_FAULT_SIGBUS; ret = VM_FAULT_SIGBUS; if (of->vm_ops->fault) ret = of->vm_ops->fault(vma, vmf); - sysfs_put_active(of->sd); + sysfs_put_active(of->kn); return ret; } @@ -303,7 +303,7 @@ static int kernfs_vma_page_mkwrite(struct vm_area_struct *vma, if (!of->vm_ops) return VM_FAULT_SIGBUS; - if (!sysfs_get_active(of->sd)) + if (!sysfs_get_active(of->kn)) return VM_FAULT_SIGBUS; ret = 0; @@ -312,7 +312,7 @@ static int kernfs_vma_page_mkwrite(struct vm_area_struct *vma, else file_update_time(file); - sysfs_put_active(of->sd); + sysfs_put_active(of->kn); return ret; } @@ -326,14 +326,14 @@ static int kernfs_vma_access(struct vm_area_struct *vma, unsigned long addr, if (!of->vm_ops) return -EINVAL; - if (!sysfs_get_active(of->sd)) + if (!sysfs_get_active(of->kn)) return -EINVAL; ret = -EINVAL; if (of->vm_ops->access) ret = of->vm_ops->access(vma, addr, buf, len, write); - sysfs_put_active(of->sd); + sysfs_put_active(of->kn); return ret; } @@ -348,14 +348,14 @@ static int kernfs_vma_set_policy(struct vm_area_struct *vma, if (!of->vm_ops) return 0; - if (!sysfs_get_active(of->sd)) + if (!sysfs_get_active(of->kn)) return -EINVAL; ret = 0; if (of->vm_ops->set_policy) ret = of->vm_ops->set_policy(vma, new); - sysfs_put_active(of->sd); + sysfs_put_active(of->kn); return ret; } @@ -369,14 +369,14 @@ static struct mempolicy *kernfs_vma_get_policy(struct vm_area_struct *vma, if (!of->vm_ops) return vma->vm_policy; - if (!sysfs_get_active(of->sd)) + if (!sysfs_get_active(of->kn)) return vma->vm_policy; pol = vma->vm_policy; if (of->vm_ops->get_policy) pol = of->vm_ops->get_policy(vma, addr); - sysfs_put_active(of->sd); + sysfs_put_active(of->kn); return pol; } @@ -391,14 +391,14 @@ static int kernfs_vma_migrate(struct vm_area_struct *vma, if (!of->vm_ops) return 0; - if (!sysfs_get_active(of->sd)) + if (!sysfs_get_active(of->kn)) return 0; ret = 0; if (of->vm_ops->migrate) ret = of->vm_ops->migrate(vma, from, to, flags); - sysfs_put_active(of->sd); + sysfs_put_active(of->kn); return ret; } #endif @@ -428,16 +428,16 @@ static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) * without grabbing @of->mutex by testing HAS_MMAP flag. See the * comment in kernfs_file_open() for more details. */ - if (!(of->sd->s_flags & SYSFS_FLAG_HAS_MMAP)) + if (!(of->kn->s_flags & SYSFS_FLAG_HAS_MMAP)) return -ENODEV; mutex_lock(&of->mutex); rc = -ENODEV; - if (!sysfs_get_active(of->sd)) + if (!sysfs_get_active(of->kn)) goto out_unlock; - ops = kernfs_ops(of->sd); + ops = kernfs_ops(of->kn); rc = ops->mmap(of, vma); /* @@ -465,7 +465,7 @@ static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) of->vm_ops = vma->vm_ops; vma->vm_ops = &kernfs_vm_ops; out_put: - sysfs_put_active(of->sd); + sysfs_put_active(of->kn); out_unlock: mutex_unlock(&of->mutex); @@ -474,10 +474,10 @@ out_unlock: /** * sysfs_get_open_dirent - get or create sysfs_open_dirent - * @sd: target sysfs_dirent + * @kn: target kernfs_node * @of: sysfs_open_file for this instance of open * - * If @sd->s_attr.open exists, increment its reference count; + * If @kn->s_attr.open exists, increment its reference count; * otherwise, create one. @of is chained to the files list. * * LOCKING: @@ -486,7 +486,7 @@ out_unlock: * RETURNS: * 0 on success, -errno on failure. */ -static int sysfs_get_open_dirent(struct sysfs_dirent *sd, +static int sysfs_get_open_dirent(struct kernfs_node *kn, struct sysfs_open_file *of) { struct sysfs_open_dirent *od, *new_od = NULL; @@ -495,12 +495,12 @@ static int sysfs_get_open_dirent(struct sysfs_dirent *sd, mutex_lock(&sysfs_open_file_mutex); spin_lock_irq(&sysfs_open_dirent_lock); - if (!sd->s_attr.open && new_od) { - sd->s_attr.open = new_od; + if (!kn->s_attr.open && new_od) { + kn->s_attr.open = new_od; new_od = NULL; } - od = sd->s_attr.open; + od = kn->s_attr.open; if (od) { atomic_inc(&od->refcnt); list_add_tail(&of->list, &od->files); @@ -528,19 +528,19 @@ static int sysfs_get_open_dirent(struct sysfs_dirent *sd, /** * sysfs_put_open_dirent - put sysfs_open_dirent - * @sd: target sysfs_dirent + * @kn: target kernfs_nodet * @of: associated sysfs_open_file * - * Put @sd->s_attr.open and unlink @of from the files list. If + * Put @kn->s_attr.open and unlink @of from the files list. If * reference count reaches zero, disassociate and free it. * * LOCKING: * None. */ -static void sysfs_put_open_dirent(struct sysfs_dirent *sd, +static void sysfs_put_open_dirent(struct kernfs_node *kn, struct sysfs_open_file *of) { - struct sysfs_open_dirent *od = sd->s_attr.open; + struct sysfs_open_dirent *od = kn->s_attr.open; unsigned long flags; mutex_lock(&sysfs_open_file_mutex); @@ -550,7 +550,7 @@ static void sysfs_put_open_dirent(struct sysfs_dirent *sd, list_del(&of->list); if (atomic_dec_and_test(&od->refcnt)) - sd->s_attr.open = NULL; + kn->s_attr.open = NULL; else od = NULL; @@ -562,16 +562,16 @@ static void sysfs_put_open_dirent(struct sysfs_dirent *sd, static int kernfs_file_open(struct inode *inode, struct file *file) { - struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; + struct kernfs_node *kn = file->f_path.dentry->d_fsdata; const struct kernfs_ops *ops; struct sysfs_open_file *of; bool has_read, has_write, has_mmap; int error = -EACCES; - if (!sysfs_get_active(attr_sd)) + if (!sysfs_get_active(kn)) return -ENODEV; - ops = kernfs_ops(attr_sd); + ops = kernfs_ops(kn); has_read = ops->seq_show || ops->read || ops->mmap; has_write = ops->write || ops->mmap; @@ -612,7 +612,7 @@ static int kernfs_file_open(struct inode *inode, struct file *file) else mutex_init(&of->mutex); - of->sd = attr_sd; + of->kn = kn; of->file = file; /* @@ -634,12 +634,12 @@ static int kernfs_file_open(struct inode *inode, struct file *file) file->f_mode |= FMODE_PWRITE; /* make sure we have open dirent struct */ - error = sysfs_get_open_dirent(attr_sd, of); + error = sysfs_get_open_dirent(kn, of); if (error) goto err_close; /* open succeeded, put active references */ - sysfs_put_active(attr_sd); + sysfs_put_active(kn); return 0; err_close: @@ -647,32 +647,32 @@ err_close: err_free: kfree(of); err_out: - sysfs_put_active(attr_sd); + sysfs_put_active(kn); return error; } static int kernfs_file_release(struct inode *inode, struct file *filp) { - struct sysfs_dirent *sd = filp->f_path.dentry->d_fsdata; + struct kernfs_node *kn = filp->f_path.dentry->d_fsdata; struct sysfs_open_file *of = sysfs_of(filp); - sysfs_put_open_dirent(sd, of); + sysfs_put_open_dirent(kn, of); seq_release(inode, filp); kfree(of); return 0; } -void sysfs_unmap_bin_file(struct sysfs_dirent *sd) +void sysfs_unmap_bin_file(struct kernfs_node *kn) { struct sysfs_open_dirent *od; struct sysfs_open_file *of; - if (!(sd->s_flags & SYSFS_FLAG_HAS_MMAP)) + if (!(kn->s_flags & SYSFS_FLAG_HAS_MMAP)) return; spin_lock_irq(&sysfs_open_dirent_lock); - od = sd->s_attr.open; + od = kn->s_attr.open; if (od) atomic_inc(&od->refcnt); spin_unlock_irq(&sysfs_open_dirent_lock); @@ -686,7 +686,7 @@ void sysfs_unmap_bin_file(struct sysfs_dirent *sd) } mutex_unlock(&sysfs_open_file_mutex); - sysfs_put_open_dirent(sd, NULL); + sysfs_put_open_dirent(kn, NULL); } /* Sysfs attribute files are pollable. The idea is that you read @@ -705,16 +705,16 @@ void sysfs_unmap_bin_file(struct sysfs_dirent *sd) static unsigned int kernfs_file_poll(struct file *filp, poll_table *wait) { struct sysfs_open_file *of = sysfs_of(filp); - struct sysfs_dirent *attr_sd = filp->f_path.dentry->d_fsdata; - struct sysfs_open_dirent *od = attr_sd->s_attr.open; + struct kernfs_node *kn = filp->f_path.dentry->d_fsdata; + struct sysfs_open_dirent *od = kn->s_attr.open; /* need parent for the kobj, grab both */ - if (!sysfs_get_active(attr_sd)) + if (!sysfs_get_active(kn)) goto trigger; poll_wait(filp, &od->poll, wait); - sysfs_put_active(attr_sd); + sysfs_put_active(kn); if (of->event != atomic_read(&od->event)) goto trigger; @@ -727,19 +727,19 @@ static unsigned int kernfs_file_poll(struct file *filp, poll_table *wait) /** * kernfs_notify - notify a kernfs file - * @sd: file to notify + * @kn: file to notify * - * Notify @sd such that poll(2) on @sd wakes up. + * Notify @kn such that poll(2) on @kn wakes up. */ -void kernfs_notify(struct sysfs_dirent *sd) +void kernfs_notify(struct kernfs_node *kn) { struct sysfs_open_dirent *od; unsigned long flags; spin_lock_irqsave(&sysfs_open_dirent_lock, flags); - if (!WARN_ON(sysfs_type(sd) != SYSFS_KOBJ_ATTR)) { - od = sd->s_attr.open; + if (!WARN_ON(sysfs_type(kn) != SYSFS_KOBJ_ATTR)) { + od = kn->s_attr.open; if (od) { atomic_inc(&od->event); wake_up_interruptible(&od->poll); @@ -773,51 +773,51 @@ const struct file_operations kernfs_file_operations = { * * Returns the created node on success, ERR_PTR() value on error. */ -struct sysfs_dirent *kernfs_create_file_ns_key(struct sysfs_dirent *parent, - const char *name, - umode_t mode, loff_t size, - const struct kernfs_ops *ops, - void *priv, const void *ns, - struct lock_class_key *key) +struct kernfs_node *kernfs_create_file_ns_key(struct kernfs_node *parent, + const char *name, + umode_t mode, loff_t size, + const struct kernfs_ops *ops, + void *priv, const void *ns, + struct lock_class_key *key) { struct sysfs_addrm_cxt acxt; - struct sysfs_dirent *sd; + struct kernfs_node *kn; int rc; - sd = sysfs_new_dirent(kernfs_root(parent), name, + kn = sysfs_new_dirent(kernfs_root(parent), name, (mode & S_IALLUGO) | S_IFREG, SYSFS_KOBJ_ATTR); - if (!sd) + if (!kn) return ERR_PTR(-ENOMEM); - sd->s_attr.ops = ops; - sd->s_attr.size = size; - sd->s_ns = ns; - sd->priv = priv; + kn->s_attr.ops = ops; + kn->s_attr.size = size; + kn->s_ns = ns; + kn->priv = priv; #ifdef CONFIG_DEBUG_LOCK_ALLOC if (key) { - lockdep_init_map(&sd->dep_map, "s_active", key, 0); - sd->s_flags |= SYSFS_FLAG_LOCKDEP; + lockdep_init_map(&kn->dep_map, "s_active", key, 0); + kn->s_flags |= SYSFS_FLAG_LOCKDEP; } #endif /* - * sd->s_attr.ops is accesible only while holding active ref. We + * kn->s_attr.ops is accesible only while holding active ref. We * need to know whether some ops are implemented outside active * ref. Cache their existence in flags. */ if (ops->seq_show) - sd->s_flags |= SYSFS_FLAG_HAS_SEQ_SHOW; + kn->s_flags |= SYSFS_FLAG_HAS_SEQ_SHOW; if (ops->mmap) - sd->s_flags |= SYSFS_FLAG_HAS_MMAP; + kn->s_flags |= SYSFS_FLAG_HAS_MMAP; sysfs_addrm_start(&acxt); - rc = sysfs_add_one(&acxt, sd, parent); + rc = sysfs_add_one(&acxt, kn, parent); sysfs_addrm_finish(&acxt); if (rc) { - kernfs_put(sd); + kernfs_put(kn); return ERR_PTR(rc); } - return sd; + return kn; } diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index 18ad431..9e74eed 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -46,36 +46,36 @@ void __init sysfs_inode_init(void) panic("failed to init sysfs_backing_dev_info"); } -static struct sysfs_inode_attrs *sysfs_inode_attrs(struct sysfs_dirent *sd) +static struct sysfs_inode_attrs *sysfs_inode_attrs(struct kernfs_node *kn) { struct iattr *iattrs; - if (sd->s_iattr) - return sd->s_iattr; + if (kn->s_iattr) + return kn->s_iattr; - sd->s_iattr = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL); - if (!sd->s_iattr) + kn->s_iattr = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL); + if (!kn->s_iattr) return NULL; - iattrs = &sd->s_iattr->ia_iattr; + iattrs = &kn->s_iattr->ia_iattr; /* assign default attributes */ - iattrs->ia_mode = sd->s_mode; + iattrs->ia_mode = kn->s_mode; iattrs->ia_uid = GLOBAL_ROOT_UID; iattrs->ia_gid = GLOBAL_ROOT_GID; iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME; - simple_xattrs_init(&sd->s_iattr->xattrs); + simple_xattrs_init(&kn->s_iattr->xattrs); - return sd->s_iattr; + return kn->s_iattr; } -static int __kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) +static int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) { struct sysfs_inode_attrs *attrs; struct iattr *iattrs; unsigned int ia_valid = iattr->ia_valid; - attrs = sysfs_inode_attrs(sd); + attrs = sysfs_inode_attrs(kn); if (!attrs) return -ENOMEM; @@ -93,24 +93,24 @@ static int __kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) iattrs->ia_ctime = iattr->ia_ctime; if (ia_valid & ATTR_MODE) { umode_t mode = iattr->ia_mode; - iattrs->ia_mode = sd->s_mode = mode; + iattrs->ia_mode = kn->s_mode = mode; } return 0; } /** * kernfs_setattr - set iattr on a node - * @sd: target node + * @kn: target node * @iattr: iattr to set * * Returns 0 on success, -errno on failure. */ -int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) +int kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) { int ret; mutex_lock(&sysfs_mutex); - ret = __kernfs_setattr(sd, iattr); + ret = __kernfs_setattr(kn, iattr); mutex_unlock(&sysfs_mutex); return ret; } @@ -118,10 +118,10 @@ int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr) int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) { struct inode *inode = dentry->d_inode; - struct sysfs_dirent *sd = dentry->d_fsdata; + struct kernfs_node *kn = dentry->d_fsdata; int error; - if (!sd) + if (!kn) return -EINVAL; mutex_lock(&sysfs_mutex); @@ -129,7 +129,7 @@ int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) if (error) goto out; - error = __kernfs_setattr(sd, iattr); + error = __kernfs_setattr(kn, iattr); if (error) goto out; @@ -141,14 +141,14 @@ out: return error; } -static int sysfs_sd_setsecdata(struct sysfs_dirent *sd, void **secdata, +static int sysfs_sd_setsecdata(struct kernfs_node *kn, void **secdata, u32 *secdata_len) { struct sysfs_inode_attrs *attrs; void *old_secdata; size_t old_secdata_len; - attrs = sysfs_inode_attrs(sd); + attrs = sysfs_inode_attrs(kn); if (!attrs) return -ENOMEM; @@ -166,13 +166,13 @@ static int sysfs_sd_setsecdata(struct sysfs_dirent *sd, void **secdata, int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { - struct sysfs_dirent *sd = dentry->d_fsdata; + struct kernfs_node *kn = dentry->d_fsdata; struct sysfs_inode_attrs *attrs; void *secdata; int error; u32 secdata_len = 0; - attrs = sysfs_inode_attrs(sd); + attrs = sysfs_inode_attrs(kn); if (!attrs) return -ENOMEM; @@ -188,7 +188,7 @@ int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, return error; mutex_lock(&sysfs_mutex); - error = sysfs_sd_setsecdata(sd, &secdata, &secdata_len); + error = sysfs_sd_setsecdata(kn, &secdata, &secdata_len); mutex_unlock(&sysfs_mutex); if (secdata) @@ -204,10 +204,10 @@ int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, int sysfs_removexattr(struct dentry *dentry, const char *name) { - struct sysfs_dirent *sd = dentry->d_fsdata; + struct kernfs_node *kn = dentry->d_fsdata; struct sysfs_inode_attrs *attrs; - attrs = sysfs_inode_attrs(sd); + attrs = sysfs_inode_attrs(kn); if (!attrs) return -ENOMEM; @@ -217,10 +217,10 @@ int sysfs_removexattr(struct dentry *dentry, const char *name) ssize_t sysfs_getxattr(struct dentry *dentry, const char *name, void *buf, size_t size) { - struct sysfs_dirent *sd = dentry->d_fsdata; + struct kernfs_node *kn = dentry->d_fsdata; struct sysfs_inode_attrs *attrs; - attrs = sysfs_inode_attrs(sd); + attrs = sysfs_inode_attrs(kn); if (!attrs) return -ENOMEM; @@ -229,10 +229,10 @@ ssize_t sysfs_getxattr(struct dentry *dentry, const char *name, void *buf, ssize_t sysfs_listxattr(struct dentry *dentry, char *buf, size_t size) { - struct sysfs_dirent *sd = dentry->d_fsdata; + struct kernfs_node *kn = dentry->d_fsdata; struct sysfs_inode_attrs *attrs; - attrs = sysfs_inode_attrs(sd); + attrs = sysfs_inode_attrs(kn); if (!attrs) return -ENOMEM; @@ -254,57 +254,58 @@ static inline void set_inode_attr(struct inode *inode, struct iattr *iattr) inode->i_ctime = iattr->ia_ctime; } -static void sysfs_refresh_inode(struct sysfs_dirent *sd, struct inode *inode) +static void sysfs_refresh_inode(struct kernfs_node *kn, struct inode *inode) { - struct sysfs_inode_attrs *attrs = sd->s_iattr; + struct sysfs_inode_attrs *attrs = kn->s_iattr; - inode->i_mode = sd->s_mode; + inode->i_mode = kn->s_mode; if (attrs) { - /* sysfs_dirent has non-default attributes - * get them from persistent copy in sysfs_dirent + /* + * kernfs_node has non-default attributes get them from + * persistent copy in kernfs_node. */ set_inode_attr(inode, &attrs->ia_iattr); security_inode_notifysecctx(inode, attrs->ia_secdata, attrs->ia_secdata_len); } - if (sysfs_type(sd) == SYSFS_DIR) - set_nlink(inode, sd->s_dir.subdirs + 2); + if (sysfs_type(kn) == SYSFS_DIR) + set_nlink(inode, kn->s_dir.subdirs + 2); } int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kstat *stat) { - struct sysfs_dirent *sd = dentry->d_fsdata; + struct kernfs_node *kn = dentry->d_fsdata; struct inode *inode = dentry->d_inode; mutex_lock(&sysfs_mutex); - sysfs_refresh_inode(sd, inode); + sysfs_refresh_inode(kn, inode); mutex_unlock(&sysfs_mutex); generic_fillattr(inode, stat); return 0; } -static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) +static void sysfs_init_inode(struct kernfs_node *kn, struct inode *inode) { - kernfs_get(sd); - inode->i_private = sd; + kernfs_get(kn); + inode->i_private = kn; inode->i_mapping->a_ops = &sysfs_aops; inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; inode->i_op = &sysfs_inode_operations; - set_default_inode_attr(inode, sd->s_mode); - sysfs_refresh_inode(sd, inode); + set_default_inode_attr(inode, kn->s_mode); + sysfs_refresh_inode(kn, inode); /* initialize inode according to type */ - switch (sysfs_type(sd)) { + switch (sysfs_type(kn)) { case SYSFS_DIR: inode->i_op = &sysfs_dir_inode_operations; inode->i_fop = &sysfs_dir_operations; break; case SYSFS_KOBJ_ATTR: - inode->i_size = sd->s_attr.size; + inode->i_size = kn->s_attr.size; inode->i_fop = &kernfs_file_operations; break; case SYSFS_KOBJ_LINK: @@ -318,13 +319,13 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) } /** - * sysfs_get_inode - get inode for sysfs_dirent + * sysfs_get_inode - get inode for kernfs_node * @sb: super block - * @sd: sysfs_dirent to allocate inode for + * @kn: kernfs_node to allocate inode for * - * Get inode for @sd. If such inode doesn't exist, a new inode - * is allocated and basics are initialized. New inode is - * returned locked. + * Get inode for @kn. 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). @@ -332,44 +333,44 @@ static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) * RETURNS: * Pointer to allocated inode on success, NULL on failure. */ -struct inode *sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd) +struct inode *sysfs_get_inode(struct super_block *sb, struct kernfs_node *kn) { struct inode *inode; - inode = iget_locked(sb, sd->s_ino); + inode = iget_locked(sb, kn->s_ino); if (inode && (inode->i_state & I_NEW)) - sysfs_init_inode(sd, inode); + sysfs_init_inode(kn, inode); return inode; } /* - * The sysfs_dirent serves as both an inode and a directory entry for sysfs. - * To prevent the sysfs inode numbers from being freed prematurely we take a - * reference to sysfs_dirent from the sysfs inode. A + * The kernfs_node serves as both an inode and a directory entry for sysfs. + * To prevent the sysfs inode numbers from being freed prematurely we take + * a reference to kernfs_node from the sysfs inode. A * super_operations.evict_inode() implementation is needed to drop that * reference upon inode destruction. */ void sysfs_evict_inode(struct inode *inode) { - struct sysfs_dirent *sd = inode->i_private; + struct kernfs_node *kn = inode->i_private; truncate_inode_pages(&inode->i_data, 0); clear_inode(inode); - kernfs_put(sd); + kernfs_put(kn); } int sysfs_permission(struct inode *inode, int mask) { - struct sysfs_dirent *sd; + struct kernfs_node *kn; if (mask & MAY_NOT_BLOCK) return -ECHILD; - sd = inode->i_private; + kn = inode->i_private; mutex_lock(&sysfs_mutex); - sysfs_refresh_inode(sd, inode); + sysfs_refresh_inode(kn, inode); mutex_unlock(&sysfs_mutex); return generic_permission(inode, mask); diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 910e485..b7ea76c 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -31,24 +31,24 @@ struct sysfs_inode_attrs { /* SYSFS_TYPE_MASK and types are defined in include/linux/kernfs.h */ /** - * kernfs_root - find out the kernfs_root a sysfs_dirent belongs to - * @sd: sysfs_dirent of interest + * kernfs_root - find out the kernfs_root a kernfs_node belongs to + * @kn: kernfs_node of interest * - * Return the kernfs_root @sd belongs to. + * Return the kernfs_root @kn belongs to. */ -static inline struct kernfs_root *kernfs_root(struct sysfs_dirent *sd) +static inline struct kernfs_root *kernfs_root(struct kernfs_node *kn) { /* if parent exists, it's always a dir; otherwise, @sd is a dir */ - if (sd->s_parent) - sd = sd->s_parent; - return sd->s_dir.root; + if (kn->s_parent) + kn = kn->s_parent; + return kn->s_dir.root; } /* * Context structure to be used while adding/removing nodes. */ struct sysfs_addrm_cxt { - struct sysfs_dirent *removed; + struct kernfs_node *removed; }; /* @@ -62,10 +62,10 @@ struct sysfs_super_info { struct kernfs_root *root; /* - * Each sb is associated with one namespace tag, currently the network - * namespace of the task which mounted this sysfs instance. If multiple - * tags become necessary, make the following an array and compare - * sysfs_dirent tag against every entry. + * Each sb is associated with one namespace tag, currently the + * network namespace of the task which mounted this sysfs instance. + * If multiple tags become necessary, make the following an array + * and compare kernfs_node tag against every entry. */ const void *ns; }; @@ -76,7 +76,7 @@ extern struct kmem_cache *sysfs_dir_cachep; /* * inode.c */ -struct inode *sysfs_get_inode(struct super_block *sb, struct sysfs_dirent *sd); +struct inode *sysfs_get_inode(struct super_block *sb, struct kernfs_node *kn); void sysfs_evict_inode(struct inode *inode); int sysfs_permission(struct inode *inode, int mask); int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); @@ -98,21 +98,21 @@ extern const struct dentry_operations sysfs_dentry_ops; extern const struct file_operations sysfs_dir_operations; extern const struct inode_operations sysfs_dir_inode_operations; -struct sysfs_dirent *sysfs_get_active(struct sysfs_dirent *sd); -void sysfs_put_active(struct sysfs_dirent *sd); +struct kernfs_node *sysfs_get_active(struct kernfs_node *kn); +void sysfs_put_active(struct kernfs_node *kn); void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt); -int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct sysfs_dirent *sd, - struct sysfs_dirent *parent_sd); +int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct kernfs_node *kn, + struct kernfs_node *parent); void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); -struct sysfs_dirent *sysfs_new_dirent(struct kernfs_root *root, - const char *name, umode_t mode, int type); +struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, + const char *name, umode_t mode, int type); /* * file.c */ extern const struct file_operations kernfs_file_operations; -void sysfs_unmap_bin_file(struct sysfs_dirent *sd); +void sysfs_unmap_bin_file(struct kernfs_node *kn); /* * symlink.c diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index 84c83e2..9dbbf37 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -39,7 +39,7 @@ static int sysfs_fill_super(struct super_block *sb) /* get root inode, initialize and unlock it */ mutex_lock(&sysfs_mutex); - inode = sysfs_get_inode(sb, info->root->sd); + inode = sysfs_get_inode(sb, info->root->kn); mutex_unlock(&sysfs_mutex); if (!inode) { pr_debug("sysfs: could not get root inode\n"); @@ -52,8 +52,8 @@ static int sysfs_fill_super(struct super_block *sb) pr_debug("%s: could not get root dentry!\n", __func__); return -ENOMEM; } - kernfs_get(info->root->sd); - root->d_fsdata = info->root->sd; + kernfs_get(info->root->kn); + root->d_fsdata = info->root->kn; sb->s_root = root; sb->s_d_op = &sysfs_dentry_ops; return 0; @@ -145,7 +145,7 @@ struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, void kernfs_kill_sb(struct super_block *sb) { struct sysfs_super_info *info = sysfs_info(sb); - struct sysfs_dirent *root_sd = sb->s_root->d_fsdata; + struct kernfs_node *root_kn = sb->s_root->d_fsdata; /* * Remove the superblock from fs_supers/s_instances @@ -153,13 +153,13 @@ void kernfs_kill_sb(struct super_block *sb) */ kill_anon_super(sb); kfree(info); - kernfs_put(root_sd); + kernfs_put(root_kn); } void __init kernfs_init(void) { sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache", - sizeof(struct sysfs_dirent), + sizeof(struct kernfs_node), 0, SLAB_PANIC, NULL); sysfs_inode_init(); } diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index adf2875..29dcf5e 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -22,50 +22,50 @@ * * Returns the created node on success, ERR_PTR() value on error. */ -struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, - const char *name, - struct sysfs_dirent *target) +struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, + const char *name, + struct kernfs_node *target) { - struct sysfs_dirent *sd; + struct kernfs_node *kn; struct sysfs_addrm_cxt acxt; int error; - sd = sysfs_new_dirent(kernfs_root(parent), name, S_IFLNK|S_IRWXUGO, + kn = sysfs_new_dirent(kernfs_root(parent), name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK); - if (!sd) + if (!kn) return ERR_PTR(-ENOMEM); if (kernfs_ns_enabled(parent)) - sd->s_ns = target->s_ns; - sd->s_symlink.target_sd = target; + kn->s_ns = target->s_ns; + kn->s_symlink.target_kn = target; kernfs_get(target); /* ref owned by symlink */ sysfs_addrm_start(&acxt); - error = sysfs_add_one(&acxt, sd, parent); + error = sysfs_add_one(&acxt, kn, parent); sysfs_addrm_finish(&acxt); if (!error) - return sd; + return kn; - kernfs_put(sd); + kernfs_put(kn); return ERR_PTR(error); } -static int sysfs_get_target_path(struct sysfs_dirent *parent_sd, - struct sysfs_dirent *target_sd, char *path) +static int sysfs_get_target_path(struct kernfs_node *parent, + struct kernfs_node *target, char *path) { - struct sysfs_dirent *base, *sd; + struct kernfs_node *base, *kn; char *s = path; int len = 0; /* go up to the root, stop at the base */ - base = parent_sd; + base = parent; while (base->s_parent) { - sd = target_sd->s_parent; - while (sd->s_parent && base != sd) - sd = sd->s_parent; + kn = target->s_parent; + while (kn->s_parent && base != kn) + kn = kn->s_parent; - if (base == sd) + if (base == kn) break; strcpy(s, "../"); @@ -74,10 +74,10 @@ static int sysfs_get_target_path(struct sysfs_dirent *parent_sd, } /* determine end of target string for reverse fillup */ - sd = target_sd; - while (sd->s_parent && sd != base) { - len += strlen(sd->s_name) + 1; - sd = sd->s_parent; + kn = target; + while (kn->s_parent && kn != base) { + len += strlen(kn->s_name) + 1; + kn = kn->s_parent; } /* check limits */ @@ -88,16 +88,16 @@ static int sysfs_get_target_path(struct sysfs_dirent *parent_sd, return -ENAMETOOLONG; /* reverse fillup of target string from target to base */ - sd = target_sd; - while (sd->s_parent && sd != base) { - int slen = strlen(sd->s_name); + kn = target; + while (kn->s_parent && kn != base) { + int slen = strlen(kn->s_name); len -= slen; - strncpy(s + len, sd->s_name, slen); + strncpy(s + len, kn->s_name, slen); if (len) s[--len] = '/'; - sd = sd->s_parent; + kn = kn->s_parent; } return 0; @@ -105,13 +105,13 @@ static int sysfs_get_target_path(struct sysfs_dirent *parent_sd, static int sysfs_getlink(struct dentry *dentry, char *path) { - struct sysfs_dirent *sd = dentry->d_fsdata; - struct sysfs_dirent *parent_sd = sd->s_parent; - struct sysfs_dirent *target_sd = sd->s_symlink.target_sd; + struct kernfs_node *kn = dentry->d_fsdata; + struct kernfs_node *parent = kn->s_parent; + struct kernfs_node *target = kn->s_symlink.target_kn; int error; mutex_lock(&sysfs_mutex); - error = sysfs_get_target_path(parent_sd, target_sd, path); + error = sysfs_get_target_path(parent, target, path); mutex_unlock(&sysfs_mutex); return error; diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 2fea501..f1efe3d 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -21,23 +21,23 @@ DEFINE_SPINLOCK(sysfs_symlink_target_lock); /** * sysfs_pathname - return full path to sysfs dirent - * @sd: sysfs_dirent whose path we want + * @kn: kernfs_node whose path we want * @path: caller allocated buffer of size PATH_MAX * * Gives the name "/" to the sysfs_root entry; any path returned * is relative to wherever sysfs is mounted. */ -static char *sysfs_pathname(struct sysfs_dirent *sd, char *path) +static char *sysfs_pathname(struct kernfs_node *kn, char *path) { - if (sd->s_parent) { - sysfs_pathname(sd->s_parent, path); + if (kn->s_parent) { + sysfs_pathname(kn->s_parent, path); strlcat(path, "/", PATH_MAX); } - strlcat(path, sd->s_name, PATH_MAX); + strlcat(path, kn->s_name, PATH_MAX); return path; } -void sysfs_warn_dup(struct sysfs_dirent *parent, const char *name) +void sysfs_warn_dup(struct kernfs_node *parent, const char *name) { char *path; @@ -61,26 +61,26 @@ void sysfs_warn_dup(struct sysfs_dirent *parent, const char *name) */ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) { - struct sysfs_dirent *parent_sd, *sd; + struct kernfs_node *parent, *kn; BUG_ON(!kobj); if (kobj->parent) - parent_sd = kobj->parent->sd; + parent = kobj->parent->sd; else - parent_sd = sysfs_root_sd; + parent = sysfs_root_kn; - if (!parent_sd) + if (!parent) return -ENOENT; - sd = kernfs_create_dir_ns(parent_sd, kobject_name(kobj), kobj, ns); - if (IS_ERR(sd)) { - if (PTR_ERR(sd) == -EEXIST) - sysfs_warn_dup(parent_sd, kobject_name(kobj)); - return PTR_ERR(sd); + kn = kernfs_create_dir_ns(parent, kobject_name(kobj), kobj, ns); + if (IS_ERR(kn)) { + if (PTR_ERR(kn) == -EEXIST) + sysfs_warn_dup(parent, kobject_name(kobj)); + return PTR_ERR(kn); } - kobj->sd = sd; + kobj->sd = kn; return 0; } @@ -94,47 +94,47 @@ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) */ void sysfs_remove_dir(struct kobject *kobj) { - struct sysfs_dirent *sd = kobj->sd; + struct kernfs_node *kn = kobj->sd; /* * In general, kboject owner is responsible for ensuring removal * doesn't race with other operations and sysfs doesn't provide any * protection; however, when @kobj is used as a symlink target, the * symlinking entity usually doesn't own @kobj and thus has no - * control over removal. @kobj->sd may be removed anytime and - * symlink code may end up dereferencing an already freed sd. + * control over removal. @kobj->sd may be removed anytime + * and symlink code may end up dereferencing an already freed node. * - * sysfs_symlink_target_lock synchronizes @kobj->sd disassociation - * against symlink operations so that symlink code can safely - * dereference @kobj->sd. + * sysfs_symlink_target_lock synchronizes @kobj->sd + * disassociation against symlink operations so that symlink code + * can safely dereference @kobj->sd. */ spin_lock(&sysfs_symlink_target_lock); kobj->sd = NULL; spin_unlock(&sysfs_symlink_target_lock); - if (sd) { - WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); - kernfs_remove(sd); + if (kn) { + WARN_ON_ONCE(sysfs_type(kn) != SYSFS_DIR); + kernfs_remove(kn); } } int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, const void *new_ns) { - struct sysfs_dirent *parent_sd = kobj->sd->s_parent; + struct kernfs_node *parent = kobj->sd->s_parent; - return kernfs_rename_ns(kobj->sd, parent_sd, new_name, new_ns); + return kernfs_rename_ns(kobj->sd, parent, new_name, new_ns); } int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, const void *new_ns) { - struct sysfs_dirent *sd = kobj->sd; - struct sysfs_dirent *new_parent_sd; + struct kernfs_node *kn = kobj->sd; + struct kernfs_node *new_parent; - BUG_ON(!sd->s_parent); - new_parent_sd = new_parent_kobj && new_parent_kobj->sd ? - new_parent_kobj->sd : sysfs_root_sd; + BUG_ON(!kn->s_parent); + new_parent = new_parent_kobj && new_parent_kobj->sd ? + new_parent_kobj->sd : sysfs_root_kn; - return kernfs_rename_ns(sd, new_parent_sd, sd->s_name, new_ns); + return kernfs_rename_ns(kn, new_parent, kn->s_name, new_ns); } diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index a67d1c6..be1cc39 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -22,15 +22,15 @@ #include "../kernfs/kernfs-internal.h" /* - * Determine ktype->sysfs_ops for the given sysfs_dirent. This function + * Determine ktype->sysfs_ops for the given kernfs_node. This function * must be called while holding an active reference. */ -static const struct sysfs_ops *sysfs_file_ops(struct sysfs_dirent *sd) +static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn) { - struct kobject *kobj = sd->s_parent->priv; + struct kobject *kobj = kn->s_parent->priv; - if (sd->s_flags & SYSFS_FLAG_LOCKDEP) - lockdep_assert_held(sd); + if (kn->s_flags & SYSFS_FLAG_LOCKDEP) + lockdep_assert_held(kn); return kobj->ktype ? kobj->ktype->sysfs_ops : NULL; } @@ -42,8 +42,8 @@ static const struct sysfs_ops *sysfs_file_ops(struct sysfs_dirent *sd) static int sysfs_kf_seq_show(struct seq_file *sf, void *v) { struct sysfs_open_file *of = sf->private; - struct kobject *kobj = of->sd->s_parent->priv; - const struct sysfs_ops *ops = sysfs_file_ops(of->sd); + struct kobject *kobj = of->kn->s_parent->priv; + const struct sysfs_ops *ops = sysfs_file_ops(of->kn); ssize_t count; char *buf; @@ -59,7 +59,7 @@ static int sysfs_kf_seq_show(struct seq_file *sf, void *v) * if @ops->show() isn't implemented. */ if (ops->show) { - count = ops->show(kobj, of->sd->priv, buf); + count = ops->show(kobj, of->kn->priv, buf); if (count < 0) return count; } @@ -81,8 +81,8 @@ static int sysfs_kf_seq_show(struct seq_file *sf, void *v) static ssize_t sysfs_kf_bin_read(struct sysfs_open_file *of, char *buf, size_t count, loff_t pos) { - struct bin_attribute *battr = of->sd->priv; - struct kobject *kobj = of->sd->s_parent->priv; + struct bin_attribute *battr = of->kn->priv; + struct kobject *kobj = of->kn->s_parent->priv; loff_t size = file_inode(of->file)->i_size; if (!count) @@ -105,21 +105,21 @@ static ssize_t sysfs_kf_bin_read(struct sysfs_open_file *of, char *buf, static ssize_t sysfs_kf_write(struct sysfs_open_file *of, char *buf, size_t count, loff_t pos) { - const struct sysfs_ops *ops = sysfs_file_ops(of->sd); - struct kobject *kobj = of->sd->s_parent->priv; + const struct sysfs_ops *ops = sysfs_file_ops(of->kn); + struct kobject *kobj = of->kn->s_parent->priv; if (!count) return 0; - return ops->store(kobj, of->sd->priv, buf, count); + return ops->store(kobj, of->kn->priv, buf, count); } /* kernfs write callback for bin sysfs files */ static ssize_t sysfs_kf_bin_write(struct sysfs_open_file *of, char *buf, size_t count, loff_t pos) { - struct bin_attribute *battr = of->sd->priv; - struct kobject *kobj = of->sd->s_parent->priv; + struct bin_attribute *battr = of->kn->priv; + struct kobject *kobj = of->kn->s_parent->priv; loff_t size = file_inode(of->file)->i_size; if (size) { @@ -139,30 +139,30 @@ static ssize_t sysfs_kf_bin_write(struct sysfs_open_file *of, char *buf, static int sysfs_kf_bin_mmap(struct sysfs_open_file *of, struct vm_area_struct *vma) { - struct bin_attribute *battr = of->sd->priv; - struct kobject *kobj = of->sd->s_parent->priv; + struct bin_attribute *battr = of->kn->priv; + struct kobject *kobj = of->kn->s_parent->priv; return battr->mmap(of->file, kobj, battr, vma); } -void sysfs_notify(struct kobject *k, const char *dir, const char *attr) +void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr) { - struct sysfs_dirent *sd = k->sd, *tmp; + struct kernfs_node *kn = kobj->sd, *tmp; - if (sd && dir) - sd = kernfs_find_and_get(sd, dir); + if (kn && dir) + kn = kernfs_find_and_get(kn, dir); else - kernfs_get(sd); + kernfs_get(kn); - if (sd && attr) { - tmp = kernfs_find_and_get(sd, attr); - kernfs_put(sd); - sd = tmp; + if (kn && attr) { + tmp = kernfs_find_and_get(kn, attr); + kernfs_put(kn); + kn = tmp; } - if (sd) { - kernfs_notify(sd); - kernfs_put(sd); + if (kn) { + kernfs_notify(kn); + kernfs_put(kn); } } EXPORT_SYMBOL_GPL(sysfs_notify); @@ -202,17 +202,17 @@ static const struct kernfs_ops sysfs_bin_kfops_mmap = { .mmap = sysfs_kf_bin_mmap, }; -int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, +int sysfs_add_file_mode_ns(struct kernfs_node *parent, const struct attribute *attr, bool is_bin, umode_t mode, const void *ns) { struct lock_class_key *key = NULL; const struct kernfs_ops *ops; - struct sysfs_dirent *sd; + struct kernfs_node *kn; loff_t size; if (!is_bin) { - struct kobject *kobj = dir_sd->priv; + struct kobject *kobj = parent->priv; const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops; /* every kobject with an attribute needs a ktype assigned */ @@ -252,20 +252,20 @@ int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, if (!attr->ignore_lockdep) key = attr->key ?: (struct lock_class_key *)&attr->skey; #endif - sd = kernfs_create_file_ns_key(dir_sd, attr->name, mode, size, + kn = kernfs_create_file_ns_key(parent, attr->name, mode, size, ops, (void *)attr, ns, key); - if (IS_ERR(sd)) { - if (PTR_ERR(sd) == -EEXIST) - sysfs_warn_dup(dir_sd, attr->name); - return PTR_ERR(sd); + if (IS_ERR(kn)) { + if (PTR_ERR(kn) == -EEXIST) + sysfs_warn_dup(parent, attr->name); + return PTR_ERR(kn); } return 0; } -int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr, +int sysfs_add_file(struct kernfs_node *parent, const struct attribute *attr, bool is_bin) { - return sysfs_add_file_mode_ns(dir_sd, attr, is_bin, attr->mode, NULL); + return sysfs_add_file_mode_ns(parent, attr, is_bin, attr->mode, NULL); } /** @@ -307,21 +307,21 @@ EXPORT_SYMBOL_GPL(sysfs_create_files); int sysfs_add_file_to_group(struct kobject *kobj, const struct attribute *attr, const char *group) { - struct sysfs_dirent *dir_sd; + struct kernfs_node *parent; int error; if (group) { - dir_sd = kernfs_find_and_get(kobj->sd, group); + parent = kernfs_find_and_get(kobj->sd, group); } else { - dir_sd = kobj->sd; - kernfs_get(dir_sd); + parent = kobj->sd; + kernfs_get(parent); } - if (!dir_sd) + if (!parent) return -ENOENT; - error = sysfs_add_file(dir_sd, attr, false); - kernfs_put(dir_sd); + error = sysfs_add_file(parent, attr, false); + kernfs_put(parent); return error; } @@ -337,20 +337,20 @@ EXPORT_SYMBOL_GPL(sysfs_add_file_to_group); int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, umode_t mode) { - struct sysfs_dirent *sd; + struct kernfs_node *kn; struct iattr newattrs; int rc; - sd = kernfs_find_and_get(kobj->sd, attr->name); - if (!sd) + kn = kernfs_find_and_get(kobj->sd, attr->name); + if (!kn) return -ENOENT; - newattrs.ia_mode = (mode & S_IALLUGO) | (sd->s_mode & ~S_IALLUGO); + newattrs.ia_mode = (mode & S_IALLUGO) | (kn->s_mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE; - rc = kernfs_setattr(sd, &newattrs); + rc = kernfs_setattr(kn, &newattrs); - kernfs_put(sd); + kernfs_put(kn); return rc; } EXPORT_SYMBOL_GPL(sysfs_chmod_file); @@ -366,9 +366,9 @@ EXPORT_SYMBOL_GPL(sysfs_chmod_file); void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, const void *ns) { - struct sysfs_dirent *dir_sd = kobj->sd; + struct kernfs_node *parent = kobj->sd; - kernfs_remove_by_name_ns(dir_sd, attr->name, ns); + kernfs_remove_by_name_ns(parent, attr->name, ns); } EXPORT_SYMBOL_GPL(sysfs_remove_file_ns); @@ -389,18 +389,18 @@ EXPORT_SYMBOL_GPL(sysfs_remove_files); void sysfs_remove_file_from_group(struct kobject *kobj, const struct attribute *attr, const char *group) { - struct sysfs_dirent *dir_sd; + struct kernfs_node *parent; if (group) { - dir_sd = kernfs_find_and_get(kobj->sd, group); + parent = kernfs_find_and_get(kobj->sd, group); } else { - dir_sd = kobj->sd; - kernfs_get(dir_sd); + parent = kobj->sd; + kernfs_get(parent); } - if (dir_sd) { - kernfs_remove_by_name(dir_sd, attr->name); - kernfs_put(dir_sd); + if (parent) { + kernfs_remove_by_name(parent, attr->name); + kernfs_put(parent); } } EXPORT_SYMBOL_GPL(sysfs_remove_file_from_group); diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 7177532..4d00d399 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -18,7 +18,7 @@ #include "sysfs.h" -static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, +static void remove_files(struct kernfs_node *parent, struct kobject *kobj, const struct attribute_group *grp) { struct attribute *const *attr; @@ -26,13 +26,13 @@ static void remove_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, if (grp->attrs) for (attr = grp->attrs; *attr; attr++) - kernfs_remove_by_name(dir_sd, (*attr)->name); + kernfs_remove_by_name(parent, (*attr)->name); if (grp->bin_attrs) for (bin_attr = grp->bin_attrs; *bin_attr; bin_attr++) sysfs_remove_bin_file(kobj, *bin_attr); } -static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, +static int create_files(struct kernfs_node *parent, struct kobject *kobj, const struct attribute_group *grp, int update) { struct attribute *const *attr; @@ -49,20 +49,20 @@ static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, * re-adding (if required) the file. */ if (update) - kernfs_remove_by_name(dir_sd, (*attr)->name); + kernfs_remove_by_name(parent, (*attr)->name); if (grp->is_visible) { mode = grp->is_visible(kobj, *attr, i); if (!mode) continue; } - error = sysfs_add_file_mode_ns(dir_sd, *attr, false, + error = sysfs_add_file_mode_ns(parent, *attr, false, (*attr)->mode | mode, NULL); if (unlikely(error)) break; } if (error) { - remove_files(dir_sd, kobj, grp); + remove_files(parent, kobj, grp); goto exit; } } @@ -76,7 +76,7 @@ static int create_files(struct sysfs_dirent *dir_sd, struct kobject *kobj, break; } if (error) - remove_files(dir_sd, kobj, grp); + remove_files(parent, kobj, grp); } exit: return error; @@ -86,7 +86,7 @@ exit: static int internal_create_group(struct kobject *kobj, int update, const struct attribute_group *grp) { - struct sysfs_dirent *sd; + struct kernfs_node *kn; int error; BUG_ON(!kobj || (!update && !kobj->sd)); @@ -100,21 +100,21 @@ static int internal_create_group(struct kobject *kobj, int update, return -EINVAL; } if (grp->name) { - sd = kernfs_create_dir(kobj->sd, grp->name, kobj); - if (IS_ERR(sd)) { - if (PTR_ERR(sd) == -EEXIST) + kn = kernfs_create_dir(kobj->sd, grp->name, kobj); + if (IS_ERR(kn)) { + if (PTR_ERR(kn) == -EEXIST) sysfs_warn_dup(kobj->sd, grp->name); - return PTR_ERR(sd); + return PTR_ERR(kn); } } else - sd = kobj->sd; - kernfs_get(sd); - error = create_files(sd, kobj, grp, update); + kn = kobj->sd; + kernfs_get(kn); + error = create_files(kn, kobj, grp, update); if (error) { if (grp->name) - kernfs_remove(sd); + kernfs_remove(kn); } - kernfs_put(sd); + kernfs_put(kn); return error; } @@ -204,27 +204,27 @@ EXPORT_SYMBOL_GPL(sysfs_update_group); void sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp) { - struct sysfs_dirent *dir_sd = kobj->sd; - struct sysfs_dirent *sd; + struct kernfs_node *parent = kobj->sd; + struct kernfs_node *kn; if (grp->name) { - sd = kernfs_find_and_get(dir_sd, grp->name); - if (!sd) { - WARN(!sd, KERN_WARNING + kn = kernfs_find_and_get(parent, grp->name); + if (!kn) { + WARN(!kn, KERN_WARNING "sysfs group %p not found for kobject '%s'\n", grp, kobject_name(kobj)); return; } } else { - sd = dir_sd; - kernfs_get(sd); + kn = parent; + kernfs_get(kn); } - remove_files(sd, kobj, grp); + remove_files(kn, kobj, grp); if (grp->name) - kernfs_remove(sd); + kernfs_remove(kn); - kernfs_put(sd); + kernfs_put(kn); } EXPORT_SYMBOL_GPL(sysfs_remove_group); @@ -260,22 +260,22 @@ EXPORT_SYMBOL_GPL(sysfs_remove_groups); int sysfs_merge_group(struct kobject *kobj, const struct attribute_group *grp) { - struct sysfs_dirent *dir_sd; + struct kernfs_node *parent; int error = 0; struct attribute *const *attr; int i; - dir_sd = kernfs_find_and_get(kobj->sd, grp->name); - if (!dir_sd) + parent = kernfs_find_and_get(kobj->sd, grp->name); + if (!parent) return -ENOENT; for ((i = 0, attr = grp->attrs); *attr && !error; (++i, ++attr)) - error = sysfs_add_file(dir_sd, *attr, false); + error = sysfs_add_file(parent, *attr, false); if (error) { while (--i >= 0) - kernfs_remove_by_name(dir_sd, (*--attr)->name); + kernfs_remove_by_name(parent, (*--attr)->name); } - kernfs_put(dir_sd); + kernfs_put(parent); return error; } @@ -289,14 +289,14 @@ EXPORT_SYMBOL_GPL(sysfs_merge_group); void sysfs_unmerge_group(struct kobject *kobj, const struct attribute_group *grp) { - struct sysfs_dirent *dir_sd; + struct kernfs_node *parent; struct attribute *const *attr; - dir_sd = kernfs_find_and_get(kobj->sd, grp->name); - if (dir_sd) { + parent = kernfs_find_and_get(kobj->sd, grp->name); + if (parent) { for (attr = grp->attrs; *attr; ++attr) - kernfs_remove_by_name(dir_sd, (*attr)->name); - kernfs_put(dir_sd); + kernfs_remove_by_name(parent, (*attr)->name); + kernfs_put(parent); } } EXPORT_SYMBOL_GPL(sysfs_unmerge_group); @@ -311,15 +311,15 @@ EXPORT_SYMBOL_GPL(sysfs_unmerge_group); int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name, struct kobject *target, const char *link_name) { - struct sysfs_dirent *dir_sd; + struct kernfs_node *parent; int error = 0; - dir_sd = kernfs_find_and_get(kobj->sd, group_name); - if (!dir_sd) + parent = kernfs_find_and_get(kobj->sd, group_name); + if (!parent) return -ENOENT; - error = sysfs_create_link_sd(dir_sd, target, link_name); - kernfs_put(dir_sd); + error = sysfs_create_link_sd(parent, target, link_name); + kernfs_put(parent); return error; } @@ -334,12 +334,12 @@ EXPORT_SYMBOL_GPL(sysfs_add_link_to_group); void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, const char *link_name) { - struct sysfs_dirent *dir_sd; + struct kernfs_node *parent; - dir_sd = kernfs_find_and_get(kobj->sd, group_name); - if (dir_sd) { - kernfs_remove_by_name(dir_sd, link_name); - kernfs_put(dir_sd); + parent = kernfs_find_and_get(kobj->sd, group_name); + if (parent) { + kernfs_remove_by_name(parent, link_name); + kernfs_put(parent); } } EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group); diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 8d07527..701a56f 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -20,7 +20,7 @@ #include "sysfs.h" static struct kernfs_root *sysfs_root; -struct sysfs_dirent *sysfs_root_sd; +struct kernfs_node *sysfs_root_kn; static struct dentry *sysfs_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *data) @@ -66,7 +66,7 @@ int __init sysfs_init(void) if (IS_ERR(sysfs_root)) return PTR_ERR(sysfs_root); - sysfs_root_sd = sysfs_root->sd; + sysfs_root_kn = sysfs_root->kn; err = register_filesystem(&sysfs_fs_type); if (err) { diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 1b8c9ed..4ed3d49 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -18,66 +18,66 @@ #include "sysfs.h" -static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd, - struct kobject *target, +static int sysfs_do_create_link_sd(struct kernfs_node *parent, + struct kobject *target_kobj, const char *name, int warn) { - struct sysfs_dirent *sd, *target_sd = NULL; + struct kernfs_node *kn, *target = NULL; - BUG_ON(!name || !parent_sd); + BUG_ON(!name || !parent); /* - * We don't own @target and it may be removed at any time. + * We don't own @target_kobj and it may be removed at any time. * Synchronize using sysfs_symlink_target_lock. See * sysfs_remove_dir() for details. */ spin_lock(&sysfs_symlink_target_lock); - if (target->sd) { - target_sd = target->sd; - kernfs_get(target_sd); + if (target_kobj->sd) { + target = target_kobj->sd; + kernfs_get(target); } spin_unlock(&sysfs_symlink_target_lock); - if (!target_sd) + if (!target) return -ENOENT; - sd = kernfs_create_link(parent_sd, name, target_sd); - kernfs_put(target_sd); + kn = kernfs_create_link(parent, name, target); + kernfs_put(target); - if (!IS_ERR(sd)) + if (!IS_ERR(kn)) return 0; - if (warn && PTR_ERR(sd) == -EEXIST) - sysfs_warn_dup(parent_sd, name); - return PTR_ERR(sd); + if (warn && PTR_ERR(kn) == -EEXIST) + sysfs_warn_dup(parent, name); + return PTR_ERR(kn); } /** * sysfs_create_link_sd - create symlink to a given object. - * @sd: directory we're creating the link in. + * @kn: directory we're creating the link in. * @target: object we're pointing to. * @name: name of the symlink. */ -int sysfs_create_link_sd(struct sysfs_dirent *sd, struct kobject *target, +int sysfs_create_link_sd(struct kernfs_node *kn, struct kobject *target, const char *name) { - return sysfs_do_create_link_sd(sd, target, name, 1); + return sysfs_do_create_link_sd(kn, target, name, 1); } static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target, const char *name, int warn) { - struct sysfs_dirent *parent_sd = NULL; + struct kernfs_node *parent = NULL; if (!kobj) - parent_sd = sysfs_root_sd; + parent = sysfs_root_kn; else - parent_sd = kobj->sd; + parent = kobj->sd; - if (!parent_sd) + if (!parent) return -EFAULT; - return sysfs_do_create_link_sd(parent_sd, target, name, warn); + return sysfs_do_create_link_sd(parent, target, name, warn); } /** @@ -141,14 +141,14 @@ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, */ void sysfs_remove_link(struct kobject *kobj, const char *name) { - struct sysfs_dirent *parent_sd = NULL; + struct kernfs_node *parent = NULL; if (!kobj) - parent_sd = sysfs_root_sd; + parent = sysfs_root_kn; else - parent_sd = kobj->sd; + parent = kobj->sd; - kernfs_remove_by_name(parent_sd, name); + kernfs_remove_by_name(parent, name); } EXPORT_SYMBOL_GPL(sysfs_remove_link); @@ -165,33 +165,33 @@ EXPORT_SYMBOL_GPL(sysfs_remove_link); int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, const char *old, const char *new, const void *new_ns) { - struct sysfs_dirent *parent_sd, *sd = NULL; + struct kernfs_node *parent, *kn = NULL; const void *old_ns = NULL; int result; if (!kobj) - parent_sd = sysfs_root_sd; + parent = sysfs_root_kn; else - parent_sd = kobj->sd; + parent = kobj->sd; if (targ->sd) old_ns = targ->sd->s_ns; result = -ENOENT; - sd = kernfs_find_and_get_ns(parent_sd, old, old_ns); - if (!sd) + kn = kernfs_find_and_get_ns(parent, old, old_ns); + if (!kn) goto out; result = -EINVAL; - if (sysfs_type(sd) != SYSFS_KOBJ_LINK) + if (sysfs_type(kn) != SYSFS_KOBJ_LINK) goto out; - if (sd->s_symlink.target_sd->priv != targ) + if (kn->s_symlink.target_kn->priv != targ) goto out; - result = kernfs_rename_ns(sd, parent_sd, new, new_ns); + result = kernfs_rename_ns(kn, parent, new, new_ns); out: - kernfs_put(sd); + kernfs_put(kn); return result; } EXPORT_SYMBOL_GPL(sysfs_rename_link_ns); diff --git a/fs/sysfs/sysfs.h b/fs/sysfs/sysfs.h index c8e395b..0e2f1cc 100644 --- a/fs/sysfs/sysfs.h +++ b/fs/sysfs/sysfs.h @@ -16,28 +16,28 @@ /* * mount.c */ -extern struct sysfs_dirent *sysfs_root_sd; +extern struct kernfs_node *sysfs_root_kn; /* * dir.c */ extern spinlock_t sysfs_symlink_target_lock; -void sysfs_warn_dup(struct sysfs_dirent *parent, const char *name); +void sysfs_warn_dup(struct kernfs_node *parent, const char *name); /* * file.c */ -int sysfs_add_file(struct sysfs_dirent *dir_sd, +int sysfs_add_file(struct kernfs_node *parent, const struct attribute *attr, bool is_bin); -int sysfs_add_file_mode_ns(struct sysfs_dirent *dir_sd, +int sysfs_add_file_mode_ns(struct kernfs_node *parent, const struct attribute *attr, bool is_bin, umode_t amode, const void *ns); /* * symlink.c */ -int sysfs_create_link_sd(struct sysfs_dirent *sd, struct kobject *target, +int sysfs_create_link_sd(struct kernfs_node *kn, struct kobject *target, const char *name); #endif /* __SYSFS_INTERNAL_H */ diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index d655413..195d1c6 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -46,61 +46,61 @@ enum kernfs_node_flag { SYSFS_FLAG_LOCKDEP = 0x0100, }; -/* type-specific structures for sysfs_dirent->s_* union members */ -struct sysfs_elem_dir { +/* type-specific structures for kernfs_node union members */ +struct kernfs_elem_dir { unsigned long subdirs; - /* children rbtree starts here and goes through sd->s_rb */ + /* children rbtree starts here and goes through kn->s_rb */ struct rb_root children; /* * The kernfs hierarchy this directory belongs to. This fits - * better directly in sysfs_dirent but is here to save space. + * better directly in kernfs_node but is here to save space. */ struct kernfs_root *root; }; -struct sysfs_elem_symlink { - struct sysfs_dirent *target_sd; +struct kernfs_elem_symlink { + struct kernfs_node *target_kn; }; -struct sysfs_elem_attr { +struct kernfs_elem_attr { const struct kernfs_ops *ops; struct sysfs_open_dirent *open; loff_t size; }; /* - * sysfs_dirent - the building block of sysfs hierarchy. Each and every - * sysfs node is represented by single sysfs_dirent. Most fields are + * kernfs_node - the building block of kernfs hierarchy. Each and every + * kernfs node is represented by single kernfs_node. Most fields are * private to kernfs and shouldn't be accessed directly by kernfs users. * - * 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. + * As long as s_count reference is held, the kernfs_node itself is + * accessible. Dereferencing elem or any other outer entity requires + * active reference. */ -struct sysfs_dirent { +struct kernfs_node { atomic_t s_count; atomic_t s_active; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif /* the following two fields are published */ - struct sysfs_dirent *s_parent; + struct kernfs_node *s_parent; const char *s_name; struct rb_node s_rb; union { struct completion *completion; - struct sysfs_dirent *removed_list; + struct kernfs_node *removed_list; } u; const void *s_ns; /* namespace tag */ unsigned int s_hash; /* ns + name hash */ union { - struct sysfs_elem_dir s_dir; - struct sysfs_elem_symlink s_symlink; - struct sysfs_elem_attr s_attr; + struct kernfs_elem_dir s_dir; + struct kernfs_elem_symlink s_symlink; + struct kernfs_elem_attr s_attr; }; void *priv; @@ -113,7 +113,7 @@ struct sysfs_dirent { struct kernfs_root { /* published fields */ - struct sysfs_dirent *sd; + struct kernfs_node *kn; /* private fields, do not use outside kernfs proper */ struct ida ino_ida; @@ -121,7 +121,7 @@ struct kernfs_root { struct sysfs_open_file { /* published fields */ - struct sysfs_dirent *sd; + struct kernfs_node *kn; struct file *file; /* private fields, do not use outside kernfs proper */ @@ -170,64 +170,64 @@ struct kernfs_ops { #ifdef CONFIG_SYSFS -static inline enum kernfs_node_type sysfs_type(struct sysfs_dirent *sd) +static inline enum kernfs_node_type sysfs_type(struct kernfs_node *kn) { - return sd->s_flags & SYSFS_TYPE_MASK; + return kn->s_flags & SYSFS_TYPE_MASK; } /** * kernfs_enable_ns - enable namespace under a directory - * @sd: directory of interest, should be empty + * @kn: directory of interest, should be empty * - * This is to be called right after @sd is created to enable namespace - * under it. All children of @sd must have non-NULL namespace tags and + * This is to be called right after @kn is created to enable namespace + * under it. All children of @kn must have non-NULL namespace tags and * only the ones which match the super_block's tag will be visible. */ -static inline void kernfs_enable_ns(struct sysfs_dirent *sd) +static inline void kernfs_enable_ns(struct kernfs_node *kn) { - WARN_ON_ONCE(sysfs_type(sd) != SYSFS_DIR); - WARN_ON_ONCE(!RB_EMPTY_ROOT(&sd->s_dir.children)); - sd->s_flags |= SYSFS_FLAG_NS; + WARN_ON_ONCE(sysfs_type(kn) != SYSFS_DIR); + WARN_ON_ONCE(!RB_EMPTY_ROOT(&kn->s_dir.children)); + kn->s_flags |= SYSFS_FLAG_NS; } /** * kernfs_ns_enabled - test whether namespace is enabled - * @sd: the node to test + * @kn: the node to test * * Test whether namespace filtering is enabled for the children of @ns. */ -static inline bool kernfs_ns_enabled(struct sysfs_dirent *sd) +static inline bool kernfs_ns_enabled(struct kernfs_node *kn) { - return sd->s_flags & SYSFS_FLAG_NS; + return kn->s_flags & SYSFS_FLAG_NS; } -struct sysfs_dirent *kernfs_find_and_get_ns(struct sysfs_dirent *parent, - const char *name, const void *ns); -void kernfs_get(struct sysfs_dirent *sd); -void kernfs_put(struct sysfs_dirent *sd); +struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, + const char *name, const void *ns); +void kernfs_get(struct kernfs_node *kn); +void kernfs_put(struct kernfs_node *kn); struct kernfs_root *kernfs_create_root(void *priv); void kernfs_destroy_root(struct kernfs_root *root); -struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, - const char *name, void *priv, - const void *ns); -struct sysfs_dirent *kernfs_create_file_ns_key(struct sysfs_dirent *parent, - const char *name, - umode_t mode, loff_t size, - const struct kernfs_ops *ops, - void *priv, const void *ns, - struct lock_class_key *key); -struct sysfs_dirent *kernfs_create_link(struct sysfs_dirent *parent, - const char *name, - struct sysfs_dirent *target); -void kernfs_remove(struct sysfs_dirent *sd); -int kernfs_remove_by_name_ns(struct sysfs_dirent *parent, const char *name, +struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, + const char *name, void *priv, + const void *ns); +struct kernfs_node *kernfs_create_file_ns_key(struct kernfs_node *parent, + const char *name, + umode_t mode, loff_t size, + const struct kernfs_ops *ops, + void *priv, const void *ns, + struct lock_class_key *key); +struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, + const char *name, + struct kernfs_node *target); +void kernfs_remove(struct kernfs_node *kn); +int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, const void *ns); -int kernfs_rename_ns(struct sysfs_dirent *sd, struct sysfs_dirent *new_parent, +int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, const char *new_name, const void *new_ns); -int kernfs_setattr(struct sysfs_dirent *sd, const struct iattr *iattr); -void kernfs_notify(struct sysfs_dirent *sd); +int kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr); +void kernfs_notify(struct kernfs_node *kn); const void *kernfs_super_ns(struct super_block *sb); struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, @@ -238,60 +238,60 @@ void kernfs_init(void); #else /* CONFIG_SYSFS */ -static inline enum kernfs_node_type sysfs_type(struct sysfs_dirent *sd) +static inline enum kernfs_node_type sysfs_type(struct kernfs_node *kn) { return 0; } /* whatever */ -static inline void kernfs_enable_ns(struct sysfs_dirent *sd) { } +static inline void kernfs_enable_ns(struct kernfs_node *kn) { } -static inline bool kernfs_ns_enabled(struct sysfs_dirent *sd) +static inline bool kernfs_ns_enabled(struct kernfs_node *kn) { return false; } -static inline struct sysfs_dirent * -kernfs_find_and_get_ns(struct sysfs_dirent *parent, const char *name, +static inline struct kernfs_node * +kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name, const void *ns) { return NULL; } -static inline void kernfs_get(struct sysfs_dirent *sd) { } -static inline void kernfs_put(struct sysfs_dirent *sd) { } +static inline void kernfs_get(struct kernfs_node *kn) { } +static inline void kernfs_put(struct kernfs_node *kn) { } static inline struct kernfs_root *kernfs_create_root(void *priv) { return ERR_PTR(-ENOSYS); } static inline void kernfs_destroy_root(struct kernfs_root *root) { } -static inline struct sysfs_dirent * -kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, +static inline struct kernfs_node * +kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, void *priv, const void *ns) { return ERR_PTR(-ENOSYS); } -static inline struct sysfs_dirent * -kernfs_create_file_ns_key(struct sysfs_dirent *parent, const char *name, +static inline struct kernfs_node * +kernfs_create_file_ns_key(struct kernfs_node *parent, const char *name, umode_t mode, loff_t size, const struct kernfs_ops *ops, void *priv, const void *ns, struct lock_class_key *key) { return ERR_PTR(-ENOSYS); } -static inline struct sysfs_dirent * -kernfs_create_link(struct sysfs_dirent *parent, const char *name, - struct sysfs_dirent *target) +static inline struct kernfs_node * +kernfs_create_link(struct kernfs_node *parent, const char *name, + struct kernfs_node *target) { return ERR_PTR(-ENOSYS); } -static inline void kernfs_remove(struct sysfs_dirent *sd) { } +static inline void kernfs_remove(struct kernfs_node *kn) { } -static inline int kernfs_remove_by_name_ns(struct sysfs_dirent *parent, +static inline int kernfs_remove_by_name_ns(struct kernfs_node *kn, const char *name, const void *ns) { return -ENOSYS; } -static inline int kernfs_rename_ns(struct sysfs_dirent *sd, - struct sysfs_dirent *new_parent, +static inline int kernfs_rename_ns(struct kernfs_node *kn, + struct kernfs_node *new_parent, const char *new_name, const void *new_ns) { return -ENOSYS; } -static inline int kernfs_setattr(struct sysfs_dirent *sd, +static inline int kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) { return -ENOSYS; } -static inline void kernfs_notify(struct sysfs_dirent *sd) { } +static inline void kernfs_notify(struct kernfs_node *kn) { } static inline const void *kernfs_super_ns(struct super_block *sb) { return NULL; } @@ -307,20 +307,20 @@ static inline void kernfs_init(void) { } #endif /* CONFIG_SYSFS */ -static inline struct sysfs_dirent * -kernfs_find_and_get(struct sysfs_dirent *sd, const char *name) +static inline struct kernfs_node * +kernfs_find_and_get(struct kernfs_node *kn, const char *name) { - return kernfs_find_and_get_ns(sd, name, NULL); + return kernfs_find_and_get_ns(kn, name, NULL); } -static inline struct sysfs_dirent * -kernfs_create_dir(struct sysfs_dirent *parent, const char *name, void *priv) +static inline struct kernfs_node * +kernfs_create_dir(struct kernfs_node *parent, const char *name, void *priv) { return kernfs_create_dir_ns(parent, name, priv, NULL); } -static inline struct sysfs_dirent * -kernfs_create_file_ns(struct sysfs_dirent *parent, const char *name, +static inline struct kernfs_node * +kernfs_create_file_ns(struct kernfs_node *parent, const char *name, umode_t mode, loff_t size, const struct kernfs_ops *ops, void *priv, const void *ns) { @@ -333,14 +333,14 @@ kernfs_create_file_ns(struct sysfs_dirent *parent, const char *name, ns, key); } -static inline struct sysfs_dirent * -kernfs_create_file(struct sysfs_dirent *parent, const char *name, umode_t mode, +static inline struct kernfs_node * +kernfs_create_file(struct kernfs_node *parent, const char *name, umode_t mode, loff_t size, const struct kernfs_ops *ops, void *priv) { return kernfs_create_file_ns(parent, name, mode, size, ops, priv, NULL); } -static inline int kernfs_remove_by_name(struct sysfs_dirent *parent, +static inline int kernfs_remove_by_name(struct kernfs_node *parent, const char *name) { return kernfs_remove_by_name_ns(parent, name, NULL); diff --git a/include/linux/kobject.h b/include/linux/kobject.h index e7ba650..926afb6 100644 --- a/include/linux/kobject.h +++ b/include/linux/kobject.h @@ -64,7 +64,7 @@ struct kobject { struct kobject *parent; struct kset *kset; struct kobj_type *ktype; - struct sysfs_dirent *sd; + struct kernfs_node *sd; struct kref kref; #ifdef CONFIG_DEBUG_KOBJECT_RELEASE struct delayed_work release; diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index cd8f90b..30b2ebe 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -438,26 +438,26 @@ static inline int sysfs_rename_link(struct kobject *kobj, struct kobject *target return sysfs_rename_link_ns(kobj, target, old_name, new_name, NULL); } -static inline void sysfs_notify_dirent(struct sysfs_dirent *sd) +static inline void sysfs_notify_dirent(struct kernfs_node *kn) { - kernfs_notify(sd); + kernfs_notify(kn); } -static inline struct sysfs_dirent * -sysfs_get_dirent(struct sysfs_dirent *parent_sd, const unsigned char *name) +static inline struct kernfs_node *sysfs_get_dirent(struct kernfs_node *parent, + const unsigned char *name) { - return kernfs_find_and_get(parent_sd, name); + return kernfs_find_and_get(parent, name); } -static inline struct sysfs_dirent *sysfs_get(struct sysfs_dirent *sd) +static inline struct kernfs_node *sysfs_get(struct kernfs_node *kn) { - kernfs_get(sd); - return sd; + kernfs_get(kn); + return kn; } -static inline void sysfs_put(struct sysfs_dirent *sd) +static inline void sysfs_put(struct kernfs_node *kn) { - kernfs_put(sd); + kernfs_put(kn); } #endif /* _SYSFS_H_ */ diff --git a/lib/kobject.c b/lib/kobject.c index 94b321f..064451f 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -556,7 +556,7 @@ out: */ void kobject_del(struct kobject *kobj) { - struct sysfs_dirent *sd; + struct kernfs_node *sd; if (!kobj) return; -- cgit v1.1 From adc5e8b58f4886d45f79f4ff41a09001a76a6b12 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 11 Dec 2013 14:11:54 -0500 Subject: kernfs: drop s_ prefix from kernfs_node members kernfs has just been separated out from sysfs and we're already in full conflict mode. Nothing can make the situation any worse. Let's take the chance to name things properly. s_ prefix for kernfs members is used inconsistently and a misnomer now. It's not like kernfs_node is used widely across the kernel making the ability to grep for the members particularly useful. Let's just drop the prefix. This patch is strictly rename only and doesn't introduce any functional difference. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 198 ++++++++++++++++++++++---------------------- fs/kernfs/file.c | 52 ++++++------ fs/kernfs/inode.c | 30 +++---- fs/kernfs/kernfs-internal.h | 6 +- fs/kernfs/symlink.c | 32 +++---- fs/sysfs/dir.c | 12 +-- fs/sysfs/file.c | 16 ++-- fs/sysfs/symlink.c | 6 +- include/linux/kernfs.h | 38 ++++----- 9 files changed, 193 insertions(+), 197 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 800ebf5..51fff9d 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -19,7 +19,7 @@ DEFINE_MUTEX(sysfs_mutex); -#define rb_to_kn(X) rb_entry((X), struct kernfs_node, s_rb) +#define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb) /** * sysfs_name_hash @@ -47,18 +47,17 @@ static unsigned int sysfs_name_hash(const char *name, const void *ns) static int sysfs_name_compare(unsigned int hash, const char *name, const void *ns, const struct kernfs_node *kn) { - if (hash != kn->s_hash) - return hash - kn->s_hash; - if (ns != kn->s_ns) - return ns - kn->s_ns; - return strcmp(name, kn->s_name); + if (hash != kn->hash) + return hash - kn->hash; + if (ns != kn->ns) + return ns - kn->ns; + return strcmp(name, kn->name); } static int sysfs_sd_compare(const struct kernfs_node *left, const struct kernfs_node *right) { - return sysfs_name_compare(left->s_hash, left->s_name, left->s_ns, - right); + return sysfs_name_compare(left->hash, left->name, left->ns, right); } /** @@ -66,7 +65,7 @@ static int sysfs_sd_compare(const struct kernfs_node *left, * @kn: kernfs_node of interest * * Link @kn into its sibling rbtree which starts from - * @kn->s_parent->s_dir.children. + * @kn->parent->dir.children. * * Locking: * mutex_lock(sysfs_mutex) @@ -76,11 +75,11 @@ static int sysfs_sd_compare(const struct kernfs_node *left, */ static int sysfs_link_sibling(struct kernfs_node *kn) { - struct rb_node **node = &kn->s_parent->s_dir.children.rb_node; + struct rb_node **node = &kn->parent->dir.children.rb_node; struct rb_node *parent = NULL; if (sysfs_type(kn) == SYSFS_DIR) - kn->s_parent->s_dir.subdirs++; + kn->parent->dir.subdirs++; while (*node) { struct kernfs_node *pos; @@ -90,15 +89,15 @@ static int sysfs_link_sibling(struct kernfs_node *kn) parent = *node; result = sysfs_sd_compare(kn, pos); if (result < 0) - node = &pos->s_rb.rb_left; + node = &pos->rb.rb_left; else if (result > 0) - node = &pos->s_rb.rb_right; + node = &pos->rb.rb_right; else return -EEXIST; } /* add new node and rebalance the tree */ - rb_link_node(&kn->s_rb, parent, node); - rb_insert_color(&kn->s_rb, &kn->s_parent->s_dir.children); + rb_link_node(&kn->rb, parent, node); + rb_insert_color(&kn->rb, &kn->parent->dir.children); return 0; } @@ -107,7 +106,7 @@ static int sysfs_link_sibling(struct kernfs_node *kn) * @kn: kernfs_node of interest * * Unlink @kn from its sibling rbtree which starts from - * kn->s_parent->s_dir.children. + * kn->parent->dir.children. * * Locking: * mutex_lock(sysfs_mutex) @@ -115,9 +114,9 @@ static int sysfs_link_sibling(struct kernfs_node *kn) static void sysfs_unlink_sibling(struct kernfs_node *kn) { if (sysfs_type(kn) == SYSFS_DIR) - kn->s_parent->s_dir.subdirs--; + kn->parent->dir.subdirs--; - rb_erase(&kn->s_rb, &kn->s_parent->s_dir.children); + rb_erase(&kn->rb, &kn->parent->dir.children); } /** @@ -135,10 +134,10 @@ struct kernfs_node *sysfs_get_active(struct kernfs_node *kn) if (unlikely(!kn)) return NULL; - if (!atomic_inc_unless_negative(&kn->s_active)) + if (!atomic_inc_unless_negative(&kn->active)) return NULL; - if (kn->s_flags & SYSFS_FLAG_LOCKDEP) + if (kn->flags & SYSFS_FLAG_LOCKDEP) rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_); return kn; } @@ -157,9 +156,9 @@ void sysfs_put_active(struct kernfs_node *kn) if (unlikely(!kn)) return; - if (kn->s_flags & SYSFS_FLAG_LOCKDEP) + if (kn->flags & SYSFS_FLAG_LOCKDEP) rwsem_release(&kn->dep_map, 1, _RET_IP_); - v = atomic_dec_return(&kn->s_active); + v = atomic_dec_return(&kn->active); if (likely(v != SD_DEACTIVATED_BIAS)) return; @@ -181,7 +180,7 @@ static void sysfs_deactivate(struct kernfs_node *kn) DECLARE_COMPLETION_ONSTACK(wait); int v; - BUG_ON(!(kn->s_flags & SYSFS_FLAG_REMOVED)); + BUG_ON(!(kn->flags & SYSFS_FLAG_REMOVED)); if (!(sysfs_type(kn) & SYSFS_ACTIVE_REF)) return; @@ -192,7 +191,7 @@ static void sysfs_deactivate(struct kernfs_node *kn) /* atomic_add_return() is a mb(), put_active() will always see * the updated kn->u.completion. */ - v = atomic_add_return(SD_DEACTIVATED_BIAS, &kn->s_active); + v = atomic_add_return(SD_DEACTIVATED_BIAS, &kn->active); if (v != SD_DEACTIVATED_BIAS) { lock_contended(&kn->dep_map, _RET_IP_); @@ -210,8 +209,8 @@ static void sysfs_deactivate(struct kernfs_node *kn) void kernfs_get(struct kernfs_node *kn) { if (kn) { - WARN_ON(!atomic_read(&kn->s_count)); - atomic_inc(&kn->s_count); + WARN_ON(!atomic_read(&kn->count)); + atomic_inc(&kn->count); } } EXPORT_SYMBOL_GPL(kernfs_get); @@ -227,36 +226,36 @@ void kernfs_put(struct kernfs_node *kn) struct kernfs_node *parent; struct kernfs_root *root; - if (!kn || !atomic_dec_and_test(&kn->s_count)) + if (!kn || !atomic_dec_and_test(&kn->count)) return; root = kernfs_root(kn); repeat: /* Moving/renaming is always done while holding reference. - * kn->s_parent won't change beneath us. + * kn->parent won't change beneath us. */ - parent = kn->s_parent; + parent = kn->parent; - WARN(!(kn->s_flags & SYSFS_FLAG_REMOVED), + WARN(!(kn->flags & SYSFS_FLAG_REMOVED), "sysfs: free using entry: %s/%s\n", - parent ? parent->s_name : "", kn->s_name); + parent ? parent->name : "", kn->name); if (sysfs_type(kn) == SYSFS_KOBJ_LINK) - kernfs_put(kn->s_symlink.target_kn); + kernfs_put(kn->symlink.target_kn); if (sysfs_type(kn) & SYSFS_COPY_NAME) - kfree(kn->s_name); - if (kn->s_iattr) { - if (kn->s_iattr->ia_secdata) - security_release_secctx(kn->s_iattr->ia_secdata, - kn->s_iattr->ia_secdata_len); - simple_xattrs_free(&kn->s_iattr->xattrs); + kfree(kn->name); + if (kn->iattr) { + if (kn->iattr->ia_secdata) + security_release_secctx(kn->iattr->ia_secdata, + kn->iattr->ia_secdata_len); + simple_xattrs_free(&kn->iattr->xattrs); } - kfree(kn->s_iattr); - ida_simple_remove(&root->ino_ida, kn->s_ino); + kfree(kn->iattr); + ida_simple_remove(&root->ino_ida, kn->ino); kmem_cache_free(sysfs_dir_cachep, kn); kn = parent; if (kn) { - if (atomic_dec_and_test(&kn->s_count)) + if (atomic_dec_and_test(&kn->count)) goto repeat; } else { /* just released the root kn, free @root too */ @@ -269,7 +268,7 @@ EXPORT_SYMBOL_GPL(kernfs_put); static int sysfs_dentry_delete(const struct dentry *dentry) { struct kernfs_node *kn = dentry->d_fsdata; - return !(kn && !(kn->s_flags & SYSFS_FLAG_REMOVED)); + return !(kn && !(kn->flags & SYSFS_FLAG_REMOVED)); } static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) @@ -283,20 +282,20 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) mutex_lock(&sysfs_mutex); /* The sysfs dirent has been deleted */ - if (kn->s_flags & SYSFS_FLAG_REMOVED) + if (kn->flags & SYSFS_FLAG_REMOVED) goto out_bad; /* The sysfs dirent has been moved? */ - if (dentry->d_parent->d_fsdata != kn->s_parent) + if (dentry->d_parent->d_fsdata != kn->parent) goto out_bad; /* The sysfs dirent has been renamed */ - if (strcmp(dentry->d_name.name, kn->s_name) != 0) + if (strcmp(dentry->d_name.name, kn->name) != 0) goto out_bad; /* The sysfs dirent has been moved to a different namespace */ - if (kn->s_parent && kernfs_ns_enabled(kn->s_parent) && - sysfs_info(dentry->d_sb)->ns != kn->s_ns) + if (kn->parent && kernfs_ns_enabled(kn->parent) && + sysfs_info(dentry->d_sb)->ns != kn->ns) goto out_bad; mutex_unlock(&sysfs_mutex); @@ -356,14 +355,14 @@ struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, ret = ida_simple_get(&root->ino_ida, 1, 0, GFP_KERNEL); if (ret < 0) goto err_out2; - kn->s_ino = ret; + kn->ino = ret; - atomic_set(&kn->s_count, 1); - atomic_set(&kn->s_active, 0); + atomic_set(&kn->count, 1); + atomic_set(&kn->active, 0); - kn->s_name = name; - kn->s_mode = mode; - kn->s_flags = type | SYSFS_FLAG_REMOVED; + kn->name = name; + kn->mode = mode; + kn->flags = type | SYSFS_FLAG_REMOVED; return kn; @@ -400,9 +399,9 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) * @kn: kernfs_node to be added * @parent: the parent kernfs_node to add @kn to * - * Get @parent and set @kn->s_parent to it and increment nlink of - * the parent inode if @kn is a directory and link into the children - * list of the parent. + * Get @parent and set @kn->parent to it and increment nlink of the + * parent inode if @kn is a directory and link into the children list + * of the parent. * * This function should be called between calls to * sysfs_addrm_start() and sysfs_addrm_finish() and should be @@ -422,18 +421,17 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct kernfs_node *kn, struct sysfs_inode_attrs *ps_iattr; int ret; - if (has_ns != (bool)kn->s_ns) { + if (has_ns != (bool)kn->ns) { WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", - has_ns ? "required" : "invalid", - parent->s_name, kn->s_name); + has_ns ? "required" : "invalid", parent->name, kn->name); return -EINVAL; } if (sysfs_type(parent) != SYSFS_DIR) return -EINVAL; - kn->s_hash = sysfs_name_hash(kn->s_name, kn->s_ns); - kn->s_parent = parent; + kn->hash = sysfs_name_hash(kn->name, kn->ns); + kn->parent = parent; kernfs_get(parent); ret = sysfs_link_sibling(kn); @@ -441,14 +439,14 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct kernfs_node *kn, return ret; /* Update timestamps on the parent */ - ps_iattr = parent->s_iattr; + ps_iattr = parent->iattr; if (ps_iattr) { struct iattr *ps_iattrs = &ps_iattr->ia_iattr; ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; } /* Mark the entry added into directory tree */ - kn->s_flags &= ~SYSFS_FLAG_REMOVED; + kn->flags &= ~SYSFS_FLAG_REMOVED; return 0; } @@ -477,21 +475,21 @@ static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, * Removal can be called multiple times on the same node. Only the * first invocation is effective and puts the base ref. */ - if (kn->s_flags & SYSFS_FLAG_REMOVED) + if (kn->flags & SYSFS_FLAG_REMOVED) return; - if (kn->s_parent) { + if (kn->parent) { sysfs_unlink_sibling(kn); /* Update timestamps on the parent */ - ps_iattr = kn->s_parent->s_iattr; + ps_iattr = kn->parent->iattr; if (ps_iattr) { ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; } } - kn->s_flags |= SYSFS_FLAG_REMOVED; + kn->flags |= SYSFS_FLAG_REMOVED; kn->u.removed_list = acxt->removed; acxt->removed = kn; } @@ -538,7 +536,7 @@ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent, const unsigned char *name, const void *ns) { - struct rb_node *node = parent->s_dir.children.rb_node; + struct rb_node *node = parent->dir.children.rb_node; bool has_ns = kernfs_ns_enabled(parent); unsigned int hash; @@ -546,8 +544,7 @@ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent, if (has_ns != (bool)ns) { WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", - has_ns ? "required" : "invalid", - parent->s_name, name); + has_ns ? "required" : "invalid", parent->name, name); return NULL; } @@ -617,9 +614,9 @@ struct kernfs_root *kernfs_create_root(void *priv) return ERR_PTR(-ENOMEM); } - kn->s_flags &= ~SYSFS_FLAG_REMOVED; + kn->flags &= ~SYSFS_FLAG_REMOVED; kn->priv = priv; - kn->s_dir.root = root; + kn->dir.root = root; root->kn = kn; @@ -661,8 +658,8 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, if (!kn) return ERR_PTR(-ENOMEM); - kn->s_dir.root = parent->s_dir.root; - kn->s_ns = ns; + kn->dir.root = parent->dir.root; + kn->ns = ns; kn->priv = priv; /* link in */ @@ -738,7 +735,7 @@ static struct kernfs_node *sysfs_leftmost_descendant(struct kernfs_node *pos) if (sysfs_type(pos) != SYSFS_DIR) break; - rbn = rb_first(&pos->s_dir.children); + rbn = rb_first(&pos->dir.children); if (!rbn) break; @@ -773,12 +770,12 @@ static struct kernfs_node *sysfs_next_descendant_post(struct kernfs_node *pos, return NULL; /* if there's an unvisited sibling, visit its leftmost descendant */ - rbn = rb_next(&pos->s_rb); + rbn = rb_next(&pos->rb); if (rbn) return sysfs_leftmost_descendant(rb_to_kn(rbn)); /* no sibling left, visit parent */ - return pos->s_parent; + return pos->parent; } static void __kernfs_remove(struct sysfs_addrm_cxt *acxt, @@ -789,7 +786,7 @@ static void __kernfs_remove(struct sysfs_addrm_cxt *acxt, if (!kn) return; - pr_debug("sysfs %s: removing\n", kn->s_name); + pr_debug("sysfs %s: removing\n", kn->name); next = NULL; do { @@ -865,8 +862,8 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, mutex_lock(&sysfs_mutex); error = 0; - if ((kn->s_parent == new_parent) && (kn->s_ns == new_ns) && - (strcmp(kn->s_name, new_name) == 0)) + if ((kn->parent == new_parent) && (kn->ns == new_ns) && + (strcmp(kn->name, new_name) == 0)) goto out; /* nothing to rename */ error = -EEXIST; @@ -874,14 +871,14 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, goto out; /* rename kernfs_node */ - if (strcmp(kn->s_name, new_name) != 0) { + if (strcmp(kn->name, new_name) != 0) { error = -ENOMEM; new_name = kstrdup(new_name, GFP_KERNEL); if (!new_name) goto out; - kfree(kn->s_name); - kn->s_name = new_name; + kfree(kn->name); + kn->name = new_name; } /* @@ -889,10 +886,10 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, */ sysfs_unlink_sibling(kn); kernfs_get(new_parent); - kernfs_put(kn->s_parent); - kn->s_ns = new_ns; - kn->s_hash = sysfs_name_hash(kn->s_name, kn->s_ns); - kn->s_parent = new_parent; + kernfs_put(kn->parent); + kn->ns = new_ns; + kn->hash = sysfs_name_hash(kn->name, kn->ns); + kn->parent = new_parent; sysfs_link_sibling(kn); error = 0; @@ -904,7 +901,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, /* Relationship between s_mode and the DT_xxx types */ static inline unsigned char dt_type(struct kernfs_node *kn) { - return (kn->s_mode >> 12) & 15; + return (kn->mode >> 12) & 15; } static int sysfs_dir_release(struct inode *inode, struct file *filp) @@ -917,29 +914,28 @@ static struct kernfs_node *sysfs_dir_pos(const void *ns, struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) { if (pos) { - int valid = !(pos->s_flags & SYSFS_FLAG_REMOVED) && - pos->s_parent == parent && - hash == pos->s_hash; + int valid = !(pos->flags & SYSFS_FLAG_REMOVED) && + pos->parent == parent && hash == pos->hash; kernfs_put(pos); if (!valid) pos = NULL; } if (!pos && (hash > 1) && (hash < INT_MAX)) { - struct rb_node *node = parent->s_dir.children.rb_node; + struct rb_node *node = parent->dir.children.rb_node; while (node) { pos = rb_to_kn(node); - if (hash < pos->s_hash) + if (hash < pos->hash) node = node->rb_left; - else if (hash > pos->s_hash) + else if (hash > pos->hash) node = node->rb_right; else break; } } /* Skip over entries in the wrong namespace */ - while (pos && pos->s_ns != ns) { - struct rb_node *node = rb_next(&pos->s_rb); + while (pos && pos->ns != ns) { + struct rb_node *node = rb_next(&pos->rb); if (!node) pos = NULL; else @@ -954,12 +950,12 @@ static struct kernfs_node *sysfs_dir_next_pos(const void *ns, pos = sysfs_dir_pos(ns, parent, ino, pos); if (pos) do { - struct rb_node *node = rb_next(&pos->s_rb); + struct rb_node *node = rb_next(&pos->rb); if (!node) pos = NULL; else pos = rb_to_kn(node); - } while (pos && pos->s_ns != ns); + } while (pos && pos->ns != ns); return pos; } @@ -980,12 +976,12 @@ static int sysfs_readdir(struct file *file, struct dir_context *ctx) for (pos = sysfs_dir_pos(ns, parent, ctx->pos, pos); pos; pos = sysfs_dir_next_pos(ns, parent, ctx->pos, pos)) { - const char *name = pos->s_name; + const char *name = pos->name; unsigned int type = dt_type(pos); int len = strlen(name); - ino_t ino = pos->s_ino; + ino_t ino = pos->ino; - ctx->pos = pos->s_hash; + ctx->pos = pos->hash; file->private_data = pos; kernfs_get(pos); diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 1bf07de..5277021 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -21,7 +21,7 @@ * There's one sysfs_open_file for each open file and one sysfs_open_dirent * for each kernfs_node with one or more open files. * - * kernfs_node->s_attr.open points to sysfs_open_dirent. s_attr.open is + * kernfs_node->attr.open points to sysfs_open_dirent. attr.open is * protected by sysfs_open_dirent_lock. * * filp->private_data points to seq_file whose ->private points to @@ -49,9 +49,9 @@ static struct sysfs_open_file *sysfs_of(struct file *file) */ static const struct kernfs_ops *kernfs_ops(struct kernfs_node *kn) { - if (kn->s_flags & SYSFS_FLAG_LOCKDEP) + if (kn->flags & SYSFS_FLAG_LOCKDEP) lockdep_assert_held(kn); - return kn->s_attr.ops; + return kn->attr.ops; } static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) @@ -112,9 +112,9 @@ static int kernfs_seq_show(struct seq_file *sf, void *v) { struct sysfs_open_file *of = sf->private; - of->event = atomic_read(&of->kn->s_attr.open->event); + of->event = atomic_read(&of->kn->attr.open->event); - return of->kn->s_attr.ops->seq_show(sf, v); + return of->kn->attr.ops->seq_show(sf, v); } static const struct seq_operations kernfs_seq_ops = { @@ -189,7 +189,7 @@ static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, { struct sysfs_open_file *of = sysfs_of(file); - if (of->kn->s_flags & SYSFS_FLAG_HAS_SEQ_SHOW) + if (of->kn->flags & SYSFS_FLAG_HAS_SEQ_SHOW) return seq_read(file, user_buf, count, ppos); else return kernfs_file_direct_read(of, user_buf, count, ppos); @@ -428,7 +428,7 @@ static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) * without grabbing @of->mutex by testing HAS_MMAP flag. See the * comment in kernfs_file_open() for more details. */ - if (!(of->kn->s_flags & SYSFS_FLAG_HAS_MMAP)) + if (!(of->kn->flags & SYSFS_FLAG_HAS_MMAP)) return -ENODEV; mutex_lock(&of->mutex); @@ -477,8 +477,8 @@ out_unlock: * @kn: target kernfs_node * @of: sysfs_open_file for this instance of open * - * If @kn->s_attr.open exists, increment its reference count; - * otherwise, create one. @of is chained to the files list. + * If @kn->attr.open exists, increment its reference count; otherwise, + * create one. @of is chained to the files list. * * LOCKING: * Kernel thread context (may sleep). @@ -495,12 +495,12 @@ static int sysfs_get_open_dirent(struct kernfs_node *kn, mutex_lock(&sysfs_open_file_mutex); spin_lock_irq(&sysfs_open_dirent_lock); - if (!kn->s_attr.open && new_od) { - kn->s_attr.open = new_od; + if (!kn->attr.open && new_od) { + kn->attr.open = new_od; new_od = NULL; } - od = kn->s_attr.open; + od = kn->attr.open; if (od) { atomic_inc(&od->refcnt); list_add_tail(&of->list, &od->files); @@ -531,7 +531,7 @@ static int sysfs_get_open_dirent(struct kernfs_node *kn, * @kn: target kernfs_nodet * @of: associated sysfs_open_file * - * Put @kn->s_attr.open and unlink @of from the files list. If + * Put @kn->attr.open and unlink @of from the files list. If * reference count reaches zero, disassociate and free it. * * LOCKING: @@ -540,7 +540,7 @@ static int sysfs_get_open_dirent(struct kernfs_node *kn, static void sysfs_put_open_dirent(struct kernfs_node *kn, struct sysfs_open_file *of) { - struct sysfs_open_dirent *od = kn->s_attr.open; + struct sysfs_open_dirent *od = kn->attr.open; unsigned long flags; mutex_lock(&sysfs_open_file_mutex); @@ -550,7 +550,7 @@ static void sysfs_put_open_dirent(struct kernfs_node *kn, list_del(&of->list); if (atomic_dec_and_test(&od->refcnt)) - kn->s_attr.open = NULL; + kn->attr.open = NULL; else od = NULL; @@ -668,11 +668,11 @@ void sysfs_unmap_bin_file(struct kernfs_node *kn) struct sysfs_open_dirent *od; struct sysfs_open_file *of; - if (!(kn->s_flags & SYSFS_FLAG_HAS_MMAP)) + if (!(kn->flags & SYSFS_FLAG_HAS_MMAP)) return; spin_lock_irq(&sysfs_open_dirent_lock); - od = kn->s_attr.open; + od = kn->attr.open; if (od) atomic_inc(&od->refcnt); spin_unlock_irq(&sysfs_open_dirent_lock); @@ -706,7 +706,7 @@ static unsigned int kernfs_file_poll(struct file *filp, poll_table *wait) { struct sysfs_open_file *of = sysfs_of(filp); struct kernfs_node *kn = filp->f_path.dentry->d_fsdata; - struct sysfs_open_dirent *od = kn->s_attr.open; + struct sysfs_open_dirent *od = kn->attr.open; /* need parent for the kobj, grab both */ if (!sysfs_get_active(kn)) @@ -739,7 +739,7 @@ void kernfs_notify(struct kernfs_node *kn) spin_lock_irqsave(&sysfs_open_dirent_lock, flags); if (!WARN_ON(sysfs_type(kn) != SYSFS_KOBJ_ATTR)) { - od = kn->s_attr.open; + od = kn->attr.open; if (od) { atomic_inc(&od->event); wake_up_interruptible(&od->poll); @@ -789,27 +789,27 @@ struct kernfs_node *kernfs_create_file_ns_key(struct kernfs_node *parent, if (!kn) return ERR_PTR(-ENOMEM); - kn->s_attr.ops = ops; - kn->s_attr.size = size; - kn->s_ns = ns; + kn->attr.ops = ops; + kn->attr.size = size; + kn->ns = ns; kn->priv = priv; #ifdef CONFIG_DEBUG_LOCK_ALLOC if (key) { lockdep_init_map(&kn->dep_map, "s_active", key, 0); - kn->s_flags |= SYSFS_FLAG_LOCKDEP; + kn->flags |= SYSFS_FLAG_LOCKDEP; } #endif /* - * kn->s_attr.ops is accesible only while holding active ref. We + * kn->attr.ops is accesible only while holding active ref. We * need to know whether some ops are implemented outside active * ref. Cache their existence in flags. */ if (ops->seq_show) - kn->s_flags |= SYSFS_FLAG_HAS_SEQ_SHOW; + kn->flags |= SYSFS_FLAG_HAS_SEQ_SHOW; if (ops->mmap) - kn->s_flags |= SYSFS_FLAG_HAS_MMAP; + kn->flags |= SYSFS_FLAG_HAS_MMAP; sysfs_addrm_start(&acxt); rc = sysfs_add_one(&acxt, kn, parent); diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index 9e74eed..f6c0aae 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -50,23 +50,23 @@ static struct sysfs_inode_attrs *sysfs_inode_attrs(struct kernfs_node *kn) { struct iattr *iattrs; - if (kn->s_iattr) - return kn->s_iattr; + if (kn->iattr) + return kn->iattr; - kn->s_iattr = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL); - if (!kn->s_iattr) + kn->iattr = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL); + if (!kn->iattr) return NULL; - iattrs = &kn->s_iattr->ia_iattr; + iattrs = &kn->iattr->ia_iattr; /* assign default attributes */ - iattrs->ia_mode = kn->s_mode; + iattrs->ia_mode = kn->mode; iattrs->ia_uid = GLOBAL_ROOT_UID; iattrs->ia_gid = GLOBAL_ROOT_GID; iattrs->ia_atime = iattrs->ia_mtime = iattrs->ia_ctime = CURRENT_TIME; - simple_xattrs_init(&kn->s_iattr->xattrs); + simple_xattrs_init(&kn->iattr->xattrs); - return kn->s_iattr; + return kn->iattr; } static int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) @@ -93,7 +93,7 @@ static int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) iattrs->ia_ctime = iattr->ia_ctime; if (ia_valid & ATTR_MODE) { umode_t mode = iattr->ia_mode; - iattrs->ia_mode = kn->s_mode = mode; + iattrs->ia_mode = kn->mode = mode; } return 0; } @@ -256,9 +256,9 @@ static inline void set_inode_attr(struct inode *inode, struct iattr *iattr) static void sysfs_refresh_inode(struct kernfs_node *kn, struct inode *inode) { - struct sysfs_inode_attrs *attrs = kn->s_iattr; + struct sysfs_inode_attrs *attrs = kn->iattr; - inode->i_mode = kn->s_mode; + inode->i_mode = kn->mode; if (attrs) { /* * kernfs_node has non-default attributes get them from @@ -270,7 +270,7 @@ static void sysfs_refresh_inode(struct kernfs_node *kn, struct inode *inode) } if (sysfs_type(kn) == SYSFS_DIR) - set_nlink(inode, kn->s_dir.subdirs + 2); + set_nlink(inode, kn->dir.subdirs + 2); } int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, @@ -295,7 +295,7 @@ static void sysfs_init_inode(struct kernfs_node *kn, struct inode *inode) inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info; inode->i_op = &sysfs_inode_operations; - set_default_inode_attr(inode, kn->s_mode); + set_default_inode_attr(inode, kn->mode); sysfs_refresh_inode(kn, inode); /* initialize inode according to type */ @@ -305,7 +305,7 @@ static void sysfs_init_inode(struct kernfs_node *kn, struct inode *inode) inode->i_fop = &sysfs_dir_operations; break; case SYSFS_KOBJ_ATTR: - inode->i_size = kn->s_attr.size; + inode->i_size = kn->attr.size; inode->i_fop = &kernfs_file_operations; break; case SYSFS_KOBJ_LINK: @@ -337,7 +337,7 @@ struct inode *sysfs_get_inode(struct super_block *sb, struct kernfs_node *kn) { struct inode *inode; - inode = iget_locked(sb, kn->s_ino); + inode = iget_locked(sb, kn->ino); if (inode && (inode->i_state & I_NEW)) sysfs_init_inode(kn, inode); diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index b7ea76c..2dbb1cb 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -39,9 +39,9 @@ struct sysfs_inode_attrs { static inline struct kernfs_root *kernfs_root(struct kernfs_node *kn) { /* if parent exists, it's always a dir; otherwise, @sd is a dir */ - if (kn->s_parent) - kn = kn->s_parent; - return kn->s_dir.root; + if (kn->parent) + kn = kn->parent; + return kn->dir.root; } /* diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 29dcf5e..5ac1a57 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -36,8 +36,8 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, return ERR_PTR(-ENOMEM); if (kernfs_ns_enabled(parent)) - kn->s_ns = target->s_ns; - kn->s_symlink.target_kn = target; + kn->ns = target->ns; + kn->symlink.target_kn = target; kernfs_get(target); /* ref owned by symlink */ sysfs_addrm_start(&acxt); @@ -60,24 +60,24 @@ static int sysfs_get_target_path(struct kernfs_node *parent, /* go up to the root, stop at the base */ base = parent; - while (base->s_parent) { - kn = target->s_parent; - while (kn->s_parent && base != kn) - kn = kn->s_parent; + while (base->parent) { + kn = target->parent; + while (kn->parent && base != kn) + kn = kn->parent; if (base == kn) break; strcpy(s, "../"); s += 3; - base = base->s_parent; + base = base->parent; } /* determine end of target string for reverse fillup */ kn = target; - while (kn->s_parent && kn != base) { - len += strlen(kn->s_name) + 1; - kn = kn->s_parent; + while (kn->parent && kn != base) { + len += strlen(kn->name) + 1; + kn = kn->parent; } /* check limits */ @@ -89,15 +89,15 @@ static int sysfs_get_target_path(struct kernfs_node *parent, /* reverse fillup of target string from target to base */ kn = target; - while (kn->s_parent && kn != base) { - int slen = strlen(kn->s_name); + while (kn->parent && kn != base) { + int slen = strlen(kn->name); len -= slen; - strncpy(s + len, kn->s_name, slen); + strncpy(s + len, kn->name, slen); if (len) s[--len] = '/'; - kn = kn->s_parent; + kn = kn->parent; } return 0; @@ -106,8 +106,8 @@ static int sysfs_get_target_path(struct kernfs_node *parent, static int sysfs_getlink(struct dentry *dentry, char *path) { struct kernfs_node *kn = dentry->d_fsdata; - struct kernfs_node *parent = kn->s_parent; - struct kernfs_node *target = kn->s_symlink.target_kn; + struct kernfs_node *parent = kn->parent; + struct kernfs_node *target = kn->symlink.target_kn; int error; mutex_lock(&sysfs_mutex); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index f1efe3d..4a80001 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -29,11 +29,11 @@ DEFINE_SPINLOCK(sysfs_symlink_target_lock); */ static char *sysfs_pathname(struct kernfs_node *kn, char *path) { - if (kn->s_parent) { - sysfs_pathname(kn->s_parent, path); + if (kn->parent) { + sysfs_pathname(kn->parent, path); strlcat(path, "/", PATH_MAX); } - strlcat(path, kn->s_name, PATH_MAX); + strlcat(path, kn->name, PATH_MAX); return path; } @@ -121,7 +121,7 @@ void sysfs_remove_dir(struct kobject *kobj) int sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, const void *new_ns) { - struct kernfs_node *parent = kobj->sd->s_parent; + struct kernfs_node *parent = kobj->sd->parent; return kernfs_rename_ns(kobj->sd, parent, new_name, new_ns); } @@ -132,9 +132,9 @@ int sysfs_move_dir_ns(struct kobject *kobj, struct kobject *new_parent_kobj, struct kernfs_node *kn = kobj->sd; struct kernfs_node *new_parent; - BUG_ON(!kn->s_parent); + BUG_ON(!kn->parent); new_parent = new_parent_kobj && new_parent_kobj->sd ? new_parent_kobj->sd : sysfs_root_kn; - return kernfs_rename_ns(kn, new_parent, kn->s_name, new_ns); + return kernfs_rename_ns(kn, new_parent, kn->name, new_ns); } diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index be1cc39..887703a 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -27,9 +27,9 @@ */ static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn) { - struct kobject *kobj = kn->s_parent->priv; + struct kobject *kobj = kn->parent->priv; - if (kn->s_flags & SYSFS_FLAG_LOCKDEP) + if (kn->flags & SYSFS_FLAG_LOCKDEP) lockdep_assert_held(kn); return kobj->ktype ? kobj->ktype->sysfs_ops : NULL; } @@ -42,7 +42,7 @@ static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn) static int sysfs_kf_seq_show(struct seq_file *sf, void *v) { struct sysfs_open_file *of = sf->private; - struct kobject *kobj = of->kn->s_parent->priv; + struct kobject *kobj = of->kn->parent->priv; const struct sysfs_ops *ops = sysfs_file_ops(of->kn); ssize_t count; char *buf; @@ -82,7 +82,7 @@ static ssize_t sysfs_kf_bin_read(struct sysfs_open_file *of, char *buf, size_t count, loff_t pos) { struct bin_attribute *battr = of->kn->priv; - struct kobject *kobj = of->kn->s_parent->priv; + struct kobject *kobj = of->kn->parent->priv; loff_t size = file_inode(of->file)->i_size; if (!count) @@ -106,7 +106,7 @@ static ssize_t sysfs_kf_write(struct sysfs_open_file *of, char *buf, size_t count, loff_t pos) { const struct sysfs_ops *ops = sysfs_file_ops(of->kn); - struct kobject *kobj = of->kn->s_parent->priv; + struct kobject *kobj = of->kn->parent->priv; if (!count) return 0; @@ -119,7 +119,7 @@ static ssize_t sysfs_kf_bin_write(struct sysfs_open_file *of, char *buf, size_t count, loff_t pos) { struct bin_attribute *battr = of->kn->priv; - struct kobject *kobj = of->kn->s_parent->priv; + struct kobject *kobj = of->kn->parent->priv; loff_t size = file_inode(of->file)->i_size; if (size) { @@ -140,7 +140,7 @@ static int sysfs_kf_bin_mmap(struct sysfs_open_file *of, struct vm_area_struct *vma) { struct bin_attribute *battr = of->kn->priv; - struct kobject *kobj = of->kn->s_parent->priv; + struct kobject *kobj = of->kn->parent->priv; return battr->mmap(of->file, kobj, battr, vma); } @@ -345,7 +345,7 @@ int sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, if (!kn) return -ENOENT; - newattrs.ia_mode = (mode & S_IALLUGO) | (kn->s_mode & ~S_IALLUGO); + newattrs.ia_mode = (mode & S_IALLUGO) | (kn->mode & ~S_IALLUGO); newattrs.ia_valid = ATTR_MODE; rc = kernfs_setattr(kn, &newattrs); diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 4ed3d49..0d48ea9 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -129,7 +129,7 @@ void sysfs_delete_link(struct kobject *kobj, struct kobject *targ, */ spin_lock(&sysfs_symlink_target_lock); if (targ->sd && kernfs_ns_enabled(kobj->sd)) - ns = targ->sd->s_ns; + ns = targ->sd->ns; spin_unlock(&sysfs_symlink_target_lock); kernfs_remove_by_name_ns(kobj->sd, name, ns); } @@ -175,7 +175,7 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, parent = kobj->sd; if (targ->sd) - old_ns = targ->sd->s_ns; + old_ns = targ->sd->ns; result = -ENOENT; kn = kernfs_find_and_get_ns(parent, old, old_ns); @@ -185,7 +185,7 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, result = -EINVAL; if (sysfs_type(kn) != SYSFS_KOBJ_LINK) goto out; - if (kn->s_symlink.target_kn->priv != targ) + if (kn->symlink.target_kn->priv != targ) goto out; result = kernfs_rename_ns(kn, parent, new, new_ns); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 195d1c6..092469f 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -49,7 +49,7 @@ enum kernfs_node_flag { /* type-specific structures for kernfs_node union members */ struct kernfs_elem_dir { unsigned long subdirs; - /* children rbtree starts here and goes through kn->s_rb */ + /* children rbtree starts here and goes through kn->rb */ struct rb_root children; /* @@ -79,36 +79,36 @@ struct kernfs_elem_attr { * active reference. */ struct kernfs_node { - atomic_t s_count; - atomic_t s_active; + atomic_t count; + atomic_t active; #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif /* the following two fields are published */ - struct kernfs_node *s_parent; - const char *s_name; + struct kernfs_node *parent; + const char *name; - struct rb_node s_rb; + struct rb_node rb; union { struct completion *completion; struct kernfs_node *removed_list; } u; - const void *s_ns; /* namespace tag */ - unsigned int s_hash; /* ns + name hash */ + const void *ns; /* namespace tag */ + unsigned int hash; /* ns + name hash */ union { - struct kernfs_elem_dir s_dir; - struct kernfs_elem_symlink s_symlink; - struct kernfs_elem_attr s_attr; + struct kernfs_elem_dir dir; + struct kernfs_elem_symlink symlink; + struct kernfs_elem_attr attr; }; void *priv; - unsigned short s_flags; - umode_t s_mode; - unsigned int s_ino; - struct sysfs_inode_attrs *s_iattr; + unsigned short flags; + umode_t mode; + unsigned int ino; + struct sysfs_inode_attrs *iattr; }; struct kernfs_root { @@ -172,7 +172,7 @@ struct kernfs_ops { static inline enum kernfs_node_type sysfs_type(struct kernfs_node *kn) { - return kn->s_flags & SYSFS_TYPE_MASK; + return kn->flags & SYSFS_TYPE_MASK; } /** @@ -186,8 +186,8 @@ static inline enum kernfs_node_type sysfs_type(struct kernfs_node *kn) static inline void kernfs_enable_ns(struct kernfs_node *kn) { WARN_ON_ONCE(sysfs_type(kn) != SYSFS_DIR); - WARN_ON_ONCE(!RB_EMPTY_ROOT(&kn->s_dir.children)); - kn->s_flags |= SYSFS_FLAG_NS; + WARN_ON_ONCE(!RB_EMPTY_ROOT(&kn->dir.children)); + kn->flags |= SYSFS_FLAG_NS; } /** @@ -198,7 +198,7 @@ static inline void kernfs_enable_ns(struct kernfs_node *kn) */ static inline bool kernfs_ns_enabled(struct kernfs_node *kn) { - return kn->s_flags & SYSFS_FLAG_NS; + return kn->flags & SYSFS_FLAG_NS; } struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, -- cgit v1.1 From c525aaddc366df23eb095d58a2bdf11cce62a98b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 11 Dec 2013 14:11:55 -0500 Subject: kernfs: s/sysfs/kernfs/ in various data structures kernfs has just been separated out from sysfs and we're already in full conflict mode. Nothing can make the situation any worse. Let's take the chance to name things properly. This patch performs the following renames. * s/sysfs_open_dirent/kernfs_open_node/ * s/sysfs_open_file/kernfs_open_file/ * s/sysfs_inode_attrs/kernfs_iattrs/ * s/sysfs_addrm_cxt/kernfs_addrm_cxt/ * s/sysfs_super_info/kernfs_super_info/ * s/sysfs_info()/kernfs_info()/ * s/sysfs_open_dirent_lock/kernfs_open_node_lock/ * s/sysfs_open_file_mutex/kernfs_open_file_mutex/ * s/sysfs_of()/kernfs_of()/ This patch is strictly rename only and doesn't introduce any functional difference. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 28 ++++---- fs/kernfs/file.c | 172 ++++++++++++++++++++++---------------------- fs/kernfs/inode.c | 30 ++++---- fs/kernfs/kernfs-internal.h | 20 +++--- fs/kernfs/mount.c | 14 ++-- fs/kernfs/symlink.c | 2 +- fs/sysfs/file.c | 10 +-- include/linux/kernfs.h | 18 ++--- 8 files changed, 147 insertions(+), 147 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 51fff9d..d038bb2 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -295,7 +295,7 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) /* The sysfs dirent has been moved to a different namespace */ if (kn->parent && kernfs_ns_enabled(kn->parent) && - sysfs_info(dentry->d_sb)->ns != kn->ns) + kernfs_info(dentry->d_sb)->ns != kn->ns) goto out_bad; mutex_unlock(&sysfs_mutex); @@ -375,7 +375,7 @@ struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, /** * sysfs_addrm_start - prepare for kernfs_node add/remove - * @acxt: pointer to sysfs_addrm_cxt to be used + * @acxt: pointer to kernfs_addrm_cxt to be used * * This function is called when the caller is about to add or remove * kernfs_node. This function acquires sysfs_mutex. @acxt is used to @@ -385,7 +385,7 @@ struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, * Kernel thread context (may sleep). sysfs_mutex is locked on * return. */ -void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) +void sysfs_addrm_start(struct kernfs_addrm_cxt *acxt) __acquires(sysfs_mutex) { memset(acxt, 0, sizeof(*acxt)); @@ -414,11 +414,11 @@ void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt) * 0 on success, -EEXIST if entry with the given name already * exists. */ -int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct kernfs_node *kn, +int sysfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, struct kernfs_node *parent) { bool has_ns = kernfs_ns_enabled(parent); - struct sysfs_inode_attrs *ps_iattr; + struct kernfs_iattrs *ps_iattr; int ret; if (has_ns != (bool)kn->ns) { @@ -466,10 +466,10 @@ int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct kernfs_node *kn, * LOCKING: * Determined by sysfs_addrm_start(). */ -static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, +static void sysfs_remove_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn) { - struct sysfs_inode_attrs *ps_iattr; + struct kernfs_iattrs *ps_iattr; /* * Removal can be called multiple times on the same node. Only the @@ -505,7 +505,7 @@ static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt, * LOCKING: * sysfs_mutex is released. */ -void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt) +void sysfs_addrm_finish(struct kernfs_addrm_cxt *acxt) __releases(sysfs_mutex) { /* release resources acquired by sysfs_addrm_start() */ @@ -649,7 +649,7 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, const void *ns) { umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; - struct sysfs_addrm_cxt acxt; + struct kernfs_addrm_cxt acxt; struct kernfs_node *kn; int rc; @@ -686,7 +686,7 @@ static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, mutex_lock(&sysfs_mutex); if (kernfs_ns_enabled(parent)) - ns = sysfs_info(dir->i_sb)->ns; + ns = kernfs_info(dir->i_sb)->ns; kn = kernfs_find_ns(parent, dentry->d_name.name, ns); @@ -778,7 +778,7 @@ static struct kernfs_node *sysfs_next_descendant_post(struct kernfs_node *pos, return pos->parent; } -static void __kernfs_remove(struct sysfs_addrm_cxt *acxt, +static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn) { struct kernfs_node *pos, *next; @@ -805,7 +805,7 @@ static void __kernfs_remove(struct sysfs_addrm_cxt *acxt, */ void kernfs_remove(struct kernfs_node *kn) { - struct sysfs_addrm_cxt acxt; + struct kernfs_addrm_cxt acxt; sysfs_addrm_start(&acxt); __kernfs_remove(&acxt, kn); @@ -824,7 +824,7 @@ void kernfs_remove(struct kernfs_node *kn) int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, const void *ns) { - struct sysfs_addrm_cxt acxt; + struct kernfs_addrm_cxt acxt; struct kernfs_node *kn; if (!parent) { @@ -971,7 +971,7 @@ static int sysfs_readdir(struct file *file, struct dir_context *ctx) mutex_lock(&sysfs_mutex); if (kernfs_ns_enabled(parent)) - ns = sysfs_info(dentry->d_sb)->ns; + ns = kernfs_info(dentry->d_sb)->ns; for (pos = sysfs_dir_pos(ns, parent, ctx->pos, pos); pos; diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 5277021..2714a39 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -18,27 +18,27 @@ #include "kernfs-internal.h" /* - * There's one sysfs_open_file for each open file and one sysfs_open_dirent + * There's one kernfs_open_file for each open file and one kernfs_open_node * for each kernfs_node with one or more open files. * - * kernfs_node->attr.open points to sysfs_open_dirent. attr.open is - * protected by sysfs_open_dirent_lock. + * kernfs_node->attr.open points to kernfs_open_node. attr.open is + * protected by kernfs_open_node_lock. * * filp->private_data points to seq_file whose ->private points to - * sysfs_open_file. sysfs_open_files are chained at - * sysfs_open_dirent->files, which is protected by sysfs_open_file_mutex. + * kernfs_open_file. kernfs_open_files are chained at + * kernfs_open_node->files, which is protected by kernfs_open_file_mutex. */ -static DEFINE_SPINLOCK(sysfs_open_dirent_lock); -static DEFINE_MUTEX(sysfs_open_file_mutex); +static DEFINE_SPINLOCK(kernfs_open_node_lock); +static DEFINE_MUTEX(kernfs_open_file_mutex); -struct sysfs_open_dirent { +struct kernfs_open_node { atomic_t refcnt; atomic_t event; wait_queue_head_t poll; - struct list_head files; /* goes through sysfs_open_file.list */ + struct list_head files; /* goes through kernfs_open_file.list */ }; -static struct sysfs_open_file *sysfs_of(struct file *file) +static struct kernfs_open_file *kernfs_of(struct file *file) { return ((struct seq_file *)file->private_data)->private; } @@ -56,7 +56,7 @@ static const struct kernfs_ops *kernfs_ops(struct kernfs_node *kn) static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) { - struct sysfs_open_file *of = sf->private; + struct kernfs_open_file *of = sf->private; const struct kernfs_ops *ops; /* @@ -81,7 +81,7 @@ static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) { - struct sysfs_open_file *of = sf->private; + struct kernfs_open_file *of = sf->private; const struct kernfs_ops *ops = kernfs_ops(of->kn); if (ops->seq_next) { @@ -98,7 +98,7 @@ static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) static void kernfs_seq_stop(struct seq_file *sf, void *v) { - struct sysfs_open_file *of = sf->private; + struct kernfs_open_file *of = sf->private; const struct kernfs_ops *ops = kernfs_ops(of->kn); if (ops->seq_stop) @@ -110,7 +110,7 @@ static void kernfs_seq_stop(struct seq_file *sf, void *v) static int kernfs_seq_show(struct seq_file *sf, void *v) { - struct sysfs_open_file *of = sf->private; + struct kernfs_open_file *of = sf->private; of->event = atomic_read(&of->kn->attr.open->event); @@ -130,7 +130,7 @@ static const struct seq_operations kernfs_seq_ops = { * it difficult to use seq_file. Implement simplistic custom buffering for * bin files. */ -static ssize_t kernfs_file_direct_read(struct sysfs_open_file *of, +static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of, char __user *user_buf, size_t count, loff_t *ppos) { @@ -187,7 +187,7 @@ static ssize_t kernfs_file_direct_read(struct sysfs_open_file *of, static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, size_t count, loff_t *ppos) { - struct sysfs_open_file *of = sysfs_of(file); + struct kernfs_open_file *of = kernfs_of(file); if (of->kn->flags & SYSFS_FLAG_HAS_SEQ_SHOW) return seq_read(file, user_buf, count, ppos); @@ -214,7 +214,7 @@ static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, static ssize_t kernfs_file_write(struct file *file, const char __user *user_buf, size_t count, loff_t *ppos) { - struct sysfs_open_file *of = sysfs_of(file); + struct kernfs_open_file *of = kernfs_of(file); ssize_t len = min_t(size_t, count, PAGE_SIZE); const struct kernfs_ops *ops; char *buf; @@ -259,7 +259,7 @@ out_free: static void kernfs_vma_open(struct vm_area_struct *vma) { struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); + struct kernfs_open_file *of = kernfs_of(file); if (!of->vm_ops) return; @@ -276,7 +276,7 @@ static void kernfs_vma_open(struct vm_area_struct *vma) static int kernfs_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); + struct kernfs_open_file *of = kernfs_of(file); int ret; if (!of->vm_ops) @@ -297,7 +297,7 @@ static int kernfs_vma_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf) { struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); + struct kernfs_open_file *of = kernfs_of(file); int ret; if (!of->vm_ops) @@ -320,7 +320,7 @@ static int kernfs_vma_access(struct vm_area_struct *vma, unsigned long addr, void *buf, int len, int write) { struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); + struct kernfs_open_file *of = kernfs_of(file); int ret; if (!of->vm_ops) @@ -342,7 +342,7 @@ static int kernfs_vma_set_policy(struct vm_area_struct *vma, struct mempolicy *new) { struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); + struct kernfs_open_file *of = kernfs_of(file); int ret; if (!of->vm_ops) @@ -363,7 +363,7 @@ static struct mempolicy *kernfs_vma_get_policy(struct vm_area_struct *vma, unsigned long addr) { struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); + struct kernfs_open_file *of = kernfs_of(file); struct mempolicy *pol; if (!of->vm_ops) @@ -385,7 +385,7 @@ static int kernfs_vma_migrate(struct vm_area_struct *vma, unsigned long flags) { struct file *file = vma->vm_file; - struct sysfs_open_file *of = sysfs_of(file); + struct kernfs_open_file *of = kernfs_of(file); int ret; if (!of->vm_ops) @@ -417,7 +417,7 @@ static const struct vm_operations_struct kernfs_vm_ops = { static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) { - struct sysfs_open_file *of = sysfs_of(file); + struct kernfs_open_file *of = kernfs_of(file); const struct kernfs_ops *ops; int rc; @@ -473,9 +473,9 @@ out_unlock: } /** - * sysfs_get_open_dirent - get or create sysfs_open_dirent + * sysfs_get_open_dirent - get or create kernfs_open_node * @kn: target kernfs_node - * @of: sysfs_open_file for this instance of open + * @of: kernfs_open_file for this instance of open * * If @kn->attr.open exists, increment its reference count; otherwise, * create one. @of is chained to the files list. @@ -487,49 +487,49 @@ out_unlock: * 0 on success, -errno on failure. */ static int sysfs_get_open_dirent(struct kernfs_node *kn, - struct sysfs_open_file *of) + struct kernfs_open_file *of) { - struct sysfs_open_dirent *od, *new_od = NULL; + struct kernfs_open_node *on, *new_on = NULL; retry: - mutex_lock(&sysfs_open_file_mutex); - spin_lock_irq(&sysfs_open_dirent_lock); + mutex_lock(&kernfs_open_file_mutex); + spin_lock_irq(&kernfs_open_node_lock); - if (!kn->attr.open && new_od) { - kn->attr.open = new_od; - new_od = NULL; + if (!kn->attr.open && new_on) { + kn->attr.open = new_on; + new_on = NULL; } - od = kn->attr.open; - if (od) { - atomic_inc(&od->refcnt); - list_add_tail(&of->list, &od->files); + on = kn->attr.open; + if (on) { + atomic_inc(&on->refcnt); + list_add_tail(&of->list, &on->files); } - spin_unlock_irq(&sysfs_open_dirent_lock); - mutex_unlock(&sysfs_open_file_mutex); + spin_unlock_irq(&kernfs_open_node_lock); + mutex_unlock(&kernfs_open_file_mutex); - if (od) { - kfree(new_od); + if (on) { + kfree(new_on); return 0; } /* not there, initialize a new one and retry */ - new_od = kmalloc(sizeof(*new_od), GFP_KERNEL); - if (!new_od) + new_on = kmalloc(sizeof(*new_on), GFP_KERNEL); + if (!new_on) return -ENOMEM; - atomic_set(&new_od->refcnt, 0); - atomic_set(&new_od->event, 1); - init_waitqueue_head(&new_od->poll); - INIT_LIST_HEAD(&new_od->files); + atomic_set(&new_on->refcnt, 0); + atomic_set(&new_on->event, 1); + init_waitqueue_head(&new_on->poll); + INIT_LIST_HEAD(&new_on->files); goto retry; } /** - * sysfs_put_open_dirent - put sysfs_open_dirent + * sysfs_put_open_dirent - put kernfs_open_node * @kn: target kernfs_nodet - * @of: associated sysfs_open_file + * @of: associated kernfs_open_file * * Put @kn->attr.open and unlink @of from the files list. If * reference count reaches zero, disassociate and free it. @@ -538,33 +538,33 @@ static int sysfs_get_open_dirent(struct kernfs_node *kn, * None. */ static void sysfs_put_open_dirent(struct kernfs_node *kn, - struct sysfs_open_file *of) + struct kernfs_open_file *of) { - struct sysfs_open_dirent *od = kn->attr.open; + struct kernfs_open_node *on = kn->attr.open; unsigned long flags; - mutex_lock(&sysfs_open_file_mutex); - spin_lock_irqsave(&sysfs_open_dirent_lock, flags); + mutex_lock(&kernfs_open_file_mutex); + spin_lock_irqsave(&kernfs_open_node_lock, flags); if (of) list_del(&of->list); - if (atomic_dec_and_test(&od->refcnt)) + if (atomic_dec_and_test(&on->refcnt)) kn->attr.open = NULL; else - od = NULL; + on = NULL; - spin_unlock_irqrestore(&sysfs_open_dirent_lock, flags); - mutex_unlock(&sysfs_open_file_mutex); + spin_unlock_irqrestore(&kernfs_open_node_lock, flags); + mutex_unlock(&kernfs_open_file_mutex); - kfree(od); + kfree(on); } static int kernfs_file_open(struct inode *inode, struct file *file) { struct kernfs_node *kn = file->f_path.dentry->d_fsdata; const struct kernfs_ops *ops; - struct sysfs_open_file *of; + struct kernfs_open_file *of; bool has_read, has_write, has_mmap; int error = -EACCES; @@ -586,9 +586,9 @@ static int kernfs_file_open(struct inode *inode, struct file *file) (!(inode->i_mode & S_IRUGO) || !has_read)) goto err_out; - /* allocate a sysfs_open_file for the file */ + /* allocate a kernfs_open_file for the file */ error = -ENOMEM; - of = kzalloc(sizeof(struct sysfs_open_file), GFP_KERNEL); + of = kzalloc(sizeof(struct kernfs_open_file), GFP_KERNEL); if (!of) goto err_out; @@ -654,7 +654,7 @@ err_out: static int kernfs_file_release(struct inode *inode, struct file *filp) { struct kernfs_node *kn = filp->f_path.dentry->d_fsdata; - struct sysfs_open_file *of = sysfs_of(filp); + struct kernfs_open_file *of = kernfs_of(filp); sysfs_put_open_dirent(kn, of); seq_release(inode, filp); @@ -665,26 +665,26 @@ static int kernfs_file_release(struct inode *inode, struct file *filp) void sysfs_unmap_bin_file(struct kernfs_node *kn) { - struct sysfs_open_dirent *od; - struct sysfs_open_file *of; + struct kernfs_open_node *on; + struct kernfs_open_file *of; if (!(kn->flags & SYSFS_FLAG_HAS_MMAP)) return; - spin_lock_irq(&sysfs_open_dirent_lock); - od = kn->attr.open; - if (od) - atomic_inc(&od->refcnt); - spin_unlock_irq(&sysfs_open_dirent_lock); - if (!od) + spin_lock_irq(&kernfs_open_node_lock); + on = kn->attr.open; + if (on) + atomic_inc(&on->refcnt); + spin_unlock_irq(&kernfs_open_node_lock); + if (!on) return; - mutex_lock(&sysfs_open_file_mutex); - list_for_each_entry(of, &od->files, list) { + mutex_lock(&kernfs_open_file_mutex); + list_for_each_entry(of, &on->files, list) { struct inode *inode = file_inode(of->file); unmap_mapping_range(inode->i_mapping, 0, 0, 1); } - mutex_unlock(&sysfs_open_file_mutex); + mutex_unlock(&kernfs_open_file_mutex); sysfs_put_open_dirent(kn, NULL); } @@ -704,19 +704,19 @@ void sysfs_unmap_bin_file(struct kernfs_node *kn) */ static unsigned int kernfs_file_poll(struct file *filp, poll_table *wait) { - struct sysfs_open_file *of = sysfs_of(filp); + struct kernfs_open_file *of = kernfs_of(filp); struct kernfs_node *kn = filp->f_path.dentry->d_fsdata; - struct sysfs_open_dirent *od = kn->attr.open; + struct kernfs_open_node *on = kn->attr.open; /* need parent for the kobj, grab both */ if (!sysfs_get_active(kn)) goto trigger; - poll_wait(filp, &od->poll, wait); + poll_wait(filp, &on->poll, wait); sysfs_put_active(kn); - if (of->event != atomic_read(&od->event)) + if (of->event != atomic_read(&on->event)) goto trigger; return DEFAULT_POLLMASK; @@ -733,20 +733,20 @@ static unsigned int kernfs_file_poll(struct file *filp, poll_table *wait) */ void kernfs_notify(struct kernfs_node *kn) { - struct sysfs_open_dirent *od; + struct kernfs_open_node *on; unsigned long flags; - spin_lock_irqsave(&sysfs_open_dirent_lock, flags); + spin_lock_irqsave(&kernfs_open_node_lock, flags); if (!WARN_ON(sysfs_type(kn) != SYSFS_KOBJ_ATTR)) { - od = kn->attr.open; - if (od) { - atomic_inc(&od->event); - wake_up_interruptible(&od->poll); + on = kn->attr.open; + if (on) { + atomic_inc(&on->event); + wake_up_interruptible(&on->poll); } } - spin_unlock_irqrestore(&sysfs_open_dirent_lock, flags); + spin_unlock_irqrestore(&kernfs_open_node_lock, flags); } EXPORT_SYMBOL_GPL(kernfs_notify); @@ -780,7 +780,7 @@ struct kernfs_node *kernfs_create_file_ns_key(struct kernfs_node *parent, void *priv, const void *ns, struct lock_class_key *key) { - struct sysfs_addrm_cxt acxt; + struct kernfs_addrm_cxt acxt; struct kernfs_node *kn; int rc; diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index f6c0aae..a0e0038 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -46,14 +46,14 @@ void __init sysfs_inode_init(void) panic("failed to init sysfs_backing_dev_info"); } -static struct sysfs_inode_attrs *sysfs_inode_attrs(struct kernfs_node *kn) +static struct kernfs_iattrs *kernfs_iattrs(struct kernfs_node *kn) { struct iattr *iattrs; if (kn->iattr) return kn->iattr; - kn->iattr = kzalloc(sizeof(struct sysfs_inode_attrs), GFP_KERNEL); + kn->iattr = kzalloc(sizeof(struct kernfs_iattrs), GFP_KERNEL); if (!kn->iattr) return NULL; iattrs = &kn->iattr->ia_iattr; @@ -71,11 +71,11 @@ static struct sysfs_inode_attrs *sysfs_inode_attrs(struct kernfs_node *kn) static int __kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) { - struct sysfs_inode_attrs *attrs; + struct kernfs_iattrs *attrs; struct iattr *iattrs; unsigned int ia_valid = iattr->ia_valid; - attrs = sysfs_inode_attrs(kn); + attrs = kernfs_iattrs(kn); if (!attrs) return -ENOMEM; @@ -144,11 +144,11 @@ out: static int sysfs_sd_setsecdata(struct kernfs_node *kn, void **secdata, u32 *secdata_len) { - struct sysfs_inode_attrs *attrs; + struct kernfs_iattrs *attrs; void *old_secdata; size_t old_secdata_len; - attrs = sysfs_inode_attrs(kn); + attrs = kernfs_iattrs(kn); if (!attrs) return -ENOMEM; @@ -167,12 +167,12 @@ int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct kernfs_node *kn = dentry->d_fsdata; - struct sysfs_inode_attrs *attrs; + struct kernfs_iattrs *attrs; void *secdata; int error; u32 secdata_len = 0; - attrs = sysfs_inode_attrs(kn); + attrs = kernfs_iattrs(kn); if (!attrs) return -ENOMEM; @@ -205,9 +205,9 @@ int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, int sysfs_removexattr(struct dentry *dentry, const char *name) { struct kernfs_node *kn = dentry->d_fsdata; - struct sysfs_inode_attrs *attrs; + struct kernfs_iattrs *attrs; - attrs = sysfs_inode_attrs(kn); + attrs = kernfs_iattrs(kn); if (!attrs) return -ENOMEM; @@ -218,9 +218,9 @@ ssize_t sysfs_getxattr(struct dentry *dentry, const char *name, void *buf, size_t size) { struct kernfs_node *kn = dentry->d_fsdata; - struct sysfs_inode_attrs *attrs; + struct kernfs_iattrs *attrs; - attrs = sysfs_inode_attrs(kn); + attrs = kernfs_iattrs(kn); if (!attrs) return -ENOMEM; @@ -230,9 +230,9 @@ ssize_t sysfs_getxattr(struct dentry *dentry, const char *name, void *buf, ssize_t sysfs_listxattr(struct dentry *dentry, char *buf, size_t size) { struct kernfs_node *kn = dentry->d_fsdata; - struct sysfs_inode_attrs *attrs; + struct kernfs_iattrs *attrs; - attrs = sysfs_inode_attrs(kn); + attrs = kernfs_iattrs(kn); if (!attrs) return -ENOMEM; @@ -256,7 +256,7 @@ static inline void set_inode_attr(struct inode *inode, struct iattr *iattr) static void sysfs_refresh_inode(struct kernfs_node *kn, struct inode *inode) { - struct sysfs_inode_attrs *attrs = kn->iattr; + struct kernfs_iattrs *attrs = kn->iattr; inode->i_mode = kn->mode; if (attrs) { diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 2dbb1cb..573f669 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -18,7 +18,7 @@ #include -struct sysfs_inode_attrs { +struct kernfs_iattrs { struct iattr ia_iattr; void *ia_secdata; u32 ia_secdata_len; @@ -47,14 +47,14 @@ static inline struct kernfs_root *kernfs_root(struct kernfs_node *kn) /* * Context structure to be used while adding/removing nodes. */ -struct sysfs_addrm_cxt { +struct kernfs_addrm_cxt { struct kernfs_node *removed; }; /* * mount.c */ -struct sysfs_super_info { +struct kernfs_super_info { /* * The root associated with this super_block. Each super_block is * identified by the root and ns it's associated with. @@ -63,13 +63,13 @@ struct sysfs_super_info { /* * Each sb is associated with one namespace tag, currently the - * network namespace of the task which mounted this sysfs instance. - * If multiple tags become necessary, make the following an array - * and compare kernfs_node tag against every entry. + * network namespace of the task which mounted this kernfs + * instance. If multiple tags become necessary, make the following + * an array and compare kernfs_node tag against every entry. */ const void *ns; }; -#define sysfs_info(SB) ((struct sysfs_super_info *)(SB->s_fs_info)) +#define kernfs_info(SB) ((struct kernfs_super_info *)(SB->s_fs_info)) extern struct kmem_cache *sysfs_dir_cachep; @@ -100,10 +100,10 @@ extern const struct inode_operations sysfs_dir_inode_operations; struct kernfs_node *sysfs_get_active(struct kernfs_node *kn); void sysfs_put_active(struct kernfs_node *kn); -void sysfs_addrm_start(struct sysfs_addrm_cxt *acxt); -int sysfs_add_one(struct sysfs_addrm_cxt *acxt, struct kernfs_node *kn, +void sysfs_addrm_start(struct kernfs_addrm_cxt *acxt); +int sysfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, struct kernfs_node *parent); -void sysfs_addrm_finish(struct sysfs_addrm_cxt *acxt); +void sysfs_addrm_finish(struct kernfs_addrm_cxt *acxt); struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, const char *name, umode_t mode, int type); diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index 9dbbf37..e0796dc 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -27,7 +27,7 @@ static const struct super_operations sysfs_ops = { static int sysfs_fill_super(struct super_block *sb) { - struct sysfs_super_info *info = sysfs_info(sb); + struct kernfs_super_info *info = kernfs_info(sb); struct inode *inode; struct dentry *root; @@ -61,8 +61,8 @@ static int sysfs_fill_super(struct super_block *sb) static int sysfs_test_super(struct super_block *sb, void *data) { - struct sysfs_super_info *sb_info = sysfs_info(sb); - struct sysfs_super_info *info = data; + struct kernfs_super_info *sb_info = kernfs_info(sb); + struct kernfs_super_info *info = data; return sb_info->root == info->root && sb_info->ns == info->ns; } @@ -84,7 +84,7 @@ static int sysfs_set_super(struct super_block *sb, void *data) */ const void *kernfs_super_ns(struct super_block *sb) { - struct sysfs_super_info *info = sysfs_info(sb); + struct kernfs_super_info *info = kernfs_info(sb); return info->ns; } @@ -107,7 +107,7 @@ struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, struct kernfs_root *root, const void *ns) { struct super_block *sb; - struct sysfs_super_info *info; + struct kernfs_super_info *info; int error; info = kzalloc(sizeof(*info), GFP_KERNEL); @@ -144,12 +144,12 @@ struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, */ void kernfs_kill_sb(struct super_block *sb) { - struct sysfs_super_info *info = sysfs_info(sb); + struct kernfs_super_info *info = kernfs_info(sb); struct kernfs_node *root_kn = sb->s_root->d_fsdata; /* * Remove the superblock from fs_supers/s_instances - * so we can't find it, before freeing sysfs_super_info. + * so we can't find it, before freeing kernfs_super_info. */ kill_anon_super(sb); kfree(info); diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 5ac1a57..f36e3f1 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -27,7 +27,7 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, struct kernfs_node *target) { struct kernfs_node *kn; - struct sysfs_addrm_cxt acxt; + struct kernfs_addrm_cxt acxt; int error; kn = sysfs_new_dirent(kernfs_root(parent), name, S_IFLNK|S_IRWXUGO, diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 887703a..fd104b2 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -41,7 +41,7 @@ static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn) */ static int sysfs_kf_seq_show(struct seq_file *sf, void *v) { - struct sysfs_open_file *of = sf->private; + struct kernfs_open_file *of = sf->private; struct kobject *kobj = of->kn->parent->priv; const struct sysfs_ops *ops = sysfs_file_ops(of->kn); ssize_t count; @@ -78,7 +78,7 @@ static int sysfs_kf_seq_show(struct seq_file *sf, void *v) return 0; } -static ssize_t sysfs_kf_bin_read(struct sysfs_open_file *of, char *buf, +static ssize_t sysfs_kf_bin_read(struct kernfs_open_file *of, char *buf, size_t count, loff_t pos) { struct bin_attribute *battr = of->kn->priv; @@ -102,7 +102,7 @@ static ssize_t sysfs_kf_bin_read(struct sysfs_open_file *of, char *buf, } /* kernfs write callback for regular sysfs files */ -static ssize_t sysfs_kf_write(struct sysfs_open_file *of, char *buf, +static ssize_t sysfs_kf_write(struct kernfs_open_file *of, char *buf, size_t count, loff_t pos) { const struct sysfs_ops *ops = sysfs_file_ops(of->kn); @@ -115,7 +115,7 @@ static ssize_t sysfs_kf_write(struct sysfs_open_file *of, char *buf, } /* kernfs write callback for bin sysfs files */ -static ssize_t sysfs_kf_bin_write(struct sysfs_open_file *of, char *buf, +static ssize_t sysfs_kf_bin_write(struct kernfs_open_file *of, char *buf, size_t count, loff_t pos) { struct bin_attribute *battr = of->kn->priv; @@ -136,7 +136,7 @@ static ssize_t sysfs_kf_bin_write(struct sysfs_open_file *of, char *buf, return battr->write(of->file, kobj, battr, buf, pos, count); } -static int sysfs_kf_bin_mmap(struct sysfs_open_file *of, +static int sysfs_kf_bin_mmap(struct kernfs_open_file *of, struct vm_area_struct *vma) { struct bin_attribute *battr = of->kn->priv; diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 092469f..757647c4 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -24,8 +24,8 @@ struct vm_area_struct; struct super_block; struct file_system_type; -struct sysfs_open_dirent; -struct sysfs_inode_attrs; +struct kernfs_open_node; +struct kernfs_iattrs; enum kernfs_node_type { SYSFS_DIR = 0x0001, @@ -65,7 +65,7 @@ struct kernfs_elem_symlink { struct kernfs_elem_attr { const struct kernfs_ops *ops; - struct sysfs_open_dirent *open; + struct kernfs_open_node *open; loff_t size; }; @@ -108,7 +108,7 @@ struct kernfs_node { unsigned short flags; umode_t mode; unsigned int ino; - struct sysfs_inode_attrs *iattr; + struct kernfs_iattrs *iattr; }; struct kernfs_root { @@ -119,7 +119,7 @@ struct kernfs_root { struct ida ino_ida; }; -struct sysfs_open_file { +struct kernfs_open_file { /* published fields */ struct kernfs_node *kn; struct file *file; @@ -140,7 +140,7 @@ struct kernfs_ops { * If seq_show() is present, seq_file path is active. Other seq * operations are optional and if not implemented, the behavior is * equivalent to single_open(). @sf->private points to the - * associated sysfs_open_file. + * associated kernfs_open_file. * * read() is bounced through kernel buffer and a read larger than * PAGE_SIZE results in partial operation of PAGE_SIZE. @@ -151,17 +151,17 @@ struct kernfs_ops { void *(*seq_next)(struct seq_file *sf, void *v, loff_t *ppos); void (*seq_stop)(struct seq_file *sf, void *v); - ssize_t (*read)(struct sysfs_open_file *of, char *buf, size_t bytes, + ssize_t (*read)(struct kernfs_open_file *of, char *buf, size_t bytes, loff_t off); /* * write() is bounced through kernel buffer and a write larger than * PAGE_SIZE results in partial operation of PAGE_SIZE. */ - ssize_t (*write)(struct sysfs_open_file *of, char *buf, size_t bytes, + ssize_t (*write)(struct kernfs_open_file *of, char *buf, size_t bytes, loff_t off); - int (*mmap)(struct sysfs_open_file *of, struct vm_area_struct *vma); + int (*mmap)(struct kernfs_open_file *of, struct vm_area_struct *vma); #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lock_class_key lockdep_key; -- cgit v1.1 From df23fc39bce03bb26e63bea57fc5f5bf6882d74b Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 11 Dec 2013 14:11:56 -0500 Subject: kernfs: s/sysfs/kernfs/ in constants kernfs has just been separated out from sysfs and we're already in full conflict mode. Nothing can make the situation any worse. Let's take the chance to name things properly. This patch performs the following renames. * s/SYSFS_DIR/KERNFS_DIR/ * s/SYSFS_KOBJ_ATTR/KERNFS_FILE/ * s/SYSFS_KOBJ_LINK/KERNFS_LINK/ * s/SYSFS_{TYPE_FLAGS}/KERNFS_{TYPE_FLAGS}/ * s/SYSFS_FLAG_{FLAG}/KERNFS_{FLAG}/ * s/sysfs_type()/kernfs_type()/ * s/SD_DEACTIVATED_BIAS/KN_DEACTIVATED_BIAS/ This patch is strictly rename only and doesn't introduce any functional difference. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 50 ++++++++++++++++++++++----------------------- fs/kernfs/file.c | 18 ++++++++-------- fs/kernfs/inode.c | 10 ++++----- fs/kernfs/kernfs-internal.h | 4 ++-- fs/kernfs/symlink.c | 2 +- fs/sysfs/dir.c | 2 +- fs/sysfs/file.c | 2 +- fs/sysfs/symlink.c | 2 +- include/linux/kernfs.h | 36 ++++++++++++++++---------------- 9 files changed, 63 insertions(+), 63 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index d038bb2..bc8a3b3 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -78,7 +78,7 @@ static int sysfs_link_sibling(struct kernfs_node *kn) struct rb_node **node = &kn->parent->dir.children.rb_node; struct rb_node *parent = NULL; - if (sysfs_type(kn) == SYSFS_DIR) + if (kernfs_type(kn) == KERNFS_DIR) kn->parent->dir.subdirs++; while (*node) { @@ -113,7 +113,7 @@ static int sysfs_link_sibling(struct kernfs_node *kn) */ static void sysfs_unlink_sibling(struct kernfs_node *kn) { - if (sysfs_type(kn) == SYSFS_DIR) + if (kernfs_type(kn) == KERNFS_DIR) kn->parent->dir.subdirs--; rb_erase(&kn->rb, &kn->parent->dir.children); @@ -137,7 +137,7 @@ struct kernfs_node *sysfs_get_active(struct kernfs_node *kn) if (!atomic_inc_unless_negative(&kn->active)) return NULL; - if (kn->flags & SYSFS_FLAG_LOCKDEP) + if (kn->flags & KERNFS_LOCKDEP) rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_); return kn; } @@ -156,10 +156,10 @@ void sysfs_put_active(struct kernfs_node *kn) if (unlikely(!kn)) return; - if (kn->flags & SYSFS_FLAG_LOCKDEP) + if (kn->flags & KERNFS_LOCKDEP) rwsem_release(&kn->dep_map, 1, _RET_IP_); v = atomic_dec_return(&kn->active); - if (likely(v != SD_DEACTIVATED_BIAS)) + if (likely(v != KN_DEACTIVATED_BIAS)) return; /* @@ -180,9 +180,9 @@ static void sysfs_deactivate(struct kernfs_node *kn) DECLARE_COMPLETION_ONSTACK(wait); int v; - BUG_ON(!(kn->flags & SYSFS_FLAG_REMOVED)); + BUG_ON(!(kn->flags & KERNFS_REMOVED)); - if (!(sysfs_type(kn) & SYSFS_ACTIVE_REF)) + if (!(kernfs_type(kn) & KERNFS_ACTIVE_REF)) return; kn->u.completion = (void *)&wait; @@ -191,9 +191,9 @@ static void sysfs_deactivate(struct kernfs_node *kn) /* atomic_add_return() is a mb(), put_active() will always see * the updated kn->u.completion. */ - v = atomic_add_return(SD_DEACTIVATED_BIAS, &kn->active); + v = atomic_add_return(KN_DEACTIVATED_BIAS, &kn->active); - if (v != SD_DEACTIVATED_BIAS) { + if (v != KN_DEACTIVATED_BIAS) { lock_contended(&kn->dep_map, _RET_IP_); wait_for_completion(&wait); } @@ -235,13 +235,13 @@ void kernfs_put(struct kernfs_node *kn) */ parent = kn->parent; - WARN(!(kn->flags & SYSFS_FLAG_REMOVED), + WARN(!(kn->flags & KERNFS_REMOVED), "sysfs: free using entry: %s/%s\n", parent ? parent->name : "", kn->name); - if (sysfs_type(kn) == SYSFS_KOBJ_LINK) + if (kernfs_type(kn) == KERNFS_LINK) kernfs_put(kn->symlink.target_kn); - if (sysfs_type(kn) & SYSFS_COPY_NAME) + if (kernfs_type(kn) & KERNFS_COPY_NAME) kfree(kn->name); if (kn->iattr) { if (kn->iattr->ia_secdata) @@ -268,7 +268,7 @@ EXPORT_SYMBOL_GPL(kernfs_put); static int sysfs_dentry_delete(const struct dentry *dentry) { struct kernfs_node *kn = dentry->d_fsdata; - return !(kn && !(kn->flags & SYSFS_FLAG_REMOVED)); + return !(kn && !(kn->flags & KERNFS_REMOVED)); } static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) @@ -282,7 +282,7 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) mutex_lock(&sysfs_mutex); /* The sysfs dirent has been deleted */ - if (kn->flags & SYSFS_FLAG_REMOVED) + if (kn->flags & KERNFS_REMOVED) goto out_bad; /* The sysfs dirent has been moved? */ @@ -342,7 +342,7 @@ struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, struct kernfs_node *kn; int ret; - if (type & SYSFS_COPY_NAME) { + if (type & KERNFS_COPY_NAME) { name = dup_name = kstrdup(name, GFP_KERNEL); if (!name) return NULL; @@ -362,7 +362,7 @@ struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, kn->name = name; kn->mode = mode; - kn->flags = type | SYSFS_FLAG_REMOVED; + kn->flags = type | KERNFS_REMOVED; return kn; @@ -427,7 +427,7 @@ int sysfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, return -EINVAL; } - if (sysfs_type(parent) != SYSFS_DIR) + if (kernfs_type(parent) != KERNFS_DIR) return -EINVAL; kn->hash = sysfs_name_hash(kn->name, kn->ns); @@ -446,7 +446,7 @@ int sysfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, } /* Mark the entry added into directory tree */ - kn->flags &= ~SYSFS_FLAG_REMOVED; + kn->flags &= ~KERNFS_REMOVED; return 0; } @@ -475,7 +475,7 @@ static void sysfs_remove_one(struct kernfs_addrm_cxt *acxt, * Removal can be called multiple times on the same node. Only the * first invocation is effective and puts the base ref. */ - if (kn->flags & SYSFS_FLAG_REMOVED) + if (kn->flags & KERNFS_REMOVED) return; if (kn->parent) { @@ -489,7 +489,7 @@ static void sysfs_remove_one(struct kernfs_addrm_cxt *acxt, } } - kn->flags |= SYSFS_FLAG_REMOVED; + kn->flags |= KERNFS_REMOVED; kn->u.removed_list = acxt->removed; acxt->removed = kn; } @@ -607,14 +607,14 @@ struct kernfs_root *kernfs_create_root(void *priv) ida_init(&root->ino_ida); - kn = sysfs_new_dirent(root, "", S_IFDIR | S_IRUGO | S_IXUGO, SYSFS_DIR); + kn = sysfs_new_dirent(root, "", S_IFDIR | S_IRUGO | S_IXUGO, KERNFS_DIR); if (!kn) { ida_destroy(&root->ino_ida); kfree(root); return ERR_PTR(-ENOMEM); } - kn->flags &= ~SYSFS_FLAG_REMOVED; + kn->flags &= ~KERNFS_REMOVED; kn->priv = priv; kn->dir.root = root; @@ -654,7 +654,7 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, int rc; /* allocate */ - kn = sysfs_new_dirent(kernfs_root(parent), name, mode, SYSFS_DIR); + kn = sysfs_new_dirent(kernfs_root(parent), name, mode, KERNFS_DIR); if (!kn) return ERR_PTR(-ENOMEM); @@ -732,7 +732,7 @@ static struct kernfs_node *sysfs_leftmost_descendant(struct kernfs_node *pos) last = pos; - if (sysfs_type(pos) != SYSFS_DIR) + if (kernfs_type(pos) != KERNFS_DIR) break; rbn = rb_first(&pos->dir.children); @@ -914,7 +914,7 @@ static struct kernfs_node *sysfs_dir_pos(const void *ns, struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) { if (pos) { - int valid = !(pos->flags & SYSFS_FLAG_REMOVED) && + int valid = !(pos->flags & KERNFS_REMOVED) && pos->parent == parent && hash == pos->hash; kernfs_put(pos); if (!valid) diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 2714a39..abe93e1 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -49,7 +49,7 @@ static struct kernfs_open_file *kernfs_of(struct file *file) */ static const struct kernfs_ops *kernfs_ops(struct kernfs_node *kn) { - if (kn->flags & SYSFS_FLAG_LOCKDEP) + if (kn->flags & KERNFS_LOCKDEP) lockdep_assert_held(kn); return kn->attr.ops; } @@ -189,7 +189,7 @@ static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, { struct kernfs_open_file *of = kernfs_of(file); - if (of->kn->flags & SYSFS_FLAG_HAS_SEQ_SHOW) + if (of->kn->flags & KERNFS_HAS_SEQ_SHOW) return seq_read(file, user_buf, count, ppos); else return kernfs_file_direct_read(of, user_buf, count, ppos); @@ -428,7 +428,7 @@ static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) * without grabbing @of->mutex by testing HAS_MMAP flag. See the * comment in kernfs_file_open() for more details. */ - if (!(of->kn->flags & SYSFS_FLAG_HAS_MMAP)) + if (!(of->kn->flags & KERNFS_HAS_MMAP)) return -ENODEV; mutex_lock(&of->mutex); @@ -668,7 +668,7 @@ void sysfs_unmap_bin_file(struct kernfs_node *kn) struct kernfs_open_node *on; struct kernfs_open_file *of; - if (!(kn->flags & SYSFS_FLAG_HAS_MMAP)) + if (!(kn->flags & KERNFS_HAS_MMAP)) return; spin_lock_irq(&kernfs_open_node_lock); @@ -738,7 +738,7 @@ void kernfs_notify(struct kernfs_node *kn) spin_lock_irqsave(&kernfs_open_node_lock, flags); - if (!WARN_ON(sysfs_type(kn) != SYSFS_KOBJ_ATTR)) { + if (!WARN_ON(kernfs_type(kn) != KERNFS_FILE)) { on = kn->attr.open; if (on) { atomic_inc(&on->event); @@ -785,7 +785,7 @@ struct kernfs_node *kernfs_create_file_ns_key(struct kernfs_node *parent, int rc; kn = sysfs_new_dirent(kernfs_root(parent), name, - (mode & S_IALLUGO) | S_IFREG, SYSFS_KOBJ_ATTR); + (mode & S_IALLUGO) | S_IFREG, KERNFS_FILE); if (!kn) return ERR_PTR(-ENOMEM); @@ -797,7 +797,7 @@ struct kernfs_node *kernfs_create_file_ns_key(struct kernfs_node *parent, #ifdef CONFIG_DEBUG_LOCK_ALLOC if (key) { lockdep_init_map(&kn->dep_map, "s_active", key, 0); - kn->flags |= SYSFS_FLAG_LOCKDEP; + kn->flags |= KERNFS_LOCKDEP; } #endif @@ -807,9 +807,9 @@ struct kernfs_node *kernfs_create_file_ns_key(struct kernfs_node *parent, * ref. Cache their existence in flags. */ if (ops->seq_show) - kn->flags |= SYSFS_FLAG_HAS_SEQ_SHOW; + kn->flags |= KERNFS_HAS_SEQ_SHOW; if (ops->mmap) - kn->flags |= SYSFS_FLAG_HAS_MMAP; + kn->flags |= KERNFS_HAS_MMAP; sysfs_addrm_start(&acxt); rc = sysfs_add_one(&acxt, kn, parent); diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index a0e0038..af92638 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -269,7 +269,7 @@ static void sysfs_refresh_inode(struct kernfs_node *kn, struct inode *inode) attrs->ia_secdata_len); } - if (sysfs_type(kn) == SYSFS_DIR) + if (kernfs_type(kn) == KERNFS_DIR) set_nlink(inode, kn->dir.subdirs + 2); } @@ -299,16 +299,16 @@ static void sysfs_init_inode(struct kernfs_node *kn, struct inode *inode) sysfs_refresh_inode(kn, inode); /* initialize inode according to type */ - switch (sysfs_type(kn)) { - case SYSFS_DIR: + switch (kernfs_type(kn)) { + case KERNFS_DIR: inode->i_op = &sysfs_dir_inode_operations; inode->i_fop = &sysfs_dir_operations; break; - case SYSFS_KOBJ_ATTR: + case KERNFS_FILE: inode->i_size = kn->attr.size; inode->i_fop = &kernfs_file_operations; break; - case SYSFS_KOBJ_LINK: + case KERNFS_LINK: inode->i_op = &sysfs_symlink_inode_operations; break; default: diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 573f669..c4bf5bf 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -26,9 +26,9 @@ struct kernfs_iattrs { struct simple_xattrs xattrs; }; -#define SD_DEACTIVATED_BIAS INT_MIN +#define KN_DEACTIVATED_BIAS INT_MIN -/* SYSFS_TYPE_MASK and types are defined in include/linux/kernfs.h */ +/* KERNFS_TYPE_MASK and types are defined in include/linux/kernfs.h */ /** * kernfs_root - find out the kernfs_root a kernfs_node belongs to diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index f36e3f1..a92284d 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -31,7 +31,7 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, int error; kn = sysfs_new_dirent(kernfs_root(parent), name, S_IFLNK|S_IRWXUGO, - SYSFS_KOBJ_LINK); + KERNFS_LINK); if (!kn) return ERR_PTR(-ENOMEM); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index 4a80001..aa00740 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -113,7 +113,7 @@ void sysfs_remove_dir(struct kobject *kobj) spin_unlock(&sysfs_symlink_target_lock); if (kn) { - WARN_ON_ONCE(sysfs_type(kn) != SYSFS_DIR); + WARN_ON_ONCE(kernfs_type(kn) != KERNFS_DIR); kernfs_remove(kn); } } diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index fd104b2..fe6388f 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -29,7 +29,7 @@ static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn) { struct kobject *kobj = kn->parent->priv; - if (kn->flags & SYSFS_FLAG_LOCKDEP) + if (kn->flags & KERNFS_LOCKDEP) lockdep_assert_held(kn); return kobj->ktype ? kobj->ktype->sysfs_ops : NULL; } diff --git a/fs/sysfs/symlink.c b/fs/sysfs/symlink.c index 0d48ea9..aecb15f 100644 --- a/fs/sysfs/symlink.c +++ b/fs/sysfs/symlink.c @@ -183,7 +183,7 @@ int sysfs_rename_link_ns(struct kobject *kobj, struct kobject *targ, goto out; result = -EINVAL; - if (sysfs_type(kn) != SYSFS_KOBJ_LINK) + if (kernfs_type(kn) != KERNFS_LINK) goto out; if (kn->symlink.target_kn->priv != targ) goto out; diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 757647c4..e9c4e3a 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -28,22 +28,22 @@ struct kernfs_open_node; struct kernfs_iattrs; enum kernfs_node_type { - SYSFS_DIR = 0x0001, - SYSFS_KOBJ_ATTR = 0x0002, - SYSFS_KOBJ_LINK = 0x0004, + KERNFS_DIR = 0x0001, + KERNFS_FILE = 0x0002, + KERNFS_LINK = 0x0004, }; -#define SYSFS_TYPE_MASK 0x000f -#define SYSFS_COPY_NAME (SYSFS_DIR | SYSFS_KOBJ_LINK) -#define SYSFS_ACTIVE_REF SYSFS_KOBJ_ATTR -#define SYSFS_FLAG_MASK ~SYSFS_TYPE_MASK +#define KERNFS_TYPE_MASK 0x000f +#define KERNFS_COPY_NAME (KERNFS_DIR | KERNFS_LINK) +#define KERNFS_ACTIVE_REF KERNFS_FILE +#define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK enum kernfs_node_flag { - SYSFS_FLAG_REMOVED = 0x0010, - SYSFS_FLAG_NS = 0x0020, - SYSFS_FLAG_HAS_SEQ_SHOW = 0x0040, - SYSFS_FLAG_HAS_MMAP = 0x0080, - SYSFS_FLAG_LOCKDEP = 0x0100, + KERNFS_REMOVED = 0x0010, + KERNFS_NS = 0x0020, + KERNFS_HAS_SEQ_SHOW = 0x0040, + KERNFS_HAS_MMAP = 0x0080, + KERNFS_LOCKDEP = 0x0100, }; /* type-specific structures for kernfs_node union members */ @@ -170,9 +170,9 @@ struct kernfs_ops { #ifdef CONFIG_SYSFS -static inline enum kernfs_node_type sysfs_type(struct kernfs_node *kn) +static inline enum kernfs_node_type kernfs_type(struct kernfs_node *kn) { - return kn->flags & SYSFS_TYPE_MASK; + return kn->flags & KERNFS_TYPE_MASK; } /** @@ -185,9 +185,9 @@ static inline enum kernfs_node_type sysfs_type(struct kernfs_node *kn) */ static inline void kernfs_enable_ns(struct kernfs_node *kn) { - WARN_ON_ONCE(sysfs_type(kn) != SYSFS_DIR); + WARN_ON_ONCE(kernfs_type(kn) != KERNFS_DIR); WARN_ON_ONCE(!RB_EMPTY_ROOT(&kn->dir.children)); - kn->flags |= SYSFS_FLAG_NS; + kn->flags |= KERNFS_NS; } /** @@ -198,7 +198,7 @@ static inline void kernfs_enable_ns(struct kernfs_node *kn) */ static inline bool kernfs_ns_enabled(struct kernfs_node *kn) { - return kn->flags & SYSFS_FLAG_NS; + return kn->flags & KERNFS_NS; } struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, @@ -238,7 +238,7 @@ void kernfs_init(void); #else /* CONFIG_SYSFS */ -static inline enum kernfs_node_type sysfs_type(struct kernfs_node *kn) +static inline enum kernfs_node_type kernfs_type(struct kernfs_node *kn) { return 0; } /* whatever */ static inline void kernfs_enable_ns(struct kernfs_node *kn) { } -- cgit v1.1 From a797bfc30532388e8a11ca726df60cdd77aa8675 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 11 Dec 2013 14:11:57 -0500 Subject: kernfs: s/sysfs/kernfs/ in global variables kernfs has just been separated out from sysfs and we're already in full conflict mode. Nothing can make the situation any worse. Let's take the chance to name things properly. This patch performs the following renames. * s/sysfs_mutex/kernfs_mutex/ * s/sysfs_dentry_ops/kernfs_dops/ * s/sysfs_dir_operations/kernfs_dir_fops/ * s/sysfs_dir_inode_operations/kernfs_dir_iops/ * s/kernfs_file_operations/kernfs_file_fops/ - renamed for consistency * s/sysfs_symlink_inode_operations/kernfs_symlink_iops/ * s/sysfs_aops/kernfs_aops/ * s/sysfs_backing_dev_info/kernfs_bdi/ * s/sysfs_inode_operations/kernfs_iops/ * s/sysfs_dir_cachep/kernfs_node_cache/ * s/sysfs_ops/kernfs_sops/ This patch is strictly rename only and doesn't introduce any functional difference. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 64 ++++++++++++++++++++++----------------------- fs/kernfs/file.c | 2 +- fs/kernfs/inode.c | 46 ++++++++++++++++---------------- fs/kernfs/kernfs-internal.h | 14 +++++----- fs/kernfs/mount.c | 14 +++++----- fs/kernfs/symlink.c | 6 ++--- 6 files changed, 73 insertions(+), 73 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index bc8a3b3..d3c6623 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -17,7 +17,7 @@ #include "kernfs-internal.h" -DEFINE_MUTEX(sysfs_mutex); +DEFINE_MUTEX(kernfs_mutex); #define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb) @@ -68,7 +68,7 @@ static int sysfs_sd_compare(const struct kernfs_node *left, * @kn->parent->dir.children. * * Locking: - * mutex_lock(sysfs_mutex) + * mutex_lock(kernfs_mutex) * * RETURNS: * 0 on susccess -EEXIST on failure. @@ -109,7 +109,7 @@ static int sysfs_link_sibling(struct kernfs_node *kn) * kn->parent->dir.children. * * Locking: - * mutex_lock(sysfs_mutex) + * mutex_lock(kernfs_mutex) */ static void sysfs_unlink_sibling(struct kernfs_node *kn) { @@ -251,7 +251,7 @@ void kernfs_put(struct kernfs_node *kn) } kfree(kn->iattr); ida_simple_remove(&root->ino_ida, kn->ino); - kmem_cache_free(sysfs_dir_cachep, kn); + kmem_cache_free(kernfs_node_cache, kn); kn = parent; if (kn) { @@ -279,7 +279,7 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) return -ECHILD; kn = dentry->d_fsdata; - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); /* The sysfs dirent has been deleted */ if (kn->flags & KERNFS_REMOVED) @@ -298,7 +298,7 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) kernfs_info(dentry->d_sb)->ns != kn->ns) goto out_bad; - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); out_valid: return 1; out_bad: @@ -312,7 +312,7 @@ out_bad: * is performed at its new name the dentry will be readded * to the dcache hashes. */ - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); /* If we have submounts we must allow the vfs caches * to lie about the state of the filesystem to prevent @@ -329,7 +329,7 @@ static void sysfs_dentry_release(struct dentry *dentry) kernfs_put(dentry->d_fsdata); } -const struct dentry_operations sysfs_dentry_ops = { +const struct dentry_operations kernfs_dops = { .d_revalidate = sysfs_dentry_revalidate, .d_delete = sysfs_dentry_delete, .d_release = sysfs_dentry_release, @@ -348,7 +348,7 @@ struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, return NULL; } - kn = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL); + kn = kmem_cache_zalloc(kernfs_node_cache, GFP_KERNEL); if (!kn) goto err_out1; @@ -367,7 +367,7 @@ struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, return kn; err_out2: - kmem_cache_free(sysfs_dir_cachep, kn); + kmem_cache_free(kernfs_node_cache, kn); err_out1: kfree(dup_name); return NULL; @@ -378,19 +378,19 @@ struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, * @acxt: pointer to kernfs_addrm_cxt to be used * * This function is called when the caller is about to add or remove - * kernfs_node. This function acquires sysfs_mutex. @acxt is used to - * keep and pass context to other addrm functions. + * kernfs_node. This function acquires kernfs_mutex. @acxt is used + * to keep and pass context to other addrm functions. * * LOCKING: - * Kernel thread context (may sleep). sysfs_mutex is locked on + * Kernel thread context (may sleep). kernfs_mutex is locked on * return. */ void sysfs_addrm_start(struct kernfs_addrm_cxt *acxt) - __acquires(sysfs_mutex) + __acquires(kernfs_mutex) { memset(acxt, 0, sizeof(*acxt)); - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); } /** @@ -503,13 +503,13 @@ static void sysfs_remove_one(struct kernfs_addrm_cxt *acxt, * cleaned up. * * LOCKING: - * sysfs_mutex is released. + * kernfs_mutex is released. */ void sysfs_addrm_finish(struct kernfs_addrm_cxt *acxt) - __releases(sysfs_mutex) + __releases(kernfs_mutex) { /* release resources acquired by sysfs_addrm_start() */ - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); /* kill removed kernfs_nodes */ while (acxt->removed) { @@ -540,7 +540,7 @@ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent, bool has_ns = kernfs_ns_enabled(parent); unsigned int hash; - lockdep_assert_held(&sysfs_mutex); + lockdep_assert_held(&kernfs_mutex); if (has_ns != (bool)ns) { WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", @@ -580,10 +580,10 @@ struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, { struct kernfs_node *kn; - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); kn = kernfs_find_ns(parent, name, ns); kernfs_get(kn); - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); return kn; } @@ -683,7 +683,7 @@ static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, struct inode *inode; const void *ns = NULL; - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); if (kernfs_ns_enabled(parent)) ns = kernfs_info(dir->i_sb)->ns; @@ -708,11 +708,11 @@ static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, /* instantiate and hash dentry */ ret = d_materialise_unique(dentry, inode); out_unlock: - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); return ret; } -const struct inode_operations sysfs_dir_inode_operations = { +const struct inode_operations kernfs_dir_iops = { .lookup = sysfs_lookup, .permission = sysfs_permission, .setattr = sysfs_setattr, @@ -759,7 +759,7 @@ static struct kernfs_node *sysfs_next_descendant_post(struct kernfs_node *pos, { struct rb_node *rbn; - lockdep_assert_held(&sysfs_mutex); + lockdep_assert_held(&kernfs_mutex); /* if first iteration, visit leftmost descendant which may be root */ if (!pos) @@ -859,7 +859,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, { int error; - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); error = 0; if ((kn->parent == new_parent) && (kn->ns == new_ns) && @@ -894,7 +894,7 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, error = 0; out: - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); return error; } @@ -968,7 +968,7 @@ static int sysfs_readdir(struct file *file, struct dir_context *ctx) if (!dir_emit_dots(file, ctx)) return 0; - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); if (kernfs_ns_enabled(parent)) ns = kernfs_info(dentry->d_sb)->ns; @@ -985,12 +985,12 @@ static int sysfs_readdir(struct file *file, struct dir_context *ctx) file->private_data = pos; kernfs_get(pos); - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); if (!dir_emit(ctx, name, len, ino, type)) return 0; - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); } - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); file->private_data = NULL; ctx->pos = INT_MAX; return 0; @@ -1008,7 +1008,7 @@ static loff_t sysfs_dir_llseek(struct file *file, loff_t offset, int whence) return ret; } -const struct file_operations sysfs_dir_operations = { +const struct file_operations kernfs_dir_fops = { .read = generic_read_dir, .iterate = sysfs_readdir, .release = sysfs_dir_release, diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index abe93e1..32364dd 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -750,7 +750,7 @@ void kernfs_notify(struct kernfs_node *kn) } EXPORT_SYMBOL_GPL(kernfs_notify); -const struct file_operations kernfs_file_operations = { +const struct file_operations kernfs_file_fops = { .read = kernfs_file_read, .write = kernfs_file_write, .llseek = generic_file_llseek, diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index af92638..c5f231e 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -18,19 +18,19 @@ #include "kernfs-internal.h" -static const struct address_space_operations sysfs_aops = { +static const struct address_space_operations kernfs_aops = { .readpage = simple_readpage, .write_begin = simple_write_begin, .write_end = simple_write_end, }; -static struct backing_dev_info sysfs_backing_dev_info = { - .name = "sysfs", +static struct backing_dev_info kernfs_bdi = { + .name = "kernfs", .ra_pages = 0, /* No readahead */ .capabilities = BDI_CAP_NO_ACCT_AND_WRITEBACK, }; -static const struct inode_operations sysfs_inode_operations = { +static const struct inode_operations kernfs_iops = { .permission = sysfs_permission, .setattr = sysfs_setattr, .getattr = sysfs_getattr, @@ -42,8 +42,8 @@ static const struct inode_operations sysfs_inode_operations = { void __init sysfs_inode_init(void) { - if (bdi_init(&sysfs_backing_dev_info)) - panic("failed to init sysfs_backing_dev_info"); + if (bdi_init(&kernfs_bdi)) + panic("failed to init kernfs_bdi"); } static struct kernfs_iattrs *kernfs_iattrs(struct kernfs_node *kn) @@ -109,9 +109,9 @@ int kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) { int ret; - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); ret = __kernfs_setattr(kn, iattr); - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); return ret; } @@ -124,7 +124,7 @@ int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) if (!kn) return -EINVAL; - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); error = inode_change_ok(inode, iattr); if (error) goto out; @@ -137,7 +137,7 @@ int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) setattr_copy(inode, iattr); out: - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); return error; } @@ -187,9 +187,9 @@ int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, if (error) return error; - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); error = sysfs_sd_setsecdata(kn, &secdata, &secdata_len); - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); if (secdata) security_release_secctx(secdata, secdata_len); @@ -279,9 +279,9 @@ int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, struct kernfs_node *kn = dentry->d_fsdata; struct inode *inode = dentry->d_inode; - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); sysfs_refresh_inode(kn, inode); - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); generic_fillattr(inode, stat); return 0; @@ -291,9 +291,9 @@ static void sysfs_init_inode(struct kernfs_node *kn, struct inode *inode) { kernfs_get(kn); inode->i_private = kn; - 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_mapping->a_ops = &kernfs_aops; + inode->i_mapping->backing_dev_info = &kernfs_bdi; + inode->i_op = &kernfs_iops; set_default_inode_attr(inode, kn->mode); sysfs_refresh_inode(kn, inode); @@ -301,15 +301,15 @@ static void sysfs_init_inode(struct kernfs_node *kn, struct inode *inode) /* initialize inode according to type */ switch (kernfs_type(kn)) { case KERNFS_DIR: - inode->i_op = &sysfs_dir_inode_operations; - inode->i_fop = &sysfs_dir_operations; + inode->i_op = &kernfs_dir_iops; + inode->i_fop = &kernfs_dir_fops; break; case KERNFS_FILE: inode->i_size = kn->attr.size; - inode->i_fop = &kernfs_file_operations; + inode->i_fop = &kernfs_file_fops; break; case KERNFS_LINK: - inode->i_op = &sysfs_symlink_inode_operations; + inode->i_op = &kernfs_symlink_iops; break; default: BUG(); @@ -369,9 +369,9 @@ int sysfs_permission(struct inode *inode, int mask) kn = inode->i_private; - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); sysfs_refresh_inode(kn, inode); - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); return generic_permission(inode, mask); } diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index c4bf5bf..e62e8ec 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -71,7 +71,7 @@ struct kernfs_super_info { }; #define kernfs_info(SB) ((struct kernfs_super_info *)(SB->s_fs_info)) -extern struct kmem_cache *sysfs_dir_cachep; +extern struct kmem_cache *kernfs_node_cache; /* * inode.c @@ -93,10 +93,10 @@ void sysfs_inode_init(void); /* * dir.c */ -extern struct mutex sysfs_mutex; -extern const struct dentry_operations sysfs_dentry_ops; -extern const struct file_operations sysfs_dir_operations; -extern const struct inode_operations sysfs_dir_inode_operations; +extern struct mutex kernfs_mutex; +extern const struct dentry_operations kernfs_dops; +extern const struct file_operations kernfs_dir_fops; +extern const struct inode_operations kernfs_dir_iops; struct kernfs_node *sysfs_get_active(struct kernfs_node *kn); void sysfs_put_active(struct kernfs_node *kn); @@ -110,13 +110,13 @@ struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, /* * file.c */ -extern const struct file_operations kernfs_file_operations; +extern const struct file_operations kernfs_file_fops; void sysfs_unmap_bin_file(struct kernfs_node *kn); /* * symlink.c */ -extern const struct inode_operations sysfs_symlink_inode_operations; +extern const struct inode_operations kernfs_symlink_iops; #endif /* __KERNFS_INTERNAL_H */ diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index e0796dc..27d967b 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -17,9 +17,9 @@ #include "kernfs-internal.h" -struct kmem_cache *sysfs_dir_cachep; +struct kmem_cache *kernfs_node_cache; -static const struct super_operations sysfs_ops = { +static const struct super_operations kernfs_sops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, .evict_inode = sysfs_evict_inode, @@ -34,13 +34,13 @@ static int sysfs_fill_super(struct super_block *sb) sb->s_blocksize = PAGE_CACHE_SIZE; sb->s_blocksize_bits = PAGE_CACHE_SHIFT; sb->s_magic = SYSFS_MAGIC; - sb->s_op = &sysfs_ops; + sb->s_op = &kernfs_sops; sb->s_time_gran = 1; /* get root inode, initialize and unlock it */ - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); inode = sysfs_get_inode(sb, info->root->kn); - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); if (!inode) { pr_debug("sysfs: could not get root inode\n"); return -ENOMEM; @@ -55,7 +55,7 @@ static int sysfs_fill_super(struct super_block *sb) kernfs_get(info->root->kn); root->d_fsdata = info->root->kn; sb->s_root = root; - sb->s_d_op = &sysfs_dentry_ops; + sb->s_d_op = &kernfs_dops; return 0; } @@ -158,7 +158,7 @@ void kernfs_kill_sb(struct super_block *sb) void __init kernfs_init(void) { - sysfs_dir_cachep = kmem_cache_create("sysfs_dir_cache", + kernfs_node_cache = kmem_cache_create("kernfs_node_cache", sizeof(struct kernfs_node), 0, SLAB_PANIC, NULL); sysfs_inode_init(); diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index a92284d..4105bd0 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -110,9 +110,9 @@ static int sysfs_getlink(struct dentry *dentry, char *path) struct kernfs_node *target = kn->symlink.target_kn; int error; - mutex_lock(&sysfs_mutex); + mutex_lock(&kernfs_mutex); error = sysfs_get_target_path(parent, target, path); - mutex_unlock(&sysfs_mutex); + mutex_unlock(&kernfs_mutex); return error; } @@ -138,7 +138,7 @@ static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, free_page((unsigned long)page); } -const struct inode_operations sysfs_symlink_inode_operations = { +const struct inode_operations kernfs_symlink_iops = { .setxattr = sysfs_setxattr, .removexattr = sysfs_removexattr, .getxattr = sysfs_getxattr, -- cgit v1.1 From c637b8acbe079edb477d887041755b489036f146 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 11 Dec 2013 14:11:58 -0500 Subject: kernfs: s/sysfs/kernfs/ in internal functions and whatever is left kernfs has just been separated out from sysfs and we're already in full conflict mode. Nothing can make the situation any worse. Let's take the chance to name things properly. This patch performs the following renames. * s/sysfs_*()/kernfs_*()/ in all internal functions * s/sysfs/kernfs/ in internal strings, comments and whatever is remaining * Uniformly rename various vfs operations so that they're consistently named and distinguishable. This patch is strictly rename only and doesn't introduce any functional difference. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 212 ++++++++++++++++++++++---------------------- fs/kernfs/file.c | 121 ++++++++++++------------- fs/kernfs/inode.c | 66 +++++++------- fs/kernfs/kernfs-internal.h | 44 ++++----- fs/kernfs/mount.c | 18 ++-- fs/kernfs/symlink.c | 44 ++++----- 6 files changed, 254 insertions(+), 251 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index d3c6623..6520066 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -22,13 +22,13 @@ DEFINE_MUTEX(kernfs_mutex); #define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb) /** - * sysfs_name_hash + * kernfs_name_hash * @name: Null terminated string to hash * @ns: Namespace tag to hash * * Returns 31 bit hash of ns + name (so it fits in an off_t ) */ -static unsigned int sysfs_name_hash(const char *name, const void *ns) +static unsigned int kernfs_name_hash(const char *name, const void *ns) { unsigned long hash = init_name_hash(); unsigned int len = strlen(name); @@ -44,8 +44,8 @@ static unsigned int sysfs_name_hash(const char *name, const void *ns) return hash; } -static int sysfs_name_compare(unsigned int hash, const char *name, - const void *ns, const struct kernfs_node *kn) +static int kernfs_name_compare(unsigned int hash, const char *name, + const void *ns, const struct kernfs_node *kn) { if (hash != kn->hash) return hash - kn->hash; @@ -54,14 +54,14 @@ static int sysfs_name_compare(unsigned int hash, const char *name, return strcmp(name, kn->name); } -static int sysfs_sd_compare(const struct kernfs_node *left, - const struct kernfs_node *right) +static int kernfs_sd_compare(const struct kernfs_node *left, + const struct kernfs_node *right) { - return sysfs_name_compare(left->hash, left->name, left->ns, right); + return kernfs_name_compare(left->hash, left->name, left->ns, right); } /** - * sysfs_link_sibling - link kernfs_node into sibling rbtree + * kernfs_link_sibling - link kernfs_node into sibling rbtree * @kn: kernfs_node of interest * * Link @kn into its sibling rbtree which starts from @@ -73,7 +73,7 @@ static int sysfs_sd_compare(const struct kernfs_node *left, * RETURNS: * 0 on susccess -EEXIST on failure. */ -static int sysfs_link_sibling(struct kernfs_node *kn) +static int kernfs_link_sibling(struct kernfs_node *kn) { struct rb_node **node = &kn->parent->dir.children.rb_node; struct rb_node *parent = NULL; @@ -87,7 +87,7 @@ static int sysfs_link_sibling(struct kernfs_node *kn) pos = rb_to_kn(*node); parent = *node; - result = sysfs_sd_compare(kn, pos); + result = kernfs_sd_compare(kn, pos); if (result < 0) node = &pos->rb.rb_left; else if (result > 0) @@ -102,7 +102,7 @@ static int sysfs_link_sibling(struct kernfs_node *kn) } /** - * sysfs_unlink_sibling - unlink kernfs_node from sibling rbtree + * kernfs_unlink_sibling - unlink kernfs_node from sibling rbtree * @kn: kernfs_node of interest * * Unlink @kn from its sibling rbtree which starts from @@ -111,7 +111,7 @@ static int sysfs_link_sibling(struct kernfs_node *kn) * Locking: * mutex_lock(kernfs_mutex) */ -static void sysfs_unlink_sibling(struct kernfs_node *kn) +static void kernfs_unlink_sibling(struct kernfs_node *kn) { if (kernfs_type(kn) == KERNFS_DIR) kn->parent->dir.subdirs--; @@ -120,7 +120,7 @@ static void sysfs_unlink_sibling(struct kernfs_node *kn) } /** - * sysfs_get_active - get an active reference to kernfs_node + * kernfs_get_active - get an active reference to kernfs_node * @kn: kernfs_node to get an active reference to * * Get an active reference of @kn. This function is noop if @kn @@ -129,7 +129,7 @@ static void sysfs_unlink_sibling(struct kernfs_node *kn) * RETURNS: * Pointer to @kn on success, NULL on failure. */ -struct kernfs_node *sysfs_get_active(struct kernfs_node *kn) +struct kernfs_node *kernfs_get_active(struct kernfs_node *kn) { if (unlikely(!kn)) return NULL; @@ -143,13 +143,13 @@ struct kernfs_node *sysfs_get_active(struct kernfs_node *kn) } /** - * sysfs_put_active - put an active reference to kernfs_node + * kernfs_put_active - put an active reference to kernfs_node * @kn: kernfs_node to put an active reference to * * Put an active reference to @kn. This function is noop if @kn * is NULL. */ -void sysfs_put_active(struct kernfs_node *kn) +void kernfs_put_active(struct kernfs_node *kn) { int v; @@ -170,12 +170,12 @@ void sysfs_put_active(struct kernfs_node *kn) } /** - * sysfs_deactivate - deactivate kernfs_node + * kernfs_deactivate - deactivate kernfs_node * @kn: kernfs_node to deactivate * * Deny new active references and drain existing ones. */ -static void sysfs_deactivate(struct kernfs_node *kn) +static void kernfs_deactivate(struct kernfs_node *kn) { DECLARE_COMPLETION_ONSTACK(wait); int v; @@ -235,9 +235,8 @@ void kernfs_put(struct kernfs_node *kn) */ parent = kn->parent; - WARN(!(kn->flags & KERNFS_REMOVED), - "sysfs: free using entry: %s/%s\n", - parent ? parent->name : "", kn->name); + WARN(!(kn->flags & KERNFS_REMOVED), "kernfs: free using entry: %s/%s\n", + parent ? parent->name : "", kn->name); if (kernfs_type(kn) == KERNFS_LINK) kernfs_put(kn->symlink.target_kn); @@ -265,13 +264,13 @@ void kernfs_put(struct kernfs_node *kn) } EXPORT_SYMBOL_GPL(kernfs_put); -static int sysfs_dentry_delete(const struct dentry *dentry) +static int kernfs_dop_delete(const struct dentry *dentry) { struct kernfs_node *kn = dentry->d_fsdata; return !(kn && !(kn->flags & KERNFS_REMOVED)); } -static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) +static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) { struct kernfs_node *kn; @@ -281,19 +280,19 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) kn = dentry->d_fsdata; mutex_lock(&kernfs_mutex); - /* The sysfs dirent has been deleted */ + /* The kernfs node has been deleted */ if (kn->flags & KERNFS_REMOVED) goto out_bad; - /* The sysfs dirent has been moved? */ + /* The kernfs node has been moved? */ if (dentry->d_parent->d_fsdata != kn->parent) goto out_bad; - /* The sysfs dirent has been renamed */ + /* The kernfs node has been renamed */ if (strcmp(dentry->d_name.name, kn->name) != 0) goto out_bad; - /* The sysfs dirent has been moved to a different namespace */ + /* The kernfs node has been moved to a different namespace */ if (kn->parent && kernfs_ns_enabled(kn->parent) && kernfs_info(dentry->d_sb)->ns != kn->ns) goto out_bad; @@ -302,9 +301,10 @@ static int sysfs_dentry_revalidate(struct dentry *dentry, unsigned int flags) out_valid: return 1; out_bad: - /* Remove the dentry from the dcache hashes. + /* + * Remove the dentry from the dcache hashes. * If this is a deleted dentry we use d_drop instead of d_delete - * so sysfs doesn't need to cope with negative dentries. + * so kernfs doesn't need to cope with negative dentries. * * If this is a dentry that has simply been renamed we * use d_drop to remove it from the dcache lookup on its @@ -324,19 +324,19 @@ out_bad: return 0; } -static void sysfs_dentry_release(struct dentry *dentry) +static void kernfs_dop_release(struct dentry *dentry) { kernfs_put(dentry->d_fsdata); } const struct dentry_operations kernfs_dops = { - .d_revalidate = sysfs_dentry_revalidate, - .d_delete = sysfs_dentry_delete, - .d_release = sysfs_dentry_release, + .d_revalidate = kernfs_dop_revalidate, + .d_delete = kernfs_dop_delete, + .d_release = kernfs_dop_release, }; -struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, - const char *name, umode_t mode, int type) +struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, + umode_t mode, int type) { char *dup_name = NULL; struct kernfs_node *kn; @@ -374,7 +374,7 @@ struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, } /** - * sysfs_addrm_start - prepare for kernfs_node add/remove + * kernfs_addrm_start - prepare for kernfs_node add/remove * @acxt: pointer to kernfs_addrm_cxt to be used * * This function is called when the caller is about to add or remove @@ -385,7 +385,7 @@ struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, * Kernel thread context (may sleep). kernfs_mutex is locked on * return. */ -void sysfs_addrm_start(struct kernfs_addrm_cxt *acxt) +void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt) __acquires(kernfs_mutex) { memset(acxt, 0, sizeof(*acxt)); @@ -394,7 +394,7 @@ void sysfs_addrm_start(struct kernfs_addrm_cxt *acxt) } /** - * sysfs_add_one - add kernfs_node to parent without warning + * kernfs_add_one - add kernfs_node to parent without warning * @acxt: addrm context to use * @kn: kernfs_node to be added * @parent: the parent kernfs_node to add @kn to @@ -404,17 +404,17 @@ void sysfs_addrm_start(struct kernfs_addrm_cxt *acxt) * of the parent. * * 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(). + * kernfs_addrm_start() and kernfs_addrm_finish() and should be passed + * the same @acxt as passed to kernfs_addrm_start(). * * LOCKING: - * Determined by sysfs_addrm_start(). + * Determined by kernfs_addrm_start(). * * RETURNS: * 0 on success, -EEXIST if entry with the given name already * exists. */ -int sysfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, +int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, struct kernfs_node *parent) { bool has_ns = kernfs_ns_enabled(parent); @@ -422,7 +422,7 @@ int sysfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, int ret; if (has_ns != (bool)kn->ns) { - WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", + WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", has_ns ? "required" : "invalid", parent->name, kn->name); return -EINVAL; } @@ -430,11 +430,11 @@ int sysfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, if (kernfs_type(parent) != KERNFS_DIR) return -EINVAL; - kn->hash = sysfs_name_hash(kn->name, kn->ns); + kn->hash = kernfs_name_hash(kn->name, kn->ns); kn->parent = parent; kernfs_get(parent); - ret = sysfs_link_sibling(kn); + ret = kernfs_link_sibling(kn); if (ret) return ret; @@ -452,7 +452,7 @@ int sysfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, } /** - * sysfs_remove_one - remove kernfs_node from parent + * kernfs_remove_one - remove kernfs_node from parent * @acxt: addrm context to use * @kn: kernfs_node to be removed * @@ -460,14 +460,14 @@ int sysfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, * directory. @kn is unlinked from 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(). + * kernfs_addrm_start() and kernfs_addrm_finish() and should be + * passed the same @acxt as passed to kernfs_addrm_start(). * * LOCKING: - * Determined by sysfs_addrm_start(). + * Determined by kernfs_addrm_start(). */ -static void sysfs_remove_one(struct kernfs_addrm_cxt *acxt, - struct kernfs_node *kn) +static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt, + struct kernfs_node *kn) { struct kernfs_iattrs *ps_iattr; @@ -479,7 +479,7 @@ static void sysfs_remove_one(struct kernfs_addrm_cxt *acxt, return; if (kn->parent) { - sysfs_unlink_sibling(kn); + kernfs_unlink_sibling(kn); /* Update timestamps on the parent */ ps_iattr = kn->parent->iattr; @@ -495,20 +495,20 @@ static void sysfs_remove_one(struct kernfs_addrm_cxt *acxt, } /** - * sysfs_addrm_finish - finish up kernfs_node add/remove + * kernfs_addrm_finish - finish up kernfs_node add/remove * @acxt: addrm context to finish up * * Finish up kernfs_node add/remove. Resources acquired by - * sysfs_addrm_start() are released and removed kernfs_nodes are + * kernfs_addrm_start() are released and removed kernfs_nodes are * cleaned up. * * LOCKING: * kernfs_mutex is released. */ -void sysfs_addrm_finish(struct kernfs_addrm_cxt *acxt) +void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt) __releases(kernfs_mutex) { - /* release resources acquired by sysfs_addrm_start() */ + /* release resources acquired by kernfs_addrm_start() */ mutex_unlock(&kernfs_mutex); /* kill removed kernfs_nodes */ @@ -517,8 +517,8 @@ void sysfs_addrm_finish(struct kernfs_addrm_cxt *acxt) acxt->removed = kn->u.removed_list; - sysfs_deactivate(kn); - sysfs_unmap_bin_file(kn); + kernfs_deactivate(kn); + kernfs_unmap_bin_file(kn); kernfs_put(kn); } } @@ -543,18 +543,18 @@ static struct kernfs_node *kernfs_find_ns(struct kernfs_node *parent, lockdep_assert_held(&kernfs_mutex); if (has_ns != (bool)ns) { - WARN(1, KERN_WARNING "sysfs: ns %s in '%s' for '%s'\n", + WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", has_ns ? "required" : "invalid", parent->name, name); return NULL; } - hash = sysfs_name_hash(name, ns); + hash = kernfs_name_hash(name, ns); while (node) { struct kernfs_node *kn; int result; kn = rb_to_kn(node); - result = sysfs_name_compare(hash, name, ns, kn); + result = kernfs_name_compare(hash, name, ns, kn); if (result < 0) node = node->rb_left; else if (result > 0) @@ -607,7 +607,7 @@ struct kernfs_root *kernfs_create_root(void *priv) ida_init(&root->ino_ida); - kn = sysfs_new_dirent(root, "", S_IFDIR | S_IRUGO | S_IXUGO, KERNFS_DIR); + kn = kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO, KERNFS_DIR); if (!kn) { ida_destroy(&root->ino_ida); kfree(root); @@ -654,7 +654,7 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, int rc; /* allocate */ - kn = sysfs_new_dirent(kernfs_root(parent), name, mode, KERNFS_DIR); + kn = kernfs_new_node(kernfs_root(parent), name, mode, KERNFS_DIR); if (!kn) return ERR_PTR(-ENOMEM); @@ -663,9 +663,9 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, kn->priv = priv; /* link in */ - sysfs_addrm_start(&acxt); - rc = sysfs_add_one(&acxt, kn, parent); - sysfs_addrm_finish(&acxt); + kernfs_addrm_start(&acxt); + rc = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); if (!rc) return kn; @@ -674,8 +674,9 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, return ERR_PTR(rc); } -static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, - unsigned int flags) +static struct dentry *kernfs_iop_lookup(struct inode *dir, + struct dentry *dentry, + unsigned int flags) { struct dentry *ret = NULL; struct kernfs_node *parent = dentry->d_parent->d_fsdata; @@ -699,7 +700,7 @@ static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, dentry->d_fsdata = kn; /* attach dentry and inode */ - inode = sysfs_get_inode(dir->i_sb, kn); + inode = kernfs_get_inode(dir->i_sb, kn); if (!inode) { ret = ERR_PTR(-ENOMEM); goto out_unlock; @@ -713,17 +714,17 @@ static struct dentry *sysfs_lookup(struct inode *dir, struct dentry *dentry, } const struct inode_operations kernfs_dir_iops = { - .lookup = sysfs_lookup, - .permission = sysfs_permission, - .setattr = sysfs_setattr, - .getattr = sysfs_getattr, - .setxattr = sysfs_setxattr, - .removexattr = sysfs_removexattr, - .getxattr = sysfs_getxattr, - .listxattr = sysfs_listxattr, + .lookup = kernfs_iop_lookup, + .permission = kernfs_iop_permission, + .setattr = kernfs_iop_setattr, + .getattr = kernfs_iop_getattr, + .setxattr = kernfs_iop_setxattr, + .removexattr = kernfs_iop_removexattr, + .getxattr = kernfs_iop_getxattr, + .listxattr = kernfs_iop_listxattr, }; -static struct kernfs_node *sysfs_leftmost_descendant(struct kernfs_node *pos) +static struct kernfs_node *kernfs_leftmost_descendant(struct kernfs_node *pos) { struct kernfs_node *last; @@ -746,7 +747,7 @@ static struct kernfs_node *sysfs_leftmost_descendant(struct kernfs_node *pos) } /** - * sysfs_next_descendant_post - find the next descendant for post-order walk + * kernfs_next_descendant_post - find the next descendant for post-order walk * @pos: the current position (%NULL to initiate traversal) * @root: kernfs_node whose descendants to walk * @@ -754,8 +755,8 @@ static struct kernfs_node *sysfs_leftmost_descendant(struct kernfs_node *pos) * descendants. @root is included in the iteration and the last node to be * visited. */ -static struct kernfs_node *sysfs_next_descendant_post(struct kernfs_node *pos, - struct kernfs_node *root) +static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos, + struct kernfs_node *root) { struct rb_node *rbn; @@ -763,7 +764,7 @@ static struct kernfs_node *sysfs_next_descendant_post(struct kernfs_node *pos, /* if first iteration, visit leftmost descendant which may be root */ if (!pos) - return sysfs_leftmost_descendant(root); + return kernfs_leftmost_descendant(root); /* if we visited @root, we're done */ if (pos == root) @@ -772,7 +773,7 @@ static struct kernfs_node *sysfs_next_descendant_post(struct kernfs_node *pos, /* if there's an unvisited sibling, visit its leftmost descendant */ rbn = rb_next(&pos->rb); if (rbn) - return sysfs_leftmost_descendant(rb_to_kn(rbn)); + return kernfs_leftmost_descendant(rb_to_kn(rbn)); /* no sibling left, visit parent */ return pos->parent; @@ -786,14 +787,14 @@ static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, if (!kn) return; - pr_debug("sysfs %s: removing\n", kn->name); + pr_debug("kernfs %s: removing\n", kn->name); next = NULL; do { pos = next; - next = sysfs_next_descendant_post(pos, kn); + next = kernfs_next_descendant_post(pos, kn); if (pos) - sysfs_remove_one(acxt, pos); + kernfs_remove_one(acxt, pos); } while (next); } @@ -807,9 +808,9 @@ void kernfs_remove(struct kernfs_node *kn) { struct kernfs_addrm_cxt acxt; - sysfs_addrm_start(&acxt); + kernfs_addrm_start(&acxt); __kernfs_remove(&acxt, kn); - sysfs_addrm_finish(&acxt); + kernfs_addrm_finish(&acxt); } /** @@ -828,18 +829,18 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, struct kernfs_node *kn; if (!parent) { - WARN(1, KERN_WARNING "sysfs: can not remove '%s', no directory\n", + WARN(1, KERN_WARNING "kernfs: can not remove '%s', no directory\n", name); return -ENOENT; } - sysfs_addrm_start(&acxt); + kernfs_addrm_start(&acxt); kn = kernfs_find_ns(parent, name, ns); if (kn) __kernfs_remove(&acxt, kn); - sysfs_addrm_finish(&acxt); + kernfs_addrm_finish(&acxt); if (kn) return 0; @@ -884,13 +885,13 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, /* * Move to the appropriate place in the appropriate directories rbtree. */ - sysfs_unlink_sibling(kn); + kernfs_unlink_sibling(kn); kernfs_get(new_parent); kernfs_put(kn->parent); kn->ns = new_ns; - kn->hash = sysfs_name_hash(kn->name, kn->ns); + kn->hash = kernfs_name_hash(kn->name, kn->ns); kn->parent = new_parent; - sysfs_link_sibling(kn); + kernfs_link_sibling(kn); error = 0; out: @@ -904,13 +905,13 @@ static inline unsigned char dt_type(struct kernfs_node *kn) return (kn->mode >> 12) & 15; } -static int sysfs_dir_release(struct inode *inode, struct file *filp) +static int kernfs_dir_fop_release(struct inode *inode, struct file *filp) { kernfs_put(filp->private_data); return 0; } -static struct kernfs_node *sysfs_dir_pos(const void *ns, +static struct kernfs_node *kernfs_dir_pos(const void *ns, struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) { if (pos) { @@ -944,10 +945,10 @@ static struct kernfs_node *sysfs_dir_pos(const void *ns, return pos; } -static struct kernfs_node *sysfs_dir_next_pos(const void *ns, +static struct kernfs_node *kernfs_dir_next_pos(const void *ns, struct kernfs_node *parent, ino_t ino, struct kernfs_node *pos) { - pos = sysfs_dir_pos(ns, parent, ino, pos); + pos = kernfs_dir_pos(ns, parent, ino, pos); if (pos) do { struct rb_node *node = rb_next(&pos->rb); @@ -959,7 +960,7 @@ static struct kernfs_node *sysfs_dir_next_pos(const void *ns, return pos; } -static int sysfs_readdir(struct file *file, struct dir_context *ctx) +static int kernfs_fop_readdir(struct file *file, struct dir_context *ctx) { struct dentry *dentry = file->f_path.dentry; struct kernfs_node *parent = dentry->d_fsdata; @@ -973,9 +974,9 @@ static int sysfs_readdir(struct file *file, struct dir_context *ctx) if (kernfs_ns_enabled(parent)) ns = kernfs_info(dentry->d_sb)->ns; - for (pos = sysfs_dir_pos(ns, parent, ctx->pos, pos); + for (pos = kernfs_dir_pos(ns, parent, ctx->pos, pos); pos; - pos = sysfs_dir_next_pos(ns, parent, ctx->pos, pos)) { + pos = kernfs_dir_next_pos(ns, parent, ctx->pos, pos)) { const char *name = pos->name; unsigned int type = dt_type(pos); int len = strlen(name); @@ -996,7 +997,8 @@ static int sysfs_readdir(struct file *file, struct dir_context *ctx) return 0; } -static loff_t sysfs_dir_llseek(struct file *file, loff_t offset, int whence) +static loff_t kernfs_dir_fop_llseek(struct file *file, loff_t offset, + int whence) { struct inode *inode = file_inode(file); loff_t ret; @@ -1010,7 +1012,7 @@ static loff_t sysfs_dir_llseek(struct file *file, loff_t offset, int whence) const struct file_operations kernfs_dir_fops = { .read = generic_read_dir, - .iterate = sysfs_readdir, - .release = sysfs_dir_release, - .llseek = sysfs_dir_llseek, + .iterate = kernfs_fop_readdir, + .release = kernfs_dir_fop_release, + .llseek = kernfs_dir_fop_llseek, }; diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 32364dd..053cfd9 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -64,7 +64,7 @@ static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) * the ops aren't called concurrently for the same open file. */ mutex_lock(&of->mutex); - if (!sysfs_get_active(of->kn)) + if (!kernfs_get_active(of->kn)) return ERR_PTR(-ENODEV); ops = kernfs_ops(of->kn); @@ -104,7 +104,7 @@ static void kernfs_seq_stop(struct seq_file *sf, void *v) if (ops->seq_stop) ops->seq_stop(sf, v); - sysfs_put_active(of->kn); + kernfs_put_active(of->kn); mutex_unlock(&of->mutex); } @@ -147,7 +147,7 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of, * the ops aren't called concurrently for the same open file. */ mutex_lock(&of->mutex); - if (!sysfs_get_active(of->kn)) { + if (!kernfs_get_active(of->kn)) { len = -ENODEV; mutex_unlock(&of->mutex); goto out_free; @@ -159,7 +159,7 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of, else len = -EINVAL; - sysfs_put_active(of->kn); + kernfs_put_active(of->kn); mutex_unlock(&of->mutex); if (len < 0) @@ -178,14 +178,14 @@ static ssize_t kernfs_file_direct_read(struct kernfs_open_file *of, } /** - * kernfs_file_read - kernfs vfs read callback + * kernfs_fop_read - kernfs vfs read callback * @file: file pointer * @user_buf: data to write * @count: number of bytes * @ppos: starting offset */ -static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, - size_t count, loff_t *ppos) +static ssize_t kernfs_fop_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) { struct kernfs_open_file *of = kernfs_of(file); @@ -196,7 +196,7 @@ static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, } /** - * kernfs_file_write - kernfs vfs write callback + * kernfs_fop_write - kernfs vfs write callback * @file: file pointer * @user_buf: data to write * @count: number of bytes @@ -211,8 +211,8 @@ static ssize_t kernfs_file_read(struct file *file, char __user *user_buf, * modify only the the value you're changing, then write entire buffer * back. */ -static ssize_t kernfs_file_write(struct file *file, const char __user *user_buf, - size_t count, loff_t *ppos) +static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *ppos) { struct kernfs_open_file *of = kernfs_of(file); ssize_t len = min_t(size_t, count, PAGE_SIZE); @@ -234,7 +234,7 @@ static ssize_t kernfs_file_write(struct file *file, const char __user *user_buf, * the ops aren't called concurrently for the same open file. */ mutex_lock(&of->mutex); - if (!sysfs_get_active(of->kn)) { + if (!kernfs_get_active(of->kn)) { mutex_unlock(&of->mutex); len = -ENODEV; goto out_free; @@ -246,7 +246,7 @@ static ssize_t kernfs_file_write(struct file *file, const char __user *user_buf, else len = -EINVAL; - sysfs_put_active(of->kn); + kernfs_put_active(of->kn); mutex_unlock(&of->mutex); if (len > 0) @@ -264,13 +264,13 @@ static void kernfs_vma_open(struct vm_area_struct *vma) if (!of->vm_ops) return; - if (!sysfs_get_active(of->kn)) + if (!kernfs_get_active(of->kn)) return; if (of->vm_ops->open) of->vm_ops->open(vma); - sysfs_put_active(of->kn); + kernfs_put_active(of->kn); } static int kernfs_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) @@ -282,14 +282,14 @@ static int kernfs_vma_fault(struct vm_area_struct *vma, struct vm_fault *vmf) if (!of->vm_ops) return VM_FAULT_SIGBUS; - if (!sysfs_get_active(of->kn)) + if (!kernfs_get_active(of->kn)) return VM_FAULT_SIGBUS; ret = VM_FAULT_SIGBUS; if (of->vm_ops->fault) ret = of->vm_ops->fault(vma, vmf); - sysfs_put_active(of->kn); + kernfs_put_active(of->kn); return ret; } @@ -303,7 +303,7 @@ static int kernfs_vma_page_mkwrite(struct vm_area_struct *vma, if (!of->vm_ops) return VM_FAULT_SIGBUS; - if (!sysfs_get_active(of->kn)) + if (!kernfs_get_active(of->kn)) return VM_FAULT_SIGBUS; ret = 0; @@ -312,7 +312,7 @@ static int kernfs_vma_page_mkwrite(struct vm_area_struct *vma, else file_update_time(file); - sysfs_put_active(of->kn); + kernfs_put_active(of->kn); return ret; } @@ -326,14 +326,14 @@ static int kernfs_vma_access(struct vm_area_struct *vma, unsigned long addr, if (!of->vm_ops) return -EINVAL; - if (!sysfs_get_active(of->kn)) + if (!kernfs_get_active(of->kn)) return -EINVAL; ret = -EINVAL; if (of->vm_ops->access) ret = of->vm_ops->access(vma, addr, buf, len, write); - sysfs_put_active(of->kn); + kernfs_put_active(of->kn); return ret; } @@ -348,14 +348,14 @@ static int kernfs_vma_set_policy(struct vm_area_struct *vma, if (!of->vm_ops) return 0; - if (!sysfs_get_active(of->kn)) + if (!kernfs_get_active(of->kn)) return -EINVAL; ret = 0; if (of->vm_ops->set_policy) ret = of->vm_ops->set_policy(vma, new); - sysfs_put_active(of->kn); + kernfs_put_active(of->kn); return ret; } @@ -369,14 +369,14 @@ static struct mempolicy *kernfs_vma_get_policy(struct vm_area_struct *vma, if (!of->vm_ops) return vma->vm_policy; - if (!sysfs_get_active(of->kn)) + if (!kernfs_get_active(of->kn)) return vma->vm_policy; pol = vma->vm_policy; if (of->vm_ops->get_policy) pol = of->vm_ops->get_policy(vma, addr); - sysfs_put_active(of->kn); + kernfs_put_active(of->kn); return pol; } @@ -391,14 +391,14 @@ static int kernfs_vma_migrate(struct vm_area_struct *vma, if (!of->vm_ops) return 0; - if (!sysfs_get_active(of->kn)) + if (!kernfs_get_active(of->kn)) return 0; ret = 0; if (of->vm_ops->migrate) ret = of->vm_ops->migrate(vma, from, to, flags); - sysfs_put_active(of->kn); + kernfs_put_active(of->kn); return ret; } #endif @@ -415,7 +415,7 @@ static const struct vm_operations_struct kernfs_vm_ops = { #endif }; -static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) +static int kernfs_fop_mmap(struct file *file, struct vm_area_struct *vma) { struct kernfs_open_file *of = kernfs_of(file); const struct kernfs_ops *ops; @@ -434,7 +434,7 @@ static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) mutex_lock(&of->mutex); rc = -ENODEV; - if (!sysfs_get_active(of->kn)) + if (!kernfs_get_active(of->kn)) goto out_unlock; ops = kernfs_ops(of->kn); @@ -465,7 +465,7 @@ static int kernfs_file_mmap(struct file *file, struct vm_area_struct *vma) of->vm_ops = vma->vm_ops; vma->vm_ops = &kernfs_vm_ops; out_put: - sysfs_put_active(of->kn); + kernfs_put_active(of->kn); out_unlock: mutex_unlock(&of->mutex); @@ -473,7 +473,7 @@ out_unlock: } /** - * sysfs_get_open_dirent - get or create kernfs_open_node + * kernfs_get_open_node - get or create kernfs_open_node * @kn: target kernfs_node * @of: kernfs_open_file for this instance of open * @@ -486,8 +486,8 @@ out_unlock: * RETURNS: * 0 on success, -errno on failure. */ -static int sysfs_get_open_dirent(struct kernfs_node *kn, - struct kernfs_open_file *of) +static int kernfs_get_open_node(struct kernfs_node *kn, + struct kernfs_open_file *of) { struct kernfs_open_node *on, *new_on = NULL; @@ -527,7 +527,7 @@ static int sysfs_get_open_dirent(struct kernfs_node *kn, } /** - * sysfs_put_open_dirent - put kernfs_open_node + * kernfs_put_open_node - put kernfs_open_node * @kn: target kernfs_nodet * @of: associated kernfs_open_file * @@ -537,8 +537,8 @@ static int sysfs_get_open_dirent(struct kernfs_node *kn, * LOCKING: * None. */ -static void sysfs_put_open_dirent(struct kernfs_node *kn, - struct kernfs_open_file *of) +static void kernfs_put_open_node(struct kernfs_node *kn, + struct kernfs_open_file *of) { struct kernfs_open_node *on = kn->attr.open; unsigned long flags; @@ -560,7 +560,7 @@ static void sysfs_put_open_dirent(struct kernfs_node *kn, kfree(on); } -static int kernfs_file_open(struct inode *inode, struct file *file) +static int kernfs_fop_open(struct inode *inode, struct file *file) { struct kernfs_node *kn = file->f_path.dentry->d_fsdata; const struct kernfs_ops *ops; @@ -568,7 +568,7 @@ static int kernfs_file_open(struct inode *inode, struct file *file) bool has_read, has_write, has_mmap; int error = -EACCES; - if (!sysfs_get_active(kn)) + if (!kernfs_get_active(kn)) return -ENODEV; ops = kernfs_ops(kn); @@ -633,13 +633,13 @@ static int kernfs_file_open(struct inode *inode, struct file *file) if (file->f_mode & FMODE_WRITE) file->f_mode |= FMODE_PWRITE; - /* make sure we have open dirent struct */ - error = sysfs_get_open_dirent(kn, of); + /* make sure we have open node struct */ + error = kernfs_get_open_node(kn, of); if (error) goto err_close; /* open succeeded, put active references */ - sysfs_put_active(kn); + kernfs_put_active(kn); return 0; err_close: @@ -647,23 +647,23 @@ err_close: err_free: kfree(of); err_out: - sysfs_put_active(kn); + kernfs_put_active(kn); return error; } -static int kernfs_file_release(struct inode *inode, struct file *filp) +static int kernfs_fop_release(struct inode *inode, struct file *filp) { struct kernfs_node *kn = filp->f_path.dentry->d_fsdata; struct kernfs_open_file *of = kernfs_of(filp); - sysfs_put_open_dirent(kn, of); + kernfs_put_open_node(kn, of); seq_release(inode, filp); kfree(of); return 0; } -void sysfs_unmap_bin_file(struct kernfs_node *kn) +void kernfs_unmap_bin_file(struct kernfs_node *kn) { struct kernfs_open_node *on; struct kernfs_open_file *of; @@ -686,10 +686,11 @@ void sysfs_unmap_bin_file(struct kernfs_node *kn) } mutex_unlock(&kernfs_open_file_mutex); - sysfs_put_open_dirent(kn, NULL); + kernfs_put_open_node(kn, NULL); } -/* Sysfs attribute files are pollable. The idea is that you read +/* + * Kernfs attribute files are pollable. The idea is that you read * the content and then you use 'poll' or 'select' to wait for * the content to change. When the content changes (assuming the * manager for the kobject supports notification), poll will @@ -702,19 +703,19 @@ void sysfs_unmap_bin_file(struct kernfs_node *kn) * to see if it supports poll (Neither 'poll' nor 'select' return * an appropriate error code). When in doubt, set a suitable timeout value. */ -static unsigned int kernfs_file_poll(struct file *filp, poll_table *wait) +static unsigned int kernfs_fop_poll(struct file *filp, poll_table *wait) { struct kernfs_open_file *of = kernfs_of(filp); struct kernfs_node *kn = filp->f_path.dentry->d_fsdata; struct kernfs_open_node *on = kn->attr.open; /* need parent for the kobj, grab both */ - if (!sysfs_get_active(kn)) + if (!kernfs_get_active(kn)) goto trigger; poll_wait(filp, &on->poll, wait); - sysfs_put_active(kn); + kernfs_put_active(kn); if (of->event != atomic_read(&on->event)) goto trigger; @@ -751,13 +752,13 @@ void kernfs_notify(struct kernfs_node *kn) EXPORT_SYMBOL_GPL(kernfs_notify); const struct file_operations kernfs_file_fops = { - .read = kernfs_file_read, - .write = kernfs_file_write, + .read = kernfs_fop_read, + .write = kernfs_fop_write, .llseek = generic_file_llseek, - .mmap = kernfs_file_mmap, - .open = kernfs_file_open, - .release = kernfs_file_release, - .poll = kernfs_file_poll, + .mmap = kernfs_fop_mmap, + .open = kernfs_fop_open, + .release = kernfs_fop_release, + .poll = kernfs_fop_poll, }; /** @@ -784,8 +785,8 @@ struct kernfs_node *kernfs_create_file_ns_key(struct kernfs_node *parent, struct kernfs_node *kn; int rc; - kn = sysfs_new_dirent(kernfs_root(parent), name, - (mode & S_IALLUGO) | S_IFREG, KERNFS_FILE); + kn = kernfs_new_node(kernfs_root(parent), name, + (mode & S_IALLUGO) | S_IFREG, KERNFS_FILE); if (!kn) return ERR_PTR(-ENOMEM); @@ -811,9 +812,9 @@ struct kernfs_node *kernfs_create_file_ns_key(struct kernfs_node *parent, if (ops->mmap) kn->flags |= KERNFS_HAS_MMAP; - sysfs_addrm_start(&acxt); - rc = sysfs_add_one(&acxt, kn, parent); - sysfs_addrm_finish(&acxt); + kernfs_addrm_start(&acxt); + rc = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); if (rc) { kernfs_put(kn); diff --git a/fs/kernfs/inode.c b/fs/kernfs/inode.c index c5f231e..e55126f 100644 --- a/fs/kernfs/inode.c +++ b/fs/kernfs/inode.c @@ -31,16 +31,16 @@ static struct backing_dev_info kernfs_bdi = { }; static const struct inode_operations kernfs_iops = { - .permission = sysfs_permission, - .setattr = sysfs_setattr, - .getattr = sysfs_getattr, - .setxattr = sysfs_setxattr, - .removexattr = sysfs_removexattr, - .getxattr = sysfs_getxattr, - .listxattr = sysfs_listxattr, + .permission = kernfs_iop_permission, + .setattr = kernfs_iop_setattr, + .getattr = kernfs_iop_getattr, + .setxattr = kernfs_iop_setxattr, + .removexattr = kernfs_iop_removexattr, + .getxattr = kernfs_iop_getxattr, + .listxattr = kernfs_iop_listxattr, }; -void __init sysfs_inode_init(void) +void __init kernfs_inode_init(void) { if (bdi_init(&kernfs_bdi)) panic("failed to init kernfs_bdi"); @@ -115,7 +115,7 @@ int kernfs_setattr(struct kernfs_node *kn, const struct iattr *iattr) return ret; } -int sysfs_setattr(struct dentry *dentry, struct iattr *iattr) +int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr) { struct inode *inode = dentry->d_inode; struct kernfs_node *kn = dentry->d_fsdata; @@ -141,8 +141,8 @@ out: return error; } -static int sysfs_sd_setsecdata(struct kernfs_node *kn, void **secdata, - u32 *secdata_len) +static int kernfs_node_setsecdata(struct kernfs_node *kn, void **secdata, + u32 *secdata_len) { struct kernfs_iattrs *attrs; void *old_secdata; @@ -163,8 +163,8 @@ static int sysfs_sd_setsecdata(struct kernfs_node *kn, void **secdata, return 0; } -int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, - size_t size, int flags) +int kernfs_iop_setxattr(struct dentry *dentry, const char *name, + const void *value, size_t size, int flags) { struct kernfs_node *kn = dentry->d_fsdata; struct kernfs_iattrs *attrs; @@ -188,7 +188,7 @@ int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, return error; mutex_lock(&kernfs_mutex); - error = sysfs_sd_setsecdata(kn, &secdata, &secdata_len); + error = kernfs_node_setsecdata(kn, &secdata, &secdata_len); mutex_unlock(&kernfs_mutex); if (secdata) @@ -202,7 +202,7 @@ int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, return -EINVAL; } -int sysfs_removexattr(struct dentry *dentry, const char *name) +int kernfs_iop_removexattr(struct dentry *dentry, const char *name) { struct kernfs_node *kn = dentry->d_fsdata; struct kernfs_iattrs *attrs; @@ -214,8 +214,8 @@ int sysfs_removexattr(struct dentry *dentry, const char *name) return simple_xattr_remove(&attrs->xattrs, name); } -ssize_t sysfs_getxattr(struct dentry *dentry, const char *name, void *buf, - size_t size) +ssize_t kernfs_iop_getxattr(struct dentry *dentry, const char *name, void *buf, + size_t size) { struct kernfs_node *kn = dentry->d_fsdata; struct kernfs_iattrs *attrs; @@ -227,7 +227,7 @@ ssize_t sysfs_getxattr(struct dentry *dentry, const char *name, void *buf, return simple_xattr_get(&attrs->xattrs, name, buf, size); } -ssize_t sysfs_listxattr(struct dentry *dentry, char *buf, size_t size) +ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size) { struct kernfs_node *kn = dentry->d_fsdata; struct kernfs_iattrs *attrs; @@ -254,7 +254,7 @@ static inline void set_inode_attr(struct inode *inode, struct iattr *iattr) inode->i_ctime = iattr->ia_ctime; } -static void sysfs_refresh_inode(struct kernfs_node *kn, struct inode *inode) +static void kernfs_refresh_inode(struct kernfs_node *kn, struct inode *inode) { struct kernfs_iattrs *attrs = kn->iattr; @@ -273,21 +273,21 @@ static void sysfs_refresh_inode(struct kernfs_node *kn, struct inode *inode) set_nlink(inode, kn->dir.subdirs + 2); } -int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat) +int kernfs_iop_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat) { struct kernfs_node *kn = dentry->d_fsdata; struct inode *inode = dentry->d_inode; mutex_lock(&kernfs_mutex); - sysfs_refresh_inode(kn, inode); + kernfs_refresh_inode(kn, inode); mutex_unlock(&kernfs_mutex); generic_fillattr(inode, stat); return 0; } -static void sysfs_init_inode(struct kernfs_node *kn, struct inode *inode) +static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode) { kernfs_get(kn); inode->i_private = kn; @@ -296,7 +296,7 @@ static void sysfs_init_inode(struct kernfs_node *kn, struct inode *inode) inode->i_op = &kernfs_iops; set_default_inode_attr(inode, kn->mode); - sysfs_refresh_inode(kn, inode); + kernfs_refresh_inode(kn, inode); /* initialize inode according to type */ switch (kernfs_type(kn)) { @@ -319,7 +319,7 @@ static void sysfs_init_inode(struct kernfs_node *kn, struct inode *inode) } /** - * sysfs_get_inode - get inode for kernfs_node + * kernfs_get_inode - get inode for kernfs_node * @sb: super block * @kn: kernfs_node to allocate inode for * @@ -333,25 +333,25 @@ static void sysfs_init_inode(struct kernfs_node *kn, struct inode *inode) * RETURNS: * Pointer to allocated inode on success, NULL on failure. */ -struct inode *sysfs_get_inode(struct super_block *sb, struct kernfs_node *kn) +struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn) { struct inode *inode; inode = iget_locked(sb, kn->ino); if (inode && (inode->i_state & I_NEW)) - sysfs_init_inode(kn, inode); + kernfs_init_inode(kn, inode); return inode; } /* - * The kernfs_node serves as both an inode and a directory entry for sysfs. - * To prevent the sysfs inode numbers from being freed prematurely we take - * a reference to kernfs_node from the sysfs inode. A + * The kernfs_node serves as both an inode and a directory entry for + * kernfs. To prevent the kernfs inode numbers from being freed + * prematurely we take a reference to kernfs_node from the kernfs inode. A * super_operations.evict_inode() implementation is needed to drop that * reference upon inode destruction. */ -void sysfs_evict_inode(struct inode *inode) +void kernfs_evict_inode(struct inode *inode) { struct kernfs_node *kn = inode->i_private; @@ -360,7 +360,7 @@ void sysfs_evict_inode(struct inode *inode) kernfs_put(kn); } -int sysfs_permission(struct inode *inode, int mask) +int kernfs_iop_permission(struct inode *inode, int mask) { struct kernfs_node *kn; @@ -370,7 +370,7 @@ int sysfs_permission(struct inode *inode, int mask) kn = inode->i_private; mutex_lock(&kernfs_mutex); - sysfs_refresh_inode(kn, inode); + kernfs_refresh_inode(kn, inode); mutex_unlock(&kernfs_mutex); return generic_permission(inode, mask); diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index e62e8ec..a4ff491 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -76,19 +76,19 @@ extern struct kmem_cache *kernfs_node_cache; /* * inode.c */ -struct inode *sysfs_get_inode(struct super_block *sb, struct kernfs_node *kn); -void sysfs_evict_inode(struct inode *inode); -int sysfs_permission(struct inode *inode, int mask); -int sysfs_setattr(struct dentry *dentry, struct iattr *iattr); -int sysfs_getattr(struct vfsmount *mnt, struct dentry *dentry, - struct kstat *stat); -int sysfs_setxattr(struct dentry *dentry, const char *name, const void *value, - size_t size, int flags); -int sysfs_removexattr(struct dentry *dentry, const char *name); -ssize_t sysfs_getxattr(struct dentry *dentry, const char *name, void *buf, - size_t size); -ssize_t sysfs_listxattr(struct dentry *dentry, char *buf, size_t size); -void sysfs_inode_init(void); +struct inode *kernfs_get_inode(struct super_block *sb, struct kernfs_node *kn); +void kernfs_evict_inode(struct inode *inode); +int kernfs_iop_permission(struct inode *inode, int mask); +int kernfs_iop_setattr(struct dentry *dentry, struct iattr *iattr); +int kernfs_iop_getattr(struct vfsmount *mnt, struct dentry *dentry, + struct kstat *stat); +int kernfs_iop_setxattr(struct dentry *dentry, const char *name, const void *value, + size_t size, int flags); +int kernfs_iop_removexattr(struct dentry *dentry, const char *name); +ssize_t kernfs_iop_getxattr(struct dentry *dentry, const char *name, void *buf, + size_t size); +ssize_t kernfs_iop_listxattr(struct dentry *dentry, char *buf, size_t size); +void kernfs_inode_init(void); /* * dir.c @@ -98,21 +98,21 @@ extern const struct dentry_operations kernfs_dops; extern const struct file_operations kernfs_dir_fops; extern const struct inode_operations kernfs_dir_iops; -struct kernfs_node *sysfs_get_active(struct kernfs_node *kn); -void sysfs_put_active(struct kernfs_node *kn); -void sysfs_addrm_start(struct kernfs_addrm_cxt *acxt); -int sysfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, - struct kernfs_node *parent); -void sysfs_addrm_finish(struct kernfs_addrm_cxt *acxt); -struct kernfs_node *sysfs_new_dirent(struct kernfs_root *root, - const char *name, umode_t mode, int type); +struct kernfs_node *kernfs_get_active(struct kernfs_node *kn); +void kernfs_put_active(struct kernfs_node *kn); +void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt); +int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, + struct kernfs_node *parent); +void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt); +struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, + umode_t mode, int type); /* * file.c */ extern const struct file_operations kernfs_file_fops; -void sysfs_unmap_bin_file(struct kernfs_node *kn); +void kernfs_unmap_bin_file(struct kernfs_node *kn); /* * symlink.c diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index 27d967b..0d6ce89 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -22,10 +22,10 @@ struct kmem_cache *kernfs_node_cache; static const struct super_operations kernfs_sops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, - .evict_inode = sysfs_evict_inode, + .evict_inode = kernfs_evict_inode, }; -static int sysfs_fill_super(struct super_block *sb) +static int kernfs_fill_super(struct super_block *sb) { struct kernfs_super_info *info = kernfs_info(sb); struct inode *inode; @@ -39,10 +39,10 @@ static int sysfs_fill_super(struct super_block *sb) /* get root inode, initialize and unlock it */ mutex_lock(&kernfs_mutex); - inode = sysfs_get_inode(sb, info->root->kn); + inode = kernfs_get_inode(sb, info->root->kn); mutex_unlock(&kernfs_mutex); if (!inode) { - pr_debug("sysfs: could not get root inode\n"); + pr_debug("kernfs: could not get root inode\n"); return -ENOMEM; } @@ -59,7 +59,7 @@ static int sysfs_fill_super(struct super_block *sb) return 0; } -static int sysfs_test_super(struct super_block *sb, void *data) +static int kernfs_test_super(struct super_block *sb, void *data) { struct kernfs_super_info *sb_info = kernfs_info(sb); struct kernfs_super_info *info = data; @@ -67,7 +67,7 @@ static int sysfs_test_super(struct super_block *sb, void *data) return sb_info->root == info->root && sb_info->ns == info->ns; } -static int sysfs_set_super(struct super_block *sb, void *data) +static int kernfs_set_super(struct super_block *sb, void *data) { int error; error = set_anon_super(sb, data); @@ -117,13 +117,13 @@ struct dentry *kernfs_mount_ns(struct file_system_type *fs_type, int flags, info->root = root; info->ns = ns; - sb = sget(fs_type, sysfs_test_super, sysfs_set_super, flags, info); + sb = sget(fs_type, kernfs_test_super, kernfs_set_super, flags, info); if (IS_ERR(sb) || sb->s_fs_info != info) kfree(info); if (IS_ERR(sb)) return ERR_CAST(sb); if (!sb->s_root) { - error = sysfs_fill_super(sb); + error = kernfs_fill_super(sb); if (error) { deactivate_locked_super(sb); return ERR_PTR(error); @@ -161,5 +161,5 @@ void __init kernfs_init(void) kernfs_node_cache = kmem_cache_create("kernfs_node_cache", sizeof(struct kernfs_node), 0, SLAB_PANIC, NULL); - sysfs_inode_init(); + kernfs_inode_init(); } diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 4105bd0..a03e260 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -30,8 +30,8 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, struct kernfs_addrm_cxt acxt; int error; - kn = sysfs_new_dirent(kernfs_root(parent), name, S_IFLNK|S_IRWXUGO, - KERNFS_LINK); + kn = kernfs_new_node(kernfs_root(parent), name, S_IFLNK|S_IRWXUGO, + KERNFS_LINK); if (!kn) return ERR_PTR(-ENOMEM); @@ -40,9 +40,9 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, kn->symlink.target_kn = target; kernfs_get(target); /* ref owned by symlink */ - sysfs_addrm_start(&acxt); - error = sysfs_add_one(&acxt, kn, parent); - sysfs_addrm_finish(&acxt); + kernfs_addrm_start(&acxt); + error = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); if (!error) return kn; @@ -51,8 +51,8 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, return ERR_PTR(error); } -static int sysfs_get_target_path(struct kernfs_node *parent, - struct kernfs_node *target, char *path) +static int kernfs_get_target_path(struct kernfs_node *parent, + struct kernfs_node *target, char *path) { struct kernfs_node *base, *kn; char *s = path; @@ -103,7 +103,7 @@ static int sysfs_get_target_path(struct kernfs_node *parent, return 0; } -static int sysfs_getlink(struct dentry *dentry, char *path) +static int kernfs_getlink(struct dentry *dentry, char *path) { struct kernfs_node *kn = dentry->d_fsdata; struct kernfs_node *parent = kn->parent; @@ -111,18 +111,18 @@ static int sysfs_getlink(struct dentry *dentry, char *path) int error; mutex_lock(&kernfs_mutex); - error = sysfs_get_target_path(parent, target, path); + error = kernfs_get_target_path(parent, target, path); mutex_unlock(&kernfs_mutex); return error; } -static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) +static void *kernfs_iop_follow_link(struct dentry *dentry, struct nameidata *nd) { int error = -ENOMEM; unsigned long page = get_zeroed_page(GFP_KERNEL); if (page) { - error = sysfs_getlink(dentry, (char *) page); + error = kernfs_getlink(dentry, (char *) page); if (error < 0) free_page((unsigned long)page); } @@ -130,8 +130,8 @@ static void *sysfs_follow_link(struct dentry *dentry, struct nameidata *nd) return NULL; } -static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, - void *cookie) +static void kernfs_iop_put_link(struct dentry *dentry, struct nameidata *nd, + void *cookie) { char *page = nd_get_link(nd); if (!IS_ERR(page)) @@ -139,14 +139,14 @@ static void sysfs_put_link(struct dentry *dentry, struct nameidata *nd, } const struct inode_operations kernfs_symlink_iops = { - .setxattr = sysfs_setxattr, - .removexattr = sysfs_removexattr, - .getxattr = sysfs_getxattr, - .listxattr = sysfs_listxattr, + .setxattr = kernfs_iop_setxattr, + .removexattr = kernfs_iop_removexattr, + .getxattr = kernfs_iop_getxattr, + .listxattr = kernfs_iop_listxattr, .readlink = generic_readlink, - .follow_link = sysfs_follow_link, - .put_link = sysfs_put_link, - .setattr = sysfs_setattr, - .getattr = sysfs_getattr, - .permission = sysfs_permission, + .follow_link = kernfs_iop_follow_link, + .put_link = kernfs_iop_put_link, + .setattr = kernfs_iop_setattr, + .getattr = kernfs_iop_getattr, + .permission = kernfs_iop_permission, }; -- cgit v1.1 From bb8b9d095c5c56cce99576cfef0cf9b989f7120d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 11 Dec 2013 16:02:55 -0500 Subject: kernfs: add @mode to kernfs_create_dir[_ns]() sysfs assumed 0755 for all newly created directories and kernfs inherited it. This assumption is unnecessarily restrictive and inconsistent with kernfs_create_file[_ns](). This patch adds @mode parameter to kernfs_create_dir[_ns]() and update uses in sysfs accordingly. Among others, this will be useful for implementations of the planned ->mkdir() method. This patch doesn't introduce any behavior differences. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 9 +++++---- fs/sysfs/dir.c | 3 ++- fs/sysfs/group.c | 3 ++- include/linux/kernfs.h | 13 +++++++------ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 6520066..e55bb02 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -639,22 +639,23 @@ void kernfs_destroy_root(struct kernfs_root *root) * kernfs_create_dir_ns - create a directory * @parent: parent in which to create a new directory * @name: name of the new directory + * @mode: mode of the new directory * @priv: opaque data associated with the new directory * @ns: optional namespace tag of the directory * * Returns the created node on success, ERR_PTR() value on failure. */ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, - const char *name, void *priv, - const void *ns) + const char *name, umode_t mode, + void *priv, const void *ns) { - umode_t mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO; struct kernfs_addrm_cxt acxt; struct kernfs_node *kn; int rc; /* allocate */ - kn = kernfs_new_node(kernfs_root(parent), name, mode, KERNFS_DIR); + kn = kernfs_new_node(kernfs_root(parent), name, mode | S_IFDIR, + KERNFS_DIR); if (!kn) return ERR_PTR(-ENOMEM); diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c index aa00740..ee0d761 100644 --- a/fs/sysfs/dir.c +++ b/fs/sysfs/dir.c @@ -73,7 +73,8 @@ int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) if (!parent) return -ENOENT; - kn = kernfs_create_dir_ns(parent, kobject_name(kobj), kobj, ns); + kn = kernfs_create_dir_ns(parent, kobject_name(kobj), + S_IRWXU | S_IRUGO | S_IXUGO, kobj, ns); if (IS_ERR(kn)) { if (PTR_ERR(kn) == -EEXIST) sysfs_warn_dup(parent, kobject_name(kobj)); diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 4d00d399..6b57938 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -100,7 +100,8 @@ static int internal_create_group(struct kobject *kobj, int update, return -EINVAL; } if (grp->name) { - kn = kernfs_create_dir(kobj->sd, grp->name, kobj); + kn = kernfs_create_dir(kobj->sd, grp->name, + S_IRWXU | S_IRUGO | S_IXUGO, kobj); if (IS_ERR(kn)) { if (PTR_ERR(kn) == -EEXIST) sysfs_warn_dup(kobj->sd, grp->name); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index e9c4e3a..0ca2aed 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -210,8 +210,8 @@ struct kernfs_root *kernfs_create_root(void *priv); void kernfs_destroy_root(struct kernfs_root *root); struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, - const char *name, void *priv, - const void *ns); + const char *name, umode_t mode, + void *priv, const void *ns); struct kernfs_node *kernfs_create_file_ns_key(struct kernfs_node *parent, const char *name, umode_t mode, loff_t size, @@ -260,8 +260,8 @@ static inline struct kernfs_root *kernfs_create_root(void *priv) static inline void kernfs_destroy_root(struct kernfs_root *root) { } static inline struct kernfs_node * -kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, void *priv, - const void *ns) +kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, + umode_t mode, void *priv, const void *ns) { return ERR_PTR(-ENOSYS); } static inline struct kernfs_node * @@ -314,9 +314,10 @@ kernfs_find_and_get(struct kernfs_node *kn, const char *name) } static inline struct kernfs_node * -kernfs_create_dir(struct kernfs_node *parent, const char *name, void *priv) +kernfs_create_dir(struct kernfs_node *parent, const char *name, umode_t mode, + void *priv) { - return kernfs_create_dir_ns(parent, name, priv, NULL); + return kernfs_create_dir_ns(parent, name, mode, priv, NULL); } static inline struct kernfs_node * -- cgit v1.1 From d0ae3d4347ee025cf23b850d484fe8593f7dd0f2 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 11 Dec 2013 16:02:56 -0500 Subject: kernfs: add REMOVED check to create and rename paths kernfs currently assumes that the caller doesn't try to create a new node under a removed parent, rename a removed node, or move a node under a removed node. While this works fine for sysfs, it'd be nice to have protection against such cases especially given that kernfs is planned to add support for mkdir, rmdir and rename requsts from userland which may make race conditions more likely. This patch updates create and rename paths to check REMOVED and fail the operation with -ENOENT if performed on or towards removed nodes. Note that remove path already has such check. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index e55bb02..ba5f372 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -430,6 +430,9 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, if (kernfs_type(parent) != KERNFS_DIR) return -EINVAL; + if (parent->flags & KERNFS_REMOVED) + return -ENOENT; + kn->hash = kernfs_name_hash(kn->name, kn->ns); kn->parent = parent; kernfs_get(parent); @@ -863,6 +866,10 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, mutex_lock(&kernfs_mutex); + error = -ENOENT; + if ((kn->flags | new_parent->flags) & KERNFS_REMOVED) + goto out; + error = 0; if ((kn->parent == new_parent) && (kn->ns == new_ns) && (strcmp(kn->name, new_name) == 0)) -- cgit v1.1 From 2063d608f5110d120db60e896ec2c70c95bb7978 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 11 Dec 2013 16:02:57 -0500 Subject: kernfs: mark static names with KERNFS_STATIC_NAME Because sysfs used struct attribute which are supposed to stay constant, sysfs didn't copy names when creating regular files. The specified string for name was supposed to stay constant. Such distinction isn't inherent for kernfs. kernfs_create_file[_ns]() should be able to take the same @name as kernfs_create_dir[_ns]() As there can be huge number of sysfs attributes, we still want to be able to use static names for sysfs attributes. This patch renames kernfs_create_file_ns_key() to __kernfs_create_file() and adds @name_is_static parameter so that the caller can explicitly indicate that @name can be used without copying. kernfs is updated to use KERNFS_STATIC_NAME to distinguish static and copied names. This patch doesn't introduce any behavior changes. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 8 ++++---- fs/kernfs/file.c | 23 +++++++++++++++-------- fs/kernfs/kernfs-internal.h | 2 +- fs/sysfs/file.c | 4 ++-- include/linux/kernfs.h | 27 ++++++++++++++------------- 5 files changed, 36 insertions(+), 28 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index ba5f372..e168177 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -240,7 +240,7 @@ void kernfs_put(struct kernfs_node *kn) if (kernfs_type(kn) == KERNFS_LINK) kernfs_put(kn->symlink.target_kn); - if (kernfs_type(kn) & KERNFS_COPY_NAME) + if (!(kn->flags & KERNFS_STATIC_NAME)) kfree(kn->name); if (kn->iattr) { if (kn->iattr->ia_secdata) @@ -336,13 +336,13 @@ const struct dentry_operations kernfs_dops = { }; struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, - umode_t mode, int type) + umode_t mode, unsigned flags) { char *dup_name = NULL; struct kernfs_node *kn; int ret; - if (type & KERNFS_COPY_NAME) { + if (!(flags & KERNFS_STATIC_NAME)) { name = dup_name = kstrdup(name, GFP_KERNEL); if (!name) return NULL; @@ -362,7 +362,7 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, kn->name = name; kn->mode = mode; - kn->flags = type | KERNFS_REMOVED; + kn->flags = flags | KERNFS_REMOVED; return kn; diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 053cfd9..316604c 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -762,7 +762,7 @@ const struct file_operations kernfs_file_fops = { }; /** - * kernfs_create_file_ns_key - create a file + * __kernfs_create_file - kernfs internal function to create a file * @parent: directory to create the file in * @name: name of the file * @mode: mode of the file @@ -770,23 +770,30 @@ const struct file_operations kernfs_file_fops = { * @ops: kernfs operations for the file * @priv: private data for the file * @ns: optional namespace tag of the file + * @static_name: don't copy file name * @key: lockdep key for the file's active_ref, %NULL to disable lockdep * * Returns the created node on success, ERR_PTR() value on error. */ -struct kernfs_node *kernfs_create_file_ns_key(struct kernfs_node *parent, - const char *name, - umode_t mode, loff_t size, - const struct kernfs_ops *ops, - void *priv, const void *ns, - struct lock_class_key *key) +struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, + const char *name, + umode_t mode, loff_t size, + const struct kernfs_ops *ops, + void *priv, const void *ns, + bool name_is_static, + struct lock_class_key *key) { struct kernfs_addrm_cxt acxt; struct kernfs_node *kn; + unsigned flags; int rc; + flags = KERNFS_FILE; + if (name_is_static) + flags |= KERNFS_STATIC_NAME; + kn = kernfs_new_node(kernfs_root(parent), name, - (mode & S_IALLUGO) | S_IFREG, KERNFS_FILE); + (mode & S_IALLUGO) | S_IFREG, flags); if (!kn) return ERR_PTR(-ENOMEM); diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index a4ff491..c6ba5bc 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -105,7 +105,7 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, struct kernfs_node *parent); void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt); struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, - umode_t mode, int type); + umode_t mode, unsigned flags); /* * file.c diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index fe6388f..810cf6e 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -252,8 +252,8 @@ int sysfs_add_file_mode_ns(struct kernfs_node *parent, if (!attr->ignore_lockdep) key = attr->key ?: (struct lock_class_key *)&attr->skey; #endif - kn = kernfs_create_file_ns_key(parent, attr->name, mode, size, - ops, (void *)attr, ns, key); + kn = __kernfs_create_file(parent, attr->name, mode, size, ops, + (void *)attr, ns, true, key); if (IS_ERR(kn)) { if (PTR_ERR(kn) == -EEXIST) sysfs_warn_dup(parent, attr->name); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 0ca2aed..321ed84 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -34,7 +34,6 @@ enum kernfs_node_type { }; #define KERNFS_TYPE_MASK 0x000f -#define KERNFS_COPY_NAME (KERNFS_DIR | KERNFS_LINK) #define KERNFS_ACTIVE_REF KERNFS_FILE #define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK @@ -44,6 +43,7 @@ enum kernfs_node_flag { KERNFS_HAS_SEQ_SHOW = 0x0040, KERNFS_HAS_MMAP = 0x0080, KERNFS_LOCKDEP = 0x0100, + KERNFS_STATIC_NAME = 0x0200, }; /* type-specific structures for kernfs_node union members */ @@ -212,12 +212,13 @@ void kernfs_destroy_root(struct kernfs_root *root); struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, umode_t mode, void *priv, const void *ns); -struct kernfs_node *kernfs_create_file_ns_key(struct kernfs_node *parent, - const char *name, - umode_t mode, loff_t size, - const struct kernfs_ops *ops, - void *priv, const void *ns, - struct lock_class_key *key); +struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, + const char *name, + umode_t mode, loff_t size, + const struct kernfs_ops *ops, + void *priv, const void *ns, + bool name_is_static, + struct lock_class_key *key); struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, const char *name, struct kernfs_node *target); @@ -265,10 +266,10 @@ kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, { return ERR_PTR(-ENOSYS); } static inline struct kernfs_node * -kernfs_create_file_ns_key(struct kernfs_node *parent, const char *name, - umode_t mode, loff_t size, - const struct kernfs_ops *ops, void *priv, - const void *ns, struct lock_class_key *key) +__kernfs_create_file(struct kernfs_node *parent, const char *name, + umode_t mode, loff_t size, const struct kernfs_ops *ops, + void *priv, const void *ns, bool name_is_static, + struct lock_class_key *key) { return ERR_PTR(-ENOSYS); } static inline struct kernfs_node * @@ -330,8 +331,8 @@ kernfs_create_file_ns(struct kernfs_node *parent, const char *name, #ifdef CONFIG_DEBUG_LOCK_ALLOC key = (struct lock_class_key *)&ops->lockdep_key; #endif - return kernfs_create_file_ns_key(parent, name, mode, size, ops, priv, - ns, key); + return __kernfs_create_file(parent, name, mode, size, ops, priv, ns, + false, key); } static inline struct kernfs_node * -- cgit v1.1 From 47a52e91f485dfd935042dbd2f66df1ac3fdfbb9 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 11 Dec 2013 16:02:58 -0500 Subject: kernfs: update kernfs_rename_ns() to consider KERNFS_STATIC_NAME kernfs_rename_ns() currently assumes that the target sysfs_dirent has a copied name. This has been okay because sysfs supports rename only for directories which always have copied names; however, there's nothing in kernfs interface which calls for such restriction and currently invoking kernfs_rename_ns() on a regular file leads to oops because it ends up trying to kfree() a static name. This patch updates kernfs_rename_ns() so that it skips kfree() of the old name if it's static. This allows it to be used for all node types. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index e168177..d33af95 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -886,7 +886,11 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, if (!new_name) goto out; - kfree(kn->name); + if (kn->flags & KERNFS_STATIC_NAME) + kn->flags &= ~KERNFS_STATIC_NAME; + else + kfree(kn->name); + kn->name = new_name; } -- cgit v1.1 From 19bbb926203dbcf3a03915e934c36d7681bf6e13 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 11 Dec 2013 16:02:59 -0500 Subject: kernfs: allow negative dentries kernfs doesn't allow negative dentries - kernfs_iop_lookup() returns ERR_PTR(-ENOENT) instead of NULL which short-circuits negative dentry creation and kernfs's d_delete() callback, kernfs_dop_delete(), returns 1 for all removed nodes. This in turn allows kernfs_dop_revalidate() to assume that there's no negative dentry for kernfs. This worked fine for sysfs but kernfs is scheduled to grow mkdir(2) support which depend on negative dentries. This patch updates so that kernfs allows negative dentries. The required changes are almost trivial - kernfs_iop_lookup() now returns NULL instead of ERR_PTR(-ENOENT) when the target kernfs_node doesn't exist, kernfs_dop_delete() is removed and kernfs_dop_revalidate() is updated to check whether the target dentry is negative and request fresh lookup if so. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 37 +++++++++++++------------------------ 1 file changed, 13 insertions(+), 24 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index d33af95..42c5b9f 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -264,12 +264,6 @@ void kernfs_put(struct kernfs_node *kn) } EXPORT_SYMBOL_GPL(kernfs_put); -static int kernfs_dop_delete(const struct dentry *dentry) -{ - struct kernfs_node *kn = dentry->d_fsdata; - return !(kn && !(kn->flags & KERNFS_REMOVED)); -} - static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) { struct kernfs_node *kn; @@ -277,6 +271,10 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) if (flags & LOOKUP_RCU) return -ECHILD; + /* Always perform fresh lookup for negatives */ + if (!dentry->d_inode) + goto out_bad_unlocked; + kn = dentry->d_fsdata; mutex_lock(&kernfs_mutex); @@ -301,22 +299,14 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) out_valid: return 1; out_bad: - /* - * Remove the dentry from the dcache hashes. - * If this is a deleted dentry we use d_drop instead of d_delete - * so kernfs doesn't need to cope with negative dentries. - * - * If this is a dentry that has simply been renamed we - * use d_drop to remove it from the dcache lookup on its - * old parent. If this dentry persists later when a lookup - * is performed at its new name the dentry will be readded - * to the dcache hashes. - */ mutex_unlock(&kernfs_mutex); - - /* If we have submounts we must allow the vfs caches - * to lie about the state of the filesystem to prevent - * leaks and other nasty things. +out_bad_unlocked: + /* + * @dentry doesn't match the underlying kernfs node, drop the + * dentry and force lookup. If we have submounts we must allow the + * vfs caches to lie about the state of the filesystem to prevent + * leaks and other nasty things, so use check_submounts_and_drop() + * instead of d_drop(). */ if (check_submounts_and_drop(dentry) != 0) goto out_valid; @@ -331,7 +321,6 @@ static void kernfs_dop_release(struct dentry *dentry) const struct dentry_operations kernfs_dops = { .d_revalidate = kernfs_dop_revalidate, - .d_delete = kernfs_dop_delete, .d_release = kernfs_dop_release, }; @@ -682,7 +671,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) { - struct dentry *ret = NULL; + struct dentry *ret; struct kernfs_node *parent = dentry->d_parent->d_fsdata; struct kernfs_node *kn; struct inode *inode; @@ -697,7 +686,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir, /* no such entry */ if (!kn) { - ret = ERR_PTR(-ENOENT); + ret = NULL; goto out_unlock; } kernfs_get(kn); -- cgit v1.1 From 80b9bbefc345079bddc4959de016ba4074b0c8d6 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Wed, 11 Dec 2013 16:03:00 -0500 Subject: kernfs: add kernfs_dir_ops Add support for mkdir(2), rmdir(2) and rename(2) syscalls. This is implemented through optional kernfs_dir_ops callback table which can be specified on kernfs_create_root(). An implemented callback is invoked when the matching syscall is invoked. As kernfs keep dcache syncs with internal representation and revalidates dentries on each access, the implementation of these methods is extremely simple. Each just discovers the relevant kernfs_node(s) and invokes the requested callback which is allowed to do any kernfs operations and the end result doesn't necessarily have to match the expected semantics of the syscall. This will be used to convert cgroup to use kernfs instead of its own filesystem implementation. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 44 +++++++++++++++++++++++++++++++++++++++++++- fs/sysfs/mount.c | 2 +- include/linux/kernfs.h | 21 +++++++++++++++++++-- 3 files changed, 63 insertions(+), 4 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 42c5b9f..510b506 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -583,12 +583,13 @@ EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); /** * kernfs_create_root - create a new kernfs hierarchy + * @kdops: optional directory syscall operations for the hierarchy * @priv: opaque data associated with the new directory * * Returns the root of the new hierarchy on success, ERR_PTR() value on * failure. */ -struct kernfs_root *kernfs_create_root(void *priv) +struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) { struct kernfs_root *root; struct kernfs_node *kn; @@ -610,6 +611,7 @@ struct kernfs_root *kernfs_create_root(void *priv) kn->priv = priv; kn->dir.root = root; + root->dir_ops = kdops; root->kn = kn; return root; @@ -706,6 +708,42 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir, return ret; } +static int kernfs_iop_mkdir(struct inode *dir, struct dentry *dentry, + umode_t mode) +{ + struct kernfs_node *parent = dir->i_private; + struct kernfs_dir_ops *kdops = kernfs_root(parent)->dir_ops; + + if (!kdops || !kdops->mkdir) + return -EPERM; + + return kdops->mkdir(parent, dentry->d_name.name, mode); +} + +static int kernfs_iop_rmdir(struct inode *dir, struct dentry *dentry) +{ + struct kernfs_node *kn = dentry->d_fsdata; + struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops; + + if (!kdops || !kdops->rmdir) + return -EPERM; + + return kdops->rmdir(kn); +} + +static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry, + struct inode *new_dir, struct dentry *new_dentry) +{ + struct kernfs_node *kn = old_dentry->d_fsdata; + struct kernfs_node *new_parent = new_dir->i_private; + struct kernfs_dir_ops *kdops = kernfs_root(kn)->dir_ops; + + if (!kdops || !kdops->rename) + return -EPERM; + + return kdops->rename(kn, new_parent, new_dentry->d_name.name); +} + const struct inode_operations kernfs_dir_iops = { .lookup = kernfs_iop_lookup, .permission = kernfs_iop_permission, @@ -715,6 +753,10 @@ const struct inode_operations kernfs_dir_iops = { .removexattr = kernfs_iop_removexattr, .getxattr = kernfs_iop_getxattr, .listxattr = kernfs_iop_listxattr, + + .mkdir = kernfs_iop_mkdir, + .rmdir = kernfs_iop_rmdir, + .rename = kernfs_iop_rename, }; static struct kernfs_node *kernfs_leftmost_descendant(struct kernfs_node *pos) diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c index 701a56f..6211230 100644 --- a/fs/sysfs/mount.c +++ b/fs/sysfs/mount.c @@ -62,7 +62,7 @@ int __init sysfs_init(void) { int err; - sysfs_root = kernfs_create_root(NULL); + sysfs_root = kernfs_create_root(NULL, NULL); if (IS_ERR(sysfs_root)) return PTR_ERR(sysfs_root); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 321ed84..d2c439d 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -111,12 +111,27 @@ struct kernfs_node { struct kernfs_iattrs *iattr; }; +/* + * kernfs_dir_ops may be specified on kernfs_create_root() to support + * directory manipulation syscalls. These optional callbacks are invoked + * on the matching syscalls and can perform any kernfs operations which + * don't necessarily have to be the exact operation requested. + */ +struct kernfs_dir_ops { + int (*mkdir)(struct kernfs_node *parent, const char *name, + umode_t mode); + int (*rmdir)(struct kernfs_node *kn); + int (*rename)(struct kernfs_node *kn, struct kernfs_node *new_parent, + const char *new_name); +}; + struct kernfs_root { /* published fields */ struct kernfs_node *kn; /* private fields, do not use outside kernfs proper */ struct ida ino_ida; + struct kernfs_dir_ops *dir_ops; }; struct kernfs_open_file { @@ -206,7 +221,8 @@ struct kernfs_node *kernfs_find_and_get_ns(struct kernfs_node *parent, void kernfs_get(struct kernfs_node *kn); void kernfs_put(struct kernfs_node *kn); -struct kernfs_root *kernfs_create_root(void *priv); +struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, + void *priv); void kernfs_destroy_root(struct kernfs_root *root); struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, @@ -255,7 +271,8 @@ kernfs_find_and_get_ns(struct kernfs_node *parent, const char *name, static inline void kernfs_get(struct kernfs_node *kn) { } static inline void kernfs_put(struct kernfs_node *kn) { } -static inline struct kernfs_root *kernfs_create_root(void *priv) +static inline struct kernfs_root * +kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) { return ERR_PTR(-ENOSYS); } static inline void kernfs_destroy_root(struct kernfs_root *root) { } -- cgit v1.1 From ecfbf6fd9c03be7dfe3eafc3846641b9d463607b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 12 Dec 2013 06:11:02 +0100 Subject: Driver core: Fix device_add_attrs() error code path If the addition of dev_attr_online fails, device_add_attrs() should remove device attribute groups as well as type and class attribute groups before returning an error code. Make that happen. Signed-off-by: Rafael J. Wysocki Acked-by: Toshi Kani Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index aab43fb..2b56717 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -491,11 +491,13 @@ static int device_add_attrs(struct device *dev) if (device_supports_offline(dev) && !dev->offline_disabled) { error = device_create_file(dev, &dev_attr_online); if (error) - goto err_remove_type_groups; + goto err_remove_dev_groups; } return 0; + err_remove_dev_groups: + device_remove_groups(dev, dev->groups); err_remove_type_groups: if (type) device_remove_groups(dev, type->groups); -- cgit v1.1 From fbde7c6119f99a0e316ad1a8fe0788bd76d277b1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Sat, 16 Nov 2013 16:15:23 +0800 Subject: devtmpfs: Calling delete_path() only when necessary The deleted variable is always 1 in current code. Initialize deleted variable to be 0, so delete_path() will be called only when necessary. Signed-off-by: Axel Lin Signed-off-by: Greg Kroah-Hartman --- drivers/base/devtmpfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 0f38201..25798db 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -299,7 +299,7 @@ static int handle_remove(const char *nodename, struct device *dev) { struct path parent; struct dentry *dentry; - int deleted = 1; + int deleted = 0; int err; dentry = kern_path_locked(nodename, &parent); -- cgit v1.1 From 880ae359173c229f5d5a44e08b529cffb102bbbc Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 12 Dec 2013 15:27:12 +0100 Subject: Documentation: fix spelling in design-patterns This fixes two spelling mistakes in the design pattern doc. Reported-by: Randy Dunlap Signed-off-by: Linus Walleij Signed-off-by: Greg Kroah-Hartman --- Documentation/driver-model/design-patterns.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/driver-model/design-patterns.txt b/Documentation/driver-model/design-patterns.txt index 9ef8c16..ba7b2df 100644 --- a/Documentation/driver-model/design-patterns.txt +++ b/Documentation/driver-model/design-patterns.txt @@ -66,7 +66,7 @@ your interrupt handler. 2. container_of() ~~~~~~~~~~~~~~~~~ -Continuing on the above example we add a offloaded work: +Continuing on the above example we add an offloaded work: struct foo { spinlock_t lock; @@ -99,7 +99,7 @@ static int foo_probe(...) (...) } -The design pattern is the same for a a hrtimer or something similar that will +The design pattern is the same for an hrtimer or something similar that will return a single argument which is a pointer to a struct member in the callback. -- cgit v1.1 From 41f107266b19d100c1bcef9e1e1aef00692c1209 Mon Sep 17 00:00:00 2001 From: Rashika Kheria Date: Fri, 20 Dec 2013 20:38:23 +0530 Subject: drivers: base: Add prototype declaration to the header file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add prototype declaration of function memory_block_size_bytes() to the header file include/linux/memory.h. This eliminates the following warning in memory.c: drivers/base/memory.c:87:1: warning: no previous prototype for ‘memory_block_size_bytes’ [-Wmissing-prototypes] Signed-off-by: Rashika Kheria Signed-off-by: Greg Kroah-Hartman --- include/linux/memory.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/memory.h b/include/linux/memory.h index 9a6bbf7..bb7384e 100644 --- a/include/linux/memory.h +++ b/include/linux/memory.h @@ -35,6 +35,7 @@ struct memory_block { }; int arch_get_memory_phys_device(unsigned long start_pfn); +unsigned long __weak memory_block_size_bytes(void); /* These states are exposed to userspace as text strings in sysfs */ #define MEM_ONLINE (1<<0) /* exposed to userspace */ -- cgit v1.1 From eb4c69033fd1dda8b60e48af3accdd578c2e59ed Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sat, 4 Jan 2014 19:56:40 -0800 Subject: Revert "kobject: introduce kobj_completion" This reverts commit eee031649707db3c9920d9498f8d03819b74fc23. Jeff writes: I have no objections to reverting it. There were concerns from Al Viro that it'd be tough to get right by callers and I had assumed it got dropped after that. I had planned on using it in my btrfs sysfs exports patchset but came up with a better way. Cc: Jeff Mahoney Cc: Al Viro Signed-off-by: Greg Kroah-Hartman --- include/linux/kobj_completion.h | 18 --------------- lib/kobject.c | 50 ----------------------------------------- 2 files changed, 68 deletions(-) delete mode 100644 include/linux/kobj_completion.h diff --git a/include/linux/kobj_completion.h b/include/linux/kobj_completion.h deleted file mode 100644 index a428f64..0000000 --- a/include/linux/kobj_completion.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _KOBJ_COMPLETION_H_ -#define _KOBJ_COMPLETION_H_ - -#include -#include - -struct kobj_completion { - struct kobject kc_kobj; - struct completion kc_unregister; -}; - -#define kobj_to_kobj_completion(kobj) \ - container_of(kobj, struct kobj_completion, kc_kobj) - -void kobj_completion_init(struct kobj_completion *kc, struct kobj_type *ktype); -void kobj_completion_release(struct kobject *kobj); -void kobj_completion_del_and_wait(struct kobj_completion *kc); -#endif /* _KOBJ_COMPLETION_H_ */ diff --git a/lib/kobject.c b/lib/kobject.c index 064451f..f7f69cb 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -13,7 +13,6 @@ */ #include -#include #include #include #include @@ -782,55 +781,6 @@ const struct sysfs_ops kobj_sysfs_ops = { }; /** - * kobj_completion_init - initialize a kobj_completion object. - * @kc: kobj_completion - * @ktype: type of kobject to initialize - * - * kobj_completion structures can be embedded within structures with different - * lifetime rules. During the release of the enclosing object, we can - * wait on the release of the kobject so that we don't free it while it's - * still busy. - */ -void kobj_completion_init(struct kobj_completion *kc, struct kobj_type *ktype) -{ - init_completion(&kc->kc_unregister); - kobject_init(&kc->kc_kobj, ktype); -} -EXPORT_SYMBOL_GPL(kobj_completion_init); - -/** - * kobj_completion_release - release a kobj_completion object - * @kobj: kobject embedded in kobj_completion - * - * Used with kobject_release to notify waiters that the kobject has been - * released. - */ -void kobj_completion_release(struct kobject *kobj) -{ - struct kobj_completion *kc = kobj_to_kobj_completion(kobj); - complete(&kc->kc_unregister); -} -EXPORT_SYMBOL_GPL(kobj_completion_release); - -/** - * kobj_completion_del_and_wait - release the kobject and wait for it - * @kc: kobj_completion object to release - * - * Delete the kobject from sysfs and drop the reference count. Then wait - * until any other outstanding references are also dropped. This routine - * is only necessary once other references may have been taken on the - * kobject. Typically this happens when the kobject has been published - * to sysfs via kobject_add. - */ -void kobj_completion_del_and_wait(struct kobj_completion *kc) -{ - kobject_del(&kc->kc_kobj); - kobject_put(&kc->kc_kobj); - wait_for_completion(&kc->kc_unregister); -} -EXPORT_SYMBOL_GPL(kobj_completion_del_and_wait); - -/** * kset_register - initialize and add a kset. * @k: kset. */ -- cgit v1.1 From 9705710e40b9e5acaf003f90d6ed883ba193b314 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Sat, 4 Jan 2014 14:20:04 +0100 Subject: kobject: Fix source code comment spelling Signed-off-by: Bart Van Assche Signed-off-by: Greg Kroah-Hartman --- lib/kobject.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/kobject.c b/lib/kobject.c index f7f69cb..b0b2666 100644 --- a/lib/kobject.c +++ b/lib/kobject.c @@ -365,7 +365,7 @@ static int kobject_add_varg(struct kobject *kobj, struct kobject *parent, * * If @parent is set, then the parent of the @kobj will be set to it. * If @parent is NULL, then the parent of the @kobj will be set to the - * kobject associted with the kset assigned to this kobject. If no kset + * kobject associated with the kset assigned to this kobject. If no kset * is assigned to the kobject, then the kobject will be located in the * root of the sysfs tree. * -- cgit v1.1 From 98233b21cd6e6b9bd8ad5d4756bda1c051567468 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Sat, 4 Jan 2014 14:20:36 +0100 Subject: firmware loader: Add sparse annotation Avoid that sparse reports the following warning on __fw_free_buf(): drivers/base/firmware_class.c:230:9: warning: context imbalance in '__fw_free_buf' - unexpected unlock Signed-off-by: Bart Van Assche Acked-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 33b87bf..54592e6 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -228,6 +228,7 @@ static int fw_lookup_and_allocate_buf(const char *fw_name, } static void __fw_free_buf(struct kref *ref) + __releases(&fwc->lock) { struct firmware_buf *buf = to_fwbuf(ref); struct firmware_cache *fwc = buf->fwc; -- cgit v1.1 From 174be70b638ceb1038f466b5ca014f6b1d9c8a59 Mon Sep 17 00:00:00 2001 From: Bart Van Assche Date: Sat, 4 Jan 2014 14:21:42 +0100 Subject: driver-core: Fix use-after-free triggered by bus_unregister() Avoid that bus_unregister() triggers a use-after-free with CONFIG_DEBUG_KOBJECT_RELEASE=y. This patch avoids that the following sequence triggers a kernel crash with memory poisoning enabled: * bus_register() * driver_register() * driver_unregister() * bus_unregister() The above sequence causes the bus private data to be freed from inside the bus_unregister() call although it is not guaranteed in that function that the reference count on the bus private data has dropped to zero. As an example, with CONFIG_DEBUG_KOBJECT_RELEASE=y the ${bus}/drivers kobject is still holding a reference on bus->p->subsys.kobj via its parent pointer at the time the bus private data is freed. Fix this by deferring freeing the bus private data until the last kobject_put() call on bus->p->subsys.kobj. The kernel oops triggered by the above sequence and with memory poisoning enabled and that is fixed by this patch is as follows: general protection fault: 0000 [#1] PREEMPT SMP CPU: 3 PID: 2711 Comm: kworker/3:32 Tainted: G W O 3.13.0-rc4-debug+ #1 Hardware name: Bochs Bochs, BIOS Bochs 01/01/2011 Workqueue: events kobject_delayed_cleanup task: ffff880037f866d0 ti: ffff88003b638000 task.ti: ffff88003b638000 Call Trace: [] ? kobject_get_path+0x25/0x100 [] kobject_uevent_env+0x134/0x600 [] kobject_uevent+0xb/0x10 [] kobject_delayed_cleanup+0xc2/0x1b0 [] process_one_work+0x217/0x700 [] ? process_one_work+0x1ab/0x700 [] worker_thread+0x11b/0x3a0 [] ? process_one_work+0x700/0x700 [] kthread+0xf0/0x110 [] ? insert_kthread_work+0x80/0x80 [] ret_from_fork+0x7c/0xb0 [] ? insert_kthread_work+0x80/0x80 Code: 89 f8 48 89 e5 f6 82 c0 27 63 81 20 74 15 0f 1f 44 00 00 48 83 c0 01 0f b6 10 f6 82 c0 27 63 81 20 75 f0 5d c3 66 0f 1f 44 00 00 <80> 3f 00 55 48 89 e5 74 15 48 89 f8 0f 1f 40 00 48 83 c0 01 80 RIP [] strlen+0x0/0x30 RSP ---[ end trace 210f883ef80376aa ]--- Signed-off-by: Bart Van Assche Acked-by: Ming Lei Signed-off-by: Greg Kroah-Hartman --- drivers/base/bus.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 73f6c29..59dc808 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -146,8 +146,19 @@ void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr) } EXPORT_SYMBOL_GPL(bus_remove_file); +static void bus_release(struct kobject *kobj) +{ + struct subsys_private *priv = + container_of(kobj, typeof(*priv), subsys.kobj); + struct bus_type *bus = priv->bus; + + kfree(priv); + bus->p = NULL; +} + static struct kobj_type bus_ktype = { .sysfs_ops = &bus_sysfs_ops, + .release = bus_release, }; static int bus_uevent_filter(struct kset *kset, struct kobject *kobj) @@ -953,8 +964,6 @@ void bus_unregister(struct bus_type *bus) kset_unregister(bus->p->devices_kset); bus_remove_file(bus, &bus_attr_uevent); kset_unregister(&bus->p->subsys); - kfree(bus->p); - bus->p = NULL; } EXPORT_SYMBOL_GPL(bus_unregister); -- cgit v1.1 From 08da2012e0bb0f3f1422cce3f76c36a90da366b5 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Sat, 28 Dec 2013 16:53:59 +0100 Subject: firmware_class: Fix the file size check We expect to read firmware blobs with a single call to kernel_read(), which returns int. Therefore the size must be within the range of int, not long. Signed-off-by: Ben Hutchings Signed-off-by: Greg Kroah-Hartman --- drivers/base/firmware_class.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 54592e6..8a97ddf 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -280,21 +280,21 @@ module_param_string(path, fw_path_para, sizeof(fw_path_para), 0644); MODULE_PARM_DESC(path, "customized firmware image search path with a higher priority than default path"); /* Don't inline this: 'struct kstat' is biggish */ -static noinline_for_stack long fw_file_size(struct file *file) +static noinline_for_stack int fw_file_size(struct file *file) { struct kstat st; if (vfs_getattr(&file->f_path, &st)) return -1; if (!S_ISREG(st.mode)) return -1; - if (st.size != (long)st.size) + if (st.size != (int)st.size) return -1; return st.size; } static int fw_read_file_contents(struct file *file, struct firmware_buf *fw_buf) { - long size; + int size; char *buf; int rc; -- cgit v1.1 From d92d2e6bd72b653f9811e0c9c46307c743b3fc58 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:18 -0500 Subject: kernfs: fix get_active failure handling in kernfs_seq_*() When kernfs_seq_start() fails to obtain an active reference, it returns ERR_PTR(-ENODEV). kernfs_seq_stop() is then invoked with the error pointer value; however, it still proceeds to invoke kernfs_put_active() on the node leading to unbalanced put. If kernfs_seq_stop() is called even after active ref failure, it should skip invocation of @ops->seq_stop() and put_active. Unfortunately, this is a bit complicated because active ref failure isn't the only thing which may fail with ERR_PTR(-ENODEV). @ops->seq_start/next() may also fail with the error value and kernfs_seq_stop() doesn't have a way to tell apart those failures. Work it around by factoring out the active part of kernfs_seq_stop() into kernfs_seq_stop_active() and invoking it directly if @ops->seq_start/next() fail with ERR_PTR(-ENODEV) and updating kernfs_seq_stop() to skip kernfs_seq_stop_active() on ERR_PTR(-ENODEV). This is a bit nasty but ensures that the active put is skipped iff get_active failed in kernfs_seq_start(). Signed-off-by: Tejun Heo Cc: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/file.c | 51 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 316604c..bdd3885 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -54,6 +54,38 @@ static const struct kernfs_ops *kernfs_ops(struct kernfs_node *kn) return kn->attr.ops; } +/* + * As kernfs_seq_stop() is also called after kernfs_seq_start() or + * kernfs_seq_next() failure, it needs to distinguish whether it's stopping + * a seq_file iteration which is fully initialized with an active reference + * or an aborted kernfs_seq_start() due to get_active failure. The + * position pointer is the only context for each seq_file iteration and + * thus the stop condition should be encoded in it. As the return value is + * directly visible to userland, ERR_PTR(-ENODEV) is the only acceptable + * choice to indicate get_active failure. + * + * Unfortunately, this is complicated due to the optional custom seq_file + * operations which may return ERR_PTR(-ENODEV) too. kernfs_seq_stop() + * can't distinguish whether ERR_PTR(-ENODEV) is from get_active failure or + * custom seq_file operations and thus can't decide whether put_active + * should be performed or not only on ERR_PTR(-ENODEV). + * + * This is worked around by factoring out the custom seq_stop() and + * put_active part into kernfs_seq_stop_active(), skipping it from + * kernfs_seq_stop() if ERR_PTR(-ENODEV) while invoking it directly after + * custom seq_file operations fail with ERR_PTR(-ENODEV) - this ensures + * that kernfs_seq_stop_active() is skipped only after get_active failure. + */ +static void kernfs_seq_stop_active(struct seq_file *sf, void *v) +{ + struct kernfs_open_file *of = sf->private; + const struct kernfs_ops *ops = kernfs_ops(of->kn); + + if (ops->seq_stop) + ops->seq_stop(sf, v); + kernfs_put_active(of->kn); +} + static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) { struct kernfs_open_file *of = sf->private; @@ -69,7 +101,11 @@ static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) ops = kernfs_ops(of->kn); if (ops->seq_start) { - return ops->seq_start(sf, ppos); + void *next = ops->seq_start(sf, ppos); + /* see the comment above kernfs_seq_stop_active() */ + if (next == ERR_PTR(-ENODEV)) + kernfs_seq_stop_active(sf, next); + return next; } else { /* * The same behavior and code as single_open(). Returns @@ -85,7 +121,11 @@ static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) const struct kernfs_ops *ops = kernfs_ops(of->kn); if (ops->seq_next) { - return ops->seq_next(sf, v, ppos); + void *next = ops->seq_next(sf, v, ppos); + /* see the comment above kernfs_seq_stop_active() */ + if (next == ERR_PTR(-ENODEV)) + kernfs_seq_stop_active(sf, next); + return next; } else { /* * The same behavior and code as single_open(), always @@ -99,12 +139,9 @@ static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) static void kernfs_seq_stop(struct seq_file *sf, void *v) { struct kernfs_open_file *of = sf->private; - const struct kernfs_ops *ops = kernfs_ops(of->kn); - if (ops->seq_stop) - ops->seq_stop(sf, v); - - kernfs_put_active(of->kn); + if (v != ERR_PTR(-ENODEV)) + kernfs_seq_stop_active(sf, v); mutex_unlock(&of->mutex); } -- cgit v1.1 From ea1c472dfeada211a0100daa7976e8e8e779b858 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:19 -0500 Subject: kernfs: replace kernfs_node->u.completion with kernfs_root->deactivate_waitq kernfs_node->u.completion is used to notify deactivation completion from kernfs_put_active() to kernfs_deactivate(). We now allow multiple racing removals of the same node and the current removal scheme is no longer correct - kernfs_remove() invocation may return before the node is properly deactivated if it races against another removal. The removal path will be restructured to address the issue. To help such restructure which requires supporting multiple waiters, this patch replaces kernfs_node->u.completion with kernfs_root->deactivate_waitq. This makes deactivation event notifications share a per-root waitqueue_head; however, the wait path is quite cold and this will also allow shaving one pointer off kernfs_node. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 27 +++++++++++---------------- include/linux/kernfs.h | 4 ++-- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 510b506..ed62de6 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -8,6 +8,7 @@ * This file is released under the GPLv2. */ +#include #include #include #include @@ -151,6 +152,7 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn) */ void kernfs_put_active(struct kernfs_node *kn) { + struct kernfs_root *root = kernfs_root(kn); int v; if (unlikely(!kn)) @@ -162,11 +164,7 @@ void kernfs_put_active(struct kernfs_node *kn) if (likely(v != KN_DEACTIVATED_BIAS)) return; - /* - * atomic_dec_return() is a mb(), we'll always see the updated - * kn->u.completion. - */ - complete(kn->u.completion); + wake_up_all(&root->deactivate_waitq); } /** @@ -177,26 +175,22 @@ void kernfs_put_active(struct kernfs_node *kn) */ static void kernfs_deactivate(struct kernfs_node *kn) { - DECLARE_COMPLETION_ONSTACK(wait); - int v; + struct kernfs_root *root = kernfs_root(kn); BUG_ON(!(kn->flags & KERNFS_REMOVED)); if (!(kernfs_type(kn) & KERNFS_ACTIVE_REF)) return; - kn->u.completion = (void *)&wait; - rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); - /* atomic_add_return() is a mb(), put_active() will always see - * the updated kn->u.completion. - */ - v = atomic_add_return(KN_DEACTIVATED_BIAS, &kn->active); - if (v != KN_DEACTIVATED_BIAS) { + atomic_add(KN_DEACTIVATED_BIAS, &kn->active); + + if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) lock_contended(&kn->dep_map, _RET_IP_); - wait_for_completion(&wait); - } + + wait_event(root->deactivate_waitq, + atomic_read(&kn->active) == KN_DEACTIVATED_BIAS); lock_acquired(&kn->dep_map, _RET_IP_); rwsem_release(&kn->dep_map, 1, _RET_IP_); @@ -613,6 +607,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) root->dir_ops = kdops; root->kn = kn; + init_waitqueue_head(&root->deactivate_waitq); return root; } diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index d2c439d..232f1a6 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include struct file; struct iattr; @@ -91,7 +91,6 @@ struct kernfs_node { struct rb_node rb; union { - struct completion *completion; struct kernfs_node *removed_list; } u; @@ -132,6 +131,7 @@ struct kernfs_root { /* private fields, do not use outside kernfs proper */ struct ida ino_ida; struct kernfs_dir_ops *dir_ops; + wait_queue_head_t deactivate_waitq; }; struct kernfs_open_file { -- cgit v1.1 From a69d001cfc712b96ec9d7ba44d6285702a38dabf Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:20 -0500 Subject: kernfs: remove KERNFS_ACTIVE_REF and add kernfs_lockdep() There currently are two mechanisms gating active ref lockdep annotations - KERNFS_LOCKDEP flag and KERNFS_ACTIVE_REF type mask. The former disables lockdep annotations in kernfs_get/put_active() while the latter disables all of kernfs_deactivate(). While KERNFS_ACTIVE_REF also behaves as an optimization to skip the deactivation step for non-file nodes, the benefit is marginal and it needlessly diverges code paths. Let's drop KERNFS_ACTIVE_REF and use KERNFS_LOCKDEP in kernfs_deactivate() too. While at it, add a test helper kernfs_lockdep() to test KERNFS_LOCKDEP flag so that it's more convenient and the related code can be compiled out when not enabled. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 31 ++++++++++++++++++++----------- include/linux/kernfs.h | 1 - 2 files changed, 20 insertions(+), 12 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index ed62de6..1c9130a3 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -22,6 +22,15 @@ DEFINE_MUTEX(kernfs_mutex); #define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb) +static bool kernfs_lockdep(struct kernfs_node *kn) +{ +#ifdef CONFIG_DEBUG_LOCK_ALLOC + return kn->flags & KERNFS_LOCKDEP; +#else + return false; +#endif +} + /** * kernfs_name_hash * @name: Null terminated string to hash @@ -138,7 +147,7 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn) if (!atomic_inc_unless_negative(&kn->active)) return NULL; - if (kn->flags & KERNFS_LOCKDEP) + if (kernfs_lockdep(kn)) rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_); return kn; } @@ -158,7 +167,7 @@ void kernfs_put_active(struct kernfs_node *kn) if (unlikely(!kn)) return; - if (kn->flags & KERNFS_LOCKDEP) + if (kernfs_lockdep(kn)) rwsem_release(&kn->dep_map, 1, _RET_IP_); v = atomic_dec_return(&kn->active); if (likely(v != KN_DEACTIVATED_BIAS)) @@ -179,21 +188,21 @@ static void kernfs_deactivate(struct kernfs_node *kn) BUG_ON(!(kn->flags & KERNFS_REMOVED)); - if (!(kernfs_type(kn) & KERNFS_ACTIVE_REF)) - return; - - rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); - atomic_add(KN_DEACTIVATED_BIAS, &kn->active); - if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) - lock_contended(&kn->dep_map, _RET_IP_); + if (kernfs_lockdep(kn)) { + rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); + if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) + lock_contended(&kn->dep_map, _RET_IP_); + } wait_event(root->deactivate_waitq, atomic_read(&kn->active) == KN_DEACTIVATED_BIAS); - lock_acquired(&kn->dep_map, _RET_IP_); - rwsem_release(&kn->dep_map, 1, _RET_IP_); + if (kernfs_lockdep(kn)) { + lock_acquired(&kn->dep_map, _RET_IP_); + rwsem_release(&kn->dep_map, 1, _RET_IP_); + } } /** diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 232f1a6..42ad32f 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -34,7 +34,6 @@ enum kernfs_node_type { }; #define KERNFS_TYPE_MASK 0x000f -#define KERNFS_ACTIVE_REF KERNFS_FILE #define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK enum kernfs_node_flag { -- cgit v1.1 From ae34372eb8408b3d07e870f1939f99007a730d28 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:21 -0500 Subject: kernfs: remove KERNFS_REMOVED KERNFS_REMOVED is used to mark half-initialized and dying nodes so that they don't show up in lookups and deny adding new nodes under or renaming it; however, its role overlaps those of deactivation and removal from rbtree. It's necessary to deny addition of new children while removal is in progress; however, this role considerably intersects with deactivation - KERNFS_REMOVED prevents new children while deactivation prevents new file operations. There's no reason to have them separate making things more complex than necessary. KERNFS_REMOVED is also used to decide whether a node is still visible to vfs layer, which is rather redundant as equivalent determination can be made by testing whether the node is on its parent's children rbtree or not. This patch removes KERNFS_REMOVED. * Instead of KERNFS_REMOVED, each node now starts its life deactivated. This means that we now use both atomic_add() and atomic_sub() on KN_DEACTIVATED_BIAS, which is INT_MIN. The compiler generates an overflow warnings when negating INT_MIN as the negation can't be represented as a positive number. Nothing is actually broken but let's bump BIAS by one to avoid the warnings for archs which negates the subtrahend.. * KERNFS_REMOVED tests in add and rename paths are replaced with kernfs_get/put_active() of the target nodes. Due to the way the add path is structured now, active ref handling is done in the callers of kernfs_add_one(). This will be consolidated up later. * kernfs_remove_one() is updated to deactivate instead of setting KERNFS_REMOVED. This removes deactivation from kernfs_deactivate(), which is now renamed to kernfs_drain(). * kernfs_dop_revalidate() now tests RB_EMPTY_NODE(&kn->rb) instead of KERNFS_REMOVED and KERNFS_REMOVED test in kernfs_dir_pos() is dropped. A node which is removed from the children rbtree is not included in the iteration in the first place. This means that a node may be visible through vfs a bit longer - it's now also visible after deactivation until the actual removal. This slightly enlarged window difference doesn't make any difference to the userland. * Sanity check on KERNFS_REMOVED in kernfs_put() is replaced with checks on the active ref. * Some comment style updates in the affected area. v2: Reordered before removal path restructuring. kernfs_active() dropped and kernfs_get/put_active() used instead. RB_EMPTY_NODE() used in the lookup paths. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 79 +++++++++++++++++++++++++-------------------- fs/kernfs/file.c | 10 ++++-- fs/kernfs/kernfs-internal.h | 3 +- fs/kernfs/symlink.c | 10 ++++-- include/linux/kernfs.h | 1 - 5 files changed, 60 insertions(+), 43 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 1c9130a3..7f8afc1 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -127,6 +127,7 @@ static void kernfs_unlink_sibling(struct kernfs_node *kn) kn->parent->dir.subdirs--; rb_erase(&kn->rb, &kn->parent->dir.children); + RB_CLEAR_NODE(&kn->rb); } /** @@ -177,18 +178,16 @@ void kernfs_put_active(struct kernfs_node *kn) } /** - * kernfs_deactivate - deactivate kernfs_node - * @kn: kernfs_node to deactivate + * kernfs_drain - drain kernfs_node + * @kn: kernfs_node to drain * - * Deny new active references and drain existing ones. + * Drain existing usages. */ -static void kernfs_deactivate(struct kernfs_node *kn) +static void kernfs_drain(struct kernfs_node *kn) { struct kernfs_root *root = kernfs_root(kn); - BUG_ON(!(kn->flags & KERNFS_REMOVED)); - - atomic_add(KN_DEACTIVATED_BIAS, &kn->active); + WARN_ON_ONCE(atomic_read(&kn->active) >= 0); if (kernfs_lockdep(kn)) { rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); @@ -233,13 +232,15 @@ void kernfs_put(struct kernfs_node *kn) return; root = kernfs_root(kn); repeat: - /* Moving/renaming is always done while holding reference. + /* + * Moving/renaming is always done while holding reference. * kn->parent won't change beneath us. */ parent = kn->parent; - WARN(!(kn->flags & KERNFS_REMOVED), "kernfs: free using entry: %s/%s\n", - parent ? parent->name : "", kn->name); + WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS, + "kernfs_put: %s/%s: released with incorrect active_ref %d\n", + parent ? parent->name : "", kn->name, atomic_read(&kn->active)); if (kernfs_type(kn) == KERNFS_LINK) kernfs_put(kn->symlink.target_kn); @@ -281,8 +282,8 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) kn = dentry->d_fsdata; mutex_lock(&kernfs_mutex); - /* The kernfs node has been deleted */ - if (kn->flags & KERNFS_REMOVED) + /* Force fresh lookup if removed */ + if (kn->parent && RB_EMPTY_NODE(&kn->rb)) goto out_bad; /* The kernfs node has been moved? */ @@ -350,11 +351,12 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, kn->ino = ret; atomic_set(&kn->count, 1); - atomic_set(&kn->active, 0); + atomic_set(&kn->active, KN_DEACTIVATED_BIAS); + RB_CLEAR_NODE(&kn->rb); kn->name = name; kn->mode = mode; - kn->flags = flags | KERNFS_REMOVED; + kn->flags = flags; return kn; @@ -413,6 +415,8 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, struct kernfs_iattrs *ps_iattr; int ret; + WARN_ON_ONCE(atomic_read(&parent->active) < 0); + if (has_ns != (bool)kn->ns) { WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", has_ns ? "required" : "invalid", parent->name, kn->name); @@ -422,9 +426,6 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, if (kernfs_type(parent) != KERNFS_DIR) return -EINVAL; - if (parent->flags & KERNFS_REMOVED) - return -ENOENT; - kn->hash = kernfs_name_hash(kn->name, kn->ns); kn->parent = parent; kernfs_get(parent); @@ -441,8 +442,7 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, } /* Mark the entry added into directory tree */ - kn->flags &= ~KERNFS_REMOVED; - + atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); return 0; } @@ -470,7 +470,7 @@ static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt, * Removal can be called multiple times on the same node. Only the * first invocation is effective and puts the base ref. */ - if (kn->flags & KERNFS_REMOVED) + if (atomic_read(&kn->active) < 0) return; if (kn->parent) { @@ -484,7 +484,7 @@ static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt, } } - kn->flags |= KERNFS_REMOVED; + atomic_add(KN_DEACTIVATED_BIAS, &kn->active); kn->u.removed_list = acxt->removed; acxt->removed = kn; } @@ -512,7 +512,7 @@ void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt) acxt->removed = kn->u.removed_list; - kernfs_deactivate(kn); + kernfs_drain(kn); kernfs_unmap_bin_file(kn); kernfs_put(kn); } @@ -610,7 +610,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) return ERR_PTR(-ENOMEM); } - kn->flags &= ~KERNFS_REMOVED; + atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); kn->priv = priv; kn->dir.root = root; @@ -662,9 +662,13 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, kn->priv = priv; /* link in */ - kernfs_addrm_start(&acxt); - rc = kernfs_add_one(&acxt, kn, parent); - kernfs_addrm_finish(&acxt); + rc = -ENOENT; + if (kernfs_get_active(parent)) { + kernfs_addrm_start(&acxt); + rc = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); + kernfs_put_active(parent); + } if (!rc) return kn; @@ -899,27 +903,29 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, { int error; - mutex_lock(&kernfs_mutex); - error = -ENOENT; - if ((kn->flags | new_parent->flags) & KERNFS_REMOVED) + if (!kernfs_get_active(new_parent)) goto out; + if (!kernfs_get_active(kn)) + goto out_put_new_parent; + + mutex_lock(&kernfs_mutex); error = 0; if ((kn->parent == new_parent) && (kn->ns == new_ns) && (strcmp(kn->name, new_name) == 0)) - goto out; /* nothing to rename */ + goto out_unlock; /* nothing to rename */ error = -EEXIST; if (kernfs_find_ns(new_parent, new_name, new_ns)) - goto out; + goto out_unlock; /* rename kernfs_node */ if (strcmp(kn->name, new_name) != 0) { error = -ENOMEM; new_name = kstrdup(new_name, GFP_KERNEL); if (!new_name) - goto out; + goto out_unlock; if (kn->flags & KERNFS_STATIC_NAME) kn->flags &= ~KERNFS_STATIC_NAME; @@ -941,8 +947,12 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, kernfs_link_sibling(kn); error = 0; - out: +out_unlock: mutex_unlock(&kernfs_mutex); + kernfs_put_active(kn); +out_put_new_parent: + kernfs_put_active(new_parent); +out: return error; } @@ -962,8 +972,7 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns, struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) { if (pos) { - int valid = !(pos->flags & KERNFS_REMOVED) && - pos->parent == parent && hash == pos->hash; + int valid = pos->parent == parent && hash == pos->hash; kernfs_put(pos); if (!valid) pos = NULL; diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index bdd3885..231a171 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -856,9 +856,13 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, if (ops->mmap) kn->flags |= KERNFS_HAS_MMAP; - kernfs_addrm_start(&acxt); - rc = kernfs_add_one(&acxt, kn, parent); - kernfs_addrm_finish(&acxt); + rc = -ENOENT; + if (kernfs_get_active(parent)) { + kernfs_addrm_start(&acxt); + rc = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); + kernfs_put_active(parent); + } if (rc) { kernfs_put(kn); diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index c6ba5bc..57a93f4 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -26,7 +26,8 @@ struct kernfs_iattrs { struct simple_xattrs xattrs; }; -#define KN_DEACTIVATED_BIAS INT_MIN +/* +1 to avoid triggering overflow warning when negating it */ +#define KN_DEACTIVATED_BIAS (INT_MIN + 1) /* KERNFS_TYPE_MASK and types are defined in include/linux/kernfs.h */ diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index a03e260..b2c106c 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -40,9 +40,13 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, kn->symlink.target_kn = target; kernfs_get(target); /* ref owned by symlink */ - kernfs_addrm_start(&acxt); - error = kernfs_add_one(&acxt, kn, parent); - kernfs_addrm_finish(&acxt); + error = -ENOENT; + if (kernfs_get_active(parent)) { + kernfs_addrm_start(&acxt); + error = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); + kernfs_put_active(parent); + } if (!error) return kn; diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 42ad32f..289d4f6 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -37,7 +37,6 @@ enum kernfs_node_type { #define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK enum kernfs_node_flag { - KERNFS_REMOVED = 0x0010, KERNFS_NS = 0x0020, KERNFS_HAS_SEQ_SHOW = 0x0040, KERNFS_HAS_MMAP = 0x0080, -- cgit v1.1 From 45a140e587f3d32d8d424ed940dffb61e1739047 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:22 -0500 Subject: kernfs: restructure removal path to fix possible premature return The recursive nature of kernfs_remove() means that, even if kernfs_remove() is not allowed to be called multiple times on the same node, there may be race conditions between removal of parent and its descendants. While we can claim that kernfs_remove() shouldn't be called on one of the descendants while the removal of an ancestor is in progress, such rule is unnecessarily restrictive and very difficult to enforce. It's better to simply allow invoking kernfs_remove() as the caller sees fit as long as the caller ensures that the node is accessible. The current behavior in such situations is broken. Whoever enters removal path first takes the node off the hierarchy and then deactivates. Following removers either return as soon as it notices that it's not the first one or can't even find the target node as it has already been removed from the hierarchy. In both cases, the following removers may finish prematurely while the nodes which should be removed and drained are still being processed by the first one. This patch restructures so that multiple removers, whether through recursion or direction invocation, always follow the following rules. * When there are multiple concurrent removers, only one puts the base ref. * Regardless of which one puts the base ref, all removers are blocked until the target node is fully deactivated and removed. To achieve the above, removal path now first deactivates the subtree, drains it and then unlinks one-by-one. __kernfs_deactivate() is called directly from __kernfs_removal() and drops and regrabs kernfs_mutex for each descendant to drain active refs. As this means that multiple removers can enter __kernfs_deactivate() for the same node, the function is updated so that it can handle multiple deactivators of the same node - only one actually deactivates but all wait till drain completion. The restructured removal path guarantees that a removed node gets unlinked only after the node is deactivated and drained. Combined with proper multiple deactivator handling, this guarantees that any invocation of kernfs_remove() returns only after the node itself and all its descendants are deactivated, drained and removed. v2: Draining separated into a separate loop (used to be in the same loop as unlink) and done from __kernfs_deactivate(). This is to allow exposing deactivation as a separate interface later. Root node removal was broken in v1 patch. Fixed. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 139 ++++++++++++++++++++++++++++++------------------- include/linux/kernfs.h | 1 + 2 files changed, 87 insertions(+), 53 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 7f8afc1..e565ec0 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -181,14 +181,38 @@ void kernfs_put_active(struct kernfs_node *kn) * kernfs_drain - drain kernfs_node * @kn: kernfs_node to drain * - * Drain existing usages. + * Drain existing usages of @kn. Mutiple removers may invoke this function + * concurrently on @kn and all will return after draining is complete. + * Returns %true if drain is performed and kernfs_mutex was temporarily + * released. %false if @kn was already drained and no operation was + * necessary. + * + * The caller is responsible for ensuring @kn stays pinned while this + * function is in progress even if it gets removed by someone else. */ -static void kernfs_drain(struct kernfs_node *kn) +static bool kernfs_drain(struct kernfs_node *kn) + __releases(&kernfs_mutex) __acquires(&kernfs_mutex) { struct kernfs_root *root = kernfs_root(kn); + lockdep_assert_held(&kernfs_mutex); WARN_ON_ONCE(atomic_read(&kn->active) >= 0); + /* + * We want to go through the active ref lockdep annotation at least + * once for all node removals, but the lockdep annotation can't be + * nested inside kernfs_mutex and deactivation can't make forward + * progress if we keep dropping the mutex. Use JUST_ACTIVATED to + * force the slow path once for each deactivation if lockdep is + * enabled. + */ + if ((!kernfs_lockdep(kn) || !(kn->flags & KERNFS_JUST_DEACTIVATED)) && + atomic_read(&kn->active) == KN_DEACTIVATED_BIAS) + return false; + + kn->flags &= ~KERNFS_JUST_DEACTIVATED; + mutex_unlock(&kernfs_mutex); + if (kernfs_lockdep(kn)) { rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) @@ -202,6 +226,9 @@ static void kernfs_drain(struct kernfs_node *kn) lock_acquired(&kn->dep_map, _RET_IP_); rwsem_release(&kn->dep_map, 1, _RET_IP_); } + + mutex_lock(&kernfs_mutex); + return true; } /** @@ -447,49 +474,6 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, } /** - * kernfs_remove_one - remove kernfs_node from parent - * @acxt: addrm context to use - * @kn: kernfs_node to be removed - * - * Mark @kn removed and drop nlink of parent inode if @kn is a - * directory. @kn is unlinked from the children list. - * - * This function should be called between calls to - * kernfs_addrm_start() and kernfs_addrm_finish() and should be - * passed the same @acxt as passed to kernfs_addrm_start(). - * - * LOCKING: - * Determined by kernfs_addrm_start(). - */ -static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt, - struct kernfs_node *kn) -{ - struct kernfs_iattrs *ps_iattr; - - /* - * Removal can be called multiple times on the same node. Only the - * first invocation is effective and puts the base ref. - */ - if (atomic_read(&kn->active) < 0) - return; - - if (kn->parent) { - kernfs_unlink_sibling(kn); - - /* Update timestamps on the parent */ - ps_iattr = kn->parent->iattr; - if (ps_iattr) { - ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; - ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; - } - } - - atomic_add(KN_DEACTIVATED_BIAS, &kn->active); - kn->u.removed_list = acxt->removed; - acxt->removed = kn; -} - -/** * kernfs_addrm_finish - finish up kernfs_node add/remove * @acxt: addrm context to finish up * @@ -512,7 +496,6 @@ void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt) acxt->removed = kn->u.removed_list; - kernfs_drain(kn); kernfs_unmap_bin_file(kn); kernfs_put(kn); } @@ -822,23 +805,73 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos, return pos->parent; } +static void __kernfs_deactivate(struct kernfs_node *kn) +{ + struct kernfs_node *pos; + + lockdep_assert_held(&kernfs_mutex); + + /* prevent any new usage under @kn by deactivating all nodes */ + pos = NULL; + while ((pos = kernfs_next_descendant_post(pos, kn))) { + if (atomic_read(&pos->active) >= 0) { + atomic_add(KN_DEACTIVATED_BIAS, &pos->active); + pos->flags |= KERNFS_JUST_DEACTIVATED; + } + } + + /* + * Drain the subtree. If kernfs_drain() blocked to drain, which is + * indicated by %true return, it temporarily released kernfs_mutex + * and the rbtree might have been modified inbetween breaking our + * future walk. Restart the walk after each %true return. + */ + pos = NULL; + while ((pos = kernfs_next_descendant_post(pos, kn))) { + bool drained; + + kernfs_get(pos); + drained = kernfs_drain(pos); + kernfs_put(pos); + if (drained) + pos = NULL; + } +} + static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn) { - struct kernfs_node *pos, *next; + struct kernfs_node *pos; + + lockdep_assert_held(&kernfs_mutex); if (!kn) return; pr_debug("kernfs %s: removing\n", kn->name); - next = NULL; + __kernfs_deactivate(kn); + + /* unlink the subtree node-by-node */ do { - pos = next; - next = kernfs_next_descendant_post(pos, kn); - if (pos) - kernfs_remove_one(acxt, pos); - } while (next); + struct kernfs_iattrs *ps_iattr; + + pos = kernfs_leftmost_descendant(kn); + + if (pos->parent) { + kernfs_unlink_sibling(pos); + + /* update timestamps on the parent */ + ps_iattr = pos->parent->iattr; + if (ps_iattr) { + ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; + ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; + } + } + + pos->u.removed_list = acxt->removed; + acxt->removed = pos; + } while (pos != kn); } /** diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 289d4f6..61bd7ae 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -37,6 +37,7 @@ enum kernfs_node_type { #define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK enum kernfs_node_flag { + KERNFS_JUST_DEACTIVATED = 0x0010, /* used to aid lockdep annotation */ KERNFS_NS = 0x0020, KERNFS_HAS_SEQ_SHOW = 0x0040, KERNFS_HAS_MMAP = 0x0080, -- cgit v1.1 From f601f9a2bf7dc1f7ee18feece4c4e2fc6845d6c4 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:23 -0500 Subject: kernfs: invoke kernfs_unmap_bin_file() directly from __kernfs_remove() kernfs_unmap_bin_file() is supposed to unmap all memory mappings of the target file before kernfs_remove() finishes; however, it currently is being called from kernfs_addrm_finish() and has the same race problem as the original implementation of deactivation when there are multiple removers - only the remover which snatches the node to its addrm_cxt->removed list is guaranteed to wait for its completion before returning. It can be fixed by moving kernfs_unmap_bin_file() invocation from kernfs_addrm_finish() to __kernfs_remove(). The function may be called multiple times but that shouldn't do any harm. We end up dropping kernfs_mutex in the removal loop and the node may be removed inbetween by someone else. kernfs_unlink_sibling() is updated to test whether the node has already been removed and return accordingly. __kernfs_remove() in turn performs post-unlinking cleanup only if it actually unlinked the node. KERNFS_HAS_MMAP test is moved out of the unmap function into __kernfs_remove() so that we don't unlock kernfs_mutex unnecessarily. While at it, drop the now meaningless "bin" qualifier from the function name. v2: Rewritten to fit the v2 restructuring of removal path. HAS_MMAP test relocated. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 42 +++++++++++++++++++++++++++++++++--------- fs/kernfs/file.c | 5 +---- fs/kernfs/kernfs-internal.h | 2 +- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index e565ec0..f878e4f 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -121,13 +121,17 @@ static int kernfs_link_sibling(struct kernfs_node *kn) * Locking: * mutex_lock(kernfs_mutex) */ -static void kernfs_unlink_sibling(struct kernfs_node *kn) +static bool kernfs_unlink_sibling(struct kernfs_node *kn) { + if (RB_EMPTY_NODE(&kn->rb)) + return false; + if (kernfs_type(kn) == KERNFS_DIR) kn->parent->dir.subdirs--; rb_erase(&kn->rb, &kn->parent->dir.children); RB_CLEAR_NODE(&kn->rb); + return true; } /** @@ -496,7 +500,6 @@ void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt) acxt->removed = kn->u.removed_list; - kernfs_unmap_bin_file(kn); kernfs_put(kn); } } @@ -854,23 +857,44 @@ static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, /* unlink the subtree node-by-node */ do { - struct kernfs_iattrs *ps_iattr; - pos = kernfs_leftmost_descendant(kn); - if (pos->parent) { - kernfs_unlink_sibling(pos); + /* + * We're gonna release kernfs_mutex to unmap bin files, + * Make sure @pos doesn't go away inbetween. + */ + kernfs_get(pos); + + /* + * This must be come before unlinking; otherwise, when + * there are multiple removers, some may finish before + * unmapping is complete. + */ + if (pos->flags & KERNFS_HAS_MMAP) { + mutex_unlock(&kernfs_mutex); + kernfs_unmap_file(pos); + mutex_lock(&kernfs_mutex); + } + + /* + * kernfs_unlink_sibling() succeeds once per node. Use it + * to decide who's responsible for cleanups. + */ + if (!pos->parent || kernfs_unlink_sibling(pos)) { + struct kernfs_iattrs *ps_iattr = + pos->parent ? pos->parent->iattr : NULL; /* update timestamps on the parent */ - ps_iattr = pos->parent->iattr; if (ps_iattr) { ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; } + + pos->u.removed_list = acxt->removed; + acxt->removed = pos; } - pos->u.removed_list = acxt->removed; - acxt->removed = pos; + kernfs_put(pos); } while (pos != kn); } diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 231a171..404ffd2 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -700,14 +700,11 @@ static int kernfs_fop_release(struct inode *inode, struct file *filp) return 0; } -void kernfs_unmap_bin_file(struct kernfs_node *kn) +void kernfs_unmap_file(struct kernfs_node *kn) { struct kernfs_open_node *on; struct kernfs_open_file *of; - if (!(kn->flags & KERNFS_HAS_MMAP)) - return; - spin_lock_irq(&kernfs_open_node_lock); on = kn->attr.open; if (on) diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 57a93f4..e9ec38c 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -113,7 +113,7 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, */ extern const struct file_operations kernfs_file_fops; -void kernfs_unmap_bin_file(struct kernfs_node *kn); +void kernfs_unmap_file(struct kernfs_node *kn); /* * symlink.c -- cgit v1.1 From 99177a34110889a8f2c36420c34e3bcc9bfd8a70 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:24 -0500 Subject: kernfs: remove kernfs_addrm_cxt kernfs_addrm_cxt and the accompanying kernfs_addrm_start/finish() were added because there were operations which should be performed outside kernfs_mutex after adding and removing kernfs_nodes. The necessary operations were recorded in kernfs_addrm_cxt and performed by kernfs_addrm_finish(); however, after the recent changes which relocated deactivation and unmapping so that they're performed directly during removal, the only operation kernfs_addrm_finish() performs is kernfs_put(), which can be moved inside the removal path too. This patch moves the kernfs_put() of the base ref to __kernfs_remove() and remove kernfs_addrm_cxt and kernfs_addrm_start/finish(). * kernfs_add_one() is updated to grab and release the parent's active ref and kernfs_mutex itself. kernfs_get/put_active() and kernfs_addrm_start/finish() invocations around it are removed from all users. * __kernfs_remove() puts an unlinked node directly instead of chaining it to kernfs_addrm_cxt. Its callers are updated to grab and release kernfs_mutex instead of calling kernfs_addrm_start/finish() around it. v2: Updated to fit the v2 restructuring of removal path. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 114 ++++++++++---------------------------------- fs/kernfs/file.c | 10 +--- fs/kernfs/kernfs-internal.h | 12 +---- fs/kernfs/symlink.c | 10 +--- include/linux/kernfs.h | 4 -- 5 files changed, 29 insertions(+), 121 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index f878e4f..770d687 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -399,28 +399,7 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, } /** - * kernfs_addrm_start - prepare for kernfs_node add/remove - * @acxt: pointer to kernfs_addrm_cxt to be used - * - * This function is called when the caller is about to add or remove - * kernfs_node. This function acquires kernfs_mutex. @acxt is used - * to keep and pass context to other addrm functions. - * - * LOCKING: - * Kernel thread context (may sleep). kernfs_mutex is locked on - * return. - */ -void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt) - __acquires(kernfs_mutex) -{ - memset(acxt, 0, sizeof(*acxt)); - - mutex_lock(&kernfs_mutex); -} - -/** * kernfs_add_one - add kernfs_node to parent without warning - * @acxt: addrm context to use * @kn: kernfs_node to be added * @parent: the parent kernfs_node to add @kn to * @@ -428,34 +407,29 @@ void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt) * parent inode if @kn is a directory and link into the children list * of the parent. * - * This function should be called between calls to - * kernfs_addrm_start() and kernfs_addrm_finish() and should be passed - * the same @acxt as passed to kernfs_addrm_start(). - * - * LOCKING: - * Determined by kernfs_addrm_start(). - * * RETURNS: * 0 on success, -EEXIST if entry with the given name already * exists. */ -int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, - struct kernfs_node *parent) +int kernfs_add_one(struct kernfs_node *kn, struct kernfs_node *parent) { - bool has_ns = kernfs_ns_enabled(parent); struct kernfs_iattrs *ps_iattr; + bool has_ns; int ret; - WARN_ON_ONCE(atomic_read(&parent->active) < 0); + if (!kernfs_get_active(parent)) + return -ENOENT; - if (has_ns != (bool)kn->ns) { - WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", - has_ns ? "required" : "invalid", parent->name, kn->name); - return -EINVAL; - } + mutex_lock(&kernfs_mutex); + + ret = -EINVAL; + has_ns = kernfs_ns_enabled(parent); + if (WARN(has_ns != (bool)kn->ns, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", + has_ns ? "required" : "invalid", parent->name, kn->name)) + goto out_unlock; if (kernfs_type(parent) != KERNFS_DIR) - return -EINVAL; + goto out_unlock; kn->hash = kernfs_name_hash(kn->name, kn->ns); kn->parent = parent; @@ -463,7 +437,7 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, ret = kernfs_link_sibling(kn); if (ret) - return ret; + goto out_unlock; /* Update timestamps on the parent */ ps_iattr = parent->iattr; @@ -474,34 +448,11 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, /* Mark the entry added into directory tree */ atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); - return 0; -} - -/** - * kernfs_addrm_finish - finish up kernfs_node add/remove - * @acxt: addrm context to finish up - * - * Finish up kernfs_node add/remove. Resources acquired by - * kernfs_addrm_start() are released and removed kernfs_nodes are - * cleaned up. - * - * LOCKING: - * kernfs_mutex is released. - */ -void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt) - __releases(kernfs_mutex) -{ - /* release resources acquired by kernfs_addrm_start() */ + ret = 0; +out_unlock: mutex_unlock(&kernfs_mutex); - - /* kill removed kernfs_nodes */ - while (acxt->removed) { - struct kernfs_node *kn = acxt->removed; - - acxt->removed = kn->u.removed_list; - - kernfs_put(kn); - } + kernfs_put_active(parent); + return ret; } /** @@ -633,7 +584,6 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, umode_t mode, void *priv, const void *ns) { - struct kernfs_addrm_cxt acxt; struct kernfs_node *kn; int rc; @@ -648,14 +598,7 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, kn->priv = priv; /* link in */ - rc = -ENOENT; - if (kernfs_get_active(parent)) { - kernfs_addrm_start(&acxt); - rc = kernfs_add_one(&acxt, kn, parent); - kernfs_addrm_finish(&acxt); - kernfs_put_active(parent); - } - + rc = kernfs_add_one(kn, parent); if (!rc) return kn; @@ -841,8 +784,7 @@ static void __kernfs_deactivate(struct kernfs_node *kn) } } -static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, - struct kernfs_node *kn) +static void __kernfs_remove(struct kernfs_node *kn) { struct kernfs_node *pos; @@ -890,8 +832,7 @@ static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; } - pos->u.removed_list = acxt->removed; - acxt->removed = pos; + kernfs_put(pos); } kernfs_put(pos); @@ -906,11 +847,9 @@ static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, */ void kernfs_remove(struct kernfs_node *kn) { - struct kernfs_addrm_cxt acxt; - - kernfs_addrm_start(&acxt); - __kernfs_remove(&acxt, kn); - kernfs_addrm_finish(&acxt); + mutex_lock(&kernfs_mutex); + __kernfs_remove(kn); + mutex_unlock(&kernfs_mutex); } /** @@ -925,7 +864,6 @@ void kernfs_remove(struct kernfs_node *kn) int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, const void *ns) { - struct kernfs_addrm_cxt acxt; struct kernfs_node *kn; if (!parent) { @@ -934,13 +872,13 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, return -ENOENT; } - kernfs_addrm_start(&acxt); + mutex_lock(&kernfs_mutex); kn = kernfs_find_ns(parent, name, ns); if (kn) - __kernfs_remove(&acxt, kn); + __kernfs_remove(kn); - kernfs_addrm_finish(&acxt); + mutex_unlock(&kernfs_mutex); if (kn) return 0; diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 404ffd2..ffe1beb 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -817,7 +817,6 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, bool name_is_static, struct lock_class_key *key) { - struct kernfs_addrm_cxt acxt; struct kernfs_node *kn; unsigned flags; int rc; @@ -853,14 +852,7 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, if (ops->mmap) kn->flags |= KERNFS_HAS_MMAP; - rc = -ENOENT; - if (kernfs_get_active(parent)) { - kernfs_addrm_start(&acxt); - rc = kernfs_add_one(&acxt, kn, parent); - kernfs_addrm_finish(&acxt); - kernfs_put_active(parent); - } - + rc = kernfs_add_one(kn, parent); if (rc) { kernfs_put(kn); return ERR_PTR(rc); diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index e9ec38c..4bc5784 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -46,13 +46,6 @@ static inline struct kernfs_root *kernfs_root(struct kernfs_node *kn) } /* - * Context structure to be used while adding/removing nodes. - */ -struct kernfs_addrm_cxt { - struct kernfs_node *removed; -}; - -/* * mount.c */ struct kernfs_super_info { @@ -101,10 +94,7 @@ extern const struct inode_operations kernfs_dir_iops; struct kernfs_node *kernfs_get_active(struct kernfs_node *kn); void kernfs_put_active(struct kernfs_node *kn); -void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt); -int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, - struct kernfs_node *parent); -void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt); +int kernfs_add_one(struct kernfs_node *kn, struct kernfs_node *parent); struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, umode_t mode, unsigned flags); diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index b2c106c..3a939c263 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -27,7 +27,6 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, struct kernfs_node *target) { struct kernfs_node *kn; - struct kernfs_addrm_cxt acxt; int error; kn = kernfs_new_node(kernfs_root(parent), name, S_IFLNK|S_IRWXUGO, @@ -40,14 +39,7 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, kn->symlink.target_kn = target; kernfs_get(target); /* ref owned by symlink */ - error = -ENOENT; - if (kernfs_get_active(parent)) { - kernfs_addrm_start(&acxt); - error = kernfs_add_one(&acxt, kn, parent); - kernfs_addrm_finish(&acxt); - kernfs_put_active(parent); - } - + error = kernfs_add_one(kn, parent); if (!error) return kn; diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 61bd7ae..9b5a4bb 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -89,10 +89,6 @@ struct kernfs_node { struct rb_node rb; - union { - struct kernfs_node *removed_list; - } u; - const void *ns; /* namespace tag */ unsigned int hash; /* ns + name hash */ union { -- cgit v1.1 From 895a068a524e134900b9d98b519309b7aae7bbb1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:25 -0500 Subject: kernfs: make kernfs_get_active() block if the node is deactivated but not removed Currently, kernfs_get_active() fails if the target node is deactivated. This is fine as a node always gets removed after deactivation; however, we're gonna add reactivation so the assumption won't hold. It'd be incorrect for kernfs_get_active() to fail for a node which was deactivated only temporarily. This patch makes kernfs_get_active() block if the node is deactivated but not removed. If the node gets reactivated (not yet implemented), it will be retried and succeed. If the node gets removed, it will be woken up and fail. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 770d687..37dd640 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -149,12 +149,25 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn) if (unlikely(!kn)) return NULL; - if (!atomic_inc_unless_negative(&kn->active)) - return NULL; - if (kernfs_lockdep(kn)) rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_); - return kn; + + /* + * Try to obtain an active ref. If @kn is deactivated, we block + * till either it's reactivated or killed. + */ + do { + if (atomic_inc_unless_negative(&kn->active)) + return kn; + + wait_event(kernfs_root(kn)->deactivate_waitq, + atomic_read(&kn->active) >= 0 || + RB_EMPTY_NODE(&kn->rb)); + } while (!RB_EMPTY_NODE(&kn->rb)); + + if (kernfs_lockdep(kn)) + rwsem_release(&kn->dep_map, 1, _RET_IP_); + return NULL; } /** @@ -786,6 +799,7 @@ static void __kernfs_deactivate(struct kernfs_node *kn) static void __kernfs_remove(struct kernfs_node *kn) { + struct kernfs_root *root = kernfs_root(kn); struct kernfs_node *pos; lockdep_assert_held(&kernfs_mutex); @@ -837,6 +851,9 @@ static void __kernfs_remove(struct kernfs_node *kn) kernfs_put(pos); } while (pos != kn); + + /* some nodes killed, kick get_active waiters */ + wake_up_all(&root->deactivate_waitq); } /** -- cgit v1.1 From 9f010c2ad5194a4b682e747984477850fabd03be Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:26 -0500 Subject: kernfs: implement kernfs_{de|re}activate[_self]() This patch implements four functions to manipulate deactivation state - deactivate, reactivate and the _self suffixed pair. A new fields kernfs_node->deact_depth is added so that concurrent and nested deactivations are handled properly. kernfs_node->hash is moved so that it's paired with the new field so that it doesn't increase the size of kernfs_node. A kernfs user's lock would normally nest inside active ref but during removal the user may want to perform kernfs_remove() while holding the said lock, which would introduce a reverse locking dependency. This function can be used to break such reverse dependency by allowing deactivation step to performed separately outside user's critical section. This will also be used implement kernfs_remove_self(). Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++- include/linux/kernfs.h | 7 ++- 2 files changed, 123 insertions(+), 2 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 37dd640..1aeb579 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -396,6 +396,7 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, atomic_set(&kn->count, 1); atomic_set(&kn->active, KN_DEACTIVATED_BIAS); + kn->deact_depth = 1; RB_CLEAR_NODE(&kn->rb); kn->name = name; @@ -461,6 +462,7 @@ int kernfs_add_one(struct kernfs_node *kn, struct kernfs_node *parent) /* Mark the entry added into directory tree */ atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); + kn->deact_depth--; ret = 0; out_unlock: mutex_unlock(&kernfs_mutex); @@ -561,6 +563,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) } atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); + kn->deact_depth--; kn->priv = priv; kn->dir.root = root; @@ -773,7 +776,8 @@ static void __kernfs_deactivate(struct kernfs_node *kn) /* prevent any new usage under @kn by deactivating all nodes */ pos = NULL; while ((pos = kernfs_next_descendant_post(pos, kn))) { - if (atomic_read(&pos->active) >= 0) { + if (!pos->deact_depth++) { + WARN_ON_ONCE(atomic_read(&pos->active) < 0); atomic_add(KN_DEACTIVATED_BIAS, &pos->active); pos->flags |= KERNFS_JUST_DEACTIVATED; } @@ -797,6 +801,118 @@ static void __kernfs_deactivate(struct kernfs_node *kn) } } +static void __kernfs_reactivate(struct kernfs_node *kn) +{ + struct kernfs_node *pos; + + lockdep_assert_held(&kernfs_mutex); + + pos = NULL; + while ((pos = kernfs_next_descendant_post(pos, kn))) { + if (!--pos->deact_depth) { + WARN_ON_ONCE(atomic_read(&pos->active) >= 0); + atomic_sub(KN_DEACTIVATED_BIAS, &pos->active); + } + WARN_ON_ONCE(pos->deact_depth < 0); + } + + /* some nodes reactivated, kick get_active waiters */ + wake_up_all(&kernfs_root(kn)->deactivate_waitq); +} + +static void __kernfs_deactivate_self(struct kernfs_node *kn) +{ + /* + * Take out ourself out of the active ref dependency chain and + * deactivate. If we're called without an active ref, lockdep will + * complain. + */ + kernfs_put_active(kn); + __kernfs_deactivate(kn); +} + +static void __kernfs_reactivate_self(struct kernfs_node *kn) +{ + __kernfs_reactivate(kn); + /* + * Restore active ref dropped by deactivate_self() so that it's + * balanced on return. put_active() will soon be called on @kn, so + * this can't break anything regardless of @kn's state. + */ + atomic_inc(&kn->active); + if (kernfs_lockdep(kn)) + rwsem_acquire(&kn->dep_map, 0, 1, _RET_IP_); +} + +/** + * kernfs_deactivate - deactivate subtree of a node + * @kn: kernfs_node to deactivate subtree of + * + * Deactivate the subtree of @kn. On return, there's no active operation + * going on under @kn and creation or renaming of a node under @kn is + * blocked until @kn is reactivated or removed. This function can be + * called multiple times and nests properly. Each invocation should be + * paired with kernfs_reactivate(). + * + * For a kernfs user which uses simple locking, the subsystem lock would + * nest inside active reference. This becomes problematic if the user + * tries to remove nodes while holding the subystem lock as it would create + * a reverse locking dependency from the subsystem lock to active ref. + * This function can be used to break such reverse dependency. The user + * can call this function outside the subsystem lock and then proceed to + * invoke kernfs_remove() while holding the subsystem lock without + * introducing such reverse dependency. + */ +void kernfs_deactivate(struct kernfs_node *kn) +{ + mutex_lock(&kernfs_mutex); + __kernfs_deactivate(kn); + mutex_unlock(&kernfs_mutex); +} + +/** + * kernfs_reactivate - reactivate subtree of a node + * @kn: kernfs_node to reactivate subtree of + * + * Undo kernfs_deactivate(). + */ +void kernfs_reactivate(struct kernfs_node *kn) +{ + mutex_lock(&kernfs_mutex); + __kernfs_reactivate(kn); + mutex_unlock(&kernfs_mutex); +} + +/** + * kernfs_deactivate_self - deactivate subtree of a node from its own method + * @kn: the self kernfs_node to deactivate subtree of + * + * The caller must be running off of a kernfs operation which is invoked + * with an active reference - e.g. one of kernfs_ops. Once this function + * is called, @kn may be removed by someone else while the enclosing method + * is in progress. Other than that, this function is equivalent to + * kernfs_deactivate() and should be paired with kernfs_reactivate_self(). + */ +void kernfs_deactivate_self(struct kernfs_node *kn) +{ + mutex_lock(&kernfs_mutex); + __kernfs_deactivate_self(kn); + mutex_unlock(&kernfs_mutex); +} + +/** + * kernfs_reactivate_self - reactivate subtree of a node from its own method + * @kn: the self kernfs_node to reactivate subtree of + * + * Undo kernfs_deactivate_self(). + */ +void kernfs_reactivate_self(struct kernfs_node *kn) +{ + mutex_lock(&kernfs_mutex); + __kernfs_reactivate_self(kn); + mutex_unlock(&kernfs_mutex); +} + static void __kernfs_remove(struct kernfs_node *kn) { struct kernfs_root *root = kernfs_root(kn); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 9b5a4bb..ac86930 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -80,6 +80,8 @@ struct kernfs_elem_attr { struct kernfs_node { atomic_t count; atomic_t active; + int deact_depth; + unsigned int hash; /* ns + name hash */ #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif @@ -90,7 +92,6 @@ struct kernfs_node { struct rb_node rb; const void *ns; /* namespace tag */ - unsigned int hash; /* ns + name hash */ union { struct kernfs_elem_dir dir; struct kernfs_elem_symlink symlink; @@ -233,6 +234,10 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, const char *name, struct kernfs_node *target); +void kernfs_deactivate(struct kernfs_node *kn); +void kernfs_reactivate(struct kernfs_node *kn); +void kernfs_deactivate_self(struct kernfs_node *kn); +void kernfs_reactivate_self(struct kernfs_node *kn); void kernfs_remove(struct kernfs_node *kn); int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, const void *ns); -- cgit v1.1 From 1ae06819c77cff1ea2833c94f8c093fe8a5c79db Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:27 -0500 Subject: kernfs, sysfs, driver-core: implement kernfs_remove_self() and its wrappers Sometimes it's necessary to implement a node which wants to delete nodes including itself. This isn't straightforward because of kernfs active reference. While a file operation is in progress, an active reference is held and kernfs_remove() waits for all such references to drain before completing. For a self-deleting node, this is a deadlock as kernfs_remove() ends up waiting for an active reference that itself is sitting on top of. This currently is worked around in the sysfs layer using sysfs_schedule_callback() which makes such removals asynchronous. While it works, it's rather cumbersome and inherently breaks synchronicity of the operation - the file operation which triggered the operation may complete before the removal is finished (or even started) and the removal may fail asynchronously. If a removal operation is immmediately followed by another operation which expects the specific name to be available (e.g. removal followed by rename onto the same name), there's no way to make the latter operation reliable. The thing is there's no inherent reason for this to be asynchrnous. All that's necessary to do this synchronous is a dedicated operation which drops its own active ref and deactivates self. This patch implements kernfs_remove_self() and its wrappers in sysfs and driver core. kernfs_remove_self() is to be called from one of the file operations, drops the active ref and deactivates using __kernfs_deactivate_self(), removes the self node, and restores active ref to the dead node using __kernfs_reactivate_self() so that the ref is balanced afterwards. __kernfs_remove() is updated so that it takes an early exit if the target node is already fully removed so that the active ref restored by kernfs_remove_self() after removal doesn't confuse the deactivation path. This makes implementing self-deleting nodes very easy. The normal removal path doesn't even need to be changed to use kernfs_remove_self() for the self-deleting node. The method can invoke kernfs_remove_self() on itself before proceeding the normal removal path. kernfs_remove() invoked on the node by the normal deletion path will simply be ignored. This will replace sysfs_schedule_callback(). A subtle feature of sysfs_schedule_callback() is that it collapses multiple invocations - even if multiple removals are triggered, the removal callback is run only once. An equivalent effect can be achieved by testing the return value of kernfs_remove_self() - only the one which gets %true return value should proceed with actual deletion. All other instances of kernfs_remove_self() will wait till the enclosing kernfs operation which invoked the winning instance of kernfs_remove_self() finishes and then return %false. This trivially makes all users of kernfs_remove_self() automatically show correct synchronous behavior even when there are multiple concurrent operations - all "echo 1 > delete" instances will finish only after the whole operation is completed by one of the instances. v2: For !CONFIG_SYSFS, dummy version kernfs_remove_self() was missing and sysfs_remove_file_self() had incorrect return type. Fix it. Reported by kbuild test bot. v3: Updated to use __kernfs_{de|re}activate_self(). Signed-off-by: Tejun Heo Cc: Alan Stern Cc: kbuild test robot Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 17 ++++++++++++ fs/kernfs/dir.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ fs/sysfs/file.c | 23 ++++++++++++++++ include/linux/device.h | 2 ++ include/linux/kernfs.h | 6 +++++ include/linux/sysfs.h | 7 +++++ 6 files changed, 127 insertions(+) diff --git a/drivers/base/core.c b/drivers/base/core.c index 2b56717..9db57af 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -571,6 +571,23 @@ void device_remove_file(struct device *dev, EXPORT_SYMBOL_GPL(device_remove_file); /** + * device_remove_file_self - remove sysfs attribute file from its own method. + * @dev: device. + * @attr: device attribute descriptor. + * + * See kernfs_remove_self() for details. + */ +bool device_remove_file_self(struct device *dev, + const struct device_attribute *attr) +{ + if (dev) + return sysfs_remove_file_self(&dev->kobj, &attr->attr); + else + return false; +} +EXPORT_SYMBOL_GPL(device_remove_file_self); + +/** * device_create_bin_file - create sysfs binary attribute file for device. * @dev: device. * @attr: device binary attribute descriptor. diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 1aeb579..a8028be 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -986,6 +986,78 @@ void kernfs_remove(struct kernfs_node *kn) } /** + * kernfs_remove_self - remove a kernfs_node from its own method + * @kn: the self kernfs_node to remove + * + * The caller must be running off of a kernfs operation which is invoked + * with an active reference - e.g. one of kernfs_ops. This can be used to + * implement a file operation which deletes itself. + * + * For example, the "delete" file for a sysfs device directory can be + * implemented by invoking kernfs_remove_self() on the "delete" file + * itself. This function breaks the circular dependency of trying to + * deactivate self while holding an active ref itself. It isn't necessary + * to modify the usual removal path to use kernfs_remove_self(). The + * "delete" implementation can simply invoke kernfs_remove_self() on self + * before proceeding with the usual removal path. kernfs will ignore later + * kernfs_remove() on self. + * + * kernfs_remove_self() can be called multiple times concurrently on the + * same kernfs_node. Only the first one actually performs removal and + * returns %true. All others will wait until the kernfs operation which + * won self-removal finishes and return %false. Note that the losers wait + * for the completion of not only the winning kernfs_remove_self() but also + * the whole kernfs_ops which won the arbitration. This can be used to + * guarantee, for example, all concurrent writes to a "delete" file to + * finish only after the whole operation is complete. + */ +bool kernfs_remove_self(struct kernfs_node *kn) +{ + bool ret; + + mutex_lock(&kernfs_mutex); + __kernfs_deactivate_self(kn); + + /* + * SUICIDAL is used to arbitrate among competing invocations. Only + * the first one will actually perform removal. When the removal + * is complete, SUICIDED is set and the active ref is restored + * while holding kernfs_mutex. The ones which lost arbitration + * waits for SUICDED && drained which can happen only after the + * enclosing kernfs operation which executed the winning instance + * of kernfs_remove_self() finished. + */ + if (!(kn->flags & KERNFS_SUICIDAL)) { + kn->flags |= KERNFS_SUICIDAL; + __kernfs_remove(kn); + kn->flags |= KERNFS_SUICIDED; + ret = true; + } else { + wait_queue_head_t *waitq = &kernfs_root(kn)->deactivate_waitq; + DEFINE_WAIT(wait); + + while (true) { + prepare_to_wait(waitq, &wait, TASK_UNINTERRUPTIBLE); + + if ((kn->flags & KERNFS_SUICIDED) && + atomic_read(&kn->active) == KN_DEACTIVATED_BIAS) + break; + + mutex_unlock(&kernfs_mutex); + schedule(); + mutex_lock(&kernfs_mutex); + } + finish_wait(waitq, &wait); + WARN_ON_ONCE(!RB_EMPTY_NODE(&kn->rb)); + ret = false; + } + + __kernfs_reactivate_self(kn); + mutex_unlock(&kernfs_mutex); + return ret; +} + +/** * kernfs_remove_by_name_ns - find a kernfs_node by name and remove it * @parent: parent of the target * @name: name of the kernfs_node to remove diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 810cf6e..1b8b91b 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -372,6 +372,29 @@ void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, } EXPORT_SYMBOL_GPL(sysfs_remove_file_ns); +/** + * sysfs_remove_file_self - remove an object attribute from its own method + * @kobj: object we're acting for + * @attr: attribute descriptor + * + * See kernfs_remove_self() for details. + */ +bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr) +{ + struct kernfs_node *parent = kobj->sd; + struct kernfs_node *kn; + bool ret; + + kn = kernfs_find_and_get(parent, attr->name); + if (WARN_ON_ONCE(!kn)) + return false; + + ret = kernfs_remove_self(kn); + + kernfs_put(kn); + return ret; +} + void sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr) { int i; diff --git a/include/linux/device.h b/include/linux/device.h index 952b010..1ff3f16 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -560,6 +560,8 @@ extern int device_create_file(struct device *device, const struct device_attribute *entry); extern void device_remove_file(struct device *dev, const struct device_attribute *attr); +extern bool device_remove_file_self(struct device *dev, + const struct device_attribute *attr); extern int __must_check device_create_bin_file(struct device *dev, const struct bin_attribute *attr); extern void device_remove_bin_file(struct device *dev, diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index ac86930..0b7b7cc 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -43,6 +43,8 @@ enum kernfs_node_flag { KERNFS_HAS_MMAP = 0x0080, KERNFS_LOCKDEP = 0x0100, KERNFS_STATIC_NAME = 0x0200, + KERNFS_SUICIDAL = 0x0400, + KERNFS_SUICIDED = 0x0800, }; /* type-specific structures for kernfs_node union members */ @@ -239,6 +241,7 @@ void kernfs_reactivate(struct kernfs_node *kn); void kernfs_deactivate_self(struct kernfs_node *kn); void kernfs_reactivate_self(struct kernfs_node *kn); void kernfs_remove(struct kernfs_node *kn); +bool kernfs_remove_self(struct kernfs_node *kn); int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, const void *ns); int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, @@ -296,6 +299,9 @@ kernfs_create_link(struct kernfs_node *parent, const char *name, static inline void kernfs_remove(struct kernfs_node *kn) { } +static inline bool kernfs_remove_self(struct kernfs_node *kn) +{ return false; } + static inline int kernfs_remove_by_name_ns(struct kernfs_node *kn, const char *name, const void *ns) { return -ENOSYS; } diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 30b2ebe..bd96c60 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -198,6 +198,7 @@ int __must_check sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, umode_t mode); void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, const void *ns); +bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr); void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr); int __must_check sysfs_create_bin_file(struct kobject *kobj, @@ -301,6 +302,12 @@ static inline void sysfs_remove_file_ns(struct kobject *kobj, { } +static inline bool sysfs_remove_file_self(struct kobject *kobj, + const struct attribute *attr) +{ + return false; +} + static inline void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr) { -- cgit v1.1 From 6716d289c437ad42dee455d241b29b71a816fbff Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:28 -0500 Subject: pci: use device_remove_file_self() instead of device_schedule_callback() driver-core now supports synchrnous self-deletion of attributes and the asynchrnous removal mechanism is scheduled for removal. Use it instead of device_schedule_callback(). This makes "remove" behave synchronously. Signed-off-by: Tejun Heo Cc: Bjorn Helgaas Cc: linux-pci@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-sysfs.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index c91e6c1..94d1cb8 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -352,32 +352,20 @@ static struct device_attribute dev_rescan_attr = __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store); -static void remove_callback(struct device *dev) -{ - struct pci_dev *pdev = to_pci_dev(dev); - - mutex_lock(&pci_remove_rescan_mutex); - pci_stop_and_remove_bus_device(pdev); - mutex_unlock(&pci_remove_rescan_mutex); -} - static ssize_t -remove_store(struct device *dev, struct device_attribute *dummy, +remove_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int ret = 0; unsigned long val; if (kstrtoul(buf, 0, &val) < 0) return -EINVAL; - /* An attribute cannot be unregistered by one of its own methods, - * so we have to use this roundabout approach. - */ - if (val) - ret = device_schedule_callback(dev, remove_callback); - if (ret) - count = ret; + if (val && device_remove_file_self(dev, attr)) { + mutex_lock(&pci_remove_rescan_mutex); + pci_stop_and_remove_bus_device(to_pci_dev(dev)); + mutex_unlock(&pci_remove_rescan_mutex); + } return count; } static struct device_attribute dev_remove_attr = __ATTR(remove, -- cgit v1.1 From de1dee7820c44b1a5765265ed7ca8ee44f2367c1 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:29 -0500 Subject: scsi: use device_remove_file_self() instead of device_schedule_callback() driver-core now supports synchrnous self-deletion of attributes and the asynchrnous removal mechanism is scheduled for removal. Use it instead of device_schedule_callback(). This makes "delete" behave synchronously. Signed-off-by: Tejun Heo Cc: "James E.J. Bottomley" Cc: linux-scsi@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_sysfs.c | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 8ff62c2..33e6199 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -635,23 +635,12 @@ store_rescan_field (struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field); -static void sdev_store_delete_callback(struct device *dev) -{ - scsi_remove_device(to_scsi_device(dev)); -} - static ssize_t sdev_store_delete(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - int rc; - - /* An attribute cannot be unregistered by one of its own methods, - * so we have to use this roundabout approach. - */ - rc = device_schedule_callback(dev, sdev_store_delete_callback); - if (rc) - count = rc; + if (device_remove_file_self(dev, attr)) + scsi_remove_device(to_scsi_device(dev)); return count; }; static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); -- cgit v1.1 From bdbb0a1376635d80e096f6433595a38984cf5408 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:30 -0500 Subject: s390: use device_remove_file_self() instead of device_schedule_callback() driver-core now supports synchrnous self-deletion of attributes and the asynchrnous removal mechanism is scheduled for removal. Use it instead of device_schedule_callback(). * Conversions in arch/s390/pci/pci_sysfs.c and drivers/s390/block/dcssblk.c are straightforward. * drivers/s390/cio/ccwgroup.c is a bit more tricky because ccwgroup_notifier() was (ab)using device_schedule_callback() to purely obtain a process context to kick off ungroup operation which may block from a notifier callback. Rename ccwgroup_ungroup_callback() to ccwgroup_ungroup() and make it take ccwgroup_device * instead. The new function is now called directly from ccwgroup_ungroup_store(). ccwgroup_notifier() chain is updated to explicitly bounce through ccwgroup_device->ungroup_work. This also removes possible failure from memory pressure. Only compile-tested. Signed-off-by: Tejun Heo Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: linux390@de.ibm.com Cc: linux-s390@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/s390/include/asm/ccwgroup.h | 1 + arch/s390/pci/pci_sysfs.c | 18 ++++++++---------- drivers/s390/block/dcssblk.c | 14 +++++++------- drivers/s390/cio/ccwgroup.c | 26 ++++++++++++++++---------- 4 files changed, 32 insertions(+), 27 deletions(-) diff --git a/arch/s390/include/asm/ccwgroup.h b/arch/s390/include/asm/ccwgroup.h index 23723ce..6e670f8 100644 --- a/arch/s390/include/asm/ccwgroup.h +++ b/arch/s390/include/asm/ccwgroup.h @@ -23,6 +23,7 @@ struct ccwgroup_device { unsigned int count; struct device dev; struct ccw_device *cdev[0]; + struct work_struct ungroup_work; }; /** diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c index cf8a12f..ab4a913 100644 --- a/arch/s390/pci/pci_sysfs.c +++ b/arch/s390/pci/pci_sysfs.c @@ -48,29 +48,27 @@ static ssize_t show_pfgid(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(pfgid, S_IRUGO, show_pfgid, NULL); -static void recover_callback(struct device *dev) +static ssize_t store_recover(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) { struct pci_dev *pdev = to_pci_dev(dev); struct zpci_dev *zdev = get_zdev(pdev); int ret; + if (!device_remove_file_self(dev, attr)) + return count; + pci_stop_and_remove_bus_device(pdev); ret = zpci_disable_device(zdev); if (ret) - return; + return ret; ret = zpci_enable_device(zdev); if (ret) - return; + return ret; pci_rescan_bus(zdev->bus); -} - -static ssize_t store_recover(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - int rc = device_schedule_callback(dev, recover_callback); - return rc ? rc : count; + return count; } static DEVICE_ATTR(recover, S_IWUSR, NULL, store_recover); diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 6eca019..2e2f454 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -304,12 +304,6 @@ dcssblk_load_segment(char *name, struct segment_info **seg_info) return rc; } -static void dcssblk_unregister_callback(struct device *dev) -{ - device_unregister(dev); - put_device(dev); -} - /* * device attribute for switching shared/nonshared (exclusive) * operation (show + store) @@ -397,7 +391,13 @@ removeseg: blk_cleanup_queue(dev_info->dcssblk_queue); dev_info->gd->queue = NULL; put_disk(dev_info->gd); - rc = device_schedule_callback(dev, dcssblk_unregister_callback); + up_write(&dcssblk_devices_sem); + + if (device_remove_file_self(dev, attr)) { + device_unregister(dev); + put_device(dev); + } + return rc; out: up_write(&dcssblk_devices_sem); return rc; diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 959135a..67b9dc9 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -168,14 +168,12 @@ static ssize_t ccwgroup_online_show(struct device *dev, * Provide an 'ungroup' attribute so the user can remove group devices no * longer needed or accidentially created. Saves memory :) */ -static void ccwgroup_ungroup_callback(struct device *dev) +static void ccwgroup_ungroup(struct ccwgroup_device *gdev) { - struct ccwgroup_device *gdev = to_ccwgroupdev(dev); - mutex_lock(&gdev->reg_mutex); if (device_is_registered(&gdev->dev)) { __ccwgroup_remove_symlinks(gdev); - device_unregister(dev); + device_unregister(&gdev->dev); __ccwgroup_remove_cdev_refs(gdev); } mutex_unlock(&gdev->reg_mutex); @@ -195,10 +193,9 @@ static ssize_t ccwgroup_ungroup_store(struct device *dev, rc = -EINVAL; goto out; } - /* Note that we cannot unregister the device from one of its - * attribute methods, so we have to use this roundabout approach. - */ - rc = device_schedule_callback(dev, ccwgroup_ungroup_callback); + + if (device_remove_file_self(dev, attr)) + ccwgroup_ungroup(gdev); out: if (rc) { if (rc != -EAGAIN) @@ -224,6 +221,14 @@ static const struct attribute_group *ccwgroup_attr_groups[] = { NULL, }; +static void ccwgroup_ungroup_workfn(struct work_struct *work) +{ + struct ccwgroup_device *gdev = + container_of(work, struct ccwgroup_device, ungroup_work); + + ccwgroup_ungroup(gdev); +} + static void ccwgroup_release(struct device *dev) { kfree(to_ccwgroupdev(dev)); @@ -323,6 +328,7 @@ int ccwgroup_create_dev(struct device *parent, struct ccwgroup_driver *gdrv, atomic_set(&gdev->onoff, 0); mutex_init(&gdev->reg_mutex); mutex_lock(&gdev->reg_mutex); + INIT_WORK(&gdev->ungroup_work, ccwgroup_ungroup_workfn); gdev->count = num_devices; gdev->dev.bus = &ccwgroup_bus_type; gdev->dev.parent = parent; @@ -404,10 +410,10 @@ EXPORT_SYMBOL(ccwgroup_create_dev); static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action, void *data) { - struct device *dev = data; + struct ccwgroup_device *gdev = to_ccwgroupdev(data); if (action == BUS_NOTIFY_UNBIND_DRIVER) - device_schedule_callback(dev, ccwgroup_ungroup_callback); + schedule_work(&gdev->ungroup_work); return NOTIFY_OK; } -- cgit v1.1 From d1ba277e79889085a2faec3b68b91ce89c63f888 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 10 Jan 2014 08:57:31 -0500 Subject: sysfs, driver-core: remove unused {sysfs|device}_schedule_callback_owner() All device_schedule_callback_owner() users are converted to use device_remove_file_self(). Remove now unused {sysfs|device}_schedule_callback_owner(). Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 33 ------------------ fs/sysfs/file.c | 92 -------------------------------------------------- include/linux/device.h | 11 +----- include/linux/sysfs.h | 9 ----- 4 files changed, 1 insertion(+), 144 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 9db57af..4195364 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -615,39 +615,6 @@ void device_remove_bin_file(struct device *dev, } EXPORT_SYMBOL_GPL(device_remove_bin_file); -/** - * device_schedule_callback_owner - helper to schedule a callback for a device - * @dev: device. - * @func: callback function to invoke later. - * @owner: module owning the callback routine - * - * Attribute methods must not unregister themselves or their parent device - * (which would amount to the same thing). Attempts to do so will deadlock, - * since unregistration is mutually exclusive with driver callbacks. - * - * Instead methods can call this routine, which will attempt to allocate - * and schedule a workqueue request to call back @func with @dev as its - * argument in the workqueue's process context. @dev will be pinned until - * @func returns. - * - * This routine is usually called via the inline device_schedule_callback(), - * which automatically sets @owner to THIS_MODULE. - * - * Returns 0 if the request was submitted, -ENOMEM if storage could not - * be allocated, -ENODEV if a reference to @owner isn't available. - * - * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an - * underlying sysfs routine (since it is intended for use by attribute - * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. - */ -int device_schedule_callback_owner(struct device *dev, - void (*func)(struct device *), struct module *owner) -{ - return sysfs_schedule_callback(&dev->kobj, - (void (*)(void *)) func, dev, owner); -} -EXPORT_SYMBOL_GPL(device_schedule_callback_owner); - static void klist_children_get(struct klist_node *n) { struct device_private *p = to_device_private_parent(n); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 1b8b91b..28cc1acd 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -453,95 +453,3 @@ void sysfs_remove_bin_file(struct kobject *kobj, kernfs_remove_by_name(kobj->sd, attr->attr.name); } EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); - -struct sysfs_schedule_callback_struct { - struct list_head workq_list; - struct kobject *kobj; - void (*func)(void *); - void *data; - struct module *owner; - struct work_struct work; -}; - -static struct workqueue_struct *sysfs_workqueue; -static DEFINE_MUTEX(sysfs_workq_mutex); -static LIST_HEAD(sysfs_workq); -static void sysfs_schedule_callback_work(struct work_struct *work) -{ - struct sysfs_schedule_callback_struct *ss = container_of(work, - struct sysfs_schedule_callback_struct, work); - - (ss->func)(ss->data); - kobject_put(ss->kobj); - module_put(ss->owner); - mutex_lock(&sysfs_workq_mutex); - list_del(&ss->workq_list); - mutex_unlock(&sysfs_workq_mutex); - kfree(ss); -} - -/** - * sysfs_schedule_callback - helper to schedule a callback for a kobject - * @kobj: object we're acting for. - * @func: callback function to invoke later. - * @data: argument to pass to @func. - * @owner: module owning the callback code - * - * sysfs attribute methods must not unregister themselves or their parent - * kobject (which would amount to the same thing). Attempts to do so will - * deadlock, since unregistration is mutually exclusive with driver - * callbacks. - * - * Instead methods can call this routine, which will attempt to allocate - * and schedule a workqueue request to call back @func with @data as its - * argument in the workqueue's process context. @kobj will be pinned - * until @func returns. - * - * Returns 0 if the request was submitted, -ENOMEM if storage could not - * be allocated, -ENODEV if a reference to @owner isn't available, - * -EAGAIN if a callback has already been scheduled for @kobj. - */ -int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), - void *data, struct module *owner) -{ - struct sysfs_schedule_callback_struct *ss, *tmp; - - if (!try_module_get(owner)) - return -ENODEV; - - mutex_lock(&sysfs_workq_mutex); - list_for_each_entry_safe(ss, tmp, &sysfs_workq, workq_list) - if (ss->kobj == kobj) { - module_put(owner); - mutex_unlock(&sysfs_workq_mutex); - return -EAGAIN; - } - mutex_unlock(&sysfs_workq_mutex); - - if (sysfs_workqueue == NULL) { - sysfs_workqueue = create_singlethread_workqueue("sysfsd"); - if (sysfs_workqueue == NULL) { - module_put(owner); - return -ENOMEM; - } - } - - ss = kmalloc(sizeof(*ss), GFP_KERNEL); - if (!ss) { - module_put(owner); - return -ENOMEM; - } - kobject_get(kobj); - ss->kobj = kobj; - ss->func = func; - ss->data = data; - ss->owner = owner; - INIT_WORK(&ss->work, sysfs_schedule_callback_work); - INIT_LIST_HEAD(&ss->workq_list); - mutex_lock(&sysfs_workq_mutex); - list_add_tail(&ss->workq_list, &sysfs_workq); - mutex_unlock(&sysfs_workq_mutex); - queue_work(sysfs_workqueue, &ss->work); - return 0; -} -EXPORT_SYMBOL_GPL(sysfs_schedule_callback); diff --git a/include/linux/device.h b/include/linux/device.h index 1ff3f16..fb1ba13 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -566,12 +566,6 @@ extern int __must_check device_create_bin_file(struct device *dev, const struct bin_attribute *attr); extern void device_remove_bin_file(struct device *dev, const struct bin_attribute *attr); -extern int device_schedule_callback_owner(struct device *dev, - void (*func)(struct device *dev), struct module *owner); - -/* This is a macro to avoid include problems with THIS_MODULE */ -#define device_schedule_callback(dev, func) \ - device_schedule_callback_owner(dev, func, THIS_MODULE) /* device resource management */ typedef void (*dr_release_t)(struct device *dev, void *res); @@ -931,10 +925,7 @@ extern int device_online(struct device *dev); extern struct device *__root_device_register(const char *name, struct module *owner); -/* - * This is a macro to avoid include problems with THIS_MODULE, - * just as per what is done for device_schedule_callback() above. - */ +/* This is a macro to avoid include problems with THIS_MODULE */ #define root_device_register(name) \ __root_device_register(name, THIS_MODULE) diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index bd96c60..14df054 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -178,9 +178,6 @@ struct sysfs_ops { #ifdef CONFIG_SYSFS -int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), - void *data, struct module *owner); - int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns); void sysfs_remove_dir(struct kobject *kobj); int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, @@ -249,12 +246,6 @@ int __must_check sysfs_init(void); #else /* CONFIG_SYSFS */ -static inline int sysfs_schedule_callback(struct kobject *kobj, - void (*func)(void *), void *data, struct module *owner) -{ - return -ENOSYS; -} - static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) { return 0; -- cgit v1.1 From 2a41e6070dd7ef539d0f3b1652b4839d04378e11 Mon Sep 17 00:00:00 2001 From: Russell King Date: Fri, 10 Jan 2014 23:23:37 +0000 Subject: drivers/base: provide an infrastructure for componentised subsystems Subsystems such as ALSA, DRM and others require a single card-level device structure to represent a subsystem. However, firmware tends to describe the individual devices and the connections between them. Therefore, we need a way to gather up the individual component devices together, and indicate when we have all the component devices. We do this in DT by providing a "superdevice" node which specifies the components, eg: imx-drm { compatible = "fsl,drm"; crtcs = <&ipu1>; connectors = <&hdmi>; }; The superdevice is declared into the component support, along with the subcomponents. The superdevice receives callbacks to locate the subcomponents, and identify when all components are present. At this point, we bind the superdevice, which causes the appropriate subsystem to be initialised in the conventional way. When any of the components or superdevice are removed from the system, we unbind the superdevice, thereby taking the subsystem down. Signed-off-by: Russell King Signed-off-by: Greg Kroah-Hartman --- drivers/base/Makefile | 2 +- drivers/base/component.c | 382 ++++++++++++++++++++++++++++++++++++++++++++++ include/linux/component.h | 32 ++++ 3 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 drivers/base/component.c create mode 100644 include/linux/component.h diff --git a/drivers/base/Makefile b/drivers/base/Makefile index 94e8a80..870ecfd 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -1,6 +1,6 @@ # Makefile for the Linux device tree -obj-y := core.o bus.o dd.o syscore.o \ +obj-y := component.o core.o bus.o dd.o syscore.o \ driver.o class.o platform.o \ cpu.o firmware.o init.o map.o devres.o \ attribute_container.o transport_class.o \ diff --git a/drivers/base/component.c b/drivers/base/component.c new file mode 100644 index 0000000..c53efe6 --- /dev/null +++ b/drivers/base/component.c @@ -0,0 +1,382 @@ +/* + * Componentized device handling. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This is work in progress. We gather up the component devices into a list, + * and bind them when instructed. At the moment, we're specific to the DRM + * subsystem, and only handles one master device, but this doesn't have to be + * the case. + */ +#include +#include +#include +#include +#include +#include +#include + +struct master { + struct list_head node; + struct list_head components; + bool bound; + + const struct component_master_ops *ops; + struct device *dev; +}; + +struct component { + struct list_head node; + struct list_head master_node; + struct master *master; + bool bound; + + const struct component_ops *ops; + struct device *dev; +}; + +static DEFINE_MUTEX(component_mutex); +static LIST_HEAD(component_list); +static LIST_HEAD(masters); + +static struct master *__master_find(struct device *dev, + const struct component_master_ops *ops) +{ + struct master *m; + + list_for_each_entry(m, &masters, node) + if (m->dev == dev && (!ops || m->ops == ops)) + return m; + + return NULL; +} + +/* Attach an unattached component to a master. */ +static void component_attach_master(struct master *master, struct component *c) +{ + c->master = master; + + list_add_tail(&c->master_node, &master->components); +} + +/* Detach a component from a master. */ +static void component_detach_master(struct master *master, struct component *c) +{ + list_del(&c->master_node); + + c->master = NULL; +} + +int component_master_add_child(struct master *master, + int (*compare)(struct device *, void *), void *compare_data) +{ + struct component *c; + int ret = -ENXIO; + + list_for_each_entry(c, &component_list, node) { + if (c->master) + continue; + + if (compare(c->dev, compare_data)) { + component_attach_master(master, c); + ret = 0; + break; + } + } + + return ret; +} +EXPORT_SYMBOL_GPL(component_master_add_child); + +/* Detach all attached components from this master */ +static void master_remove_components(struct master *master) +{ + while (!list_empty(&master->components)) { + struct component *c = list_first_entry(&master->components, + struct component, master_node); + + WARN_ON(c->master != master); + + component_detach_master(master, c); + } +} + +/* + * Try to bring up a master. If component is NULL, we're interested in + * this master, otherwise it's a component which must be present to try + * and bring up the master. + * + * Returns 1 for successful bringup, 0 if not ready, or -ve errno. + */ +static int try_to_bring_up_master(struct master *master, + struct component *component) +{ + int ret = 0; + + if (!master->bound) { + /* + * Search the list of components, looking for components that + * belong to this master, and attach them to the master. + */ + if (master->ops->add_components(master->dev, master)) { + /* Failed to find all components */ + master_remove_components(master); + ret = 0; + goto out; + } + + if (component && component->master != master) { + master_remove_components(master); + ret = 0; + goto out; + } + + /* Found all components */ + ret = master->ops->bind(master->dev); + if (ret < 0) { + master_remove_components(master); + goto out; + } + + master->bound = true; + ret = 1; + } +out: + + return ret; +} + +static int try_to_bring_up_masters(struct component *component) +{ + struct master *m; + int ret = 0; + + list_for_each_entry(m, &masters, node) { + ret = try_to_bring_up_master(m, component); + if (ret != 0) + break; + } + + return ret; +} + +static void take_down_master(struct master *master) +{ + if (master->bound) { + master->ops->unbind(master->dev); + master->bound = false; + } + + master_remove_components(master); +} + +int component_master_add(struct device *dev, + const struct component_master_ops *ops) +{ + struct master *master; + int ret; + + master = kzalloc(sizeof(*master), GFP_KERNEL); + if (!master) + return -ENOMEM; + + master->dev = dev; + master->ops = ops; + INIT_LIST_HEAD(&master->components); + + /* Add to the list of available masters. */ + mutex_lock(&component_mutex); + list_add(&master->node, &masters); + + ret = try_to_bring_up_master(master, NULL); + + if (ret < 0) { + /* Delete off the list if we weren't successful */ + list_del(&master->node); + kfree(master); + } + mutex_unlock(&component_mutex); + + return ret < 0 ? ret : 0; +} +EXPORT_SYMBOL_GPL(component_master_add); + +void component_master_del(struct device *dev, + const struct component_master_ops *ops) +{ + struct master *master; + + mutex_lock(&component_mutex); + master = __master_find(dev, ops); + if (master) { + take_down_master(master); + + list_del(&master->node); + kfree(master); + } + mutex_unlock(&component_mutex); +} +EXPORT_SYMBOL_GPL(component_master_del); + +static void component_unbind(struct component *component, + struct master *master, void *data) +{ + WARN_ON(!component->bound); + + component->ops->unbind(component->dev, master->dev, data); + component->bound = false; + + /* Release all resources claimed in the binding of this component */ + devres_release_group(component->dev, component); +} + +void component_unbind_all(struct device *master_dev, void *data) +{ + struct master *master; + struct component *c; + + WARN_ON(!mutex_is_locked(&component_mutex)); + + master = __master_find(master_dev, NULL); + if (!master) + return; + + list_for_each_entry_reverse(c, &master->components, master_node) + component_unbind(c, master, data); +} +EXPORT_SYMBOL_GPL(component_unbind_all); + +static int component_bind(struct component *component, struct master *master, + void *data) +{ + int ret; + + /* + * Each component initialises inside its own devres group. + * This allows us to roll-back a failed component without + * affecting anything else. + */ + if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) + return -ENOMEM; + + /* + * Also open a group for the device itself: this allows us + * to release the resources claimed against the sub-device + * at the appropriate moment. + */ + if (!devres_open_group(component->dev, component, GFP_KERNEL)) { + devres_release_group(master->dev, NULL); + return -ENOMEM; + } + + dev_dbg(master->dev, "binding %s (ops %ps)\n", + dev_name(component->dev), component->ops); + + ret = component->ops->bind(component->dev, master->dev, data); + if (!ret) { + component->bound = true; + + /* + * Close the component device's group so that resources + * allocated in the binding are encapsulated for removal + * at unbind. Remove the group on the DRM device as we + * can clean those resources up independently. + */ + devres_close_group(component->dev, NULL); + devres_remove_group(master->dev, NULL); + + dev_info(master->dev, "bound %s (ops %ps)\n", + dev_name(component->dev), component->ops); + } else { + devres_release_group(component->dev, NULL); + devres_release_group(master->dev, NULL); + + dev_err(master->dev, "failed to bind %s (ops %ps): %d\n", + dev_name(component->dev), component->ops, ret); + } + + return ret; +} + +int component_bind_all(struct device *master_dev, void *data) +{ + struct master *master; + struct component *c; + int ret = 0; + + WARN_ON(!mutex_is_locked(&component_mutex)); + + master = __master_find(master_dev, NULL); + if (!master) + return -EINVAL; + + list_for_each_entry(c, &master->components, master_node) { + ret = component_bind(c, master, data); + if (ret) + break; + } + + if (ret != 0) { + list_for_each_entry_continue_reverse(c, &master->components, + master_node) + component_unbind(c, master, data); + } + + return ret; +} +EXPORT_SYMBOL_GPL(component_bind_all); + +int component_add(struct device *dev, const struct component_ops *ops) +{ + struct component *component; + int ret; + + component = kzalloc(sizeof(*component), GFP_KERNEL); + if (!component) + return -ENOMEM; + + component->ops = ops; + component->dev = dev; + + dev_dbg(dev, "adding component (ops %ps)\n", ops); + + mutex_lock(&component_mutex); + list_add_tail(&component->node, &component_list); + + ret = try_to_bring_up_masters(component); + if (ret < 0) { + list_del(&component->node); + + kfree(component); + } + mutex_unlock(&component_mutex); + + return ret < 0 ? ret : 0; +} +EXPORT_SYMBOL_GPL(component_add); + +void component_del(struct device *dev, const struct component_ops *ops) +{ + struct component *c, *component = NULL; + + mutex_lock(&component_mutex); + list_for_each_entry(c, &component_list, node) + if (c->dev == dev && c->ops == ops) { + list_del(&c->node); + component = c; + break; + } + + if (component && component->master) + take_down_master(component->master); + + mutex_unlock(&component_mutex); + + WARN_ON(!component); + kfree(component); +} +EXPORT_SYMBOL_GPL(component_del); + +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/component.h b/include/linux/component.h new file mode 100644 index 0000000..6887018 --- /dev/null +++ b/include/linux/component.h @@ -0,0 +1,32 @@ +#ifndef COMPONENT_H +#define COMPONENT_H + +struct device; + +struct component_ops { + int (*bind)(struct device *, struct device *, void *); + void (*unbind)(struct device *, struct device *, void *); +}; + +int component_add(struct device *, const struct component_ops *); +void component_del(struct device *, const struct component_ops *); + +int component_bind_all(struct device *, void *); +void component_unbind_all(struct device *, void *); + +struct master; + +struct component_master_ops { + int (*add_components)(struct device *, struct master *); + int (*bind)(struct device *); + void (*unbind)(struct device *); +}; + +int component_master_add(struct device *, const struct component_master_ops *); +void component_master_del(struct device *, + const struct component_master_ops *); + +int component_master_add_child(struct master *master, + int (*compare)(struct device *, void *), void *compare_data); + +#endif -- cgit v1.1 From 88533f990c616cf50c2fe585ea03f75c806a293d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Sat, 11 Jan 2014 18:23:23 -0500 Subject: kernfs: remove unnecessary NULL check in __kernfs_remove() 895a068a524e ("kernfs: make kernfs_get_active() block if the node is deactivated but not removed") added "struct kernfs_root *root = kernfs_root(kn);" at the head of the function; however, the parameter @kn is checked for later implying that the function may be called with NULL. This means that we may end up invoking kernfs_root() with NULL which will oops. None of the existing users invokes removal with NULL @kn, so this bug doesn't actually trigger. We can relocate kernfs_root() invocation after NULL check; however, allowing NULL param tends to cause more confusion than actually helping anything. As there's no existing user, let's remove the spurious NULL check. This bug was detected by smatch. Signed-off-by: Tejun Heo Reported-by: Dan Carpenter Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index a8028be..4076e8a 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -920,9 +920,6 @@ static void __kernfs_remove(struct kernfs_node *kn) lockdep_assert_held(&kernfs_mutex); - if (!kn) - return; - pr_debug("kernfs %s: removing\n", kn->name); __kernfs_deactivate(kn); -- cgit v1.1 From ce9b499c9f58d7f3f680413f3ab5407f4e647ba2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 13:50:31 -0800 Subject: Revert "kernfs: remove unnecessary NULL check in __kernfs_remove()" This reverts commit 88533f990c616cf50c2fe585ea03f75c806a293d. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 4076e8a..a8028be 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -920,6 +920,9 @@ static void __kernfs_remove(struct kernfs_node *kn) lockdep_assert_held(&kernfs_mutex); + if (!kn) + return; + pr_debug("kernfs %s: removing\n", kn->name); __kernfs_deactivate(kn); -- cgit v1.1 From a30f82b7ebc87cdec3ef48303278f02970086118 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 13:51:36 -0800 Subject: Revert "sysfs, driver-core: remove unused {sysfs|device}_schedule_callback_owner()" This reverts commit d1ba277e79889085a2faec3b68b91ce89c63f888. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 33 ++++++++++++++++++ fs/sysfs/file.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 11 +++++- include/linux/sysfs.h | 9 +++++ 4 files changed, 144 insertions(+), 1 deletion(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 4195364..9db57af 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -615,6 +615,39 @@ void device_remove_bin_file(struct device *dev, } EXPORT_SYMBOL_GPL(device_remove_bin_file); +/** + * device_schedule_callback_owner - helper to schedule a callback for a device + * @dev: device. + * @func: callback function to invoke later. + * @owner: module owning the callback routine + * + * Attribute methods must not unregister themselves or their parent device + * (which would amount to the same thing). Attempts to do so will deadlock, + * since unregistration is mutually exclusive with driver callbacks. + * + * Instead methods can call this routine, which will attempt to allocate + * and schedule a workqueue request to call back @func with @dev as its + * argument in the workqueue's process context. @dev will be pinned until + * @func returns. + * + * This routine is usually called via the inline device_schedule_callback(), + * which automatically sets @owner to THIS_MODULE. + * + * Returns 0 if the request was submitted, -ENOMEM if storage could not + * be allocated, -ENODEV if a reference to @owner isn't available. + * + * NOTE: This routine won't work if CONFIG_SYSFS isn't set! It uses an + * underlying sysfs routine (since it is intended for use by attribute + * methods), and if sysfs isn't available you'll get nothing but -ENOSYS. + */ +int device_schedule_callback_owner(struct device *dev, + void (*func)(struct device *), struct module *owner) +{ + return sysfs_schedule_callback(&dev->kobj, + (void (*)(void *)) func, dev, owner); +} +EXPORT_SYMBOL_GPL(device_schedule_callback_owner); + static void klist_children_get(struct klist_node *n) { struct device_private *p = to_device_private_parent(n); diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 28cc1acd..1b8b91b 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -453,3 +453,95 @@ void sysfs_remove_bin_file(struct kobject *kobj, kernfs_remove_by_name(kobj->sd, attr->attr.name); } EXPORT_SYMBOL_GPL(sysfs_remove_bin_file); + +struct sysfs_schedule_callback_struct { + struct list_head workq_list; + struct kobject *kobj; + void (*func)(void *); + void *data; + struct module *owner; + struct work_struct work; +}; + +static struct workqueue_struct *sysfs_workqueue; +static DEFINE_MUTEX(sysfs_workq_mutex); +static LIST_HEAD(sysfs_workq); +static void sysfs_schedule_callback_work(struct work_struct *work) +{ + struct sysfs_schedule_callback_struct *ss = container_of(work, + struct sysfs_schedule_callback_struct, work); + + (ss->func)(ss->data); + kobject_put(ss->kobj); + module_put(ss->owner); + mutex_lock(&sysfs_workq_mutex); + list_del(&ss->workq_list); + mutex_unlock(&sysfs_workq_mutex); + kfree(ss); +} + +/** + * sysfs_schedule_callback - helper to schedule a callback for a kobject + * @kobj: object we're acting for. + * @func: callback function to invoke later. + * @data: argument to pass to @func. + * @owner: module owning the callback code + * + * sysfs attribute methods must not unregister themselves or their parent + * kobject (which would amount to the same thing). Attempts to do so will + * deadlock, since unregistration is mutually exclusive with driver + * callbacks. + * + * Instead methods can call this routine, which will attempt to allocate + * and schedule a workqueue request to call back @func with @data as its + * argument in the workqueue's process context. @kobj will be pinned + * until @func returns. + * + * Returns 0 if the request was submitted, -ENOMEM if storage could not + * be allocated, -ENODEV if a reference to @owner isn't available, + * -EAGAIN if a callback has already been scheduled for @kobj. + */ +int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), + void *data, struct module *owner) +{ + struct sysfs_schedule_callback_struct *ss, *tmp; + + if (!try_module_get(owner)) + return -ENODEV; + + mutex_lock(&sysfs_workq_mutex); + list_for_each_entry_safe(ss, tmp, &sysfs_workq, workq_list) + if (ss->kobj == kobj) { + module_put(owner); + mutex_unlock(&sysfs_workq_mutex); + return -EAGAIN; + } + mutex_unlock(&sysfs_workq_mutex); + + if (sysfs_workqueue == NULL) { + sysfs_workqueue = create_singlethread_workqueue("sysfsd"); + if (sysfs_workqueue == NULL) { + module_put(owner); + return -ENOMEM; + } + } + + ss = kmalloc(sizeof(*ss), GFP_KERNEL); + if (!ss) { + module_put(owner); + return -ENOMEM; + } + kobject_get(kobj); + ss->kobj = kobj; + ss->func = func; + ss->data = data; + ss->owner = owner; + INIT_WORK(&ss->work, sysfs_schedule_callback_work); + INIT_LIST_HEAD(&ss->workq_list); + mutex_lock(&sysfs_workq_mutex); + list_add_tail(&ss->workq_list, &sysfs_workq); + mutex_unlock(&sysfs_workq_mutex); + queue_work(sysfs_workqueue, &ss->work); + return 0; +} +EXPORT_SYMBOL_GPL(sysfs_schedule_callback); diff --git a/include/linux/device.h b/include/linux/device.h index fb1ba13..1ff3f16 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -566,6 +566,12 @@ extern int __must_check device_create_bin_file(struct device *dev, const struct bin_attribute *attr); extern void device_remove_bin_file(struct device *dev, const struct bin_attribute *attr); +extern int device_schedule_callback_owner(struct device *dev, + void (*func)(struct device *dev), struct module *owner); + +/* This is a macro to avoid include problems with THIS_MODULE */ +#define device_schedule_callback(dev, func) \ + device_schedule_callback_owner(dev, func, THIS_MODULE) /* device resource management */ typedef void (*dr_release_t)(struct device *dev, void *res); @@ -925,7 +931,10 @@ extern int device_online(struct device *dev); extern struct device *__root_device_register(const char *name, struct module *owner); -/* This is a macro to avoid include problems with THIS_MODULE */ +/* + * This is a macro to avoid include problems with THIS_MODULE, + * just as per what is done for device_schedule_callback() above. + */ #define root_device_register(name) \ __root_device_register(name, THIS_MODULE) diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 14df054..bd96c60 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -178,6 +178,9 @@ struct sysfs_ops { #ifdef CONFIG_SYSFS +int sysfs_schedule_callback(struct kobject *kobj, void (*func)(void *), + void *data, struct module *owner); + int __must_check sysfs_create_dir_ns(struct kobject *kobj, const void *ns); void sysfs_remove_dir(struct kobject *kobj); int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name, @@ -246,6 +249,12 @@ int __must_check sysfs_init(void); #else /* CONFIG_SYSFS */ +static inline int sysfs_schedule_callback(struct kobject *kobj, + void (*func)(void *), void *data, struct module *owner) +{ + return -ENOSYS; +} + static inline int sysfs_create_dir_ns(struct kobject *kobj, const void *ns) { return 0; -- cgit v1.1 From ff483d55ba6fb50a8d6f99e808da35218533b1ef Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 13:55:05 -0800 Subject: Revert "s390: use device_remove_file_self() instead of device_schedule_callback()" This reverts commit bdbb0a1376635d80e096f6433595a38984cf5408. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Cc: Martin Schwidefsky Cc: Heiko Carstens Cc: linux390@de.ibm.com Cc: linux-s390@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- arch/s390/include/asm/ccwgroup.h | 1 - arch/s390/pci/pci_sysfs.c | 18 ++++++++++-------- drivers/s390/block/dcssblk.c | 14 +++++++------- drivers/s390/cio/ccwgroup.c | 26 ++++++++++---------------- 4 files changed, 27 insertions(+), 32 deletions(-) diff --git a/arch/s390/include/asm/ccwgroup.h b/arch/s390/include/asm/ccwgroup.h index 6e670f8..23723ce 100644 --- a/arch/s390/include/asm/ccwgroup.h +++ b/arch/s390/include/asm/ccwgroup.h @@ -23,7 +23,6 @@ struct ccwgroup_device { unsigned int count; struct device dev; struct ccw_device *cdev[0]; - struct work_struct ungroup_work; }; /** diff --git a/arch/s390/pci/pci_sysfs.c b/arch/s390/pci/pci_sysfs.c index ab4a913..cf8a12f 100644 --- a/arch/s390/pci/pci_sysfs.c +++ b/arch/s390/pci/pci_sysfs.c @@ -48,27 +48,29 @@ static ssize_t show_pfgid(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(pfgid, S_IRUGO, show_pfgid, NULL); -static ssize_t store_recover(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) +static void recover_callback(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); struct zpci_dev *zdev = get_zdev(pdev); int ret; - if (!device_remove_file_self(dev, attr)) - return count; - pci_stop_and_remove_bus_device(pdev); ret = zpci_disable_device(zdev); if (ret) - return ret; + return; ret = zpci_enable_device(zdev); if (ret) - return ret; + return; pci_rescan_bus(zdev->bus); - return count; +} + +static ssize_t store_recover(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + int rc = device_schedule_callback(dev, recover_callback); + return rc ? rc : count; } static DEVICE_ATTR(recover, S_IWUSR, NULL, store_recover); diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 2e2f454..6eca019 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -304,6 +304,12 @@ dcssblk_load_segment(char *name, struct segment_info **seg_info) return rc; } +static void dcssblk_unregister_callback(struct device *dev) +{ + device_unregister(dev); + put_device(dev); +} + /* * device attribute for switching shared/nonshared (exclusive) * operation (show + store) @@ -391,13 +397,7 @@ removeseg: blk_cleanup_queue(dev_info->dcssblk_queue); dev_info->gd->queue = NULL; put_disk(dev_info->gd); - up_write(&dcssblk_devices_sem); - - if (device_remove_file_self(dev, attr)) { - device_unregister(dev); - put_device(dev); - } - return rc; + rc = device_schedule_callback(dev, dcssblk_unregister_callback); out: up_write(&dcssblk_devices_sem); return rc; diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index 67b9dc9..959135a 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -168,12 +168,14 @@ static ssize_t ccwgroup_online_show(struct device *dev, * Provide an 'ungroup' attribute so the user can remove group devices no * longer needed or accidentially created. Saves memory :) */ -static void ccwgroup_ungroup(struct ccwgroup_device *gdev) +static void ccwgroup_ungroup_callback(struct device *dev) { + struct ccwgroup_device *gdev = to_ccwgroupdev(dev); + mutex_lock(&gdev->reg_mutex); if (device_is_registered(&gdev->dev)) { __ccwgroup_remove_symlinks(gdev); - device_unregister(&gdev->dev); + device_unregister(dev); __ccwgroup_remove_cdev_refs(gdev); } mutex_unlock(&gdev->reg_mutex); @@ -193,9 +195,10 @@ static ssize_t ccwgroup_ungroup_store(struct device *dev, rc = -EINVAL; goto out; } - - if (device_remove_file_self(dev, attr)) - ccwgroup_ungroup(gdev); + /* Note that we cannot unregister the device from one of its + * attribute methods, so we have to use this roundabout approach. + */ + rc = device_schedule_callback(dev, ccwgroup_ungroup_callback); out: if (rc) { if (rc != -EAGAIN) @@ -221,14 +224,6 @@ static const struct attribute_group *ccwgroup_attr_groups[] = { NULL, }; -static void ccwgroup_ungroup_workfn(struct work_struct *work) -{ - struct ccwgroup_device *gdev = - container_of(work, struct ccwgroup_device, ungroup_work); - - ccwgroup_ungroup(gdev); -} - static void ccwgroup_release(struct device *dev) { kfree(to_ccwgroupdev(dev)); @@ -328,7 +323,6 @@ int ccwgroup_create_dev(struct device *parent, struct ccwgroup_driver *gdrv, atomic_set(&gdev->onoff, 0); mutex_init(&gdev->reg_mutex); mutex_lock(&gdev->reg_mutex); - INIT_WORK(&gdev->ungroup_work, ccwgroup_ungroup_workfn); gdev->count = num_devices; gdev->dev.bus = &ccwgroup_bus_type; gdev->dev.parent = parent; @@ -410,10 +404,10 @@ EXPORT_SYMBOL(ccwgroup_create_dev); static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action, void *data) { - struct ccwgroup_device *gdev = to_ccwgroupdev(data); + struct device *dev = data; if (action == BUS_NOTIFY_UNBIND_DRIVER) - schedule_work(&gdev->ungroup_work); + device_schedule_callback(dev, ccwgroup_ungroup_callback); return NOTIFY_OK; } -- cgit v1.1 From c41d9663252e766e65cc06b82618c11ecf697acb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 14:01:46 -0800 Subject: Revert "scsi: use device_remove_file_self() instead of device_schedule_callback()" This reverts commit de1dee7820c44b1a5765265ed7ca8ee44f2367c1. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Cc: "James E.J. Bottomley" Cc: linux-scsi@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/scsi/scsi_sysfs.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c index 33e6199..8ff62c2 100644 --- a/drivers/scsi/scsi_sysfs.c +++ b/drivers/scsi/scsi_sysfs.c @@ -635,12 +635,23 @@ store_rescan_field (struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR(rescan, S_IWUSR, NULL, store_rescan_field); +static void sdev_store_delete_callback(struct device *dev) +{ + scsi_remove_device(to_scsi_device(dev)); +} + static ssize_t sdev_store_delete(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - if (device_remove_file_self(dev, attr)) - scsi_remove_device(to_scsi_device(dev)); + int rc; + + /* An attribute cannot be unregistered by one of its own methods, + * so we have to use this roundabout approach. + */ + rc = device_schedule_callback(dev, sdev_store_delete_callback); + if (rc) + count = rc; return count; }; static DEVICE_ATTR(delete, S_IWUSR, NULL, sdev_store_delete); -- cgit v1.1 From 8634c422c1b7e50ca8e346f65afc140d93a3212c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 14:03:06 -0800 Subject: Revert "pci: use device_remove_file_self() instead of device_schedule_callback()" This reverts commit 6716d289c437ad42dee455d241b29b71a816fbff. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Cc: Bjorn Helgaas Cc: linux-pci@vger.kernel.org Signed-off-by: Greg Kroah-Hartman --- drivers/pci/pci-sysfs.c | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 94d1cb8..c91e6c1 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -352,20 +352,32 @@ static struct device_attribute dev_rescan_attr = __ATTR(rescan, (S_IWUSR|S_IWGRP), NULL, dev_rescan_store); +static void remove_callback(struct device *dev) +{ + struct pci_dev *pdev = to_pci_dev(dev); + + mutex_lock(&pci_remove_rescan_mutex); + pci_stop_and_remove_bus_device(pdev); + mutex_unlock(&pci_remove_rescan_mutex); +} + static ssize_t -remove_store(struct device *dev, struct device_attribute *attr, +remove_store(struct device *dev, struct device_attribute *dummy, const char *buf, size_t count) { + int ret = 0; unsigned long val; if (kstrtoul(buf, 0, &val) < 0) return -EINVAL; - if (val && device_remove_file_self(dev, attr)) { - mutex_lock(&pci_remove_rescan_mutex); - pci_stop_and_remove_bus_device(to_pci_dev(dev)); - mutex_unlock(&pci_remove_rescan_mutex); - } + /* An attribute cannot be unregistered by one of its own methods, + * so we have to use this roundabout approach. + */ + if (val) + ret = device_schedule_callback(dev, remove_callback); + if (ret) + count = ret; return count; } static struct device_attribute dev_remove_attr = __ATTR(remove, -- cgit v1.1 From a9f138b0e537de55933335d580ebd38c2bc53c47 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 14:05:13 -0800 Subject: Revert "kernfs, sysfs, driver-core: implement kernfs_remove_self() and its wrappers" This reverts commit 1ae06819c77cff1ea2833c94f8c093fe8a5c79db. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Cc: Alan Stern Cc: kbuild test robot Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 17 ------------ fs/kernfs/dir.c | 72 -------------------------------------------------- fs/sysfs/file.c | 23 ---------------- include/linux/device.h | 2 -- include/linux/kernfs.h | 6 ----- include/linux/sysfs.h | 7 ----- 6 files changed, 127 deletions(-) diff --git a/drivers/base/core.c b/drivers/base/core.c index 9db57af..2b56717 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -571,23 +571,6 @@ void device_remove_file(struct device *dev, EXPORT_SYMBOL_GPL(device_remove_file); /** - * device_remove_file_self - remove sysfs attribute file from its own method. - * @dev: device. - * @attr: device attribute descriptor. - * - * See kernfs_remove_self() for details. - */ -bool device_remove_file_self(struct device *dev, - const struct device_attribute *attr) -{ - if (dev) - return sysfs_remove_file_self(&dev->kobj, &attr->attr); - else - return false; -} -EXPORT_SYMBOL_GPL(device_remove_file_self); - -/** * device_create_bin_file - create sysfs binary attribute file for device. * @dev: device. * @attr: device binary attribute descriptor. diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index a8028be..1aeb579 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -986,78 +986,6 @@ void kernfs_remove(struct kernfs_node *kn) } /** - * kernfs_remove_self - remove a kernfs_node from its own method - * @kn: the self kernfs_node to remove - * - * The caller must be running off of a kernfs operation which is invoked - * with an active reference - e.g. one of kernfs_ops. This can be used to - * implement a file operation which deletes itself. - * - * For example, the "delete" file for a sysfs device directory can be - * implemented by invoking kernfs_remove_self() on the "delete" file - * itself. This function breaks the circular dependency of trying to - * deactivate self while holding an active ref itself. It isn't necessary - * to modify the usual removal path to use kernfs_remove_self(). The - * "delete" implementation can simply invoke kernfs_remove_self() on self - * before proceeding with the usual removal path. kernfs will ignore later - * kernfs_remove() on self. - * - * kernfs_remove_self() can be called multiple times concurrently on the - * same kernfs_node. Only the first one actually performs removal and - * returns %true. All others will wait until the kernfs operation which - * won self-removal finishes and return %false. Note that the losers wait - * for the completion of not only the winning kernfs_remove_self() but also - * the whole kernfs_ops which won the arbitration. This can be used to - * guarantee, for example, all concurrent writes to a "delete" file to - * finish only after the whole operation is complete. - */ -bool kernfs_remove_self(struct kernfs_node *kn) -{ - bool ret; - - mutex_lock(&kernfs_mutex); - __kernfs_deactivate_self(kn); - - /* - * SUICIDAL is used to arbitrate among competing invocations. Only - * the first one will actually perform removal. When the removal - * is complete, SUICIDED is set and the active ref is restored - * while holding kernfs_mutex. The ones which lost arbitration - * waits for SUICDED && drained which can happen only after the - * enclosing kernfs operation which executed the winning instance - * of kernfs_remove_self() finished. - */ - if (!(kn->flags & KERNFS_SUICIDAL)) { - kn->flags |= KERNFS_SUICIDAL; - __kernfs_remove(kn); - kn->flags |= KERNFS_SUICIDED; - ret = true; - } else { - wait_queue_head_t *waitq = &kernfs_root(kn)->deactivate_waitq; - DEFINE_WAIT(wait); - - while (true) { - prepare_to_wait(waitq, &wait, TASK_UNINTERRUPTIBLE); - - if ((kn->flags & KERNFS_SUICIDED) && - atomic_read(&kn->active) == KN_DEACTIVATED_BIAS) - break; - - mutex_unlock(&kernfs_mutex); - schedule(); - mutex_lock(&kernfs_mutex); - } - finish_wait(waitq, &wait); - WARN_ON_ONCE(!RB_EMPTY_NODE(&kn->rb)); - ret = false; - } - - __kernfs_reactivate_self(kn); - mutex_unlock(&kernfs_mutex); - return ret; -} - -/** * kernfs_remove_by_name_ns - find a kernfs_node by name and remove it * @parent: parent of the target * @name: name of the kernfs_node to remove diff --git a/fs/sysfs/file.c b/fs/sysfs/file.c index 1b8b91b..810cf6e 100644 --- a/fs/sysfs/file.c +++ b/fs/sysfs/file.c @@ -372,29 +372,6 @@ void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, } EXPORT_SYMBOL_GPL(sysfs_remove_file_ns); -/** - * sysfs_remove_file_self - remove an object attribute from its own method - * @kobj: object we're acting for - * @attr: attribute descriptor - * - * See kernfs_remove_self() for details. - */ -bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr) -{ - struct kernfs_node *parent = kobj->sd; - struct kernfs_node *kn; - bool ret; - - kn = kernfs_find_and_get(parent, attr->name); - if (WARN_ON_ONCE(!kn)) - return false; - - ret = kernfs_remove_self(kn); - - kernfs_put(kn); - return ret; -} - void sysfs_remove_files(struct kobject *kobj, const struct attribute **ptr) { int i; diff --git a/include/linux/device.h b/include/linux/device.h index 1ff3f16..952b010 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -560,8 +560,6 @@ extern int device_create_file(struct device *device, const struct device_attribute *entry); extern void device_remove_file(struct device *dev, const struct device_attribute *attr); -extern bool device_remove_file_self(struct device *dev, - const struct device_attribute *attr); extern int __must_check device_create_bin_file(struct device *dev, const struct bin_attribute *attr); extern void device_remove_bin_file(struct device *dev, diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 0b7b7cc..ac86930 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -43,8 +43,6 @@ enum kernfs_node_flag { KERNFS_HAS_MMAP = 0x0080, KERNFS_LOCKDEP = 0x0100, KERNFS_STATIC_NAME = 0x0200, - KERNFS_SUICIDAL = 0x0400, - KERNFS_SUICIDED = 0x0800, }; /* type-specific structures for kernfs_node union members */ @@ -241,7 +239,6 @@ void kernfs_reactivate(struct kernfs_node *kn); void kernfs_deactivate_self(struct kernfs_node *kn); void kernfs_reactivate_self(struct kernfs_node *kn); void kernfs_remove(struct kernfs_node *kn); -bool kernfs_remove_self(struct kernfs_node *kn); int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, const void *ns); int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, @@ -299,9 +296,6 @@ kernfs_create_link(struct kernfs_node *parent, const char *name, static inline void kernfs_remove(struct kernfs_node *kn) { } -static inline bool kernfs_remove_self(struct kernfs_node *kn) -{ return false; } - static inline int kernfs_remove_by_name_ns(struct kernfs_node *kn, const char *name, const void *ns) { return -ENOSYS; } diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index bd96c60..30b2ebe 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -198,7 +198,6 @@ int __must_check sysfs_chmod_file(struct kobject *kobj, const struct attribute *attr, umode_t mode); void sysfs_remove_file_ns(struct kobject *kobj, const struct attribute *attr, const void *ns); -bool sysfs_remove_file_self(struct kobject *kobj, const struct attribute *attr); void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr); int __must_check sysfs_create_bin_file(struct kobject *kobj, @@ -302,12 +301,6 @@ static inline void sysfs_remove_file_ns(struct kobject *kobj, { } -static inline bool sysfs_remove_file_self(struct kobject *kobj, - const struct attribute *attr) -{ - return false; -} - static inline void sysfs_remove_files(struct kobject *kobj, const struct attribute **attr) { -- cgit v1.1 From 9b0925a6ff64a33be45497e3c798bfee8790b102 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 14:09:38 -0800 Subject: Revert "kernfs: implement kernfs_{de|re}activate[_self]()" This reverts commit 9f010c2ad5194a4b682e747984477850fabd03be. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 118 +------------------------------------------------ include/linux/kernfs.h | 7 +-- 2 files changed, 2 insertions(+), 123 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 1aeb579..37dd640 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -396,7 +396,6 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, atomic_set(&kn->count, 1); atomic_set(&kn->active, KN_DEACTIVATED_BIAS); - kn->deact_depth = 1; RB_CLEAR_NODE(&kn->rb); kn->name = name; @@ -462,7 +461,6 @@ int kernfs_add_one(struct kernfs_node *kn, struct kernfs_node *parent) /* Mark the entry added into directory tree */ atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); - kn->deact_depth--; ret = 0; out_unlock: mutex_unlock(&kernfs_mutex); @@ -563,7 +561,6 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) } atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); - kn->deact_depth--; kn->priv = priv; kn->dir.root = root; @@ -776,8 +773,7 @@ static void __kernfs_deactivate(struct kernfs_node *kn) /* prevent any new usage under @kn by deactivating all nodes */ pos = NULL; while ((pos = kernfs_next_descendant_post(pos, kn))) { - if (!pos->deact_depth++) { - WARN_ON_ONCE(atomic_read(&pos->active) < 0); + if (atomic_read(&pos->active) >= 0) { atomic_add(KN_DEACTIVATED_BIAS, &pos->active); pos->flags |= KERNFS_JUST_DEACTIVATED; } @@ -801,118 +797,6 @@ static void __kernfs_deactivate(struct kernfs_node *kn) } } -static void __kernfs_reactivate(struct kernfs_node *kn) -{ - struct kernfs_node *pos; - - lockdep_assert_held(&kernfs_mutex); - - pos = NULL; - while ((pos = kernfs_next_descendant_post(pos, kn))) { - if (!--pos->deact_depth) { - WARN_ON_ONCE(atomic_read(&pos->active) >= 0); - atomic_sub(KN_DEACTIVATED_BIAS, &pos->active); - } - WARN_ON_ONCE(pos->deact_depth < 0); - } - - /* some nodes reactivated, kick get_active waiters */ - wake_up_all(&kernfs_root(kn)->deactivate_waitq); -} - -static void __kernfs_deactivate_self(struct kernfs_node *kn) -{ - /* - * Take out ourself out of the active ref dependency chain and - * deactivate. If we're called without an active ref, lockdep will - * complain. - */ - kernfs_put_active(kn); - __kernfs_deactivate(kn); -} - -static void __kernfs_reactivate_self(struct kernfs_node *kn) -{ - __kernfs_reactivate(kn); - /* - * Restore active ref dropped by deactivate_self() so that it's - * balanced on return. put_active() will soon be called on @kn, so - * this can't break anything regardless of @kn's state. - */ - atomic_inc(&kn->active); - if (kernfs_lockdep(kn)) - rwsem_acquire(&kn->dep_map, 0, 1, _RET_IP_); -} - -/** - * kernfs_deactivate - deactivate subtree of a node - * @kn: kernfs_node to deactivate subtree of - * - * Deactivate the subtree of @kn. On return, there's no active operation - * going on under @kn and creation or renaming of a node under @kn is - * blocked until @kn is reactivated or removed. This function can be - * called multiple times and nests properly. Each invocation should be - * paired with kernfs_reactivate(). - * - * For a kernfs user which uses simple locking, the subsystem lock would - * nest inside active reference. This becomes problematic if the user - * tries to remove nodes while holding the subystem lock as it would create - * a reverse locking dependency from the subsystem lock to active ref. - * This function can be used to break such reverse dependency. The user - * can call this function outside the subsystem lock and then proceed to - * invoke kernfs_remove() while holding the subsystem lock without - * introducing such reverse dependency. - */ -void kernfs_deactivate(struct kernfs_node *kn) -{ - mutex_lock(&kernfs_mutex); - __kernfs_deactivate(kn); - mutex_unlock(&kernfs_mutex); -} - -/** - * kernfs_reactivate - reactivate subtree of a node - * @kn: kernfs_node to reactivate subtree of - * - * Undo kernfs_deactivate(). - */ -void kernfs_reactivate(struct kernfs_node *kn) -{ - mutex_lock(&kernfs_mutex); - __kernfs_reactivate(kn); - mutex_unlock(&kernfs_mutex); -} - -/** - * kernfs_deactivate_self - deactivate subtree of a node from its own method - * @kn: the self kernfs_node to deactivate subtree of - * - * The caller must be running off of a kernfs operation which is invoked - * with an active reference - e.g. one of kernfs_ops. Once this function - * is called, @kn may be removed by someone else while the enclosing method - * is in progress. Other than that, this function is equivalent to - * kernfs_deactivate() and should be paired with kernfs_reactivate_self(). - */ -void kernfs_deactivate_self(struct kernfs_node *kn) -{ - mutex_lock(&kernfs_mutex); - __kernfs_deactivate_self(kn); - mutex_unlock(&kernfs_mutex); -} - -/** - * kernfs_reactivate_self - reactivate subtree of a node from its own method - * @kn: the self kernfs_node to reactivate subtree of - * - * Undo kernfs_deactivate_self(). - */ -void kernfs_reactivate_self(struct kernfs_node *kn) -{ - mutex_lock(&kernfs_mutex); - __kernfs_reactivate_self(kn); - mutex_unlock(&kernfs_mutex); -} - static void __kernfs_remove(struct kernfs_node *kn) { struct kernfs_root *root = kernfs_root(kn); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index ac86930..9b5a4bb 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -80,8 +80,6 @@ struct kernfs_elem_attr { struct kernfs_node { atomic_t count; atomic_t active; - int deact_depth; - unsigned int hash; /* ns + name hash */ #ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map; #endif @@ -92,6 +90,7 @@ struct kernfs_node { struct rb_node rb; const void *ns; /* namespace tag */ + unsigned int hash; /* ns + name hash */ union { struct kernfs_elem_dir dir; struct kernfs_elem_symlink symlink; @@ -234,10 +233,6 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, const char *name, struct kernfs_node *target); -void kernfs_deactivate(struct kernfs_node *kn); -void kernfs_reactivate(struct kernfs_node *kn); -void kernfs_deactivate_self(struct kernfs_node *kn); -void kernfs_reactivate_self(struct kernfs_node *kn); void kernfs_remove(struct kernfs_node *kn); int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, const void *ns); -- cgit v1.1 From f4b3e631b39db31f7375cce0b5e4111d14cde511 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 14:13:39 -0800 Subject: Revert "kernfs: make kernfs_get_active() block if the node is deactivated but not removed" This reverts commit 895a068a524e134900b9d98b519309b7aae7bbb1. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 37dd640..770d687 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -149,25 +149,12 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn) if (unlikely(!kn)) return NULL; - if (kernfs_lockdep(kn)) - rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_); - - /* - * Try to obtain an active ref. If @kn is deactivated, we block - * till either it's reactivated or killed. - */ - do { - if (atomic_inc_unless_negative(&kn->active)) - return kn; - - wait_event(kernfs_root(kn)->deactivate_waitq, - atomic_read(&kn->active) >= 0 || - RB_EMPTY_NODE(&kn->rb)); - } while (!RB_EMPTY_NODE(&kn->rb)); + if (!atomic_inc_unless_negative(&kn->active)) + return NULL; if (kernfs_lockdep(kn)) - rwsem_release(&kn->dep_map, 1, _RET_IP_); - return NULL; + rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_); + return kn; } /** @@ -799,7 +786,6 @@ static void __kernfs_deactivate(struct kernfs_node *kn) static void __kernfs_remove(struct kernfs_node *kn) { - struct kernfs_root *root = kernfs_root(kn); struct kernfs_node *pos; lockdep_assert_held(&kernfs_mutex); @@ -851,9 +837,6 @@ static void __kernfs_remove(struct kernfs_node *kn) kernfs_put(pos); } while (pos != kn); - - /* some nodes killed, kick get_active waiters */ - wake_up_all(&root->deactivate_waitq); } /** -- cgit v1.1 From 7653fe9d6cddc3fc5e4220608079006d8ac0054c Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 14:20:56 -0800 Subject: Revert "kernfs: remove kernfs_addrm_cxt" This reverts commit 99177a34110889a8f2c36420c34e3bcc9bfd8a70. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 114 ++++++++++++++++++++++++++++++++++---------- fs/kernfs/file.c | 10 +++- fs/kernfs/kernfs-internal.h | 12 ++++- fs/kernfs/symlink.c | 10 +++- include/linux/kernfs.h | 4 ++ 5 files changed, 121 insertions(+), 29 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 770d687..f878e4f 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -399,7 +399,28 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, } /** + * kernfs_addrm_start - prepare for kernfs_node add/remove + * @acxt: pointer to kernfs_addrm_cxt to be used + * + * This function is called when the caller is about to add or remove + * kernfs_node. This function acquires kernfs_mutex. @acxt is used + * to keep and pass context to other addrm functions. + * + * LOCKING: + * Kernel thread context (may sleep). kernfs_mutex is locked on + * return. + */ +void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt) + __acquires(kernfs_mutex) +{ + memset(acxt, 0, sizeof(*acxt)); + + mutex_lock(&kernfs_mutex); +} + +/** * kernfs_add_one - add kernfs_node to parent without warning + * @acxt: addrm context to use * @kn: kernfs_node to be added * @parent: the parent kernfs_node to add @kn to * @@ -407,29 +428,34 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, * parent inode if @kn is a directory and link into the children list * of the parent. * + * This function should be called between calls to + * kernfs_addrm_start() and kernfs_addrm_finish() and should be passed + * the same @acxt as passed to kernfs_addrm_start(). + * + * LOCKING: + * Determined by kernfs_addrm_start(). + * * RETURNS: * 0 on success, -EEXIST if entry with the given name already * exists. */ -int kernfs_add_one(struct kernfs_node *kn, struct kernfs_node *parent) +int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, + struct kernfs_node *parent) { + bool has_ns = kernfs_ns_enabled(parent); struct kernfs_iattrs *ps_iattr; - bool has_ns; int ret; - if (!kernfs_get_active(parent)) - return -ENOENT; + WARN_ON_ONCE(atomic_read(&parent->active) < 0); - mutex_lock(&kernfs_mutex); - - ret = -EINVAL; - has_ns = kernfs_ns_enabled(parent); - if (WARN(has_ns != (bool)kn->ns, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", - has_ns ? "required" : "invalid", parent->name, kn->name)) - goto out_unlock; + if (has_ns != (bool)kn->ns) { + WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", + has_ns ? "required" : "invalid", parent->name, kn->name); + return -EINVAL; + } if (kernfs_type(parent) != KERNFS_DIR) - goto out_unlock; + return -EINVAL; kn->hash = kernfs_name_hash(kn->name, kn->ns); kn->parent = parent; @@ -437,7 +463,7 @@ int kernfs_add_one(struct kernfs_node *kn, struct kernfs_node *parent) ret = kernfs_link_sibling(kn); if (ret) - goto out_unlock; + return ret; /* Update timestamps on the parent */ ps_iattr = parent->iattr; @@ -448,11 +474,34 @@ int kernfs_add_one(struct kernfs_node *kn, struct kernfs_node *parent) /* Mark the entry added into directory tree */ atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); - ret = 0; -out_unlock: + return 0; +} + +/** + * kernfs_addrm_finish - finish up kernfs_node add/remove + * @acxt: addrm context to finish up + * + * Finish up kernfs_node add/remove. Resources acquired by + * kernfs_addrm_start() are released and removed kernfs_nodes are + * cleaned up. + * + * LOCKING: + * kernfs_mutex is released. + */ +void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt) + __releases(kernfs_mutex) +{ + /* release resources acquired by kernfs_addrm_start() */ mutex_unlock(&kernfs_mutex); - kernfs_put_active(parent); - return ret; + + /* kill removed kernfs_nodes */ + while (acxt->removed) { + struct kernfs_node *kn = acxt->removed; + + acxt->removed = kn->u.removed_list; + + kernfs_put(kn); + } } /** @@ -584,6 +633,7 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, const char *name, umode_t mode, void *priv, const void *ns) { + struct kernfs_addrm_cxt acxt; struct kernfs_node *kn; int rc; @@ -598,7 +648,14 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, kn->priv = priv; /* link in */ - rc = kernfs_add_one(kn, parent); + rc = -ENOENT; + if (kernfs_get_active(parent)) { + kernfs_addrm_start(&acxt); + rc = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); + kernfs_put_active(parent); + } + if (!rc) return kn; @@ -784,7 +841,8 @@ static void __kernfs_deactivate(struct kernfs_node *kn) } } -static void __kernfs_remove(struct kernfs_node *kn) +static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, + struct kernfs_node *kn) { struct kernfs_node *pos; @@ -832,7 +890,8 @@ static void __kernfs_remove(struct kernfs_node *kn) ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; } - kernfs_put(pos); + pos->u.removed_list = acxt->removed; + acxt->removed = pos; } kernfs_put(pos); @@ -847,9 +906,11 @@ static void __kernfs_remove(struct kernfs_node *kn) */ void kernfs_remove(struct kernfs_node *kn) { - mutex_lock(&kernfs_mutex); - __kernfs_remove(kn); - mutex_unlock(&kernfs_mutex); + struct kernfs_addrm_cxt acxt; + + kernfs_addrm_start(&acxt); + __kernfs_remove(&acxt, kn); + kernfs_addrm_finish(&acxt); } /** @@ -864,6 +925,7 @@ void kernfs_remove(struct kernfs_node *kn) int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, const void *ns) { + struct kernfs_addrm_cxt acxt; struct kernfs_node *kn; if (!parent) { @@ -872,13 +934,13 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, return -ENOENT; } - mutex_lock(&kernfs_mutex); + kernfs_addrm_start(&acxt); kn = kernfs_find_ns(parent, name, ns); if (kn) - __kernfs_remove(kn); + __kernfs_remove(&acxt, kn); - mutex_unlock(&kernfs_mutex); + kernfs_addrm_finish(&acxt); if (kn) return 0; diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index ffe1beb..404ffd2 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -817,6 +817,7 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, bool name_is_static, struct lock_class_key *key) { + struct kernfs_addrm_cxt acxt; struct kernfs_node *kn; unsigned flags; int rc; @@ -852,7 +853,14 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, if (ops->mmap) kn->flags |= KERNFS_HAS_MMAP; - rc = kernfs_add_one(kn, parent); + rc = -ENOENT; + if (kernfs_get_active(parent)) { + kernfs_addrm_start(&acxt); + rc = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); + kernfs_put_active(parent); + } + if (rc) { kernfs_put(kn); return ERR_PTR(rc); diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 4bc5784..e9ec38c 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -46,6 +46,13 @@ static inline struct kernfs_root *kernfs_root(struct kernfs_node *kn) } /* + * Context structure to be used while adding/removing nodes. + */ +struct kernfs_addrm_cxt { + struct kernfs_node *removed; +}; + +/* * mount.c */ struct kernfs_super_info { @@ -94,7 +101,10 @@ extern const struct inode_operations kernfs_dir_iops; struct kernfs_node *kernfs_get_active(struct kernfs_node *kn); void kernfs_put_active(struct kernfs_node *kn); -int kernfs_add_one(struct kernfs_node *kn, struct kernfs_node *parent); +void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt); +int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, + struct kernfs_node *parent); +void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt); struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, umode_t mode, unsigned flags); diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 3a939c263..b2c106c 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -27,6 +27,7 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, struct kernfs_node *target) { struct kernfs_node *kn; + struct kernfs_addrm_cxt acxt; int error; kn = kernfs_new_node(kernfs_root(parent), name, S_IFLNK|S_IRWXUGO, @@ -39,7 +40,14 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, kn->symlink.target_kn = target; kernfs_get(target); /* ref owned by symlink */ - error = kernfs_add_one(kn, parent); + error = -ENOENT; + if (kernfs_get_active(parent)) { + kernfs_addrm_start(&acxt); + error = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); + kernfs_put_active(parent); + } + if (!error) return kn; diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 9b5a4bb..61bd7ae 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -89,6 +89,10 @@ struct kernfs_node { struct rb_node rb; + union { + struct kernfs_node *removed_list; + } u; + const void *ns; /* namespace tag */ unsigned int hash; /* ns + name hash */ union { -- cgit v1.1 From 55f6e30d0a6a8975cc0831e8a4a3715b815b6a2f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 14:27:16 -0800 Subject: Revert "kernfs: invoke kernfs_unmap_bin_file() directly from __kernfs_remove()" This reverts commit f601f9a2bf7dc1f7ee18feece4c4e2fc6845d6c4. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 42 +++++++++--------------------------------- fs/kernfs/file.c | 5 ++++- fs/kernfs/kernfs-internal.h | 2 +- 3 files changed, 14 insertions(+), 35 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index f878e4f..e565ec0 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -121,17 +121,13 @@ static int kernfs_link_sibling(struct kernfs_node *kn) * Locking: * mutex_lock(kernfs_mutex) */ -static bool kernfs_unlink_sibling(struct kernfs_node *kn) +static void kernfs_unlink_sibling(struct kernfs_node *kn) { - if (RB_EMPTY_NODE(&kn->rb)) - return false; - if (kernfs_type(kn) == KERNFS_DIR) kn->parent->dir.subdirs--; rb_erase(&kn->rb, &kn->parent->dir.children); RB_CLEAR_NODE(&kn->rb); - return true; } /** @@ -500,6 +496,7 @@ void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt) acxt->removed = kn->u.removed_list; + kernfs_unmap_bin_file(kn); kernfs_put(kn); } } @@ -857,44 +854,23 @@ static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, /* unlink the subtree node-by-node */ do { - pos = kernfs_leftmost_descendant(kn); - - /* - * We're gonna release kernfs_mutex to unmap bin files, - * Make sure @pos doesn't go away inbetween. - */ - kernfs_get(pos); + struct kernfs_iattrs *ps_iattr; - /* - * This must be come before unlinking; otherwise, when - * there are multiple removers, some may finish before - * unmapping is complete. - */ - if (pos->flags & KERNFS_HAS_MMAP) { - mutex_unlock(&kernfs_mutex); - kernfs_unmap_file(pos); - mutex_lock(&kernfs_mutex); - } + pos = kernfs_leftmost_descendant(kn); - /* - * kernfs_unlink_sibling() succeeds once per node. Use it - * to decide who's responsible for cleanups. - */ - if (!pos->parent || kernfs_unlink_sibling(pos)) { - struct kernfs_iattrs *ps_iattr = - pos->parent ? pos->parent->iattr : NULL; + if (pos->parent) { + kernfs_unlink_sibling(pos); /* update timestamps on the parent */ + ps_iattr = pos->parent->iattr; if (ps_iattr) { ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; } - - pos->u.removed_list = acxt->removed; - acxt->removed = pos; } - kernfs_put(pos); + pos->u.removed_list = acxt->removed; + acxt->removed = pos; } while (pos != kn); } diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 404ffd2..231a171 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -700,11 +700,14 @@ static int kernfs_fop_release(struct inode *inode, struct file *filp) return 0; } -void kernfs_unmap_file(struct kernfs_node *kn) +void kernfs_unmap_bin_file(struct kernfs_node *kn) { struct kernfs_open_node *on; struct kernfs_open_file *of; + if (!(kn->flags & KERNFS_HAS_MMAP)) + return; + spin_lock_irq(&kernfs_open_node_lock); on = kn->attr.open; if (on) diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index e9ec38c..57a93f4 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -113,7 +113,7 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, */ extern const struct file_operations kernfs_file_fops; -void kernfs_unmap_file(struct kernfs_node *kn); +void kernfs_unmap_bin_file(struct kernfs_node *kn); /* * symlink.c -- cgit v1.1 From 4f4b1b6471cf219d136776f9ff9631a07c4e92b5 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 14:30:47 -0800 Subject: Revert "kernfs: restructure removal path to fix possible premature return" This reverts commit 45a140e587f3d32d8d424ed940dffb61e1739047. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 139 +++++++++++++++++++------------------------------ include/linux/kernfs.h | 1 - 2 files changed, 53 insertions(+), 87 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index e565ec0..7f8afc1 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -181,38 +181,14 @@ void kernfs_put_active(struct kernfs_node *kn) * kernfs_drain - drain kernfs_node * @kn: kernfs_node to drain * - * Drain existing usages of @kn. Mutiple removers may invoke this function - * concurrently on @kn and all will return after draining is complete. - * Returns %true if drain is performed and kernfs_mutex was temporarily - * released. %false if @kn was already drained and no operation was - * necessary. - * - * The caller is responsible for ensuring @kn stays pinned while this - * function is in progress even if it gets removed by someone else. + * Drain existing usages. */ -static bool kernfs_drain(struct kernfs_node *kn) - __releases(&kernfs_mutex) __acquires(&kernfs_mutex) +static void kernfs_drain(struct kernfs_node *kn) { struct kernfs_root *root = kernfs_root(kn); - lockdep_assert_held(&kernfs_mutex); WARN_ON_ONCE(atomic_read(&kn->active) >= 0); - /* - * We want to go through the active ref lockdep annotation at least - * once for all node removals, but the lockdep annotation can't be - * nested inside kernfs_mutex and deactivation can't make forward - * progress if we keep dropping the mutex. Use JUST_ACTIVATED to - * force the slow path once for each deactivation if lockdep is - * enabled. - */ - if ((!kernfs_lockdep(kn) || !(kn->flags & KERNFS_JUST_DEACTIVATED)) && - atomic_read(&kn->active) == KN_DEACTIVATED_BIAS) - return false; - - kn->flags &= ~KERNFS_JUST_DEACTIVATED; - mutex_unlock(&kernfs_mutex); - if (kernfs_lockdep(kn)) { rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) @@ -226,9 +202,6 @@ static bool kernfs_drain(struct kernfs_node *kn) lock_acquired(&kn->dep_map, _RET_IP_); rwsem_release(&kn->dep_map, 1, _RET_IP_); } - - mutex_lock(&kernfs_mutex); - return true; } /** @@ -474,6 +447,49 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, } /** + * kernfs_remove_one - remove kernfs_node from parent + * @acxt: addrm context to use + * @kn: kernfs_node to be removed + * + * Mark @kn removed and drop nlink of parent inode if @kn is a + * directory. @kn is unlinked from the children list. + * + * This function should be called between calls to + * kernfs_addrm_start() and kernfs_addrm_finish() and should be + * passed the same @acxt as passed to kernfs_addrm_start(). + * + * LOCKING: + * Determined by kernfs_addrm_start(). + */ +static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt, + struct kernfs_node *kn) +{ + struct kernfs_iattrs *ps_iattr; + + /* + * Removal can be called multiple times on the same node. Only the + * first invocation is effective and puts the base ref. + */ + if (atomic_read(&kn->active) < 0) + return; + + if (kn->parent) { + kernfs_unlink_sibling(kn); + + /* Update timestamps on the parent */ + ps_iattr = kn->parent->iattr; + if (ps_iattr) { + ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; + ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; + } + } + + atomic_add(KN_DEACTIVATED_BIAS, &kn->active); + kn->u.removed_list = acxt->removed; + acxt->removed = kn; +} + +/** * kernfs_addrm_finish - finish up kernfs_node add/remove * @acxt: addrm context to finish up * @@ -496,6 +512,7 @@ void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt) acxt->removed = kn->u.removed_list; + kernfs_drain(kn); kernfs_unmap_bin_file(kn); kernfs_put(kn); } @@ -805,73 +822,23 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos, return pos->parent; } -static void __kernfs_deactivate(struct kernfs_node *kn) -{ - struct kernfs_node *pos; - - lockdep_assert_held(&kernfs_mutex); - - /* prevent any new usage under @kn by deactivating all nodes */ - pos = NULL; - while ((pos = kernfs_next_descendant_post(pos, kn))) { - if (atomic_read(&pos->active) >= 0) { - atomic_add(KN_DEACTIVATED_BIAS, &pos->active); - pos->flags |= KERNFS_JUST_DEACTIVATED; - } - } - - /* - * Drain the subtree. If kernfs_drain() blocked to drain, which is - * indicated by %true return, it temporarily released kernfs_mutex - * and the rbtree might have been modified inbetween breaking our - * future walk. Restart the walk after each %true return. - */ - pos = NULL; - while ((pos = kernfs_next_descendant_post(pos, kn))) { - bool drained; - - kernfs_get(pos); - drained = kernfs_drain(pos); - kernfs_put(pos); - if (drained) - pos = NULL; - } -} - static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn) { - struct kernfs_node *pos; - - lockdep_assert_held(&kernfs_mutex); + struct kernfs_node *pos, *next; if (!kn) return; pr_debug("kernfs %s: removing\n", kn->name); - __kernfs_deactivate(kn); - - /* unlink the subtree node-by-node */ + next = NULL; do { - struct kernfs_iattrs *ps_iattr; - - pos = kernfs_leftmost_descendant(kn); - - if (pos->parent) { - kernfs_unlink_sibling(pos); - - /* update timestamps on the parent */ - ps_iattr = pos->parent->iattr; - if (ps_iattr) { - ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; - ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; - } - } - - pos->u.removed_list = acxt->removed; - acxt->removed = pos; - } while (pos != kn); + pos = next; + next = kernfs_next_descendant_post(pos, kn); + if (pos) + kernfs_remove_one(acxt, pos); + } while (next); } /** diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 61bd7ae..289d4f6 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -37,7 +37,6 @@ enum kernfs_node_type { #define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK enum kernfs_node_flag { - KERNFS_JUST_DEACTIVATED = 0x0010, /* used to aid lockdep annotation */ KERNFS_NS = 0x0020, KERNFS_HAS_SEQ_SHOW = 0x0040, KERNFS_HAS_MMAP = 0x0080, -- cgit v1.1 From 798c75a0d44cdbd6e3d82a6a676e6de38525b3bb Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 14:36:03 -0800 Subject: Revert "kernfs: remove KERNFS_REMOVED" This reverts commit ae34372eb8408b3d07e870f1939f99007a730d28. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 79 ++++++++++++++++++++------------------------- fs/kernfs/file.c | 10 ++---- fs/kernfs/kernfs-internal.h | 3 +- fs/kernfs/symlink.c | 10 ++---- include/linux/kernfs.h | 1 + 5 files changed, 43 insertions(+), 60 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 7f8afc1..1c9130a3 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -127,7 +127,6 @@ static void kernfs_unlink_sibling(struct kernfs_node *kn) kn->parent->dir.subdirs--; rb_erase(&kn->rb, &kn->parent->dir.children); - RB_CLEAR_NODE(&kn->rb); } /** @@ -178,16 +177,18 @@ void kernfs_put_active(struct kernfs_node *kn) } /** - * kernfs_drain - drain kernfs_node - * @kn: kernfs_node to drain + * kernfs_deactivate - deactivate kernfs_node + * @kn: kernfs_node to deactivate * - * Drain existing usages. + * Deny new active references and drain existing ones. */ -static void kernfs_drain(struct kernfs_node *kn) +static void kernfs_deactivate(struct kernfs_node *kn) { struct kernfs_root *root = kernfs_root(kn); - WARN_ON_ONCE(atomic_read(&kn->active) >= 0); + BUG_ON(!(kn->flags & KERNFS_REMOVED)); + + atomic_add(KN_DEACTIVATED_BIAS, &kn->active); if (kernfs_lockdep(kn)) { rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); @@ -232,15 +233,13 @@ void kernfs_put(struct kernfs_node *kn) return; root = kernfs_root(kn); repeat: - /* - * Moving/renaming is always done while holding reference. + /* Moving/renaming is always done while holding reference. * kn->parent won't change beneath us. */ parent = kn->parent; - WARN_ONCE(atomic_read(&kn->active) != KN_DEACTIVATED_BIAS, - "kernfs_put: %s/%s: released with incorrect active_ref %d\n", - parent ? parent->name : "", kn->name, atomic_read(&kn->active)); + WARN(!(kn->flags & KERNFS_REMOVED), "kernfs: free using entry: %s/%s\n", + parent ? parent->name : "", kn->name); if (kernfs_type(kn) == KERNFS_LINK) kernfs_put(kn->symlink.target_kn); @@ -282,8 +281,8 @@ static int kernfs_dop_revalidate(struct dentry *dentry, unsigned int flags) kn = dentry->d_fsdata; mutex_lock(&kernfs_mutex); - /* Force fresh lookup if removed */ - if (kn->parent && RB_EMPTY_NODE(&kn->rb)) + /* The kernfs node has been deleted */ + if (kn->flags & KERNFS_REMOVED) goto out_bad; /* The kernfs node has been moved? */ @@ -351,12 +350,11 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, kn->ino = ret; atomic_set(&kn->count, 1); - atomic_set(&kn->active, KN_DEACTIVATED_BIAS); - RB_CLEAR_NODE(&kn->rb); + atomic_set(&kn->active, 0); kn->name = name; kn->mode = mode; - kn->flags = flags; + kn->flags = flags | KERNFS_REMOVED; return kn; @@ -415,8 +413,6 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, struct kernfs_iattrs *ps_iattr; int ret; - WARN_ON_ONCE(atomic_read(&parent->active) < 0); - if (has_ns != (bool)kn->ns) { WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", has_ns ? "required" : "invalid", parent->name, kn->name); @@ -426,6 +422,9 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, if (kernfs_type(parent) != KERNFS_DIR) return -EINVAL; + if (parent->flags & KERNFS_REMOVED) + return -ENOENT; + kn->hash = kernfs_name_hash(kn->name, kn->ns); kn->parent = parent; kernfs_get(parent); @@ -442,7 +441,8 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, } /* Mark the entry added into directory tree */ - atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); + kn->flags &= ~KERNFS_REMOVED; + return 0; } @@ -470,7 +470,7 @@ static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt, * Removal can be called multiple times on the same node. Only the * first invocation is effective and puts the base ref. */ - if (atomic_read(&kn->active) < 0) + if (kn->flags & KERNFS_REMOVED) return; if (kn->parent) { @@ -484,7 +484,7 @@ static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt, } } - atomic_add(KN_DEACTIVATED_BIAS, &kn->active); + kn->flags |= KERNFS_REMOVED; kn->u.removed_list = acxt->removed; acxt->removed = kn; } @@ -512,7 +512,7 @@ void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt) acxt->removed = kn->u.removed_list; - kernfs_drain(kn); + kernfs_deactivate(kn); kernfs_unmap_bin_file(kn); kernfs_put(kn); } @@ -610,7 +610,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) return ERR_PTR(-ENOMEM); } - atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); + kn->flags &= ~KERNFS_REMOVED; kn->priv = priv; kn->dir.root = root; @@ -662,13 +662,9 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, kn->priv = priv; /* link in */ - rc = -ENOENT; - if (kernfs_get_active(parent)) { - kernfs_addrm_start(&acxt); - rc = kernfs_add_one(&acxt, kn, parent); - kernfs_addrm_finish(&acxt); - kernfs_put_active(parent); - } + kernfs_addrm_start(&acxt); + rc = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); if (!rc) return kn; @@ -903,29 +899,27 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, { int error; + mutex_lock(&kernfs_mutex); + error = -ENOENT; - if (!kernfs_get_active(new_parent)) + if ((kn->flags | new_parent->flags) & KERNFS_REMOVED) goto out; - if (!kernfs_get_active(kn)) - goto out_put_new_parent; - - mutex_lock(&kernfs_mutex); error = 0; if ((kn->parent == new_parent) && (kn->ns == new_ns) && (strcmp(kn->name, new_name) == 0)) - goto out_unlock; /* nothing to rename */ + goto out; /* nothing to rename */ error = -EEXIST; if (kernfs_find_ns(new_parent, new_name, new_ns)) - goto out_unlock; + goto out; /* rename kernfs_node */ if (strcmp(kn->name, new_name) != 0) { error = -ENOMEM; new_name = kstrdup(new_name, GFP_KERNEL); if (!new_name) - goto out_unlock; + goto out; if (kn->flags & KERNFS_STATIC_NAME) kn->flags &= ~KERNFS_STATIC_NAME; @@ -947,12 +941,8 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, kernfs_link_sibling(kn); error = 0; -out_unlock: + out: mutex_unlock(&kernfs_mutex); - kernfs_put_active(kn); -out_put_new_parent: - kernfs_put_active(new_parent); -out: return error; } @@ -972,7 +962,8 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns, struct kernfs_node *parent, loff_t hash, struct kernfs_node *pos) { if (pos) { - int valid = pos->parent == parent && hash == pos->hash; + int valid = !(pos->flags & KERNFS_REMOVED) && + pos->parent == parent && hash == pos->hash; kernfs_put(pos); if (!valid) pos = NULL; diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 231a171..bdd3885 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -856,13 +856,9 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, if (ops->mmap) kn->flags |= KERNFS_HAS_MMAP; - rc = -ENOENT; - if (kernfs_get_active(parent)) { - kernfs_addrm_start(&acxt); - rc = kernfs_add_one(&acxt, kn, parent); - kernfs_addrm_finish(&acxt); - kernfs_put_active(parent); - } + kernfs_addrm_start(&acxt); + rc = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); if (rc) { kernfs_put(kn); diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 57a93f4..c6ba5bc 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -26,8 +26,7 @@ struct kernfs_iattrs { struct simple_xattrs xattrs; }; -/* +1 to avoid triggering overflow warning when negating it */ -#define KN_DEACTIVATED_BIAS (INT_MIN + 1) +#define KN_DEACTIVATED_BIAS INT_MIN /* KERNFS_TYPE_MASK and types are defined in include/linux/kernfs.h */ diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index b2c106c..a03e260 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -40,13 +40,9 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, kn->symlink.target_kn = target; kernfs_get(target); /* ref owned by symlink */ - error = -ENOENT; - if (kernfs_get_active(parent)) { - kernfs_addrm_start(&acxt); - error = kernfs_add_one(&acxt, kn, parent); - kernfs_addrm_finish(&acxt); - kernfs_put_active(parent); - } + kernfs_addrm_start(&acxt); + error = kernfs_add_one(&acxt, kn, parent); + kernfs_addrm_finish(&acxt); if (!error) return kn; diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 289d4f6..42ad32f 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -37,6 +37,7 @@ enum kernfs_node_type { #define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK enum kernfs_node_flag { + KERNFS_REMOVED = 0x0010, KERNFS_NS = 0x0020, KERNFS_HAS_SEQ_SHOW = 0x0040, KERNFS_HAS_MMAP = 0x0080, -- cgit v1.1 From 0890147fe09ff7e8275a162b1ab76ab5e3158c6d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 14:39:52 -0800 Subject: Revert "kernfs: remove KERNFS_ACTIVE_REF and add kernfs_lockdep()" This reverts commit a69d001cfc712b96ec9d7ba44d6285702a38dabf. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 31 +++++++++++-------------------- include/linux/kernfs.h | 1 + 2 files changed, 12 insertions(+), 20 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 1c9130a3..ed62de6 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -22,15 +22,6 @@ DEFINE_MUTEX(kernfs_mutex); #define rb_to_kn(X) rb_entry((X), struct kernfs_node, rb) -static bool kernfs_lockdep(struct kernfs_node *kn) -{ -#ifdef CONFIG_DEBUG_LOCK_ALLOC - return kn->flags & KERNFS_LOCKDEP; -#else - return false; -#endif -} - /** * kernfs_name_hash * @name: Null terminated string to hash @@ -147,7 +138,7 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn) if (!atomic_inc_unless_negative(&kn->active)) return NULL; - if (kernfs_lockdep(kn)) + if (kn->flags & KERNFS_LOCKDEP) rwsem_acquire_read(&kn->dep_map, 0, 1, _RET_IP_); return kn; } @@ -167,7 +158,7 @@ void kernfs_put_active(struct kernfs_node *kn) if (unlikely(!kn)) return; - if (kernfs_lockdep(kn)) + if (kn->flags & KERNFS_LOCKDEP) rwsem_release(&kn->dep_map, 1, _RET_IP_); v = atomic_dec_return(&kn->active); if (likely(v != KN_DEACTIVATED_BIAS)) @@ -188,21 +179,21 @@ static void kernfs_deactivate(struct kernfs_node *kn) BUG_ON(!(kn->flags & KERNFS_REMOVED)); + if (!(kernfs_type(kn) & KERNFS_ACTIVE_REF)) + return; + + rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); + atomic_add(KN_DEACTIVATED_BIAS, &kn->active); - if (kernfs_lockdep(kn)) { - rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); - if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) - lock_contended(&kn->dep_map, _RET_IP_); - } + if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) + lock_contended(&kn->dep_map, _RET_IP_); wait_event(root->deactivate_waitq, atomic_read(&kn->active) == KN_DEACTIVATED_BIAS); - if (kernfs_lockdep(kn)) { - lock_acquired(&kn->dep_map, _RET_IP_); - rwsem_release(&kn->dep_map, 1, _RET_IP_); - } + lock_acquired(&kn->dep_map, _RET_IP_); + rwsem_release(&kn->dep_map, 1, _RET_IP_); } /** diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 42ad32f..232f1a6 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -34,6 +34,7 @@ enum kernfs_node_type { }; #define KERNFS_TYPE_MASK 0x000f +#define KERNFS_ACTIVE_REF KERNFS_FILE #define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK enum kernfs_node_flag { -- cgit v1.1 From 87da149343c8c93f6984c0f4b9da7651709624f7 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 14:43:11 -0800 Subject: Revert "kernfs: replace kernfs_node->u.completion with kernfs_root->deactivate_waitq" This reverts commit ea1c472dfeada211a0100daa7976e8e8e779b858. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 27 ++++++++++++++++----------- include/linux/kernfs.h | 4 ++-- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index ed62de6..510b506 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -8,7 +8,6 @@ * This file is released under the GPLv2. */ -#include #include #include #include @@ -152,7 +151,6 @@ struct kernfs_node *kernfs_get_active(struct kernfs_node *kn) */ void kernfs_put_active(struct kernfs_node *kn) { - struct kernfs_root *root = kernfs_root(kn); int v; if (unlikely(!kn)) @@ -164,7 +162,11 @@ void kernfs_put_active(struct kernfs_node *kn) if (likely(v != KN_DEACTIVATED_BIAS)) return; - wake_up_all(&root->deactivate_waitq); + /* + * atomic_dec_return() is a mb(), we'll always see the updated + * kn->u.completion. + */ + complete(kn->u.completion); } /** @@ -175,22 +177,26 @@ void kernfs_put_active(struct kernfs_node *kn) */ static void kernfs_deactivate(struct kernfs_node *kn) { - struct kernfs_root *root = kernfs_root(kn); + DECLARE_COMPLETION_ONSTACK(wait); + int v; BUG_ON(!(kn->flags & KERNFS_REMOVED)); if (!(kernfs_type(kn) & KERNFS_ACTIVE_REF)) return; - rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); + kn->u.completion = (void *)&wait; - atomic_add(KN_DEACTIVATED_BIAS, &kn->active); + rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); + /* atomic_add_return() is a mb(), put_active() will always see + * the updated kn->u.completion. + */ + v = atomic_add_return(KN_DEACTIVATED_BIAS, &kn->active); - if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) + if (v != KN_DEACTIVATED_BIAS) { lock_contended(&kn->dep_map, _RET_IP_); - - wait_event(root->deactivate_waitq, - atomic_read(&kn->active) == KN_DEACTIVATED_BIAS); + wait_for_completion(&wait); + } lock_acquired(&kn->dep_map, _RET_IP_); rwsem_release(&kn->dep_map, 1, _RET_IP_); @@ -607,7 +613,6 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) root->dir_ops = kdops; root->kn = kn; - init_waitqueue_head(&root->deactivate_waitq); return root; } diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 232f1a6..d2c439d 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include struct file; struct iattr; @@ -91,6 +91,7 @@ struct kernfs_node { struct rb_node rb; union { + struct completion *completion; struct kernfs_node *removed_list; } u; @@ -131,7 +132,6 @@ struct kernfs_root { /* private fields, do not use outside kernfs proper */ struct ida ino_ida; struct kernfs_dir_ops *dir_ops; - wait_queue_head_t deactivate_waitq; }; struct kernfs_open_file { -- cgit v1.1 From 683bb2761fbf123b24aed03a1c0d5d7556ec3018 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Mon, 13 Jan 2014 14:49:01 -0800 Subject: Revert "kernfs: fix get_active failure handling in kernfs_seq_*()" This reverts commit d92d2e6bd72b653f9811e0c9c46307c743b3fc58. Tejun writes: I'm sorry but can you please revert the whole series? get_active() waiting while a node is deactivated has potential to lead to deadlock and that deactivate/reactivate interface is something fundamentally flawed and that cgroup will have to work with the remove_self() like everybody else. IOW, I think the first posting was correct. Cc: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/file.c | 51 +++++++-------------------------------------------- 1 file changed, 7 insertions(+), 44 deletions(-) diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index bdd3885..316604c 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -54,38 +54,6 @@ static const struct kernfs_ops *kernfs_ops(struct kernfs_node *kn) return kn->attr.ops; } -/* - * As kernfs_seq_stop() is also called after kernfs_seq_start() or - * kernfs_seq_next() failure, it needs to distinguish whether it's stopping - * a seq_file iteration which is fully initialized with an active reference - * or an aborted kernfs_seq_start() due to get_active failure. The - * position pointer is the only context for each seq_file iteration and - * thus the stop condition should be encoded in it. As the return value is - * directly visible to userland, ERR_PTR(-ENODEV) is the only acceptable - * choice to indicate get_active failure. - * - * Unfortunately, this is complicated due to the optional custom seq_file - * operations which may return ERR_PTR(-ENODEV) too. kernfs_seq_stop() - * can't distinguish whether ERR_PTR(-ENODEV) is from get_active failure or - * custom seq_file operations and thus can't decide whether put_active - * should be performed or not only on ERR_PTR(-ENODEV). - * - * This is worked around by factoring out the custom seq_stop() and - * put_active part into kernfs_seq_stop_active(), skipping it from - * kernfs_seq_stop() if ERR_PTR(-ENODEV) while invoking it directly after - * custom seq_file operations fail with ERR_PTR(-ENODEV) - this ensures - * that kernfs_seq_stop_active() is skipped only after get_active failure. - */ -static void kernfs_seq_stop_active(struct seq_file *sf, void *v) -{ - struct kernfs_open_file *of = sf->private; - const struct kernfs_ops *ops = kernfs_ops(of->kn); - - if (ops->seq_stop) - ops->seq_stop(sf, v); - kernfs_put_active(of->kn); -} - static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) { struct kernfs_open_file *of = sf->private; @@ -101,11 +69,7 @@ static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) ops = kernfs_ops(of->kn); if (ops->seq_start) { - void *next = ops->seq_start(sf, ppos); - /* see the comment above kernfs_seq_stop_active() */ - if (next == ERR_PTR(-ENODEV)) - kernfs_seq_stop_active(sf, next); - return next; + return ops->seq_start(sf, ppos); } else { /* * The same behavior and code as single_open(). Returns @@ -121,11 +85,7 @@ static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) const struct kernfs_ops *ops = kernfs_ops(of->kn); if (ops->seq_next) { - void *next = ops->seq_next(sf, v, ppos); - /* see the comment above kernfs_seq_stop_active() */ - if (next == ERR_PTR(-ENODEV)) - kernfs_seq_stop_active(sf, next); - return next; + return ops->seq_next(sf, v, ppos); } else { /* * The same behavior and code as single_open(), always @@ -139,9 +99,12 @@ static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) static void kernfs_seq_stop(struct seq_file *sf, void *v) { struct kernfs_open_file *of = sf->private; + const struct kernfs_ops *ops = kernfs_ops(of->kn); - if (v != ERR_PTR(-ENODEV)) - kernfs_seq_stop_active(sf, v); + if (ops->seq_stop) + ops->seq_stop(sf, v); + + kernfs_put_active(of->kn); mutex_unlock(&of->mutex); } -- cgit v1.1 From bb305947bdbb67325e1f949183cdd208fc2a7999 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 14 Jan 2014 09:52:01 -0500 Subject: kernfs: fix get_active failure handling in kernfs_seq_*() When kernfs_seq_start() fails to obtain an active reference, it returns ERR_PTR(-ENODEV). kernfs_seq_stop() is then invoked with the error pointer value; however, it still proceeds to invoke kernfs_put_active() on the node leading to unbalanced put. If kernfs_seq_stop() is called even after active ref failure, it should skip invocation of @ops->seq_stop() and put_active. Unfortunately, this is a bit complicated because active ref failure isn't the only thing which may fail with ERR_PTR(-ENODEV). @ops->seq_start/next() may also fail with the error value and kernfs_seq_stop() doesn't have a way to tell apart those failures. Work it around by factoring out the active part of kernfs_seq_stop() into kernfs_seq_stop_active() and invoking it directly if @ops->seq_start/next() fail with ERR_PTR(-ENODEV) and updating kernfs_seq_stop() to skip kernfs_seq_stop_active() on ERR_PTR(-ENODEV). This is a bit nasty but ensures that the active put is skipped iff get_active failed in kernfs_seq_start(). tj: This was originally committed as d92d2e6bd72b but got reverted by 683bb2761fbf along with other kernfs self removal patches. However, this one is an independent fix and shouldn't have been reverted together. Reinstate the change. Sorry about the mess. Signed-off-by: Tejun Heo Cc: Sasha Levin Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/file.c | 51 ++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 7 deletions(-) diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index 316604c..bdd3885 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -54,6 +54,38 @@ static const struct kernfs_ops *kernfs_ops(struct kernfs_node *kn) return kn->attr.ops; } +/* + * As kernfs_seq_stop() is also called after kernfs_seq_start() or + * kernfs_seq_next() failure, it needs to distinguish whether it's stopping + * a seq_file iteration which is fully initialized with an active reference + * or an aborted kernfs_seq_start() due to get_active failure. The + * position pointer is the only context for each seq_file iteration and + * thus the stop condition should be encoded in it. As the return value is + * directly visible to userland, ERR_PTR(-ENODEV) is the only acceptable + * choice to indicate get_active failure. + * + * Unfortunately, this is complicated due to the optional custom seq_file + * operations which may return ERR_PTR(-ENODEV) too. kernfs_seq_stop() + * can't distinguish whether ERR_PTR(-ENODEV) is from get_active failure or + * custom seq_file operations and thus can't decide whether put_active + * should be performed or not only on ERR_PTR(-ENODEV). + * + * This is worked around by factoring out the custom seq_stop() and + * put_active part into kernfs_seq_stop_active(), skipping it from + * kernfs_seq_stop() if ERR_PTR(-ENODEV) while invoking it directly after + * custom seq_file operations fail with ERR_PTR(-ENODEV) - this ensures + * that kernfs_seq_stop_active() is skipped only after get_active failure. + */ +static void kernfs_seq_stop_active(struct seq_file *sf, void *v) +{ + struct kernfs_open_file *of = sf->private; + const struct kernfs_ops *ops = kernfs_ops(of->kn); + + if (ops->seq_stop) + ops->seq_stop(sf, v); + kernfs_put_active(of->kn); +} + static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) { struct kernfs_open_file *of = sf->private; @@ -69,7 +101,11 @@ static void *kernfs_seq_start(struct seq_file *sf, loff_t *ppos) ops = kernfs_ops(of->kn); if (ops->seq_start) { - return ops->seq_start(sf, ppos); + void *next = ops->seq_start(sf, ppos); + /* see the comment above kernfs_seq_stop_active() */ + if (next == ERR_PTR(-ENODEV)) + kernfs_seq_stop_active(sf, next); + return next; } else { /* * The same behavior and code as single_open(). Returns @@ -85,7 +121,11 @@ static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) const struct kernfs_ops *ops = kernfs_ops(of->kn); if (ops->seq_next) { - return ops->seq_next(sf, v, ppos); + void *next = ops->seq_next(sf, v, ppos); + /* see the comment above kernfs_seq_stop_active() */ + if (next == ERR_PTR(-ENODEV)) + kernfs_seq_stop_active(sf, next); + return next; } else { /* * The same behavior and code as single_open(), always @@ -99,12 +139,9 @@ static void *kernfs_seq_next(struct seq_file *sf, void *v, loff_t *ppos) static void kernfs_seq_stop(struct seq_file *sf, void *v) { struct kernfs_open_file *of = sf->private; - const struct kernfs_ops *ops = kernfs_ops(of->kn); - if (ops->seq_stop) - ops->seq_stop(sf, v); - - kernfs_put_active(of->kn); + if (v != ERR_PTR(-ENODEV)) + kernfs_seq_stop_active(sf, v); mutex_unlock(&of->mutex); } -- cgit v1.1 From 917f56caaabc215f9658006dad28a9665ec0ce19 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 17 Jan 2014 09:57:49 -0500 Subject: kernfs: add struct dentry declaration in kernfs.h Hello, Greg. Two misc fixes for kernfs. Thanks. ------- 8< ------- struct dentry is used in kernfs.h but its declaration was missing, leading to compilation errors unless its declaration gets pulled in in some other way. Add the declaration. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- include/linux/kernfs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index d2c439d..5be9f02 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -18,6 +18,7 @@ #include struct file; +struct dentry; struct iattr; struct seq_file; struct vm_area_struct; -- cgit v1.1 From db4aad209bc9aefd91f0a9aeb9e37364088b39ad Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Fri, 17 Jan 2014 09:58:25 -0500 Subject: kernfs: associate a new kernfs_node with its parent on creation Once created, a kernfs_node is always destroyed by kernfs_put(). Since ba7443bc656e ("sysfs, kernfs: implement kernfs_create/destroy_root()"), kernfs_put() depends on kernfs_root() to locate the ino_ida. kernfs_root() in turn depends on kernfs_node->parent being set for !dir nodes. This means that kernfs_put() of a !dir node requires its ->parent to be initialized. This leads to oops when a newly created !dir node is destroyed without going through kernfs_add_one() or after failing kernfs_add_one() before ->parent is set. kernfs_root() invoked from kernfs_put() will try to dereference NULL parent. Fix it by moving parent association to kernfs_new_node() from kernfs_add_one(). kernfs_new_node() now takes @parent instead of @root and determines the root from the parent and also sets the new node's parent properly. @parent parameter is removed from kernfs_add_one(). As there's no parent when creating the root node, __kernfs_new_node() which takes @root as before and doesn't set the parent is used in that case. This ensures that a kernfs_node in any stage in its life has its parent associated and thus can be put. Signed-off-by: Tejun Heo Signed-off-by: Greg Kroah-Hartman --- fs/kernfs/dir.c | 40 ++++++++++++++++++++++++++-------------- fs/kernfs/file.c | 5 ++--- fs/kernfs/kernfs-internal.h | 8 ++++---- fs/kernfs/symlink.c | 5 ++--- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 510b506..5104cf5 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -324,8 +324,9 @@ const struct dentry_operations kernfs_dops = { .d_release = kernfs_dop_release, }; -struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, - umode_t mode, unsigned flags) +static struct kernfs_node *__kernfs_new_node(struct kernfs_root *root, + const char *name, umode_t mode, + unsigned flags) { char *dup_name = NULL; struct kernfs_node *kn; @@ -362,6 +363,20 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, return NULL; } +struct kernfs_node *kernfs_new_node(struct kernfs_node *parent, + const char *name, umode_t mode, + unsigned flags) +{ + struct kernfs_node *kn; + + kn = __kernfs_new_node(kernfs_root(parent), name, mode, flags); + if (kn) { + kernfs_get(parent); + kn->parent = parent; + } + return kn; +} + /** * kernfs_addrm_start - prepare for kernfs_node add/remove * @acxt: pointer to kernfs_addrm_cxt to be used @@ -386,11 +401,10 @@ void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt) * kernfs_add_one - add kernfs_node to parent without warning * @acxt: addrm context to use * @kn: kernfs_node to be added - * @parent: the parent kernfs_node to add @kn to * - * Get @parent and set @kn->parent to it and increment nlink of the - * parent inode if @kn is a directory and link into the children list - * of the parent. + * The caller must already have initialized @kn->parent. This + * function increments nlink of the parent's inode if @kn is a + * directory and link into the children list of the parent. * * This function should be called between calls to * kernfs_addrm_start() and kernfs_addrm_finish() and should be passed @@ -403,9 +417,9 @@ void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt) * 0 on success, -EEXIST if entry with the given name already * exists. */ -int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, - struct kernfs_node *parent) +int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn) { + struct kernfs_node *parent = kn->parent; bool has_ns = kernfs_ns_enabled(parent); struct kernfs_iattrs *ps_iattr; int ret; @@ -423,8 +437,6 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, return -ENOENT; kn->hash = kernfs_name_hash(kn->name, kn->ns); - kn->parent = parent; - kernfs_get(parent); ret = kernfs_link_sibling(kn); if (ret) @@ -600,7 +612,8 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) ida_init(&root->ino_ida); - kn = kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO, KERNFS_DIR); + kn = __kernfs_new_node(root, "", S_IFDIR | S_IRUGO | S_IXUGO, + KERNFS_DIR); if (!kn) { ida_destroy(&root->ino_ida); kfree(root); @@ -648,8 +661,7 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, int rc; /* allocate */ - kn = kernfs_new_node(kernfs_root(parent), name, mode | S_IFDIR, - KERNFS_DIR); + kn = kernfs_new_node(parent, name, mode | S_IFDIR, KERNFS_DIR); if (!kn) return ERR_PTR(-ENOMEM); @@ -659,7 +671,7 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, /* link in */ kernfs_addrm_start(&acxt); - rc = kernfs_add_one(&acxt, kn, parent); + rc = kernfs_add_one(&acxt, kn); kernfs_addrm_finish(&acxt); if (!rc) diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index bdd3885..dbf397b 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -829,8 +829,7 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, if (name_is_static) flags |= KERNFS_STATIC_NAME; - kn = kernfs_new_node(kernfs_root(parent), name, - (mode & S_IALLUGO) | S_IFREG, flags); + kn = kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG, flags); if (!kn) return ERR_PTR(-ENOMEM); @@ -857,7 +856,7 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, kn->flags |= KERNFS_HAS_MMAP; kernfs_addrm_start(&acxt); - rc = kernfs_add_one(&acxt, kn, parent); + rc = kernfs_add_one(&acxt, kn); kernfs_addrm_finish(&acxt); if (rc) { diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index c6ba5bc..eb536b7 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -101,11 +101,11 @@ extern const struct inode_operations kernfs_dir_iops; struct kernfs_node *kernfs_get_active(struct kernfs_node *kn); void kernfs_put_active(struct kernfs_node *kn); void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt); -int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, - struct kernfs_node *parent); +int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn); void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt); -struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, - umode_t mode, unsigned flags); +struct kernfs_node *kernfs_new_node(struct kernfs_node *parent, + const char *name, umode_t mode, + unsigned flags); /* * file.c diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index a03e260..4d45705 100644 --- a/fs/kernfs/symlink.c +++ b/fs/kernfs/symlink.c @@ -30,8 +30,7 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, struct kernfs_addrm_cxt acxt; int error; - kn = kernfs_new_node(kernfs_root(parent), name, S_IFLNK|S_IRWXUGO, - KERNFS_LINK); + kn = kernfs_new_node(parent, name, S_IFLNK|S_IRWXUGO, KERNFS_LINK); if (!kn) return ERR_PTR(-ENOMEM); @@ -41,7 +40,7 @@ struct kernfs_node *kernfs_create_link(struct kernfs_node *parent, kernfs_get(target); /* ref owned by symlink */ kernfs_addrm_start(&acxt); - error = kernfs_add_one(&acxt, kn, parent); + error = kernfs_add_one(&acxt, kn); kernfs_addrm_finish(&acxt); if (!error) -- cgit v1.1