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 | |
| 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>
| -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); |
