aboutsummaryrefslogtreecommitdiffstats
path: root/fs/dcache.c
diff options
context:
space:
mode:
authorAl Viro <viro@zeniv.linux.org.uk>2018-03-10 23:15:52 -0500
committerAl Viro <viro@zeniv.linux.org.uk>2018-03-29 15:07:49 -0400
commit076515fc926793e162fc6525bed1679ef2bbf269 (patch)
treeafadea0e756147f4631df6b1bbef1c0e670b9494 /fs/dcache.c
parenta7498968338da9b928f5d8054acc8be6ed2bc14c (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.c93
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 */
74int sysctl_vfs_cache_pressure __read_mostly = 100; 72int sysctl_vfs_cache_pressure __read_mostly = 100;
75EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure); 73EXPORT_SYMBOL_GPL(sysctl_vfs_cache_pressure);
@@ -2777,9 +2775,6 @@ static void copy_name(struct dentry *dentry, struct dentry *target)
2777 2775
2778static void dentry_lock_for_move(struct dentry *dentry, struct dentry *target) 2776static 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
2805static 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)
2840static void __d_move(struct dentry *dentry, struct dentry *target, 2806static 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);