aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTejun Heo <tj@kernel.org>2013-11-28 14:54:40 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-11-29 21:10:48 -0500
commitba7443bc656e5236c316b2acacc8b551f872910f (patch)
tree08b9a7a0504ae77abc634419167958ce4e6c4e64
parent061447a496b915f1dc8f8c645c6825f856d2bbac (diff)
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 <tj@kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--fs/kernfs/dir.c71
-rw-r--r--fs/kernfs/kernfs-internal.h20
-rw-r--r--fs/sysfs/mount.c29
-rw-r--r--include/linux/kernfs.h13
4 files changed, 113 insertions, 20 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c
index a4ca4de3cb21..246740a741ef 100644
--- a/fs/kernfs/dir.c
+++ b/fs/kernfs/dir.c
@@ -211,7 +211,7 @@ static int sysfs_alloc_ino(unsigned int *pino)
211 211
212 retry: 212 retry:
213 spin_lock(&sysfs_ino_lock); 213 spin_lock(&sysfs_ino_lock);
214 rc = ida_get_new_above(&sysfs_ino_ida, 2, &ino); 214 rc = ida_get_new_above(&sysfs_ino_ida, 1, &ino);
215 spin_unlock(&sysfs_ino_lock); 215 spin_unlock(&sysfs_ino_lock);
216 216
217 if (rc == -EAGAIN) { 217 if (rc == -EAGAIN) {
@@ -253,9 +253,11 @@ EXPORT_SYMBOL_GPL(kernfs_get);
253void kernfs_put(struct sysfs_dirent *sd) 253void kernfs_put(struct sysfs_dirent *sd)
254{ 254{
255 struct sysfs_dirent *parent_sd; 255 struct sysfs_dirent *parent_sd;
256 struct kernfs_root *root;
256 257
257 if (!sd || !atomic_dec_and_test(&sd->s_count)) 258 if (!sd || !atomic_dec_and_test(&sd->s_count))
258 return; 259 return;
260 root = kernfs_root(sd);
259 repeat: 261 repeat:
260 /* Moving/renaming is always done while holding reference. 262 /* Moving/renaming is always done while holding reference.
261 * sd->s_parent won't change beneath us. 263 * sd->s_parent won't change beneath us.
@@ -278,8 +280,13 @@ void kernfs_put(struct sysfs_dirent *sd)
278 kmem_cache_free(sysfs_dir_cachep, sd); 280 kmem_cache_free(sysfs_dir_cachep, sd);
279 281
280 sd = parent_sd; 282 sd = parent_sd;
281 if (sd && atomic_dec_and_test(&sd->s_count)) 283 if (sd) {
282 goto repeat; 284 if (atomic_dec_and_test(&sd->s_count))
285 goto repeat;
286 } else {
287 /* just released the root sd, free @root too */
288 kfree(root);
289 }
283} 290}
284EXPORT_SYMBOL_GPL(kernfs_put); 291EXPORT_SYMBOL_GPL(kernfs_put);
285 292
@@ -493,13 +500,15 @@ static void sysfs_remove_one(struct sysfs_addrm_cxt *acxt,
493 if (sd->s_flags & SYSFS_FLAG_REMOVED) 500 if (sd->s_flags & SYSFS_FLAG_REMOVED)
494 return; 501 return;
495 502
496 sysfs_unlink_sibling(sd); 503 if (sd->s_parent) {
504 sysfs_unlink_sibling(sd);
497 505
498 /* Update timestamps on the parent */ 506 /* Update timestamps on the parent */
499 ps_iattr = sd->s_parent->s_iattr; 507 ps_iattr = sd->s_parent->s_iattr;
500 if (ps_iattr) { 508 if (ps_iattr) {
501 struct iattr *ps_iattrs = &ps_iattr->ia_iattr; 509 ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME;
502 ps_iattrs->ia_ctime = ps_iattrs->ia_mtime = CURRENT_TIME; 510 ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME;
511 }
503 } 512 }
504 513
505 sd->s_flags |= SYSFS_FLAG_REMOVED; 514 sd->s_flags |= SYSFS_FLAG_REMOVED;
@@ -604,6 +613,49 @@ struct sysfs_dirent *kernfs_find_and_get_ns(struct sysfs_dirent *parent,
604EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); 613EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns);
605 614
606/** 615/**
616 * kernfs_create_root - create a new kernfs hierarchy
617 * @priv: opaque data associated with the new directory
618 *
619 * Returns the root of the new hierarchy on success, ERR_PTR() value on
620 * failure.
621 */
622struct kernfs_root *kernfs_create_root(void *priv)
623{
624 struct kernfs_root *root;
625 struct sysfs_dirent *sd;
626
627 root = kzalloc(sizeof(*root), GFP_KERNEL);
628 if (!root)
629 return ERR_PTR(-ENOMEM);
630
631 sd = sysfs_new_dirent("", S_IFDIR | S_IRUGO | S_IXUGO, SYSFS_DIR);
632 if (!sd) {
633 kfree(root);
634 return ERR_PTR(-ENOMEM);
635 }
636
637 sd->s_flags &= ~SYSFS_FLAG_REMOVED;
638 sd->priv = priv;
639 sd->s_dir.root = root;
640
641 root->sd = sd;
642
643 return root;
644}
645
646/**
647 * kernfs_destroy_root - destroy a kernfs hierarchy
648 * @root: root of the hierarchy to destroy
649 *
650 * Destroy the hierarchy anchored at @root by removing all existing
651 * directories and destroying @root.
652 */
653void kernfs_destroy_root(struct kernfs_root *root)
654{
655 kernfs_remove(root->sd); /* will also free @root */
656}
657
658/**
607 * kernfs_create_dir_ns - create a directory 659 * kernfs_create_dir_ns - create a directory
608 * @parent: parent in which to create a new directory 660 * @parent: parent in which to create a new directory
609 * @name: name of the new directory 661 * @name: name of the new directory
@@ -626,6 +678,7 @@ struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent,
626 if (!sd) 678 if (!sd)
627 return ERR_PTR(-ENOMEM); 679 return ERR_PTR(-ENOMEM);
628 680
681 sd->s_dir.root = parent->s_dir.root;
629 sd->s_ns = ns; 682 sd->s_ns = ns;
630 sd->priv = priv; 683 sd->priv = priv;
631 684
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h
index 62ae35f997f7..7dfe06278350 100644
--- a/fs/kernfs/kernfs-internal.h
+++ b/fs/kernfs/kernfs-internal.h
@@ -25,6 +25,12 @@ struct sysfs_elem_dir {
25 unsigned long subdirs; 25 unsigned long subdirs;
26 /* children rbtree starts here and goes through sd->s_rb */ 26 /* children rbtree starts here and goes through sd->s_rb */
27 struct rb_root children; 27 struct rb_root children;
28
29 /*
30 * The kernfs hierarchy this directory belongs to. This fits
31 * better directly in sysfs_dirent but is here to save space.
32 */
33 struct kernfs_root *root;
28}; 34};
29 35
30struct sysfs_elem_symlink { 36struct sysfs_elem_symlink {
@@ -104,6 +110,20 @@ static inline unsigned int sysfs_type(struct sysfs_dirent *sd)
104 return sd->s_flags & SYSFS_TYPE_MASK; 110 return sd->s_flags & SYSFS_TYPE_MASK;
105} 111}
106 112
113/**
114 * kernfs_root - find out the kernfs_root a sysfs_dirent belongs to
115 * @sd: sysfs_dirent of interest
116 *
117 * Return the kernfs_root @sd belongs to.
118 */
119static inline struct kernfs_root *kernfs_root(struct sysfs_dirent *sd)
120{
121 /* if parent exists, it's always a dir; otherwise, @sd is a dir */
122 if (sd->s_parent)
123 sd = sd->s_parent;
124 return sd->s_dir.root;
125}
126
107/* 127/*
108 * Context structure to be used while adding/removing nodes. 128 * Context structure to be used while adding/removing nodes.
109 */ 129 */
diff --git a/fs/sysfs/mount.c b/fs/sysfs/mount.c
index 7cbd1fce2826..0b5661b462f7 100644
--- a/fs/sysfs/mount.c
+++ b/fs/sysfs/mount.c
@@ -32,15 +32,8 @@ static const struct super_operations sysfs_ops = {
32 .evict_inode = sysfs_evict_inode, 32 .evict_inode = sysfs_evict_inode,
33}; 33};
34 34
35static struct sysfs_dirent sysfs_root = { 35static struct kernfs_root *sysfs_root;
36 .s_name = "", 36struct sysfs_dirent *sysfs_root_sd;
37 .s_count = ATOMIC_INIT(1),
38 .s_flags = SYSFS_DIR,
39 .s_mode = S_IFDIR | S_IRUGO | S_IXUGO,
40 .s_ino = 1,
41};
42
43struct sysfs_dirent *sysfs_root_sd = &sysfs_root;
44 37
45static int sysfs_fill_super(struct super_block *sb) 38static int sysfs_fill_super(struct super_block *sb)
46{ 39{
@@ -68,6 +61,7 @@ static int sysfs_fill_super(struct super_block *sb)
68 pr_debug("%s: could not get root dentry!\n", __func__); 61 pr_debug("%s: could not get root dentry!\n", __func__);
69 return -ENOMEM; 62 return -ENOMEM;
70 } 63 }
64 kernfs_get(sysfs_root_sd);
71 root->d_fsdata = sysfs_root_sd; 65 root->d_fsdata = sysfs_root_sd;
72 sb->s_root = root; 66 sb->s_root = root;
73 sb->s_d_op = &sysfs_dentry_ops; 67 sb->s_d_op = &sysfs_dentry_ops;
@@ -138,11 +132,15 @@ static struct dentry *sysfs_mount(struct file_system_type *fs_type,
138static void sysfs_kill_sb(struct super_block *sb) 132static void sysfs_kill_sb(struct super_block *sb)
139{ 133{
140 struct sysfs_super_info *info = sysfs_info(sb); 134 struct sysfs_super_info *info = sysfs_info(sb);
141 /* Remove the superblock from fs_supers/s_instances 135 struct sysfs_dirent *root_sd = sb->s_root->d_fsdata;
136
137 /*
138 * Remove the superblock from fs_supers/s_instances
142 * so we can't find it, before freeing sysfs_super_info. 139 * so we can't find it, before freeing sysfs_super_info.
143 */ 140 */
144 kill_anon_super(sb); 141 kill_anon_super(sb);
145 free_sysfs_super_info(info); 142 free_sysfs_super_info(info);
143 kernfs_put(root_sd);
146} 144}
147 145
148static struct file_system_type sysfs_fs_type = { 146static struct file_system_type sysfs_fs_type = {
@@ -166,12 +164,21 @@ int __init sysfs_init(void)
166 if (err) 164 if (err)
167 goto out_err; 165 goto out_err;
168 166
167 sysfs_root = kernfs_create_root(NULL);
168 if (IS_ERR(sysfs_root)) {
169 err = PTR_ERR(sysfs_root);
170 goto out_err;
171 }
172 sysfs_root_sd = sysfs_root->sd;
173
169 err = register_filesystem(&sysfs_fs_type); 174 err = register_filesystem(&sysfs_fs_type);
170 if (err) 175 if (err)
171 goto out_err; 176 goto out_destroy_root;
172 177
173 return 0; 178 return 0;
174 179
180out_destroy_root:
181 kernfs_destroy_root(sysfs_root);
175out_err: 182out_err:
176 kmem_cache_destroy(sysfs_dir_cachep); 183 kmem_cache_destroy(sysfs_dir_cachep);
177 sysfs_dir_cachep = NULL; 184 sysfs_dir_cachep = NULL;
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h
index fd8f574ef2fe..f75548b8ed7a 100644
--- a/include/linux/kernfs.h
+++ b/include/linux/kernfs.h
@@ -20,6 +20,11 @@ struct vm_area_struct;
20 20
21struct sysfs_dirent; 21struct sysfs_dirent;
22 22
23struct kernfs_root {
24 /* published fields */
25 struct sysfs_dirent *sd;
26};
27
23struct sysfs_open_file { 28struct sysfs_open_file {
24 /* published fields */ 29 /* published fields */
25 struct sysfs_dirent *sd; 30 struct sysfs_dirent *sd;
@@ -76,6 +81,9 @@ struct sysfs_dirent *kernfs_find_and_get_ns(struct sysfs_dirent *parent,
76void kernfs_get(struct sysfs_dirent *sd); 81void kernfs_get(struct sysfs_dirent *sd);
77void kernfs_put(struct sysfs_dirent *sd); 82void kernfs_put(struct sysfs_dirent *sd);
78 83
84struct kernfs_root *kernfs_create_root(void *priv);
85void kernfs_destroy_root(struct kernfs_root *root);
86
79struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, 87struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent,
80 const char *name, void *priv, 88 const char *name, void *priv,
81 const void *ns); 89 const void *ns);
@@ -107,6 +115,11 @@ kernfs_find_and_get_ns(struct sysfs_dirent *parent, const char *name,
107static inline void kernfs_get(struct sysfs_dirent *sd) { } 115static inline void kernfs_get(struct sysfs_dirent *sd) { }
108static inline void kernfs_put(struct sysfs_dirent *sd) { } 116static inline void kernfs_put(struct sysfs_dirent *sd) { }
109 117
118static inline struct kernfs_root *kernfs_create_root(void *priv)
119{ return ERR_PTR(-ENOSYS); }
120
121static inline void kernfs_destroy_root(struct kernfs_root *root) { }
122
110static inline struct sysfs_dirent * 123static inline struct sysfs_dirent *
111kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, 124kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv,
112 const void *ns) 125 const void *ns)