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 /fs/kernfs/dir.c | |
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>
Diffstat (limited to 'fs/kernfs/dir.c')
-rw-r--r-- | fs/kernfs/dir.c | 71 |
1 files changed, 62 insertions, 9 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 | ||