diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2018-03-10 23:15:52 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2018-03-29 15:07:49 -0400 |
commit | 076515fc926793e162fc6525bed1679ef2bbf269 (patch) | |
tree | afadea0e756147f4631df6b1bbef1c0e670b9494 /fs/dcache.c | |
parent | a7498968338da9b928f5d8054acc8be6ed2bc14c (diff) |
make non-exchanging __d_move() copy ->d_parent rather than swap them
Currently d_move(from, to) does the following:
* name/parent of from <- old name/parent of to, from hashed there
* to is unhashed
* name of to is preserved
* if from used to be detached, to gets detached
* if from used to be attached, parent of to <- old parent of from.
That's both user-visibly bogus and complicates reasoning a lot.
Much saner semantics would be
* name/parent of from <- name/parent of to, from hashed there.
* to is unhashed
* name/parent of to is unchanged.
The price, of course, is that old parent of from might lose a reference.
However,
* all potentially cross-directory callers of d_move() have both
parents pinned directly; typically, dentries themselves are grabbed
only after we have grabbed and locked both parents. IOW, the decrement
of old parent's refcount in case of d_move() won't reach zero.
* __d_move() from d_splice_alias() is done to detached alias.
No refcount decrements in that case
* __d_move() from __d_unalias() *can* get the refcount to zero.
So let's grab a reference to alias' old parent before calling __d_unalias()
and dput() it after we'd dropped rename_lock.
That does make d_splice_alias() potentially blocking. However, it has
no callers in non-sleepable contexts (and the case where we'd grown
that dget/dput pair is _very_ rare, so performance is not an issue).
Another thing that needs adjustment is unlocking in the end of __d_move();
folded it in. And cleaned the remnants of bogus ordering from the
"lock them in the beginning" counterpart - it's never been right and
now (well, for 7 years now) we have that thing always serialized on
rename_lock anyway.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 93 |
1 files changed, 30 insertions, 63 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index c870343f904f..0a58038091f2 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -67,9 +67,7 @@ | |||
67 | * dentry->d_lock | 67 | * dentry->d_lock |
68 | * | 68 | * |
69 | * If no ancestor relationship: | 69 | * If no ancestor relationship: |
70 | * if (dentry1 < dentry2) | 70 | * arbitrary, since it's serialized on rename_lock |
71 | * dentry1->d_lock | ||
72 | * dentry2->d_lock | ||
73 | */ | 71 | */ |
74 | int sysctl_vfs_cache_pressure __read_mostly = 100; | 72 | int sysctl_vfs_cache_pressure __read_mostly = 100; |
75 | EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure); | 73 | EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure); |
@@ -2777,9 +2775,6 @@ static void copy_name(struct dentry *dentry, struct dentry *target) | |||
2777 | 2775 | ||
2778 | static void dentry_lock_for_move(struct dentry *dentry, struct dentry *target) | 2776 | static void dentry_lock_for_move(struct dentry *dentry, struct dentry *target) |
2779 | { | 2777 | { |
2780 | /* | ||
2781 | * XXXX: do we really need to take target->d_lock? | ||
2782 | */ | ||
2783 | if (IS_ROOT(dentry) || dentry->d_parent == target->d_parent) | 2778 | if (IS_ROOT(dentry) || dentry->d_parent == target->d_parent) |
2784 | spin_lock(&target->d_parent->d_lock); | 2779 | spin_lock(&target->d_parent->d_lock); |
2785 | else { | 2780 | else { |
@@ -2793,40 +2788,11 @@ static void dentry_lock_for_move(struct dentry *dentry, struct dentry *target) | |||
2793 | DENTRY_D_LOCK_NESTED); | 2788 | DENTRY_D_LOCK_NESTED); |
2794 | } | 2789 | } |
2795 | } | 2790 | } |
2796 | if (target < dentry) { | 2791 | spin_lock_nested(&dentry->d_lock, 2); |
2797 | spin_lock_nested(&target->d_lock, 2); | 2792 | spin_lock_nested(&target->d_lock, 3); |
2798 | spin_lock_nested(&dentry->d_lock, 3); | ||
2799 | } else { | ||
2800 | spin_lock_nested(&dentry->d_lock, 2); | ||
2801 | spin_lock_nested(&target->d_lock, 3); | ||
2802 | } | ||
2803 | } | ||
2804 | |||
2805 | static void dentry_unlock_for_move(struct dentry *dentry, struct dentry *target) | ||
2806 | { | ||
2807 | if (target->d_parent != dentry->d_parent) | ||
2808 | spin_unlock(&dentry->d_parent->d_lock); | ||
2809 | if (target->d_parent != target) | ||
2810 | spin_unlock(&target->d_parent->d_lock); | ||
2811 | spin_unlock(&target->d_lock); | ||
2812 | spin_unlock(&dentry->d_lock); | ||
2813 | } | 2793 | } |
2814 | 2794 | ||
2815 | /* | 2795 | /* |
2816 | * When switching names, the actual string doesn't strictly have to | ||
2817 | * be preserved in the target - because we're dropping the target | ||
2818 | * anyway. As such, we can just do a simple memcpy() to copy over | ||
2819 | * the new name before we switch, unless we are going to rehash | ||
2820 | * it. Note that if we *do* unhash the target, we are not allowed | ||
2821 | * to rehash it without giving it a new name/hash key - whether | ||
2822 | * we swap or overwrite the names here, resulting name won't match | ||
2823 | * the reality in filesystem; it's only there for d_path() purposes. | ||
2824 | * Note that all of this is happening under rename_lock, so the | ||
2825 | * any hash lookup seeing it in the middle of manipulations will | ||
2826 | * be discarded anyway. So we do not care what happens to the hash | ||
2827 | * key in that case. | ||
2828 | */ | ||
2829 | /* | ||
2830 | * __d_move - move a dentry | 2796 | * __d_move - move a dentry |
2831 | * @dentry: entry to move | 2797 | * @dentry: entry to move |
2832 | * @target: new dentry | 2798 | * @target: new dentry |
@@ -2840,6 +2806,7 @@ static void dentry_unlock_for_move(struct dentry *dentry, struct dentry *target) | |||
2840 | static void __d_move(struct dentry *dentry, struct dentry *target, | 2806 | static void __d_move(struct dentry *dentry, struct dentry *target, |
2841 | bool exchange) | 2807 | bool exchange) |
2842 | { | 2808 | { |
2809 | struct dentry *old_parent; | ||
2843 | struct inode *dir = NULL; | 2810 | struct inode *dir = NULL; |
2844 | unsigned n; | 2811 | unsigned n; |
2845 | if (!dentry->d_inode) | 2812 | if (!dentry->d_inode) |
@@ -2858,49 +2825,47 @@ static void __d_move(struct dentry *dentry, struct dentry *target, | |||
2858 | write_seqcount_begin(&dentry->d_seq); | 2825 | write_seqcount_begin(&dentry->d_seq); |
2859 | write_seqcount_begin_nested(&target->d_seq, DENTRY_D_LOCK_NESTED); | 2826 | write_seqcount_begin_nested(&target->d_seq, DENTRY_D_LOCK_NESTED); |
2860 | 2827 | ||
2828 | old_parent = dentry->d_parent; | ||
2829 | |||
2861 | /* unhash both */ | 2830 | /* unhash both */ |
2862 | if (!d_unhashed(dentry)) | 2831 | if (!d_unhashed(dentry)) |
2863 | ___d_drop(dentry); | 2832 | ___d_drop(dentry); |
2864 | if (!d_unhashed(target)) | 2833 | if (!d_unhashed(target)) |
2865 | ___d_drop(target); | 2834 | ___d_drop(target); |
2866 | 2835 | ||
2867 | /* Switch the names.. */ | 2836 | /* ... and switch them in the tree */ |
2868 | if (exchange) | 2837 | dentry->d_parent = target->d_parent; |
2869 | swap_names(dentry, target); | 2838 | if (!exchange) { |
2870 | else | ||
2871 | copy_name(dentry, target); | 2839 | copy_name(dentry, target); |
2872 | |||
2873 | /* rehash in new place(s) */ | ||
2874 | __d_rehash(dentry); | ||
2875 | if (exchange) | ||
2876 | __d_rehash(target); | ||
2877 | else | ||
2878 | target->d_hash.pprev = NULL; | 2840 | target->d_hash.pprev = NULL; |
2879 | 2841 | dentry->d_parent->d_lockref.count++; | |
2880 | /* ... and switch them in the tree */ | 2842 | if (dentry == old_parent) |
2881 | if (IS_ROOT(dentry)) { | 2843 | dentry->d_flags |= DCACHE_RCUACCESS; |
2882 | /* splicing a tree */ | 2844 | else |
2883 | dentry->d_flags |= DCACHE_RCUACCESS; | 2845 | WARN_ON(!--old_parent->d_lockref.count); |
2884 | dentry->d_parent = target->d_parent; | ||
2885 | target->d_parent = target; | ||
2886 | list_del_init(&target->d_child); | ||
2887 | list_move(&dentry->d_child, &dentry->d_parent->d_subdirs); | ||
2888 | } else { | 2846 | } else { |
2889 | /* swapping two dentries */ | 2847 | target->d_parent = old_parent; |
2890 | swap(dentry->d_parent, target->d_parent); | 2848 | swap_names(dentry, target); |
2891 | list_move(&target->d_child, &target->d_parent->d_subdirs); | 2849 | list_move(&target->d_child, &target->d_parent->d_subdirs); |
2892 | list_move(&dentry->d_child, &dentry->d_parent->d_subdirs); | 2850 | __d_rehash(target); |
2893 | if (exchange) | 2851 | fsnotify_update_flags(target); |
2894 | fsnotify_update_flags(target); | ||
2895 | fsnotify_update_flags(dentry); | ||
2896 | } | 2852 | } |
2853 | list_move(&dentry->d_child, &dentry->d_parent->d_subdirs); | ||
2854 | __d_rehash(dentry); | ||
2855 | fsnotify_update_flags(dentry); | ||
2897 | 2856 | ||
2898 | write_seqcount_end(&target->d_seq); | 2857 | write_seqcount_end(&target->d_seq); |
2899 | write_seqcount_end(&dentry->d_seq); | 2858 | write_seqcount_end(&dentry->d_seq); |
2900 | 2859 | ||
2901 | if (dir) | 2860 | if (dir) |
2902 | end_dir_add(dir, n); | 2861 | end_dir_add(dir, n); |
2903 | dentry_unlock_for_move(dentry, target); | 2862 | |
2863 | if (dentry->d_parent != old_parent) | ||
2864 | spin_unlock(&dentry->d_parent->d_lock); | ||
2865 | if (dentry != old_parent) | ||
2866 | spin_unlock(&old_parent->d_lock); | ||
2867 | spin_unlock(&target->d_lock); | ||
2868 | spin_unlock(&dentry->d_lock); | ||
2904 | } | 2869 | } |
2905 | 2870 | ||
2906 | /* | 2871 | /* |
@@ -3048,12 +3013,14 @@ struct dentry *d_splice_alias(struct inode *inode, struct dentry *dentry) | |||
3048 | inode->i_sb->s_type->name, | 3013 | inode->i_sb->s_type->name, |
3049 | inode->i_sb->s_id); | 3014 | inode->i_sb->s_id); |
3050 | } else if (!IS_ROOT(new)) { | 3015 | } else if (!IS_ROOT(new)) { |
3016 | struct dentry *old_parent = dget(new->d_parent); | ||
3051 | int err = __d_unalias(inode, dentry, new); | 3017 | int err = __d_unalias(inode, dentry, new); |
3052 | write_sequnlock(&rename_lock); | 3018 | write_sequnlock(&rename_lock); |
3053 | if (err) { | 3019 | if (err) { |
3054 | dput(new); | 3020 | dput(new); |
3055 | new = ERR_PTR(err); | 3021 | new = ERR_PTR(err); |
3056 | } | 3022 | } |
3023 | dput(old_parent); | ||
3057 | } else { | 3024 | } else { |
3058 | __d_move(new, dentry, false); | 3025 | __d_move(new, dentry, false); |
3059 | write_sequnlock(&rename_lock); | 3026 | write_sequnlock(&rename_lock); |