diff options
author | David Howells <dhowells@redhat.com> | 2015-03-05 09:09:22 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2015-04-15 15:05:28 -0400 |
commit | 4bf46a272647d89e780126b52eda04737defd9f4 (patch) | |
tree | 989bac3ffce8a0f7f9184e14afb2dd9c28693cfe /fs | |
parent | 525d27b23555419e0e7b73fb6e78d4d678cb4f32 (diff) |
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>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/dcache.c | 47 |
1 files changed, 39 insertions, 8 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index d99736a63e3c..656ce522a218 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -269,6 +269,41 @@ 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, | ||
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 | static void dentry_free(struct dentry *dentry) | 307 | static void dentry_free(struct dentry *dentry) |
273 | { | 308 | { |
274 | WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias)); | 309 | WARN_ON(!hlist_unhashed(&dentry->d_u.d_alias)); |
@@ -311,7 +346,7 @@ static void dentry_iput(struct dentry * dentry) | |||
311 | { | 346 | { |
312 | struct inode *inode = dentry->d_inode; | 347 | struct inode *inode = dentry->d_inode; |
313 | if (inode) { | 348 | if (inode) { |
314 | dentry->d_inode = NULL; | 349 | __d_clear_type_and_inode(dentry); |
315 | hlist_del_init(&dentry->d_u.d_alias); | 350 | hlist_del_init(&dentry->d_u.d_alias); |
316 | spin_unlock(&dentry->d_lock); | 351 | spin_unlock(&dentry->d_lock); |
317 | spin_unlock(&inode->i_lock); | 352 | spin_unlock(&inode->i_lock); |
@@ -335,8 +370,7 @@ static void dentry_unlink_inode(struct dentry * dentry) | |||
335 | __releases(dentry->d_inode->i_lock) | 370 | __releases(dentry->d_inode->i_lock) |
336 | { | 371 | { |
337 | struct inode *inode = dentry->d_inode; | 372 | struct inode *inode = dentry->d_inode; |
338 | __d_clear_type(dentry); | 373 | __d_clear_type_and_inode(dentry); |
339 | dentry->d_inode = NULL; | ||
340 | hlist_del_init(&dentry->d_u.d_alias); | 374 | hlist_del_init(&dentry->d_u.d_alias); |
341 | dentry_rcuwalk_barrier(dentry); | 375 | dentry_rcuwalk_barrier(dentry); |
342 | spin_unlock(&dentry->d_lock); | 376 | spin_unlock(&dentry->d_lock); |
@@ -1715,11 +1749,9 @@ static void __d_instantiate(struct dentry *dentry, struct inode *inode) | |||
1715 | unsigned add_flags = d_flags_for_inode(inode); | 1749 | unsigned add_flags = d_flags_for_inode(inode); |
1716 | 1750 | ||
1717 | spin_lock(&dentry->d_lock); | 1751 | spin_lock(&dentry->d_lock); |
1718 | dentry->d_flags &= ~(DCACHE_ENTRY_TYPE | DCACHE_FALLTHRU); | ||
1719 | dentry->d_flags |= add_flags; | ||
1720 | if (inode) | 1752 | if (inode) |
1721 | hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry); | 1753 | hlist_add_head(&dentry->d_u.d_alias, &inode->i_dentry); |
1722 | dentry->d_inode = inode; | 1754 | __d_set_inode_and_type(dentry, inode, add_flags); |
1723 | dentry_rcuwalk_barrier(dentry); | 1755 | dentry_rcuwalk_barrier(dentry); |
1724 | spin_unlock(&dentry->d_lock); | 1756 | spin_unlock(&dentry->d_lock); |
1725 | fsnotify_d_instantiate(dentry, inode); | 1757 | fsnotify_d_instantiate(dentry, inode); |
@@ -1937,8 +1969,7 @@ static struct dentry *__d_obtain_alias(struct inode *inode, int disconnected) | |||
1937 | add_flags |= DCACHE_DISCONNECTED; | 1969 | add_flags |= DCACHE_DISCONNECTED; |
1938 | 1970 | ||
1939 | spin_lock(&tmp->d_lock); | 1971 | spin_lock(&tmp->d_lock); |
1940 | tmp->d_inode = inode; | 1972 | __d_set_inode_and_type(tmp, inode, add_flags); |
1941 | tmp->d_flags |= add_flags; | ||
1942 | hlist_add_head(&tmp->d_u.d_alias, &inode->i_dentry); | 1973 | hlist_add_head(&tmp->d_u.d_alias, &inode->i_dentry); |
1943 | hlist_bl_lock(&tmp->d_sb->s_anon); | 1974 | hlist_bl_lock(&tmp->d_sb->s_anon); |
1944 | hlist_bl_add_head(&tmp->d_hash, &tmp->d_sb->s_anon); | 1975 | hlist_bl_add_head(&tmp->d_hash, &tmp->d_sb->s_anon); |