kernfs: prepare for cgroup's kernfs conversion
Subsystems (4)
Tejun Heo
maintainerPATCH v2 1/1 kernfs: prepare for cgroup's kernfs conversion
Hello,
This patchset makes various updates in preparation of cgroup's kernfs conversion. Changes from the last take[L] are
* Refreshed on top of v3.14-rc1.
* 0010-kernfs-add-CONFIG_KERNFS.patch added.
This patchset contains the following 10 patches.
0001-kernfs-invoke-dir_ops-while-holding-active-ref-of-th.patch 0002-kernfs-rename-kernfs_dir_ops-to-kernfs_syscall_ops.patch 0003-kernfs-implement-kernfs_syscall_ops-remount_fs-and-s.patch 0004-kernfs-add-missing-kernfs_active-checks-in-directory.patch 0005-kernfs-allow-nodes-to-be-created-in-the-deactivated-.patch 0006-kernfs-implement-kernfs_ops-atomic_write_len.patch 0007-kernfs-add-kernfs_open_file-priv.patch 0008-kernfs-implement-kernfs_node_from_dentry-kernfs_root.patch 0009-kernfs-implement-kernfs_get_parent-kernfs_name-path-.patch 0010-kernfs-add-CONFIG_KERNFS.patch
0001-0003 update kernfs_dir_ops for active ref protection, add additional syscall operations and rename the struct to kernfs_syscall_ops.
0004-0005 implement an option to create new kernfs_nodes in deactivated state so that multiple nodes can be made visible atomically or removed without ever being visible to userland.
0006 implements kernfs_ops->atomic_write_len which will be used to support cgroup cftype->max_write_len.
0007 adds kernfs_open_file->priv.
0008-0009 add various helpers and accessors.
0010 adds CONFIG_KERNFS so that kernfs can be selected independently from sysfs.
This patchset is on top of
v3.14-rc1 38dbfb59d117
+ [1] [PATCHSET v5 driver-core-next] kernfs, sysfs, driver-core: implement synchronous self-removal
diffstat follows.
fs/Kconfig | 1 fs/Makefile | 3 fs/kernfs/Kconfig | 7 + fs/kernfs/dir.c | 325 ++++++++++++++++++++++++++++++++++++++++++++----- fs/kernfs/file.c | 49 ++++--- fs/kernfs/mount.c | 37 +++++ fs/sysfs/Kconfig | 1 fs/sysfs/dir.c | 44 +----- fs/sysfs/mount.c | 2 include/linux/kernfs.h | 90 +++++++++++-- 10 files changed, 460 insertions(+), 99 deletions(-)
Thanks!
--
tejun
[L] http://lkml.kernel.org/g/[email protected]
[1] http://lkml.kernel.org/g/[email protected]
Tejun Heo
maintainerPATCH 1/10 kernfs: invoke dir_ops while holding active ref of the target node
kernfs_dir_ops are currently being invoked without any active reference, which makes it tricky for the invoked operations to determine whether the objects associated those nodes are safe to access and will remain that way for the duration of such operations.
kernfs already has active_ref mechanism to deal with this which makes the removal of a given node the synchronization point for gating the file operations. There's no reason for dir_ops to be any different. Update the dir_ops handling so that active_ref is held while the dir_ops are executing. This guarantees that while a dir_ops is executing the target nodes stay alive.
As kernfs_dir_ops doesn't have any in-kernel user at this point, this doesn't affect anybody.
--- fs/kernfs/dir.c | 33 ++++++++++++++++++++++++++++++--- include/linux/kernfs.h | 3 ++- 2 files changed, 32 insertions(+), 4 deletions(-)
fs/kernfs/dir.c +30 -3
@@ -654,22 +654,36 @@ static int kernfs_iop_mkdir(struct inode *dir, struct dentry *dentry, {struct kernfs_node *parent = dir->i_private;struct kernfs_dir_ops *kdops =kernfs_root (parent)->dir_ops; +int ret;if (!kdops || !kdops->mkdir)return -EPERM ; -return kdops->mkdir (parent, dentry->d_name.name, mode); +if (!kernfs_get_active (parent)) +return -ENODEV ; + + ret = kdops->mkdir (parent, dentry->d_name.name, mode); + +kernfs_put_active (parent); +return ret; }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; +int ret;if (!kdops || !kdops->rmdir)return -EPERM ; -return kdops->rmdir (kn); +if (!kernfs_get_active (kn)) +return -ENODEV ; + + ret = kdops->rmdir (kn); + +kernfs_put_active (kn); +return ret; } static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry,
@@ -678,11 +692,24 @@ static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_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; +int ret;if (!kdops || !kdops->rename)return -EPERM ; -return kdops->rename (kn, new_parent, new_dentry->d_name.name); +if (!kernfs_get_active (kn)) +return -ENODEV ; + +if (!kernfs_get_active (new_parent)) { +kernfs_put_active (kn); +return -ENODEV ; + } + + ret = kdops->rename (kn, new_parent, new_dentry->d_name.name); + +kernfs_put_active (new_parent); +kernfs_put_active (kn); +return ret; }const struct inode_operations kernfs_dir_iops = {
include/linux/kernfs.h +2 -1
@@ -111,7 +111,8 @@ struct kernfs_node {* 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. +* don't necessarily have to be the exact operation requested. An active +* reference is held for each kernfs_node parameter. */ struct kernfs_dir_ops { int (*mkdir)(struct kernfs_node *parent, const char *name,
Signature · 1 line
1.8.5.3
Tejun Heo
maintainerPATCH 2/10 kernfs: rename kernfs_dir_ops to kernfs_syscall_ops
In reply to Tejun HeoWe're gonna need non-dir syscall callbacks, which will make dir_ops a misnomer. Let's rename kernfs_dir_ops to kernfs_syscall_ops.
This is pure rename.
--- fs/kernfs/dir.c | 25 +++++++++++++------------ include/linux/kernfs.h | 18 +++++++++--------- 2 files changed, 22 insertions(+), 21 deletions(-)
fs/kernfs/dir.c +13 -12
@@ -527,13 +527,14 @@ EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns);/** * kernfs_create_root - create a new kernfs hierarchy -* @kdops: optional directory syscall operations for the hierarchy +* @scops: optional 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 (struct kernfs_dir_ops *kdops,void *priv) +struct kernfs_root *kernfs_create_root (struct kernfs_syscall_ops *scops, +void *priv) {struct kernfs_root *root;struct kernfs_node *kn;
@@ -556,7 +557,7 @@ struct kernfs_root *kernfs_create_root(struct kernfs_dir_ops *kdops, void *priv) kn->priv = priv; kn->dir.root = root; - root->dir_ops = kdops; + root->syscall_ops = scops; root->kn = kn;init_waitqueue_head (&root->deactivate_waitq);
@@ -653,16 +654,16 @@ 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; +struct kernfs_syscall_ops *scops =kernfs_root (parent)->syscall_ops;int ret; -if (!kdops || !kdops->mkdir) +if (!scops || !scops->mkdir)return -EPERM ;if (!kernfs_get_active (parent))return -ENODEV ; - ret = kdops->mkdir (parent, dentry->d_name.name, mode); + ret = scops->mkdir (parent, dentry->d_name.name, mode);kernfs_put_active (parent);return ret;
@@ -671,16 +672,16 @@ static int kernfs_iop_mkdir(struct inode *dir, struct dentry *dentry,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; +struct kernfs_syscall_ops *scops =kernfs_root (kn)->syscall_ops;int ret; -if (!kdops || !kdops->rmdir) +if (!scops || !scops->rmdir)return -EPERM ;if (!kernfs_get_active (kn))return -ENODEV ; - ret = kdops->rmdir (kn); + ret = scops->rmdir (kn);kernfs_put_active (kn);return ret;
@@ -691,10 +692,10 @@ static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_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; +struct kernfs_syscall_ops *scops =kernfs_root (kn)->syscall_ops;int ret; -if (!kdops || !kdops->rename) +if (!scops || !scops->rename)return -EPERM ;if (!kernfs_get_active (kn))
@@ -705,7 +706,7 @@ static int kernfs_iop_rename(struct inode *old_dir, struct dentry *old_dentry,return -ENODEV ; } - ret = kdops->rename (kn, new_parent, new_dentry->d_name.name); + ret = scops->rename (kn, new_parent, new_dentry->d_name.name);kernfs_put_active (new_parent);kernfs_put_active (kn);
include/linux/kernfs.h +9 -9
@@ -108,13 +108,13 @@ struct kernfs_node { };/* -* 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. An active -* reference is held for each kernfs_node parameter. +* kernfs_syscall_ops may be specified on kernfs_create_root() to support +* 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. An active reference is held for each +* kernfs_node parameter. */ -struct kernfs_dir_ops { +struct kernfs_syscall_ops {int (*mkdir)(struct kernfs_node *parent,const char *name,umode_t mode);int (*rmdir)(struct kernfs_node *kn);
@@ -128,7 +128,7 @@ struct kernfs_root {/* private fields, do not use outside kernfs proper */ struct ida ino_ida; -struct kernfs_dir_ops *dir_ops; +struct kernfs_syscall_ops *syscall_ops;wait_queue_head_t deactivate_waitq; };
@@ -219,7 +219,7 @@ 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 (struct kernfs_dir_ops *kdops, +struct kernfs_root *kernfs_create_root (struct kernfs_syscall_ops *scops,void *priv);void kernfs_destroy_root (struct kernfs_root *root);
@@ -273,7 +273,7 @@ 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 (struct kernfs_dir_ops *kdops,void *priv) +kernfs_create_root (struct kernfs_syscall_ops *scops,void *priv) {return ERR_PTR (-ENOSYS ); }static inline void kernfs_destroy_root (struct kernfs_root *root) { }
Signature · 1 line
1.8.5.3
Tejun Heo
maintainerPATCH 3/10 kernfs: implement kernfs_syscall_ops->remount_fs() and ->show_options()
In reply to Tejun HeoAdd two super_block related syscall callbacks ->remount_fs() and ->show_options() to kernfs_syscall_ops. These simply forward the matching super_operations.
--- fs/kernfs/mount.c | 23 +++++++++++++++++++++++ include/linux/kernfs.h | 3 +++ 2 files changed, 26 insertions(+)
fs/kernfs/mount.c +23 -0
@@ -19,10 +19,33 @@struct kmem_cache *kernfs_node_cache; +static int kernfs_sop_remount_fs (struct super_block *sb,int *flags,char *data) +{ +struct kernfs_root *root =kernfs_info (sb)->root; +struct kernfs_syscall_ops *scops = root->syscall_ops; + +if (scops && scops->remount_fs) +return scops->remount_fs (root, flags, data); +return 0 ; +} + +static int kernfs_sop_show_options (struct seq_file *sf,struct dentry *dentry) +{ +struct kernfs_root *root =kernfs_root (dentry->d_fsdata); +struct kernfs_syscall_ops *scops = root->syscall_ops; + +if (scops && scops->show_options) +return scops->show_options (sf, root); +return 0 ; +} +static const struct super_operations kernfs_sops = { .statfs = simple_statfs, .drop_inode = generic_delete_inode, .evict_inode = kernfs_evict_inode, + + .remount_fs = kernfs_sop_remount_fs, + .show_options = kernfs_sop_show_options, };static int kernfs_fill_super (struct super_block *sb)
include/linux/kernfs.h +3 -0
@@ -115,6 +115,9 @@ struct kernfs_node {* kernfs_node parameter. */ struct kernfs_syscall_ops { +int (*remount_fs)(struct kernfs_root *root,int *flags,char *data); +int (*show_options)(struct seq_file *sf,struct kernfs_root *root); +int (*mkdir)(struct kernfs_node *parent,const char *name,umode_t mode);int (*rmdir)(struct kernfs_node *kn);
Signature · 1 line
1.8.5.3
Tejun Heo
maintainerPATCH 4/10 kernfs: add missing kernfs_active() checks in directory operations
In reply to Tejun Heokernfs_iop_lookup(), kernfs_dir_pos() and kernfs_dir_next_pos() were missing kernfs_active() tests before using the found kernfs_node. As deactivated state is currently visible only while a node is being removed, this doesn't pose an actual problem. e.g. lookup succeeding on a deactivated node doesn't harm anything as the eventual file operations are gonna fail and those failures are indistinguishible from the cases in which the lookups had happened before the node was deactivated.
However, we're gonna allow new nodes to be created deactivated and then activated explicitly by the kernfs user when it sees fit. This is to support atomically making multiple nodes visible to userland and thus those nodes must not be visible to userland before activated.
Let's plug the lookup and readdir holes so that deactivated nodes are invisible to userland.
--- fs/kernfs/dir.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-)
fs/kernfs/dir.c +6 -5
@@ -629,7 +629,7 @@ static struct dentry *kernfs_iop_lookup(struct inode *dir, kn =kernfs_find_ns (parent, dentry->d_name.name, ns);/* no such entry */ -if (!kn) { +if (!kn || !kernfs_active (kn)) { ret =NULL ; goto out_unlock; }
@@ -1112,8 +1112,8 @@ static struct kernfs_node *kernfs_dir_pos(const void *ns,break ; } } -/* Skip over entries in the wrong namespace */ -while (pos && pos->ns != ns) { +/* Skip over entries which are dying/dead or in the wrong namespace */ +while (pos && (!kernfs_active (pos) || pos->ns != ns)) {struct rb_node *node =rb_next (&pos->rb);if (!node) pos =NULL ;
@@ -1127,14 +1127,15 @@ static struct kernfs_node *kernfs_dir_next_pos(const void *ns, struct kernfs_node *parent, ino_t ino, struct kernfs_node *pos) { pos =kernfs_dir_pos (ns, parent, ino, pos); -if (pos) +if (pos) {do {struct rb_node *node =rb_next (&pos->rb);if (!node) pos =NULL ;else pos =rb_to_kn (node); - }while (pos && pos->ns != ns); + }while (pos && (!kernfs_active (pos) || pos->ns != ns)); + }return pos; }
Signature · 1 line
1.8.5.3
Tejun Heo
maintainerPATCH 5/10 kernfs: allow nodes to be created in the deactivated state
In reply to Tejun HeoCurrently, kernfs_nodes are made visible to userland on creation, which makes it difficult for kernfs users to atomically succeed or fail creation of multiple nodes. In addition, if something fails after creating some nodes, the created nodes might already be in use and their active refs need to be drained for removal, which has the potential to introduce tricky reverse locking dependency on active_ref depending on how the error path is synchronized.
This patch introduces per-root flag KERNFS_ROOT_CREATE_DEACTIVATED. If set, all nodes under the root are created in the deactivated state and stay invisible to userland until explicitly enabled by the new kernfs_activate() API. Also, nodes which have never been activated are guaranteed to bypass draining on removal thus allowing error paths to not worry about lockding dependency on active_ref draining.
--- fs/kernfs/dir.c | 71 +++++++++++++++++++++++++++++++++++++++++++++----- fs/sysfs/mount.c | 2 +- include/linux/kernfs.h | 15 +++++++++-- 3 files changed, 78 insertions(+), 10 deletions(-)
fs/kernfs/dir.c +64 -7
@@ -435,7 +435,7 @@ int kernfs_add_one(struct kernfs_node *kn) goto out_unlock; ret = -ENOENT ; -if (!kernfs_active (parent)) +if ((parent->flags &KERNFS_ACTIVATED ) && !kernfs_active (parent)) goto out_unlock; kn->hash =kernfs_name_hash (kn->name, kn->ns);
@@ -451,9 +451,19 @@ int kernfs_add_one(struct kernfs_node *kn) ps_iattrs->ia_ctime = ps_iattrs->ia_mtime =CURRENT_TIME ; } -/* Mark the entry added into directory tree */ -atomic_sub (KN_DEACTIVATED_BIAS , &kn->active); - ret =0 ; +mutex_unlock (&kernfs_mutex); + +/* +* Activate the new node unless CREATE_DEACTIVATED is requested. +* If not activated here, the kernfs user is responsible for +* activating the node with kernfs_activate(). A node which hasn't +* been activated is not visible to userland and its removal won't +* trigger deactivation. +*/ +if (!(kernfs_root (kn)->flags &KERNFS_ROOT_CREATE_DEACTIVATED )) +kernfs_activate (kn); +return 0 ; + out_unlock:mutex_unlock (&kernfs_mutex);return ret;
@@ -528,13 +538,14 @@ EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns);/** * kernfs_create_root - create a new kernfs hierarchy * @scops: optional syscall operations for the hierarchy +* @flags: KERNFS_ROOT_* flags * @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 (struct kernfs_syscall_ops *scops, -void *priv) +unsigned int flags,void *priv) {struct kernfs_root *root;struct kernfs_node *kn;
@@ -553,14 +564,17 @@ struct kernfs_root *kernfs_create_root(struct kernfs_syscall_ops *scops,return ERR_PTR (-ENOMEM ); } -atomic_sub (KN_DEACTIVATED_BIAS , &kn->active); kn->priv = priv; kn->dir.root = root; root->syscall_ops = scops; + root->flags = flags; root->kn = kn;init_waitqueue_head (&root->deactivate_waitq); +if (!(root->flags &KERNFS_ROOT_CREATE_DEACTIVATED )) +kernfs_activate (kn); +return root; }
@@ -783,6 +797,40 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos,return pos->parent; } +/** +* kernfs_activate - activate a node which started deactivated +* @kn: kernfs_node whose subtree is to be activated +* +* If the root has KERNFS_ROOT_CREATE_DEACTIVATED set, a newly created node +* needs to be explicitly activated. A node which hasn't been activated +* isn't visible to userland and deactivation is skipped during its +* removal. This is useful to construct atomic init sequences where +* creation of multiple nodes should either succeed or fail atomically. +* +* The caller is responsible for ensuring that this function is not called +* after kernfs_remove*() is invoked on @kn. +*/ +void kernfs_activate (struct kernfs_node *kn) +{ +struct kernfs_node *pos; + +mutex_lock (&kernfs_mutex); + + pos =NULL ; +while ((pos =kernfs_next_descendant_post (pos, kn))) { +if (!pos || (pos->flags &KERNFS_ACTIVATED )) +continue ; + +WARN_ON_ONCE (pos->parent &&RB_EMPTY_NODE (&pos->rb)); +WARN_ON_ONCE (atomic_read (&pos->active) !=KN_DEACTIVATED_BIAS ); + +atomic_sub (KN_DEACTIVATED_BIAS , &pos->active); + pos->flags |=KERNFS_ACTIVATED ; + } + +mutex_unlock (&kernfs_mutex); +} +static void __kernfs_remove (struct kernfs_node *kn) {struct kernfs_node *pos;
@@ -817,7 +865,16 @@ static void __kernfs_remove(struct kernfs_node *kn)*/ kernfs_get (pos); -kernfs_drain (pos); +/* +* Drain iff @kn was activated. This avoids draining and +* its lockdep annotations for nodes which have never been +* activated and allows embedding kernfs_remove() in create +* error paths without worrying about draining. +*/ +if (kn->flags &KERNFS_ACTIVATED ) +kernfs_drain (pos); +else +WARN_ON_ONCE (atomic_read (&kn->active) !=KN_DEACTIVATED_BIAS );/* * kernfs_unlink_sibling() succeeds once per node. Use it
fs/sysfs/mount.c +1 -1
@@ -62,7 +62,7 @@ int __init sysfs_init(void) {int err; - sysfs_root =kernfs_create_root (NULL ,NULL ); + sysfs_root =kernfs_create_root (NULL ,0 ,NULL );if (IS_ERR (sysfs_root))return PTR_ERR (sysfs_root);
include/linux/kernfs.h +13 -2
@@ -38,6 +38,7 @@ enum kernfs_node_type {#define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASKenum kernfs_node_flag { +KERNFS_ACTIVATED =0x0010 ,KERNFS_NS =0x0020 ,KERNFS_HAS_SEQ_SHOW =0x0040 ,KERNFS_HAS_MMAP =0x0080 ,
@@ -47,6 +48,11 @@ enum kernfs_node_flag { KERNFS_SUICIDED = 0x0800, }; +/* @flags for kernfs_create_root() */ +enum kernfs_root_flag { +KERNFS_ROOT_CREATE_DEACTIVATED =0x0001 , +}; +/* type-specific structures for kernfs_node union members */ struct kernfs_elem_dir {unsigned long subdirs;
@@ -128,6 +134,7 @@ struct kernfs_syscall_ops {struct kernfs_root {/* published fields */ struct kernfs_node *kn; +unsigned int flags;/* KERNFS_ROOT_* flags */ /* private fields, do not use outside kernfs proper */ struct ida ino_ida;
@@ -223,7 +230,7 @@ void kernfs_get(struct kernfs_node *kn);void kernfs_put (struct kernfs_node *kn);struct kernfs_root *kernfs_create_root (struct kernfs_syscall_ops *scops, -void *priv); +unsigned int flags,void *priv);void kernfs_destroy_root (struct kernfs_root *root); struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
@@ -239,6 +246,7 @@ 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_activate (struct kernfs_node *kn);void kernfs_remove (struct kernfs_node *kn);void kernfs_break_active_protection (struct kernfs_node *kn);void kernfs_unbreak_active_protection (struct kernfs_node *kn);
@@ -276,7 +284,8 @@ 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 (struct kernfs_syscall_ops *scops,void *priv) +kernfs_create_root (struct kernfs_syscall_ops *scops,unsigned int flags, +void *priv) {return ERR_PTR (-ENOSYS ); }static inline void kernfs_destroy_root (struct kernfs_root *root) { }
@@ -298,6 +307,8 @@ kernfs_create_link(struct kernfs_node *parent, const char *name, struct kernfs_node *target) {return ERR_PTR (-ENOSYS ); } +static inline void kernfs_activate (struct kernfs_node *kn) { } +static inline void kernfs_remove (struct kernfs_node *kn) { }static inline bool kernfs_remove_self (struct kernfs_node *kn)
Signature · 1 line
1.8.5.3
Tejun Heo
maintainerPATCH 6/10 kernfs: implement kernfs_ops->atomic_write_len
In reply to Tejun HeoA write to a kernfs_node is buffered through a kernel buffer. Writes <= PAGE_SIZE are performed atomically, while larger ones are executed in PAGE_SIZE chunks. While this is enough for sysfs, cgroup which is scheduled to be converted to use kernfs needs a bit more control over it.
This patch adds kernfs_ops->atomic_write_len. If not set (zero), the behavior stays the same. If set, writes upto the size are executed atomically and larger writes are rejected with -E2BIG.
A different implementation strategy would be allowing configuring chunking size while making the original write size available to the write method; however, such strategy, while being more complicated, doesn't really buy anything. If the write implementation has to handle chunking, the specific chunk size shouldn't matter all that much.
--- fs/kernfs/file.c | 49 +++++++++++++++++++++++++++++++------------------ include/linux/kernfs.h | 8 ++++++-- 2 files changed, 37 insertions(+), 20 deletions(-)
fs/kernfs/file.c +31 -18
@@ -252,19 +252,9 @@ 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 );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 */ +char *buf =NULL ; +ssize_t len;/* * @of->mutex nests outside active ref and is just to ensure that
@@ -273,22 +263,45 @@ static ssize_t kernfs_fop_write(struct file *file, const char __user *user_buf,mutex_lock (&of->mutex);if (!kernfs_get_active (of->kn)) {mutex_unlock (&of->mutex); - len = -ENODEV ; - goto out_free; +return -ENODEV ; } ops =kernfs_ops (of->kn); -if (ops->write) - len = ops->write (of, buf, len, *ppos); -else +if (!ops->write) { len = -EINVAL ; + goto out_unlock; + } + +if (ops->atomic_write_len) { + len = count; +if (len > ops->atomic_write_len) { + len = -E2BIG ; + goto out_unlock; + } + }else { + len =min_t (size_t, count,PAGE_SIZE ); + } + + buf =kmalloc (len +1 ,GFP_KERNEL ); +if (!buf) { + len = -ENOMEM ; + goto out_unlock; + } +if (copy_from_user (buf, user_buf, len)) { + len = -EFAULT ; + goto out_unlock; + } + buf[len] ='\0' ;/* guarantee string termination */ + + len = ops->write (of, buf, len, *ppos); +out_unlock:kernfs_put_active (of->kn);mutex_unlock (&of->mutex);if (len >0 ) *ppos += len; -out_free: +kfree (buf);return len; }
include/linux/kernfs.h +6 -2
@@ -178,9 +178,13 @@ struct kernfs_ops { loff_t off);/* -* write() is bounced through kernel buffer and a write larger than -* PAGE_SIZE results in partial operation of PAGE_SIZE. +* write() is bounced through kernel buffer. If atomic_write_len +* is not set, a write larger than PAGE_SIZE results in partial +* operations of PAGE_SIZE chunks. If atomic_write_len is set, +* writes upto the specified size are executed atomically but +* larger ones are rejected with -E2BIG. */ +size_t atomic_write_len;ssize_t (*write)(struct kernfs_open_file *of,char *buf,size_t bytes,loff_t off);
Signature · 1 line
1.8.5.3
Tejun Heo
maintainerPATCH 7/10 kernfs: add kernfs_open_file->priv
In reply to Tejun HeoAdd a private data field to be used by kernfs file operations. This generally makes sense and will be used by cgroup.
--- include/linux/kernfs.h | 1 + 1 file changed, 1 insertion(+)
include/linux/kernfs.h +1 -0
@@ -146,6 +146,7 @@ struct kernfs_open_file {/* published fields */ struct kernfs_node *kn;struct file *file; +void *priv;/* private fields, do not use outside kernfs proper */ struct mutex mutex;
Signature · 1 line
1.8.5.3
Tejun Heo
maintainerPATCH 8/10 kernfs: implement kernfs_node_from_dentry(), kernfs_root_from_sb() and kernfs_rename()
In reply to Tejun HeoImplement helpers to determine node from dentry and root from super_block. Also add a kernfs_rename_ns() wrapper which assumes NULL namespace. These generally make sense and will be used by cgroup.
v2: Some dummy implementations for !CONFIG_SYSFS was missing. Fixed.
Reported by kbuild test robot.
--- fs/kernfs/dir.c | 18 ++++++++++++++++++ fs/kernfs/mount.c | 14 ++++++++++++++ include/linux/kernfs.h | 16 ++++++++++++++++ 3 files changed, 48 insertions(+)
fs/kernfs/dir.c +18 -0
@@ -350,6 +350,24 @@ const struct dentry_operations kernfs_dops = { .d_release = kernfs_dop_release, }; +/** +* kernfs_node_from_dentry - determine kernfs_node associated with a dentry +* @dentry: the dentry in question +* +* Return the kernfs_node associated with @dentry. If @dentry is not a +* kernfs one, %NULL is returned. +* +* While the returned kernfs_node will stay accessible as long as @dentry +* is accessible, the returned node can be in any state and the caller is +* fully responsible for determining what's accessible. +*/ +struct kernfs_node *kernfs_node_from_dentry (struct dentry *dentry) +{ +if (dentry->d_op == &kernfs_dops) +return dentry->d_fsdata; +return NULL ; +} +static struct kernfs_node *__kernfs_new_node (struct kernfs_root *root,const char *name,umode_t mode,unsigned flags)
fs/kernfs/mount.c +14 -0
@@ -48,6 +48,20 @@ static const struct super_operations kernfs_sops = { .show_options = kernfs_sop_show_options, }; +/** +* kernfs_root_from_sb - determine kernfs_root associated with a super_block +* @sb: the super_block in question +* +* Return the kernfs_root associated with @sb. If @sb is not a kernfs one, +* %NULL is returned. +*/ +struct kernfs_root *kernfs_root_from_sb (struct super_block *sb) +{ +if (sb->s_op == &kernfs_sops) +return kernfs_info (sb)->root; +return NULL ; +} +static int kernfs_fill_super (struct super_block *sb) {struct kernfs_super_info *info =kernfs_info (sb);
include/linux/kernfs.h +16 -0
@@ -234,6 +234,9 @@ 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_node *kernfs_node_from_dentry (struct dentry *dentry); +struct kernfs_root *kernfs_root_from_sb (struct super_block *sb); +struct kernfs_root *kernfs_create_root (struct kernfs_syscall_ops *scops,unsigned int flags,void *priv);void kernfs_destroy_root (struct kernfs_root *root);
@@ -288,6 +291,12 @@ 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_node *kernfs_node_from_dentry (struct dentry *dentry) +{return NULL ; } + +static inline struct kernfs_root *kernfs_root_from_sb (struct super_block *sb) +{return NULL ; } +static inline struct kernfs_root *kernfs_create_root (struct kernfs_syscall_ops *scops,unsigned int flags,void *priv)
@@ -388,6 +397,13 @@ static inline int kernfs_remove_by_name(struct kernfs_node *parent,return kernfs_remove_by_name_ns (parent, name,NULL ); } +static inline int kernfs_rename (struct kernfs_node *kn, +struct kernfs_node *new_parent, +const char *new_name) +{ +return kernfs_rename_ns (kn, new_parent, new_name,NULL ); +} +static inline struct dentry *kernfs_mount (struct file_system_type *fs_type,int flags,struct kernfs_root *root)
Signature · 1 line
1.8.5.3
Tejun Heo
maintainerPATCH 9/10 kernfs: implement kernfs_get_parent(), kernfs_name/path() and friends
In reply to Tejun Heokernfs_node->parent and ->name are currently marked as "published" indicating that kernfs users may access them directly; however, those fields may get updated by kernfs_rename[_ns]() and unrestricted access may lead to erroneous values or oops.
Protect ->parent and ->name updates with a irq-safe spinlock kernfs_rename_lock and implement the following accessors for these fields.
* kernfs_name() - format the node's name into the specified buffer
* kernfs_path() - format the node's path into the specified buffer
* pr_cont_kernfs_name() - pr_cont a node's name (doesn't need buffer)
* pr_cont_kernfs_path() - pr_cont a node's path (doesn't need buffer)
* kernfs_get_parent() - pin and return a node's parent
All can be called under any context. The recursive sysfs_pathname() in fs/sysfs/dir.c is replaced with kernfs_path() and sysfs_rename_dir_ns() is updated to use kernfs_get_parent() instead of dereferencing parent directly.
--- fs/kernfs/dir.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++--- fs/sysfs/dir.c | 44 ++++--------- include/linux/kernfs.h | 26 +++++++- 3 files changed, 203 insertions(+), 42 deletions(-)
fs/kernfs/dir.c +165 -10
@@ -19,6 +19,8 @@#include "kernfs-internal.h" DEFINE_MUTEX (kernfs_mutex); +static DEFINE_SPINLOCK (kernfs_rename_lock);/* kn->parent and ->name */ +static char kernfs_pr_cont_buf[PATH_MAX ];/* protected by rename_lock */ #define rb_to_kn (X ) rb_entry((X), struct kernfs_node, rb)
@@ -37,6 +39,141 @@ static bool kernfs_lockdep(struct kernfs_node *kn)#endif } +static int kernfs_name_locked (struct kernfs_node *kn,char *buf,size_t buflen) +{ +return strlcpy (buf, kn->parent ? kn->name :"/" , buflen); +} + +static char *__must_check kernfs_path_locked (struct kernfs_node *kn,char *buf, +size_t buflen) +{ +char *p = buf + buflen; +int len; + + *--p ='\0' ; + +do { + len =strlen (kn->name); +if (p - buf < len +1 ) { + buf[0 ] ='\0' ; + p =NULL ; +break ; + } + p -= len; +memcpy (p, kn->name, len); + *--p ='/' ; + kn = kn->parent; + }while (kn && kn->parent); + +return p; +} + +/** +* kernfs_name - obtain the name of a given node +* @kn: kernfs_node of interest +* @buf: buffer to copy @kn's name into +* @buflen: size of @buf +* +* Copies the name of @kn into @buf of @buflen bytes. The behavior is +* similar to strlcpy(). It returns the length of @kn's name and if @buf +* isn't long enough, it's filled upto @buflen-1 and nul terminated. +* +* This function can be called from any context. +*/ +int kernfs_name (struct kernfs_node *kn,char *buf,size_t buflen) +{ +unsigned long flags; +int ret; + +spin_lock_irqsave (&kernfs_rename_lock, flags); + ret =kernfs_name_locked (kn, buf, buflen); +spin_unlock_irqrestore (&kernfs_rename_lock, flags); +return ret; +} + +/** +* kernfs_path - build full path of a given node +* @kn: kernfs_node of interest +* @buf: buffer to copy @kn's name into +* @buflen: size of @buf +* +* Builds and returns the full path of @kn in @buf of @buflen bytes. The +* path is built from the end of @buf so the returned pointer usually +* doesn't match @buf. If @buf isn't long enough, @buf is nul terminated +* and %NULL is returned. +*/ +char *kernfs_path (struct kernfs_node *kn,char *buf,size_t buflen) +{ +unsigned long flags; +char *p; + +spin_lock_irqsave (&kernfs_rename_lock, flags); + p =kernfs_path_locked (kn, buf, buflen); +spin_unlock_irqrestore (&kernfs_rename_lock, flags); +return p; +} + +/** +* pr_cont_kernfs_name - pr_cont name of a kernfs_node +* @kn: kernfs_node of interest +* +* This function can be called from any context. +*/ +void pr_cont_kernfs_name (struct kernfs_node *kn) +{ +unsigned long flags; + +spin_lock_irqsave (&kernfs_rename_lock, flags); + +kernfs_name_locked (kn, kernfs_pr_cont_buf,sizeof (kernfs_pr_cont_buf)); +pr_cont ("%s" , kernfs_pr_cont_buf); + +spin_unlock_irqrestore (&kernfs_rename_lock, flags); +} + +/** +* pr_cont_kernfs_path - pr_cont path of a kernfs_node +* @kn: kernfs_node of interest +* +* This function can be called from any context. +*/ +void pr_cont_kernfs_path (struct kernfs_node *kn) +{ +unsigned long flags; +char *p; + +spin_lock_irqsave (&kernfs_rename_lock, flags); + + p =kernfs_path_locked (kn, kernfs_pr_cont_buf, +sizeof (kernfs_pr_cont_buf)); +if (p) +pr_cont ("%s" , p); +else +pr_cont ("<name too long>" ); + +spin_unlock_irqrestore (&kernfs_rename_lock, flags); +} + +/** +* kernfs_get_parent - determine the parent node and pin it +* @kn: kernfs_node of interest +* +* Determines @kn's parent, pins and returns it. This function can be +* called from any context. +*/ +struct kernfs_node *kernfs_get_parent (struct kernfs_node *kn) +{ +struct kernfs_node *parent; +unsigned long flags; + +spin_lock_irqsave (&kernfs_rename_lock, flags); + parent = kn->parent; +kernfs_get (parent); +spin_unlock_irqrestore (&kernfs_rename_lock, flags); + +return parent; +} +/** * kernfs_name_hash * @name: Null terminated string to hash
@@ -1103,8 +1240,14 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,int kernfs_rename_ns (struct kernfs_node *kn,struct kernfs_node *new_parent,const char *new_name,const void *new_ns) { +struct kernfs_node *old_parent; +const char *old_name =NULL ;int error; +/* can't move or rename root */ +if (!kn->parent) +return -EINVAL ; +mutex_lock (&kernfs_mutex); error = -ENOENT ;
@@ -1126,13 +1269,8 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, new_name =kstrdup (new_name,GFP_KERNEL );if (!new_name) goto out; - -if (kn->flags &KERNFS_STATIC_NAME ) - kn->flags &= ~KERNFS_STATIC_NAME ; -else -kfree (kn->name); - - kn->name = new_name; + }else { + new_name =NULL ; }/*
@@ -1140,12 +1278,29 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,*/ kernfs_unlink_sibling (kn);kernfs_get (new_parent); -kernfs_put (kn->parent); - kn->ns = new_ns; - kn->hash =kernfs_name_hash (kn->name, kn->ns); + +/* rename_lock protects ->parent and ->name accessors */ +spin_lock_irq (&kernfs_rename_lock); + + old_parent = kn->parent; kn->parent = new_parent; + + kn->ns = new_ns; +if (new_name) { +if (!(kn->flags &KERNFS_STATIC_NAME )) + old_name = kn->name; + kn->flags &= ~KERNFS_STATIC_NAME ; + kn->name = new_name; + } + +spin_unlock_irq (&kernfs_rename_lock); + + kn->hash =kernfs_name_hash (new_name, new_ns);kernfs_link_sibling (kn); +kernfs_put (old_parent); +kfree (old_name); + error =0 ; out:mutex_unlock (&kernfs_mutex);
fs/sysfs/dir.c +13 -31
68 lines not on this page · raw message
include/linux/kernfs.h +25 -1
@@ -91,7 +91,12 @@ struct kernfs_node {#ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map;#endif -/* the following two fields are published */ +/* +* Use kernfs_get_parent() and kernfs_name/path() instead of +* accessing the following two fields directly. If the node is +* never moved to a different parent, it is safe to access the +* parent directly. +*/ struct kernfs_node *parent;const char *name;
@@ -229,6 +234,12 @@ static inline bool kernfs_ns_enabled(struct kernfs_node *kn)return kn->flags &KERNFS_NS ; } +int kernfs_name (struct kernfs_node *kn,char *buf,size_t buflen); +char *__must_check kernfs_path (struct kernfs_node *kn,char *buf, +size_t buflen); +void pr_cont_kernfs_name (struct kernfs_node *kn); +void pr_cont_kernfs_path (struct kernfs_node *kn); +struct kernfs_node *kernfs_get_parent (struct kernfs_node *kn);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);
@@ -283,6 +294,19 @@ static inline void kernfs_enable_ns(struct kernfs_node *kn) { }static inline bool kernfs_ns_enabled (struct kernfs_node *kn) {return false; } +static inline int kernfs_name (struct kernfs_node *kn,char *buf,size_t buflen) +{return -ENOSYS ; } + +char *__must_check kernfs_path (struct kernfs_node *kn,char *buf, +size_t buflen) +{return NULL ; } + +static inline void pr_cont_kernfs_name (struct kernfs_node *kn) { } +static inline void pr_cont_kernfs_path (struct kernfs_node *kn) { } + +static inline struct kernfs_node *kernfs_get_parent (struct kernfs_node *kn) +{return NULL ; } +static inline struct kernfs_node *kernfs_find_and_get_ns (struct kernfs_node *parent,const char *name,const void *ns)
Signature · 1 line
1.8.5.3
Tejun Heo
maintainerPATCH v2 9/10 kernfs: implement kernfs_get_parent(), kernfs_name/path() and friends
Quoted 1 line
From
8a753c7a38b909f95e98ecfdffa8343e79ed4256Mon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Fri, 7 Feb 2014 13:32:07 -0500
kernfs_node->parent and ->name are currently marked as "published" indicating that kernfs users may access them directly; however, those fields may get updated by kernfs_rename[_ns]() and unrestricted access may lead to erroneous values or oops.
Protect ->parent and ->name updates with a irq-safe spinlock kernfs_rename_lock and implement the following accessors for these fields.
* kernfs_name() - format the node's name into the specified buffer
* kernfs_path() - format the node's path into the specified buffer
* pr_cont_kernfs_name() - pr_cont a node's name (doesn't need buffer)
* pr_cont_kernfs_path() - pr_cont a node's path (doesn't need buffer)
* kernfs_get_parent() - pin and return a node's parent
All can be called under any context. The recursive sysfs_pathname() in fs/sysfs/dir.c is replaced with kernfs_path() and sysfs_rename_dir_ns() is updated to use kernfs_get_parent() instead of dereferencing parent directly.
v2: Dummy definition of kernfs_path() for !CONFIG_KERNFS was missing
static inline making it cause a lot of build warnings. Add it.
--- fs/kernfs/dir.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++--- fs/sysfs/dir.c | 44 ++++--------- include/linux/kernfs.h | 26 +++++++- 3 files changed, 203 insertions(+), 42 deletions(-)
fs/kernfs/dir.c +165 -10
@@ -19,6 +19,8 @@#include "kernfs-internal.h" DEFINE_MUTEX (kernfs_mutex); +static DEFINE_SPINLOCK (kernfs_rename_lock);/* kn->parent and ->name */ +static char kernfs_pr_cont_buf[PATH_MAX ];/* protected by rename_lock */ #define rb_to_kn (X ) rb_entry((X), struct kernfs_node, rb)
@@ -37,6 +39,141 @@ static bool kernfs_lockdep(struct kernfs_node *kn)#endif } +static int kernfs_name_locked (struct kernfs_node *kn,char *buf,size_t buflen) +{ +return strlcpy (buf, kn->parent ? kn->name :"/" , buflen); +} + +static char *__must_check kernfs_path_locked (struct kernfs_node *kn,char *buf, +size_t buflen) +{ +char *p = buf + buflen; +int len; + + *--p ='\0' ; + +do { + len =strlen (kn->name); +if (p - buf < len +1 ) { + buf[0 ] ='\0' ; + p =NULL ; +break ; + } + p -= len; +memcpy (p, kn->name, len); + *--p ='/' ; + kn = kn->parent; + }while (kn && kn->parent); + +return p; +} + +/** +* kernfs_name - obtain the name of a given node +* @kn: kernfs_node of interest +* @buf: buffer to copy @kn's name into +* @buflen: size of @buf +* +* Copies the name of @kn into @buf of @buflen bytes. The behavior is +* similar to strlcpy(). It returns the length of @kn's name and if @buf +* isn't long enough, it's filled upto @buflen-1 and nul terminated. +* +* This function can be called from any context. +*/ +int kernfs_name (struct kernfs_node *kn,char *buf,size_t buflen) +{ +unsigned long flags; +int ret; + +spin_lock_irqsave (&kernfs_rename_lock, flags); + ret =kernfs_name_locked (kn, buf, buflen); +spin_unlock_irqrestore (&kernfs_rename_lock, flags); +return ret; +} + +/** +* kernfs_path - build full path of a given node +* @kn: kernfs_node of interest +* @buf: buffer to copy @kn's name into +* @buflen: size of @buf +* +* Builds and returns the full path of @kn in @buf of @buflen bytes. The +* path is built from the end of @buf so the returned pointer usually +* doesn't match @buf. If @buf isn't long enough, @buf is nul terminated +* and %NULL is returned. +*/ +char *kernfs_path (struct kernfs_node *kn,char *buf,size_t buflen) +{ +unsigned long flags; +char *p; + +spin_lock_irqsave (&kernfs_rename_lock, flags); + p =kernfs_path_locked (kn, buf, buflen); +spin_unlock_irqrestore (&kernfs_rename_lock, flags); +return p; +} + +/** +* pr_cont_kernfs_name - pr_cont name of a kernfs_node +* @kn: kernfs_node of interest +* +* This function can be called from any context. +*/ +void pr_cont_kernfs_name (struct kernfs_node *kn) +{ +unsigned long flags; + +spin_lock_irqsave (&kernfs_rename_lock, flags); + +kernfs_name_locked (kn, kernfs_pr_cont_buf,sizeof (kernfs_pr_cont_buf)); +pr_cont ("%s" , kernfs_pr_cont_buf); + +spin_unlock_irqrestore (&kernfs_rename_lock, flags); +} + +/** +* pr_cont_kernfs_path - pr_cont path of a kernfs_node +* @kn: kernfs_node of interest +* +* This function can be called from any context. +*/ +void pr_cont_kernfs_path (struct kernfs_node *kn) +{ +unsigned long flags; +char *p; + +spin_lock_irqsave (&kernfs_rename_lock, flags); + + p =kernfs_path_locked (kn, kernfs_pr_cont_buf, +sizeof (kernfs_pr_cont_buf)); +if (p) +pr_cont ("%s" , p); +else +pr_cont ("<name too long>" ); + +spin_unlock_irqrestore (&kernfs_rename_lock, flags); +} + +/** +* kernfs_get_parent - determine the parent node and pin it +* @kn: kernfs_node of interest +* +* Determines @kn's parent, pins and returns it. This function can be +* called from any context. +*/ +struct kernfs_node *kernfs_get_parent (struct kernfs_node *kn) +{ +struct kernfs_node *parent; +unsigned long flags; + +spin_lock_irqsave (&kernfs_rename_lock, flags); + parent = kn->parent; +kernfs_get (parent); +spin_unlock_irqrestore (&kernfs_rename_lock, flags); + +return parent; +} +/** * kernfs_name_hash * @name: Null terminated string to hash
@@ -1103,8 +1240,14 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name,int kernfs_rename_ns (struct kernfs_node *kn,struct kernfs_node *new_parent,const char *new_name,const void *new_ns) { +struct kernfs_node *old_parent; +const char *old_name =NULL ;int error; +/* can't move or rename root */ +if (!kn->parent) +return -EINVAL ; +mutex_lock (&kernfs_mutex); error = -ENOENT ;
@@ -1126,13 +1269,8 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent, new_name =kstrdup (new_name,GFP_KERNEL );if (!new_name) goto out; - -if (kn->flags &KERNFS_STATIC_NAME ) - kn->flags &= ~KERNFS_STATIC_NAME ; -else -kfree (kn->name); - - kn->name = new_name; + }else { + new_name =NULL ; }/*
@@ -1140,12 +1278,29 @@ int kernfs_rename_ns(struct kernfs_node *kn, struct kernfs_node *new_parent,*/ kernfs_unlink_sibling (kn);kernfs_get (new_parent); -kernfs_put (kn->parent); - kn->ns = new_ns; - kn->hash =kernfs_name_hash (kn->name, kn->ns); + +/* rename_lock protects ->parent and ->name accessors */ +spin_lock_irq (&kernfs_rename_lock); + + old_parent = kn->parent; kn->parent = new_parent; + + kn->ns = new_ns; +if (new_name) { +if (!(kn->flags &KERNFS_STATIC_NAME )) + old_name = kn->name; + kn->flags &= ~KERNFS_STATIC_NAME ; + kn->name = new_name; + } + +spin_unlock_irq (&kernfs_rename_lock); + + kn->hash =kernfs_name_hash (new_name, new_ns);kernfs_link_sibling (kn); +kernfs_put (old_parent); +kfree (old_name); + error =0 ; out:mutex_unlock (&kernfs_mutex);
fs/sysfs/dir.c +13 -31
68 lines not on this page · raw message
include/linux/kernfs.h +25 -1
@@ -91,7 +91,12 @@ struct kernfs_node {#ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map;#endif -/* the following two fields are published */ +/* +* Use kernfs_get_parent() and kernfs_name/path() instead of +* accessing the following two fields directly. If the node is +* never moved to a different parent, it is safe to access the +* parent directly. +*/ struct kernfs_node *parent;const char *name;
@@ -229,6 +234,12 @@ static inline bool kernfs_ns_enabled(struct kernfs_node *kn)return kn->flags &KERNFS_NS ; } +int kernfs_name (struct kernfs_node *kn,char *buf,size_t buflen); +char *__must_check kernfs_path (struct kernfs_node *kn,char *buf, +size_t buflen); +void pr_cont_kernfs_name (struct kernfs_node *kn); +void pr_cont_kernfs_path (struct kernfs_node *kn); +struct kernfs_node *kernfs_get_parent (struct kernfs_node *kn);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);
@@ -283,6 +294,19 @@ static inline void kernfs_enable_ns(struct kernfs_node *kn) { }static inline bool kernfs_ns_enabled (struct kernfs_node *kn) {return false; } +static inline int kernfs_name (struct kernfs_node *kn,char *buf,size_t buflen) +{return -ENOSYS ; } + +static inline char *__must_check kernfs_path (struct kernfs_node *kn,char *buf, +size_t buflen) +{return NULL ; } + +static inline void pr_cont_kernfs_name (struct kernfs_node *kn) { } +static inline void pr_cont_kernfs_path (struct kernfs_node *kn) { } + +static inline struct kernfs_node *kernfs_get_parent (struct kernfs_node *kn) +{return NULL ; } +static inline struct kernfs_node *kernfs_find_and_get_ns (struct kernfs_node *parent,const char *name,const void *ns)
Signature · 1 line
1.8.5.3
Tejun Heo
maintainerPATCH 10/10 kernfs: add CONFIG_KERNFS
In reply to Tejun HeoAs sysfs was kernfs's only user, kernfs has been piggybacking on CONFIG_SYSFS; however, kernfs is scheduled to grow a new user very soon. Introduce a separate config option CONFIG_KERNFS which is to be selected by kernfs users.
--- fs/Kconfig | 1 + fs/Makefile | 3 ++- fs/kernfs/Kconfig | 7 +++++++ fs/sysfs/Kconfig | 1 + include/linux/kernfs.h | 6 +++--- 5 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 fs/kernfs/Kconfig
fs/Kconfig +1 -0
@@ -96,6 +96,7 @@ endif # BLOCK menu "Pseudo filesystems" source "fs/proc/Kconfig" +source "fs/kernfs/Kconfig" source "fs/sysfs/Kconfig" config TMPFS
fs/Makefile +2 -1
@@ -52,7 +52,8 @@ obj-$(CONFIG_FHANDLE) += fhandle.o obj-y += quota/ obj-$(CONFIG_PROC_FS) += proc/ -obj-$(CONFIG_SYSFS) += sysfs/ kernfs/ +obj-$(CONFIG_KERNFS) += kernfs/ +obj-$(CONFIG_SYSFS) += sysfs/ obj-$(CONFIG_CONFIGFS_FS) += configfs/ obj-y += devpts/
fs/kernfs/Kconfig new file +7 -0
@@ -0,0 +1,7 @@ +# +# KERNFS should be selected by its users +# + +config KERNFS + bool + default n
fs/sysfs/Kconfig +1 -0
@@ -1,6 +1,7 @@ config SYSFS bool "sysfs file system support" if EXPERT default y + select KERNFS help The sysfs filesystem is a virtual filesystem that the kernel uses to export internal kernel objects, their attributes, and their
include/linux/kernfs.h +3 -3
@@ -201,7 +201,7 @@ struct kernfs_ops {#endif }; -#ifdef CONFIG_SYSFS +#ifdef CONFIG_KERNFS static inline enum kernfs_node_type kernfs_type (struct kernfs_node *kn) {
@@ -284,7 +284,7 @@ void kernfs_kill_sb(struct super_block *sb);void kernfs_init (void ); -#else /* CONFIG_SYSFS */ +#else /* CONFIG_KERNFS */ static inline enum kernfs_node_type kernfs_type (struct kernfs_node *kn) {return 0 ; }/* whatever */
@@ -379,7 +379,7 @@ static inline void kernfs_kill_sb(struct super_block *sb) { }static inline void kernfs_init (void ) { } -#endif /* CONFIG_SYSFS */ +#endif /* CONFIG_KERNFS */ static inline struct kernfs_node *kernfs_find_and_get (struct kernfs_node *kn,const char *name)
Signature · 1 line
1.8.5.3
Tejun Heo
maintainerPATCH 1/1 sysfs, kobject: add sysfs wrapper for kernfs_enable_ns()
In reply to Tejun HeoQuoted 1 line
From
5de4dc5a029516985b6ee9add23b4bacbbea490dMon Sep 17 00:00:00 2001
From: Tejun Heo <[email protected]>
Date: Fri, 7 Feb 2014 13:32:07 -0500
Currently, kobject is invoking kernfs_enable_ns() directly. This is fine now as sysfs and kernfs are enabled and disabled together. If sysfs is disabled, kernfs_enable_ns() is switched to dummy implementation too and everything is fine; however, kernfs will soon have its own config option CONFIG_KERNFS and !SYSFS && KERNFS will be possible, which can make kobject call into non-dummy kernfs_enable_ns() with NULL kernfs_node pointers leading to an oops.
Introduce sysfs_enable_ns() which is a wrapper around kernfs_enable_ns() so that it can be made a noop depending only on CONFIG_SYSFS regardless of the planned CONFIG_KERNFS.
--- include/linux/sysfs.h | 9 +++++++++ lib/kobject.c | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-)
include/linux/sysfs.h +9 -0
@@ -244,6 +244,11 @@ void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr);int __must_check sysfs_init (void ); +static inline void sysfs_enable_ns (struct kernfs_node *kn) +{ +return kernfs_enable_ns (kn); +} +#else /* CONFIG_SYSFS */ static inline int sysfs_create_dir_ns (struct kobject *kobj,const void *ns)
@@ -416,6 +421,10 @@ static inline int __must_check sysfs_init(void)return 0 ; } +static inline void sysfs_enable_ns (struct kernfs_node *kn) +{ +} +#endif /* CONFIG_SYSFS */ static inline int __must_check sysfs_create_file(struct kobject *kobj,
lib/kobject.c +1 -1
@@ -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)); -kernfs_enable_ns (kobj->sd); +sysfs_enable_ns (kobj->sd); }return 0 ;
Signature · 1 line
1.8.5.3
Tejun Heo
maintainerHello,
Quoted 10 lines · Tejun Heo
0001-kernfs-invoke-dir_ops-while-holding-active-ref-of-th.patch 0002-kernfs-rename-kernfs_dir_ops-to-kernfs_syscall_ops.patch 0003-kernfs-implement-kernfs_syscall_ops-remount_fs-and-s.patch 0004-kernfs-add-missing-kernfs_active-checks-in-directory.patch 0005-kernfs-allow-nodes-to-be-created-in-the-deactivated-.patch 0006-kernfs-implement-kernfs_ops-atomic_write_len.patch 0007-kernfs-add-kernfs_open_file-priv.patch 0008-kernfs-implement-kernfs_node_from_dentry-kernfs_root.patch 0009-kernfs-implement-kernfs_get_parent-kernfs_name-path-.patch 0010-kernfs-add-CONFIG_KERNFS.patch
0009 updated to v2 and a new patch added between 0009 and 0010. Both are for dummy definition related issues for !CONFIG_SYSFS. The git tree has been updated accordingly.
Thanks!
Signature · 1 line
tejun
Greg KH
maintainerQuoted 14 lines · Tejun Heo
Hello,
Quoted 10 lines · Tejun Heo
0001-kernfs-invoke-dir_ops-while-holding-active-ref-of-th.patch 0002-kernfs-rename-kernfs_dir_ops-to-kernfs_syscall_ops.patch 0003-kernfs-implement-kernfs_syscall_ops-remount_fs-and-s.patch 0004-kernfs-add-missing-kernfs_active-checks-in-directory.patch 0005-kernfs-allow-nodes-to-be-created-in-the-deactivated-.patch 0006-kernfs-implement-kernfs_ops-atomic_write_len.patch 0007-kernfs-add-kernfs_open_file-priv.patch 0008-kernfs-implement-kernfs_node_from_dentry-kernfs_root.patch 0009-kernfs-implement-kernfs_get_parent-kernfs_name-path-.patch 0010-kernfs-add-CONFIG_KERNFS.patch
Quoted 3 lines
0009 updated to v2 and a new patch added between 0009 and 0010. Both are for dummy definition related issues for !CONFIG_SYSFS. The git tree has been updated accordingly.
Ok, I should have now applied all of these, and hopefully in the correct order. If I got anything wrong, please let me know.
thanks,
greg k-h
Tejun Heo
maintainerQuoted 2 lines · Greg KH
Ok, I should have now applied all of these, and hopefully in the correct order. If I got anything wrong, please let me know.
Everything looks good to me.
Thanks!
Signature · 1 line
tejun
Tejun Heo
maintainerQuoted 5 lines · Tejun Heo
Quoted 2 lines · Greg KH
Ok, I should have now applied all of these, and hopefully in the correct order. If I got anything wrong, please let me know.
Everything looks good to me.
Ooh, JFYI, I'm pulling driver-core-next into cgroup for-3.15 so that cgroup kernfs conversion can be put on top of it.
Thanks.
Signature · 1 line
tejun
Greg KH
maintainerQuoted 9 lines · Tejun Heo
Quoted 5 lines · Tejun Heo
Quoted 2 lines · Greg KH
Ok, I should have now applied all of these, and hopefully in the correct order. If I got anything wrong, please let me know.
Everything looks good to me.
Ooh, JFYI, I'm pulling driver-core-next into cgroup for-3.15 so that cgroup kernfs conversion can be put on top of it.
That's fine with me. Do you feel kernfs is good enough now for other subsystems to use? I want to port debugfs to it when ready.
thanks,
greg k-h
Tejun Heo
maintainerHello,
Quoted 6 lines · Greg KH
Quoted 5 lines · Tejun Heo
Quoted 4 lines · Tejun Heo
Quoted 2 lines · Greg KH
Ok, I should have now applied all of these, and hopefully in the correct order. If I got anything wrong, please let me know.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
Everything looks good to me.
Ooh, JFYI, I'm pulling driver-core-next into cgroup for-3.15 so that cgroup kernfs conversion can be put on top of it.
That's fine with me. Do you feel kernfs is good enough now for other subsystems to use? I want to port debugfs to it when ready.
API-wise, I think, or at least hope, so. I've been running quite a few tests with the converted cgroup and haven't found anything critical yet, so the outlook isn't too bad.
Thanks.
Signature · 1 line
tejun