diff options
author | Tejun Heo <tj@kernel.org> | 2013-11-28 14:54:40 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-11-29 21:10:48 -0500 |
commit | ba7443bc656e5236c316b2acacc8b551f872910f (patch) | |
tree | 08b9a7a0504ae77abc634419167958ce4e6c4e64 | |
parent | 061447a496b915f1dc8f8c645c6825f856d2bbac (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.c | 71 | ||||
-rw-r--r-- | fs/kernfs/kernfs-internal.h | 20 | ||||
-rw-r--r-- | fs/sysfs/mount.c | 29 | ||||
-rw-r--r-- | 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 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); | |||
253 | void kernfs_put(struct sysfs_dirent *sd) | 253 | void 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 | } |
284 | EXPORT_SYMBOL_GPL(kernfs_put); | 291 | EXPORT_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, | |||
604 | EXPORT_SYMBOL_GPL(kernfs_find_and_get_ns); | 613 | EXPORT_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 | */ | ||
622 | struct 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 | */ | ||
653 | void 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 | ||
30 | struct sysfs_elem_symlink { | 36 | struct 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 | */ | ||
119 | static 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 | ||
35 | static struct sysfs_dirent sysfs_root = { | 35 | static struct kernfs_root *sysfs_root; |
36 | .s_name = "", | 36 | struct 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 | |||
43 | struct sysfs_dirent *sysfs_root_sd = &sysfs_root; | ||
44 | 37 | ||
45 | static int sysfs_fill_super(struct super_block *sb) | 38 | static 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, | |||
138 | static void sysfs_kill_sb(struct super_block *sb) | 132 | static 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 | ||
148 | static struct file_system_type sysfs_fs_type = { | 146 | static 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 | ||
180 | out_destroy_root: | ||
181 | kernfs_destroy_root(sysfs_root); | ||
175 | out_err: | 182 | out_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 | ||
21 | struct sysfs_dirent; | 21 | struct sysfs_dirent; |
22 | 22 | ||
23 | struct kernfs_root { | ||
24 | /* published fields */ | ||
25 | struct sysfs_dirent *sd; | ||
26 | }; | ||
27 | |||
23 | struct sysfs_open_file { | 28 | struct 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, | |||
76 | void kernfs_get(struct sysfs_dirent *sd); | 81 | void kernfs_get(struct sysfs_dirent *sd); |
77 | void kernfs_put(struct sysfs_dirent *sd); | 82 | void kernfs_put(struct sysfs_dirent *sd); |
78 | 83 | ||
84 | struct kernfs_root *kernfs_create_root(void *priv); | ||
85 | void kernfs_destroy_root(struct kernfs_root *root); | ||
86 | |||
79 | struct sysfs_dirent *kernfs_create_dir_ns(struct sysfs_dirent *parent, | 87 | struct 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, | |||
107 | static inline void kernfs_get(struct sysfs_dirent *sd) { } | 115 | static inline void kernfs_get(struct sysfs_dirent *sd) { } |
108 | static inline void kernfs_put(struct sysfs_dirent *sd) { } | 116 | static inline void kernfs_put(struct sysfs_dirent *sd) { } |
109 | 117 | ||
118 | static inline struct kernfs_root *kernfs_create_root(void *priv) | ||
119 | { return ERR_PTR(-ENOSYS); } | ||
120 | |||
121 | static inline void kernfs_destroy_root(struct kernfs_root *root) { } | ||
122 | |||
110 | static inline struct sysfs_dirent * | 123 | static inline struct sysfs_dirent * |
111 | kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, | 124 | kernfs_create_dir_ns(struct sysfs_dirent *parent, const char *name, void *priv, |
112 | const void *ns) | 125 | const void *ns) |