diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2013-09-09 15:22:25 -0400 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2013-09-09 15:22:25 -0400 |
commit | 48f5ec21d9c67e881ff35343988e290ef5cf933f (patch) | |
tree | 515af9f0288fbdee85f2ba1fdc2d20e088a1fb17 /fs/dcache.c | |
parent | 232d2d60aa5469bb097f55728f65146bd49c1d25 (diff) |
split read_seqretry_or_unlock(), convert d_walk() to resulting primitives
Separate "check if we need to retry" from "unlock if we are done and
had seq_writelock"; that allows to use these guys in d_walk(), where
we need to recheck every time we ascend back to parent, but do *not*
want to unlock until the very end. Lift rcu_read_lock/rcu_read_unlock
out into callers.
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 64 |
1 files changed, 31 insertions, 33 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 38b1b0989a16..b9caf47d5389 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -100,30 +100,21 @@ static struct kmem_cache *dentry_cache __read_mostly; | |||
100 | */ | 100 | */ |
101 | static inline void read_seqbegin_or_lock(seqlock_t *lock, int *seq) | 101 | static inline void read_seqbegin_or_lock(seqlock_t *lock, int *seq) |
102 | { | 102 | { |
103 | if (!(*seq & 1)) { /* Even */ | 103 | if (!(*seq & 1)) /* Even */ |
104 | *seq = read_seqbegin(lock); | 104 | *seq = read_seqbegin(lock); |
105 | rcu_read_lock(); | 105 | else /* Odd */ |
106 | } else /* Odd */ | ||
107 | write_seqlock(lock); | 106 | write_seqlock(lock); |
108 | } | 107 | } |
109 | 108 | ||
110 | /** | 109 | static inline int need_seqretry(seqlock_t *lock, int seq) |
111 | * read_seqretry_or_unlock - end a seqretry or lock block & return retry status | ||
112 | * lock : sequence lock | ||
113 | * seq : sequence number | ||
114 | * Return: 1 to retry operation again, 0 to continue | ||
115 | */ | ||
116 | static inline int read_seqretry_or_unlock(seqlock_t *lock, int *seq) | ||
117 | { | 110 | { |
118 | if (!(*seq & 1)) { /* Even */ | 111 | return !(seq & 1) && read_seqretry(lock, seq); |
119 | rcu_read_unlock(); | 112 | } |
120 | if (read_seqretry(lock, *seq)) { | 113 | |
121 | (*seq)++; /* Take writer lock */ | 114 | static inline void done_seqretry(seqlock_t *lock, int seq) |
122 | return 1; | 115 | { |
123 | } | 116 | if (seq & 1) |
124 | } else /* Odd */ | ||
125 | write_sequnlock(lock); | 117 | write_sequnlock(lock); |
126 | return 0; | ||
127 | } | 118 | } |
128 | 119 | ||
129 | /* | 120 | /* |
@@ -1047,7 +1038,7 @@ void shrink_dcache_for_umount(struct super_block *sb) | |||
1047 | * the parenthood after dropping the lock and check | 1038 | * the parenthood after dropping the lock and check |
1048 | * that the sequence number still matches. | 1039 | * that the sequence number still matches. |
1049 | */ | 1040 | */ |
1050 | static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq) | 1041 | static struct dentry *try_to_ascend(struct dentry *old, unsigned seq) |
1051 | { | 1042 | { |
1052 | struct dentry *new = old->d_parent; | 1043 | struct dentry *new = old->d_parent; |
1053 | 1044 | ||
@@ -1061,7 +1052,7 @@ static struct dentry *try_to_ascend(struct dentry *old, int locked, unsigned seq | |||
1061 | */ | 1052 | */ |
1062 | if (new != old->d_parent || | 1053 | if (new != old->d_parent || |
1063 | (old->d_flags & DCACHE_DENTRY_KILLED) || | 1054 | (old->d_flags & DCACHE_DENTRY_KILLED) || |
1064 | (!locked && read_seqretry(&rename_lock, seq))) { | 1055 | need_seqretry(&rename_lock, seq)) { |
1065 | spin_unlock(&new->d_lock); | 1056 | spin_unlock(&new->d_lock); |
1066 | new = NULL; | 1057 | new = NULL; |
1067 | } | 1058 | } |
@@ -1098,13 +1089,12 @@ static void d_walk(struct dentry *parent, void *data, | |||
1098 | { | 1089 | { |
1099 | struct dentry *this_parent; | 1090 | struct dentry *this_parent; |
1100 | struct list_head *next; | 1091 | struct list_head *next; |
1101 | unsigned seq; | 1092 | unsigned seq = 0; |
1102 | int locked = 0; | ||
1103 | enum d_walk_ret ret; | 1093 | enum d_walk_ret ret; |
1104 | bool retry = true; | 1094 | bool retry = true; |
1105 | 1095 | ||
1106 | seq = read_seqbegin(&rename_lock); | ||
1107 | again: | 1096 | again: |
1097 | read_seqbegin_or_lock(&rename_lock, &seq); | ||
1108 | this_parent = parent; | 1098 | this_parent = parent; |
1109 | spin_lock(&this_parent->d_lock); | 1099 | spin_lock(&this_parent->d_lock); |
1110 | 1100 | ||
@@ -1158,13 +1148,13 @@ resume: | |||
1158 | */ | 1148 | */ |
1159 | if (this_parent != parent) { | 1149 | if (this_parent != parent) { |
1160 | struct dentry *child = this_parent; | 1150 | struct dentry *child = this_parent; |
1161 | this_parent = try_to_ascend(this_parent, locked, seq); | 1151 | this_parent = try_to_ascend(this_parent, seq); |
1162 | if (!this_parent) | 1152 | if (!this_parent) |
1163 | goto rename_retry; | 1153 | goto rename_retry; |
1164 | next = child->d_u.d_child.next; | 1154 | next = child->d_u.d_child.next; |
1165 | goto resume; | 1155 | goto resume; |
1166 | } | 1156 | } |
1167 | if (!locked && read_seqretry(&rename_lock, seq)) { | 1157 | if (need_seqretry(&rename_lock, seq)) { |
1168 | spin_unlock(&this_parent->d_lock); | 1158 | spin_unlock(&this_parent->d_lock); |
1169 | goto rename_retry; | 1159 | goto rename_retry; |
1170 | } | 1160 | } |
@@ -1173,17 +1163,13 @@ resume: | |||
1173 | 1163 | ||
1174 | out_unlock: | 1164 | out_unlock: |
1175 | spin_unlock(&this_parent->d_lock); | 1165 | spin_unlock(&this_parent->d_lock); |
1176 | if (locked) | 1166 | done_seqretry(&rename_lock, seq); |
1177 | write_sequnlock(&rename_lock); | ||
1178 | return; | 1167 | return; |
1179 | 1168 | ||
1180 | rename_retry: | 1169 | rename_retry: |
1181 | if (!retry) | 1170 | if (!retry) |
1182 | return; | 1171 | return; |
1183 | if (locked) | 1172 | seq = 1; |
1184 | goto again; | ||
1185 | locked = 1; | ||
1186 | write_seqlock(&rename_lock); | ||
1187 | goto again; | 1173 | goto again; |
1188 | } | 1174 | } |
1189 | 1175 | ||
@@ -2745,6 +2731,7 @@ static int prepend_path(const struct path *path, | |||
2745 | char *bptr; | 2731 | char *bptr; |
2746 | int blen; | 2732 | int blen; |
2747 | 2733 | ||
2734 | rcu_read_lock(); | ||
2748 | restart: | 2735 | restart: |
2749 | bptr = *buffer; | 2736 | bptr = *buffer; |
2750 | blen = *buflen; | 2737 | blen = *buflen; |
@@ -2783,8 +2770,13 @@ restart: | |||
2783 | 2770 | ||
2784 | dentry = parent; | 2771 | dentry = parent; |
2785 | } | 2772 | } |
2786 | if (read_seqretry_or_unlock(&rename_lock, &seq)) | 2773 | if (!(seq & 1)) |
2774 | rcu_read_unlock(); | ||
2775 | if (need_seqretry(&rename_lock, seq)) { | ||
2776 | seq = 1; | ||
2787 | goto restart; | 2777 | goto restart; |
2778 | } | ||
2779 | done_seqretry(&rename_lock, seq); | ||
2788 | 2780 | ||
2789 | if (error >= 0 && bptr == *buffer) { | 2781 | if (error >= 0 && bptr == *buffer) { |
2790 | if (--blen < 0) | 2782 | if (--blen < 0) |
@@ -2957,6 +2949,7 @@ static char *__dentry_path(struct dentry *dentry, char *buf, int buflen) | |||
2957 | int len, seq = 0; | 2949 | int len, seq = 0; |
2958 | int error = 0; | 2950 | int error = 0; |
2959 | 2951 | ||
2952 | rcu_read_lock(); | ||
2960 | restart: | 2953 | restart: |
2961 | end = buf + buflen; | 2954 | end = buf + buflen; |
2962 | len = buflen; | 2955 | len = buflen; |
@@ -2979,8 +2972,13 @@ restart: | |||
2979 | retval = end; | 2972 | retval = end; |
2980 | dentry = parent; | 2973 | dentry = parent; |
2981 | } | 2974 | } |
2982 | if (read_seqretry_or_unlock(&rename_lock, &seq)) | 2975 | if (!(seq & 1)) |
2976 | rcu_read_unlock(); | ||
2977 | if (need_seqretry(&rename_lock, seq)) { | ||
2978 | seq = 1; | ||
2983 | goto restart; | 2979 | goto restart; |
2980 | } | ||
2981 | done_seqretry(&rename_lock, seq); | ||
2984 | if (error) | 2982 | if (error) |
2985 | goto Elong; | 2983 | goto Elong; |
2986 | return retval; | 2984 | return retval; |