aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2016-03-01 18:30:45 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2016-03-01 18:30:45 -0500
commitf691b77b1fc491dae601631c8531a0a13e915466 (patch)
treeaef29eded65de2fcee4ca88b65c708f529069070
parent7d46af2084eabe73ce4dbf06072fded34b58b0c7 (diff)
parenta528aca7f359f4b0b1d72ae406097e491a5ba9ea (diff)
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs
Pull d_inode/d_flags race fix from Al Viro. I love this fix. Not only does it fix the race in the dentry type handling, it entirely gets rid of the nasty and subtle memory ordering rules for d_type and d_inode, and replaces them with the basic dentry locking rules (sequence numbers under RCU, d_lock elsewhere). * 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/viro/vfs: use ->d_seq to get coherency between ->d_inode and ->d_flags
-rw-r--r--fs/dcache.c20
-rw-r--r--include/linux/dcache.h4
2 files changed, 6 insertions, 18 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 */
275static inline void __d_set_inode_and_type(struct dentry *dentry, 272static 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 */
297static inline void __d_clear_type_and_inode(struct dentry *dentry) 285static 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}
diff --git a/include/linux/dcache.h b/include/linux/dcache.h
index 7781ce110503..c4b5f4b3f8f8 100644
--- a/include/linux/dcache.h
+++ b/include/linux/dcache.h
@@ -409,9 +409,7 @@ static inline bool d_mountpoint(const struct dentry *dentry)
409 */ 409 */
410static inline unsigned __d_entry_type(const struct dentry *dentry) 410static inline unsigned __d_entry_type(const struct dentry *dentry)
411{ 411{
412 unsigned type = READ_ONCE(dentry->d_flags); 412 return dentry->d_flags & DCACHE_ENTRY_TYPE;
413 smp_rmb();
414 return type & DCACHE_ENTRY_TYPE;
415} 413}
416 414
417static inline bool d_is_miss(const struct dentry *dentry) 415static inline bool d_is_miss(const struct dentry *dentry)