Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

VFS: Impose ordering on accesses of d_inode and d_flags

Impose ordering on accesses of d_inode and d_flags to avoid the need to do
this:

if (!dentry->d_inode || d_is_negative(dentry)) {

when this:

if (d_is_negative(dentry)) {

should suffice.

This check is especially problematic if a dentry can have its type field set
to something other than DENTRY_MISS_TYPE when d_inode is NULL (as in
unionmount).

What we really need to do is stick a write barrier between setting d_inode and
setting d_flags and a read barrier between reading d_flags and reading
d_inode.

Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

authored by

David Howells and committed by
Al Viro
4bf46a27 525d27b2

+42 -26
+39 -8
fs/dcache.c
··· 269 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, 276 + struct inode *inode, 277 + unsigned type_flags) 278 + { 279 + unsigned flags; 280 + 281 + dentry->d_inode = inode; 282 + smp_wmb(); 283 + flags = READ_ONCE(dentry->d_flags); 284 + flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU); 285 + flags |= type_flags; 286 + WRITE_ONCE(dentry->d_flags, flags); 287 + } 288 + 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) 298 + { 299 + unsigned flags = READ_ONCE(dentry->d_flags); 300 + 301 + flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU); 302 + WRITE_ONCE(dentry->d_flags, flags); 303 + smp_wmb(); 304 + dentry->d_inode = NULL; 305 + } 306 + 272 307 static void dentry_free(struct dentry *dentry) 273 308 { 274 309 WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias)); ··· 346 311 { 347 312 struct inode *inode = dentry->d_inode; 348 313 if (inode) { 349 - dentry->d_inode = NULL; 314 + __d_clear_type_and_inode(dentry); 350 315 hlist_del_init(&dentry->d_u.d_alias); 351 316 spin_unlock(&dentry->d_lock); 352 317 spin_unlock(&inode->i_lock); ··· 370 335 __releases(dentry->d_inode->i_lock) 371 336 { 372 337 struct inode *inode = dentry->d_inode; 373 - __d_clear_type(dentry); 374 - dentry->d_inode = NULL; 338 + __d_clear_type_and_inode(dentry); 375 339 hlist_del_init(&dentry->d_u.d_alias); 376 340 dentry_rcuwalk_barrier(dentry); 377 341 spin_unlock(&dentry->d_lock); ··· 1749 1715 unsigned add_flags = d_flags_for_inode(inode); 1750 1716 1751 1717 spin_lock(&dentry->d_lock); 1752 - dentry->d_flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU); 1753 - dentry->d_flags |= add_flags; 1754 1718 if (inode) 1755 1719 hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry); 1756 - dentry->d_inode = inode; 1720 + __d_set_inode_and_type(dentry, inode, add_flags); 1757 1721 dentry_rcuwalk_barrier(dentry); 1758 1722 spin_unlock(&dentry->d_lock); 1759 1723 fsnotify_d_instantiate(dentry, inode); ··· 1969 1937 add_flags |= DCACHE_DISCONNECTED; 1970 1938 1971 1939 spin_lock(&tmp->d_lock); 1972 - tmp->d_inode = inode; 1973 - tmp->d_flags |= add_flags; 1940 + __d_set_inode_and_type(tmp, inode, add_flags); 1974 1941 hlist_add_head(&tmp->d_u.d_alias, &inode->i_dentry); 1975 1942 hlist_bl_lock(&tmp->d_sb->s_anon); 1976 1943 hlist_bl_add_head(&tmp->d_hash, &tmp->d_sb->s_anon);
+3 -18
include/linux/dcache.h
··· 404 404 /* 405 405 * Directory cache entry type accessor functions. 406 406 */ 407 - static inline void __d_set_type(struct dentry *dentry, unsigned type) 408 - { 409 - dentry->d_flags = (dentry->d_flags & ~DCACHE_ENTRY_TYPE) | type; 410 - } 411 - 412 - static inline void __d_clear_type(struct dentry *dentry) 413 - { 414 - __d_set_type(dentry, DCACHE_MISS_TYPE); 415 - } 416 - 417 - static inline void d_set_type(struct dentry *dentry, unsigned type) 418 - { 419 - spin_lock(&dentry->d_lock); 420 - __d_set_type(dentry, type); 421 - spin_unlock(&dentry->d_lock); 422 - } 423 - 424 407 static inline unsigned __d_entry_type(const struct dentry *dentry) 425 408 { 426 - return dentry->d_flags & DCACHE_ENTRY_TYPE; 409 + unsigned type = READ_ONCE(dentry->d_flags); 410 + smp_rmb(); 411 + return type & DCACHE_ENTRY_TYPE; 427 412 } 428 413 429 414 static inline bool d_is_miss(const struct dentry *dentry)