| From: Al Viro <viro@zeniv.linux.org.uk> |
| Date: Sun, 29 May 2016 20:13:30 -0400 |
| Subject: unify dentry_iput() and dentry_unlink_inode() |
| |
| commit 550dce01dd606c88a837138aa448ccd367fb0cbb upstream. |
| |
| There is a lot of duplication between dentry_unlink_inode() and dentry_iput(). |
| The only real difference is that dentry_unlink_inode() bumps ->d_seq and |
| dentry_iput() doesn't. The argument of the latter is known to have been |
| unhashed, so anybody who might've found it in RCU lookup would already be |
| doomed to a ->d_seq mismatch. And we want to avoid pointless smp_rmb() there. |
| |
| This patch makes dentry_unlink_inode() bump ->d_seq only for hashed dentries. |
| It's safe (d_delete() calls that sucker only if we are holding the only |
| reference to dentry, so rehash is not going to happen) and it allows |
| to use dentry_unlink_inode() in __dentry_kill() and get rid of dentry_iput(). |
| |
| The interesting question here is profiling; it *is* a hot path, and extra |
| conditional jumps in there might or might not be painful. |
| |
| Signed-off-by: Al Viro <viro@zeniv.linux.org.uk> |
| [bwh: Backported to 3.16: adjust context] |
| Signed-off-by: Ben Hutchings <ben@decadent.org.uk> |
| --- |
| fs/dcache.c | 45 ++++++++++----------------------------------- |
| 1 file changed, 10 insertions(+), 35 deletions(-) |
| |
| --- a/fs/dcache.c |
| +++ b/fs/dcache.c |
| @@ -333,44 +333,21 @@ static inline void dentry_rcuwalk_barrie |
| |
| /* |
| * Release the dentry's inode, using the filesystem |
| - * d_iput() operation if defined. Dentry has no refcount |
| - * and is unhashed. |
| - */ |
| -static void dentry_iput(struct dentry * dentry) |
| - __releases(dentry->d_lock) |
| - __releases(dentry->d_inode->i_lock) |
| -{ |
| - struct inode *inode = dentry->d_inode; |
| - if (inode) { |
| - __d_clear_type_and_inode(dentry); |
| - hlist_del_init(&dentry->d_u.d_alias); |
| - spin_unlock(&dentry->d_lock); |
| - spin_unlock(&inode->i_lock); |
| - if (!inode->i_nlink) |
| - fsnotify_inoderemove(inode); |
| - if (dentry->d_op && dentry->d_op->d_iput) |
| - dentry->d_op->d_iput(dentry, inode); |
| - else |
| - iput(inode); |
| - } else { |
| - spin_unlock(&dentry->d_lock); |
| - } |
| -} |
| - |
| -/* |
| - * Release the dentry's inode, using the filesystem |
| - * d_iput() operation if defined. dentry remains in-use. |
| + * d_iput() operation if defined. |
| */ |
| static void dentry_unlink_inode(struct dentry * dentry) |
| __releases(dentry->d_lock) |
| __releases(dentry->d_inode->i_lock) |
| { |
| struct inode *inode = dentry->d_inode; |
| + bool hashed = !d_unhashed(dentry); |
| |
| - raw_write_seqcount_begin(&dentry->d_seq); |
| + if (hashed) |
| + raw_write_seqcount_begin(&dentry->d_seq); |
| __d_clear_type_and_inode(dentry); |
| hlist_del_init(&dentry->d_u.d_alias); |
| - raw_write_seqcount_end(&dentry->d_seq); |
| + if (hashed) |
| + raw_write_seqcount_end(&dentry->d_seq); |
| spin_unlock(&dentry->d_lock); |
| spin_unlock(&inode->i_lock); |
| if (!inode->i_nlink) |
| @@ -537,12 +514,10 @@ static void __dentry_kill(struct dentry |
| dentry->d_flags |= DCACHE_DENTRY_KILLED; |
| if (parent) |
| spin_unlock(&parent->d_lock); |
| - dentry_iput(dentry); |
| - /* |
| - * dentry_iput drops the locks, at which point nobody (except |
| - * transient RCU lookups) can reach this dentry. |
| - */ |
| - BUG_ON((int)dentry->d_lockref.count > 0); |
| + if (dentry->d_inode) |
| + dentry_unlink_inode(dentry); |
| + else |
| + spin_unlock(&dentry->d_lock); |
| this_cpu_dec(nr_dentry); |
| if (dentry->d_op && dentry->d_op->d_release) |
| dentry->d_op->d_release(dentry); |