diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/kernfs/dir.c | 114 | ||||
-rw-r--r-- | fs/kernfs/file.c | 10 | ||||
-rw-r--r-- | fs/kernfs/kernfs-internal.h | 12 | ||||
-rw-r--r-- | fs/kernfs/symlink.c | 10 |
4 files changed, 117 insertions, 29 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 770d687ee9f3..f878e4f2efe7 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, | |||
399 | } | 399 | } |
400 | 400 | ||
401 | /** | 401 | /** |
402 | * kernfs_addrm_start - prepare for kernfs_node add/remove | ||
403 | * @acxt: pointer to kernfs_addrm_cxt to be used | ||
404 | * | ||
405 | * This function is called when the caller is about to add or remove | ||
406 | * kernfs_node. This function acquires kernfs_mutex. @acxt is used | ||
407 | * to keep and pass context to other addrm functions. | ||
408 | * | ||
409 | * LOCKING: | ||
410 | * Kernel thread context (may sleep). kernfs_mutex is locked on | ||
411 | * return. | ||
412 | */ | ||
413 | void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt) | ||
414 | __acquires(kernfs_mutex) | ||
415 | { | ||
416 | memset(acxt, 0, sizeof(*acxt)); | ||
417 | |||
418 | mutex_lock(&kernfs_mutex); | ||
419 | } | ||
420 | |||
421 | /** | ||
402 | * kernfs_add_one - add kernfs_node to parent without warning | 422 | * kernfs_add_one - add kernfs_node to parent without warning |
423 | * @acxt: addrm context to use | ||
403 | * @kn: kernfs_node to be added | 424 | * @kn: kernfs_node to be added |
404 | * @parent: the parent kernfs_node to add @kn to | 425 | * @parent: the parent kernfs_node to add @kn to |
405 | * | 426 | * |
@@ -407,29 +428,34 @@ struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, | |||
407 | * parent inode if @kn is a directory and link into the children list | 428 | * parent inode if @kn is a directory and link into the children list |
408 | * of the parent. | 429 | * of the parent. |
409 | * | 430 | * |
431 | * This function should be called between calls to | ||
432 | * kernfs_addrm_start() and kernfs_addrm_finish() and should be passed | ||
433 | * the same @acxt as passed to kernfs_addrm_start(). | ||
434 | * | ||
435 | * LOCKING: | ||
436 | * Determined by kernfs_addrm_start(). | ||
437 | * | ||
410 | * RETURNS: | 438 | * RETURNS: |
411 | * 0 on success, -EEXIST if entry with the given name already | 439 | * 0 on success, -EEXIST if entry with the given name already |
412 | * exists. | 440 | * exists. |
413 | */ | 441 | */ |
414 | int kernfs_add_one(struct kernfs_node *kn, struct kernfs_node *parent) | 442 | int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, |
443 | struct kernfs_node *parent) | ||
415 | { | 444 | { |
445 | bool has_ns = kernfs_ns_enabled(parent); | ||
416 | struct kernfs_iattrs *ps_iattr; | 446 | struct kernfs_iattrs *ps_iattr; |
417 | bool has_ns; | ||
418 | int ret; | 447 | int ret; |
419 | 448 | ||
420 | if (!kernfs_get_active(parent)) | 449 | WARN_ON_ONCE(atomic_read(&parent->active) < 0); |
421 | return -ENOENT; | ||
422 | 450 | ||
423 | mutex_lock(&kernfs_mutex); | 451 | if (has_ns != (bool)kn->ns) { |
424 | 452 | WARN(1, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", | |
425 | ret = -EINVAL; | 453 | has_ns ? "required" : "invalid", parent->name, kn->name); |
426 | has_ns = kernfs_ns_enabled(parent); | 454 | return -EINVAL; |
427 | if (WARN(has_ns != (bool)kn->ns, KERN_WARNING "kernfs: ns %s in '%s' for '%s'\n", | 455 | } |
428 | has_ns ? "required" : "invalid", parent->name, kn->name)) | ||
429 | goto out_unlock; | ||
430 | 456 | ||
431 | if (kernfs_type(parent) != KERNFS_DIR) | 457 | if (kernfs_type(parent) != KERNFS_DIR) |
432 | goto out_unlock; | 458 | return -EINVAL; |
433 | 459 | ||
434 | kn->hash = kernfs_name_hash(kn->name, kn->ns); | 460 | kn->hash = kernfs_name_hash(kn->name, kn->ns); |
435 | kn->parent = parent; | 461 | kn->parent = parent; |
@@ -437,7 +463,7 @@ int kernfs_add_one(struct kernfs_node *kn, struct kernfs_node *parent) | |||
437 | 463 | ||
438 | ret = kernfs_link_sibling(kn); | 464 | ret = kernfs_link_sibling(kn); |
439 | if (ret) | 465 | if (ret) |
440 | goto out_unlock; | 466 | return ret; |
441 | 467 | ||
442 | /* Update timestamps on the parent */ | 468 | /* Update timestamps on the parent */ |
443 | ps_iattr = parent->iattr; | 469 | ps_iattr = parent->iattr; |
@@ -448,11 +474,34 @@ int kernfs_add_one(struct kernfs_node *kn, struct kernfs_node *parent) | |||
448 | 474 | ||
449 | /* Mark the entry added into directory tree */ | 475 | /* Mark the entry added into directory tree */ |
450 | atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); | 476 | atomic_sub(KN_DEACTIVATED_BIAS, &kn->active); |
451 | ret = 0; | 477 | return 0; |
452 | out_unlock: | 478 | } |
479 | |||
480 | /** | ||
481 | * kernfs_addrm_finish - finish up kernfs_node add/remove | ||
482 | * @acxt: addrm context to finish up | ||
483 | * | ||
484 | * Finish up kernfs_node add/remove. Resources acquired by | ||
485 | * kernfs_addrm_start() are released and removed kernfs_nodes are | ||
486 | * cleaned up. | ||
487 | * | ||
488 | * LOCKING: | ||
489 | * kernfs_mutex is released. | ||
490 | */ | ||
491 | void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt) | ||
492 | __releases(kernfs_mutex) | ||
493 | { | ||
494 | /* release resources acquired by kernfs_addrm_start() */ | ||
453 | mutex_unlock(&kernfs_mutex); | 495 | mutex_unlock(&kernfs_mutex); |
454 | kernfs_put_active(parent); | 496 | |
455 | return ret; | 497 | /* kill removed kernfs_nodes */ |
498 | while (acxt->removed) { | ||
499 | struct kernfs_node *kn = acxt->removed; | ||
500 | |||
501 | acxt->removed = kn->u.removed_list; | ||
502 | |||
503 | kernfs_put(kn); | ||
504 | } | ||
456 | } | 505 | } |
457 | 506 | ||
458 | /** | 507 | /** |
@@ -584,6 +633,7 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, | |||
584 | const char *name, umode_t mode, | 633 | const char *name, umode_t mode, |
585 | void *priv, const void *ns) | 634 | void *priv, const void *ns) |
586 | { | 635 | { |
636 | struct kernfs_addrm_cxt acxt; | ||
587 | struct kernfs_node *kn; | 637 | struct kernfs_node *kn; |
588 | int rc; | 638 | int rc; |
589 | 639 | ||
@@ -598,7 +648,14 @@ struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent, | |||
598 | kn->priv = priv; | 648 | kn->priv = priv; |
599 | 649 | ||
600 | /* link in */ | 650 | /* link in */ |
601 | rc = kernfs_add_one(kn, parent); | 651 | rc = -ENOENT; |
652 | if (kernfs_get_active(parent)) { | ||
653 | kernfs_addrm_start(&acxt); | ||
654 | rc = kernfs_add_one(&acxt, kn, parent); | ||
655 | kernfs_addrm_finish(&acxt); | ||
656 | kernfs_put_active(parent); | ||
657 | } | ||
658 | |||
602 | if (!rc) | 659 | if (!rc) |
603 | return kn; | 660 | return kn; |
604 | 661 | ||
@@ -784,7 +841,8 @@ static void __kernfs_deactivate(struct kernfs_node *kn) | |||
784 | } | 841 | } |
785 | } | 842 | } |
786 | 843 | ||
787 | static void __kernfs_remove(struct kernfs_node *kn) | 844 | static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, |
845 | struct kernfs_node *kn) | ||
788 | { | 846 | { |
789 | struct kernfs_node *pos; | 847 | struct kernfs_node *pos; |
790 | 848 | ||
@@ -832,7 +890,8 @@ static void __kernfs_remove(struct kernfs_node *kn) | |||
832 | ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; | 890 | ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; |
833 | } | 891 | } |
834 | 892 | ||
835 | kernfs_put(pos); | 893 | pos->u.removed_list = acxt->removed; |
894 | acxt->removed = pos; | ||
836 | } | 895 | } |
837 | 896 | ||
838 | kernfs_put(pos); | 897 | kernfs_put(pos); |
@@ -847,9 +906,11 @@ static void __kernfs_remove(struct kernfs_node *kn) | |||
847 | */ | 906 | */ |
848 | void kernfs_remove(struct kernfs_node *kn) | 907 | void kernfs_remove(struct kernfs_node *kn) |
849 | { | 908 | { |
850 | mutex_lock(&kernfs_mutex); | 909 | struct kernfs_addrm_cxt acxt; |
851 | __kernfs_remove(kn); | 910 | |
852 | mutex_unlock(&kernfs_mutex); | 911 | kernfs_addrm_start(&acxt); |
912 | __kernfs_remove(&acxt, kn); | ||
913 | kernfs_addrm_finish(&acxt); | ||
853 | } | 914 | } |
854 | 915 | ||
855 | /** | 916 | /** |
@@ -864,6 +925,7 @@ void kernfs_remove(struct kernfs_node *kn) | |||
864 | int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, | 925 | int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, |
865 | const void *ns) | 926 | const void *ns) |
866 | { | 927 | { |
928 | struct kernfs_addrm_cxt acxt; | ||
867 | struct kernfs_node *kn; | 929 | struct kernfs_node *kn; |
868 | 930 | ||
869 | if (!parent) { | 931 | if (!parent) { |
@@ -872,13 +934,13 @@ int kernfs_remove_by_name_ns(struct kernfs_node *parent, const char *name, | |||
872 | return -ENOENT; | 934 | return -ENOENT; |
873 | } | 935 | } |
874 | 936 | ||
875 | mutex_lock(&kernfs_mutex); | 937 | kernfs_addrm_start(&acxt); |
876 | 938 | ||
877 | kn = kernfs_find_ns(parent, name, ns); | 939 | kn = kernfs_find_ns(parent, name, ns); |
878 | if (kn) | 940 | if (kn) |
879 | __kernfs_remove(kn); | 941 | __kernfs_remove(&acxt, kn); |
880 | 942 | ||
881 | mutex_unlock(&kernfs_mutex); | 943 | kernfs_addrm_finish(&acxt); |
882 | 944 | ||
883 | if (kn) | 945 | if (kn) |
884 | return 0; | 946 | return 0; |
diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index ffe1bebf9197..404ffd2f27bc 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, | |||
817 | bool name_is_static, | 817 | bool name_is_static, |
818 | struct lock_class_key *key) | 818 | struct lock_class_key *key) |
819 | { | 819 | { |
820 | struct kernfs_addrm_cxt acxt; | ||
820 | struct kernfs_node *kn; | 821 | struct kernfs_node *kn; |
821 | unsigned flags; | 822 | unsigned flags; |
822 | int rc; | 823 | int rc; |
@@ -852,7 +853,14 @@ struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent, | |||
852 | if (ops->mmap) | 853 | if (ops->mmap) |
853 | kn->flags |= KERNFS_HAS_MMAP; | 854 | kn->flags |= KERNFS_HAS_MMAP; |
854 | 855 | ||
855 | rc = kernfs_add_one(kn, parent); | 856 | rc = -ENOENT; |
857 | if (kernfs_get_active(parent)) { | ||
858 | kernfs_addrm_start(&acxt); | ||
859 | rc = kernfs_add_one(&acxt, kn, parent); | ||
860 | kernfs_addrm_finish(&acxt); | ||
861 | kernfs_put_active(parent); | ||
862 | } | ||
863 | |||
856 | if (rc) { | 864 | if (rc) { |
857 | kernfs_put(kn); | 865 | kernfs_put(kn); |
858 | return ERR_PTR(rc); | 866 | return ERR_PTR(rc); |
diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index 4bc57848076c..e9ec38c86074 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) | |||
46 | } | 46 | } |
47 | 47 | ||
48 | /* | 48 | /* |
49 | * Context structure to be used while adding/removing nodes. | ||
50 | */ | ||
51 | struct kernfs_addrm_cxt { | ||
52 | struct kernfs_node *removed; | ||
53 | }; | ||
54 | |||
55 | /* | ||
49 | * mount.c | 56 | * mount.c |
50 | */ | 57 | */ |
51 | struct kernfs_super_info { | 58 | struct kernfs_super_info { |
@@ -94,7 +101,10 @@ extern const struct inode_operations kernfs_dir_iops; | |||
94 | 101 | ||
95 | struct kernfs_node *kernfs_get_active(struct kernfs_node *kn); | 102 | struct kernfs_node *kernfs_get_active(struct kernfs_node *kn); |
96 | void kernfs_put_active(struct kernfs_node *kn); | 103 | void kernfs_put_active(struct kernfs_node *kn); |
97 | int kernfs_add_one(struct kernfs_node *kn, struct kernfs_node *parent); | 104 | void kernfs_addrm_start(struct kernfs_addrm_cxt *acxt); |
105 | int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, | ||
106 | struct kernfs_node *parent); | ||
107 | void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt); | ||
98 | struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, | 108 | struct kernfs_node *kernfs_new_node(struct kernfs_root *root, const char *name, |
99 | umode_t mode, unsigned flags); | 109 | umode_t mode, unsigned flags); |
100 | 110 | ||
diff --git a/fs/kernfs/symlink.c b/fs/kernfs/symlink.c index 3a939c263ede..b2c106ca3434 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, | |||
27 | struct kernfs_node *target) | 27 | struct kernfs_node *target) |
28 | { | 28 | { |
29 | struct kernfs_node *kn; | 29 | struct kernfs_node *kn; |
30 | struct kernfs_addrm_cxt acxt; | ||
30 | int error; | 31 | int error; |
31 | 32 | ||
32 | kn = kernfs_new_node(kernfs_root(parent), name, S_IFLNK|S_IRWXUGO, | 33 | 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, | |||
39 | kn->symlink.target_kn = target; | 40 | kn->symlink.target_kn = target; |
40 | kernfs_get(target); /* ref owned by symlink */ | 41 | kernfs_get(target); /* ref owned by symlink */ |
41 | 42 | ||
42 | error = kernfs_add_one(kn, parent); | 43 | error = -ENOENT; |
44 | if (kernfs_get_active(parent)) { | ||
45 | kernfs_addrm_start(&acxt); | ||
46 | error = kernfs_add_one(&acxt, kn, parent); | ||
47 | kernfs_addrm_finish(&acxt); | ||
48 | kernfs_put_active(parent); | ||
49 | } | ||
50 | |||
43 | if (!error) | 51 | if (!error) |
44 | return kn; | 52 | return kn; |
45 | 53 | ||