diff options
-rw-r--r-- | fs/kernfs/dir.c | 139 | ||||
-rw-r--r-- | include/linux/kernfs.h | 1 |
2 files changed, 53 insertions, 87 deletions
diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index e565ec096ae9..7f8afc1d08f1 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c | |||
@@ -181,38 +181,14 @@ void kernfs_put_active(struct kernfs_node *kn) | |||
181 | * kernfs_drain - drain kernfs_node | 181 | * kernfs_drain - drain kernfs_node |
182 | * @kn: kernfs_node to drain | 182 | * @kn: kernfs_node to drain |
183 | * | 183 | * |
184 | * Drain existing usages of @kn. Mutiple removers may invoke this function | 184 | * Drain existing usages. |
185 | * concurrently on @kn and all will return after draining is complete. | ||
186 | * Returns %true if drain is performed and kernfs_mutex was temporarily | ||
187 | * released. %false if @kn was already drained and no operation was | ||
188 | * necessary. | ||
189 | * | ||
190 | * The caller is responsible for ensuring @kn stays pinned while this | ||
191 | * function is in progress even if it gets removed by someone else. | ||
192 | */ | 185 | */ |
193 | static bool kernfs_drain(struct kernfs_node *kn) | 186 | static void kernfs_drain(struct kernfs_node *kn) |
194 | __releases(&kernfs_mutex) __acquires(&kernfs_mutex) | ||
195 | { | 187 | { |
196 | struct kernfs_root *root = kernfs_root(kn); | 188 | struct kernfs_root *root = kernfs_root(kn); |
197 | 189 | ||
198 | lockdep_assert_held(&kernfs_mutex); | ||
199 | WARN_ON_ONCE(atomic_read(&kn->active) >= 0); | 190 | WARN_ON_ONCE(atomic_read(&kn->active) >= 0); |
200 | 191 | ||
201 | /* | ||
202 | * We want to go through the active ref lockdep annotation at least | ||
203 | * once for all node removals, but the lockdep annotation can't be | ||
204 | * nested inside kernfs_mutex and deactivation can't make forward | ||
205 | * progress if we keep dropping the mutex. Use JUST_ACTIVATED to | ||
206 | * force the slow path once for each deactivation if lockdep is | ||
207 | * enabled. | ||
208 | */ | ||
209 | if ((!kernfs_lockdep(kn) || !(kn->flags & KERNFS_JUST_DEACTIVATED)) && | ||
210 | atomic_read(&kn->active) == KN_DEACTIVATED_BIAS) | ||
211 | return false; | ||
212 | |||
213 | kn->flags &= ~KERNFS_JUST_DEACTIVATED; | ||
214 | mutex_unlock(&kernfs_mutex); | ||
215 | |||
216 | if (kernfs_lockdep(kn)) { | 192 | if (kernfs_lockdep(kn)) { |
217 | rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); | 193 | rwsem_acquire(&kn->dep_map, 0, 0, _RET_IP_); |
218 | if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) | 194 | if (atomic_read(&kn->active) != KN_DEACTIVATED_BIAS) |
@@ -226,9 +202,6 @@ static bool kernfs_drain(struct kernfs_node *kn) | |||
226 | lock_acquired(&kn->dep_map, _RET_IP_); | 202 | lock_acquired(&kn->dep_map, _RET_IP_); |
227 | rwsem_release(&kn->dep_map, 1, _RET_IP_); | 203 | rwsem_release(&kn->dep_map, 1, _RET_IP_); |
228 | } | 204 | } |
229 | |||
230 | mutex_lock(&kernfs_mutex); | ||
231 | return true; | ||
232 | } | 205 | } |
233 | 206 | ||
234 | /** | 207 | /** |
@@ -474,6 +447,49 @@ int kernfs_add_one(struct kernfs_addrm_cxt *acxt, struct kernfs_node *kn, | |||
474 | } | 447 | } |
475 | 448 | ||
476 | /** | 449 | /** |
450 | * kernfs_remove_one - remove kernfs_node from parent | ||
451 | * @acxt: addrm context to use | ||
452 | * @kn: kernfs_node to be removed | ||
453 | * | ||
454 | * Mark @kn removed and drop nlink of parent inode if @kn is a | ||
455 | * directory. @kn is unlinked from the children list. | ||
456 | * | ||
457 | * This function should be called between calls to | ||
458 | * kernfs_addrm_start() and kernfs_addrm_finish() and should be | ||
459 | * passed the same @acxt as passed to kernfs_addrm_start(). | ||
460 | * | ||
461 | * LOCKING: | ||
462 | * Determined by kernfs_addrm_start(). | ||
463 | */ | ||
464 | static void kernfs_remove_one(struct kernfs_addrm_cxt *acxt, | ||
465 | struct kernfs_node *kn) | ||
466 | { | ||
467 | struct kernfs_iattrs *ps_iattr; | ||
468 | |||
469 | /* | ||
470 | * Removal can be called multiple times on the same node. Only the | ||
471 | * first invocation is effective and puts the base ref. | ||
472 | */ | ||
473 | if (atomic_read(&kn->active) < 0) | ||
474 | return; | ||
475 | |||
476 | if (kn->parent) { | ||
477 | kernfs_unlink_sibling(kn); | ||
478 | |||
479 | /* Update timestamps on the parent */ | ||
480 | ps_iattr = kn->parent->iattr; | ||
481 | if (ps_iattr) { | ||
482 | ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; | ||
483 | ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; | ||
484 | } | ||
485 | } | ||
486 | |||
487 | atomic_add(KN_DEACTIVATED_BIAS, &kn->active); | ||
488 | kn->u.removed_list = acxt->removed; | ||
489 | acxt->removed = kn; | ||
490 | } | ||
491 | |||
492 | /** | ||
477 | * kernfs_addrm_finish - finish up kernfs_node add/remove | 493 | * kernfs_addrm_finish - finish up kernfs_node add/remove |
478 | * @acxt: addrm context to finish up | 494 | * @acxt: addrm context to finish up |
479 | * | 495 | * |
@@ -496,6 +512,7 @@ void kernfs_addrm_finish(struct kernfs_addrm_cxt *acxt) | |||
496 | 512 | ||
497 | acxt->removed = kn->u.removed_list; | 513 | acxt->removed = kn->u.removed_list; |
498 | 514 | ||
515 | kernfs_drain(kn); | ||
499 | kernfs_unmap_bin_file(kn); | 516 | kernfs_unmap_bin_file(kn); |
500 | kernfs_put(kn); | 517 | kernfs_put(kn); |
501 | } | 518 | } |
@@ -805,73 +822,23 @@ static struct kernfs_node *kernfs_next_descendant_post(struct kernfs_node *pos, | |||
805 | return pos->parent; | 822 | return pos->parent; |
806 | } | 823 | } |
807 | 824 | ||
808 | static void __kernfs_deactivate(struct kernfs_node *kn) | ||
809 | { | ||
810 | struct kernfs_node *pos; | ||
811 | |||
812 | lockdep_assert_held(&kernfs_mutex); | ||
813 | |||
814 | /* prevent any new usage under @kn by deactivating all nodes */ | ||
815 | pos = NULL; | ||
816 | while ((pos = kernfs_next_descendant_post(pos, kn))) { | ||
817 | if (atomic_read(&pos->active) >= 0) { | ||
818 | atomic_add(KN_DEACTIVATED_BIAS, &pos->active); | ||
819 | pos->flags |= KERNFS_JUST_DEACTIVATED; | ||
820 | } | ||
821 | } | ||
822 | |||
823 | /* | ||
824 | * Drain the subtree. If kernfs_drain() blocked to drain, which is | ||
825 | * indicated by %true return, it temporarily released kernfs_mutex | ||
826 | * and the rbtree might have been modified inbetween breaking our | ||
827 | * future walk. Restart the walk after each %true return. | ||
828 | */ | ||
829 | pos = NULL; | ||
830 | while ((pos = kernfs_next_descendant_post(pos, kn))) { | ||
831 | bool drained; | ||
832 | |||
833 | kernfs_get(pos); | ||
834 | drained = kernfs_drain(pos); | ||
835 | kernfs_put(pos); | ||
836 | if (drained) | ||
837 | pos = NULL; | ||
838 | } | ||
839 | } | ||
840 | |||
841 | static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, | 825 | static void __kernfs_remove(struct kernfs_addrm_cxt *acxt, |
842 | struct kernfs_node *kn) | 826 | struct kernfs_node *kn) |
843 | { | 827 | { |
844 | struct kernfs_node *pos; | 828 | struct kernfs_node *pos, *next; |
845 | |||
846 | lockdep_assert_held(&kernfs_mutex); | ||
847 | 829 | ||
848 | if (!kn) | 830 | if (!kn) |
849 | return; | 831 | return; |
850 | 832 | ||
851 | pr_debug("kernfs %s: removing\n", kn->name); | 833 | pr_debug("kernfs %s: removing\n", kn->name); |
852 | 834 | ||
853 | __kernfs_deactivate(kn); | 835 | next = NULL; |
854 | |||
855 | /* unlink the subtree node-by-node */ | ||
856 | do { | 836 | do { |
857 | struct kernfs_iattrs *ps_iattr; | 837 | pos = next; |
858 | 838 | next = kernfs_next_descendant_post(pos, kn); | |
859 | pos = kernfs_leftmost_descendant(kn); | 839 | if (pos) |
860 | 840 | kernfs_remove_one(acxt, pos); | |
861 | if (pos->parent) { | 841 | } while (next); |
862 | kernfs_unlink_sibling(pos); | ||
863 | |||
864 | /* update timestamps on the parent */ | ||
865 | ps_iattr = pos->parent->iattr; | ||
866 | if (ps_iattr) { | ||
867 | ps_iattr->ia_iattr.ia_ctime = CURRENT_TIME; | ||
868 | ps_iattr->ia_iattr.ia_mtime = CURRENT_TIME; | ||
869 | } | ||
870 | } | ||
871 | |||
872 | pos->u.removed_list = acxt->removed; | ||
873 | acxt->removed = pos; | ||
874 | } while (pos != kn); | ||
875 | } | 842 | } |
876 | 843 | ||
877 | /** | 844 | /** |
diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index 61bd7ae6b8e0..289d4f639ade 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h | |||
@@ -37,7 +37,6 @@ enum kernfs_node_type { | |||
37 | #define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK | 37 | #define KERNFS_FLAG_MASK ~KERNFS_TYPE_MASK |
38 | 38 | ||
39 | enum kernfs_node_flag { | 39 | enum kernfs_node_flag { |
40 | KERNFS_JUST_DEACTIVATED = 0x0010, /* used to aid lockdep annotation */ | ||
41 | KERNFS_NS = 0x0020, | 40 | KERNFS_NS = 0x0020, |
42 | KERNFS_HAS_SEQ_SHOW = 0x0040, | 41 | KERNFS_HAS_SEQ_SHOW = 0x0040, |
43 | KERNFS_HAS_MMAP = 0x0080, | 42 | KERNFS_HAS_MMAP = 0x0080, |