diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2016-02-29 12:12:46 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2016-02-29 12:16:43 -0500 |
commit | a528aca7f359f4b0b1d72ae406097e491a5ba9ea (patch) | |
tree | 26b95bc4e207d3bd6afc11affbfa0fe85ac53b6a /fs/dcache.c | |
parent | 5129fa482b16615fd4464d2f5d23acb1b7056c66 (diff) |
use ->d_seq to get coherency between ->d_inode and ->d_flags
Games with ordering and barriers are way too brittle. Just
bump ->d_seq before and after updating ->d_inode and ->d_flags
type bits, so that verifying ->d_seq would guarantee they are
coherent.
Cc: stable@vger.kernel.org # v3.13+
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 20 |
1 files changed, 5 insertions, 15 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 92d5140de851..2398f9f94337 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -269,9 +269,6 @@ static inline int dname_external(const struct dentry *dentry) | |||
269 | return dentry->d_name.name != dentry->d_iname; | 269 | return dentry->d_name.name != dentry->d_iname; |
270 | } | 270 | } |
271 | 271 | ||
272 | /* | ||
273 | * Make sure other CPUs see the inode attached before the type is set. | ||
274 | */ | ||
275 | static inline void __d_set_inode_and_type(struct dentry *dentry, | 272 | static inline void __d_set_inode_and_type(struct dentry *dentry, |
276 | struct inode *inode, | 273 | struct inode *inode, |
277 | unsigned type_flags) | 274 | unsigned type_flags) |
@@ -279,28 +276,18 @@ static inline void __d_set_inode_and_type(struct dentry *dentry, | |||
279 | unsigned flags; | 276 | unsigned flags; |
280 | 277 | ||
281 | dentry->d_inode = inode; | 278 | dentry->d_inode = inode; |
282 | smp_wmb(); | ||
283 | flags = READ_ONCE(dentry->d_flags); | 279 | flags = READ_ONCE(dentry->d_flags); |
284 | flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU); | 280 | flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU); |
285 | flags |= type_flags; | 281 | flags |= type_flags; |
286 | WRITE_ONCE(dentry->d_flags, flags); | 282 | WRITE_ONCE(dentry->d_flags, flags); |
287 | } | 283 | } |
288 | 284 | ||
289 | /* | ||
290 | * Ideally, we want to make sure that other CPUs see the flags cleared before | ||
291 | * the inode is detached, but this is really a violation of RCU principles | ||
292 | * since the ordering suggests we should always set inode before flags. | ||
293 | * | ||
294 | * We should instead replace or discard the entire dentry - but that sucks | ||
295 | * performancewise on mass deletion/rename. | ||
296 | */ | ||
297 | static inline void __d_clear_type_and_inode(struct dentry *dentry) | 285 | static inline void __d_clear_type_and_inode(struct dentry *dentry) |
298 | { | 286 | { |
299 | unsigned flags = READ_ONCE(dentry->d_flags); | 287 | unsigned flags = READ_ONCE(dentry->d_flags); |
300 | 288 | ||
301 | flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU); | 289 | flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU); |
302 | WRITE_ONCE(dentry->d_flags, flags); | 290 | WRITE_ONCE(dentry->d_flags, flags); |
303 | smp_wmb(); | ||
304 | dentry->d_inode = NULL; | 291 | dentry->d_inode = NULL; |
305 | } | 292 | } |
306 | 293 | ||
@@ -370,9 +357,11 @@ static void dentry_unlink_inode(struct dentry * dentry) | |||
370 | __releases(dentry->d_inode->i_lock) | 357 | __releases(dentry->d_inode->i_lock) |
371 | { | 358 | { |
372 | struct inode *inode = dentry->d_inode; | 359 | struct inode *inode = dentry->d_inode; |
360 | |||
361 | raw_write_seqcount_begin(&dentry->d_seq); | ||
373 | __d_clear_type_and_inode(dentry); | 362 | __d_clear_type_and_inode(dentry); |
374 | hlist_del_init(&dentry->d_u.d_alias); | 363 | hlist_del_init(&dentry->d_u.d_alias); |
375 | dentry_rcuwalk_invalidate(dentry); | 364 | raw_write_seqcount_end(&dentry->d_seq); |
376 | spin_unlock(&dentry->d_lock); | 365 | spin_unlock(&dentry->d_lock); |
377 | spin_unlock(&inode->i_lock); | 366 | spin_unlock(&inode->i_lock); |
378 | if (!inode->i_nlink) | 367 | if (!inode->i_nlink) |
@@ -1758,8 +1747,9 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode) | |||
1758 | spin_lock(&dentry->d_lock); | 1747 | spin_lock(&dentry->d_lock); |
1759 | if (inode) | 1748 | if (inode) |
1760 | hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry); | 1749 | hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry); |
1750 | raw_write_seqcount_begin(&dentry->d_seq); | ||
1761 | __d_set_inode_and_type(dentry, inode, add_flags); | 1751 | __d_set_inode_and_type(dentry, inode, add_flags); |
1762 | dentry_rcuwalk_invalidate(dentry); | 1752 | raw_write_seqcount_end(&dentry->d_seq); |
1763 | spin_unlock(&dentry->d_lock); | 1753 | spin_unlock(&dentry->d_lock); |
1764 | fsnotify_d_instantiate(dentry, inode); | 1754 | fsnotify_d_instantiate(dentry, inode); |
1765 | } | 1755 | } |