Message ID | 878tbov9i6.fsf@linutronix.de (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
On Mon, Feb 19, 2018 at 3:34 PM, John Ogness <john.ogness@linutronix.de> wrote: > > IMHO the original v1 patchset is the simplest codewise. And if the > comments were updated to not mislead, it is the way I would want to go. Ok, looking at your alternatives, I am inclined to agree. Linus
On Tue, Feb 20, 2018 at 12:34:57AM +0100, John Ogness wrote: > Implementation 2: Using switch on a dentry_lock_inode() that returns a > tristate value. Does not support branch prediction. This approach is > probably easiest to understand. > > /* > * Lock the inode. Might drop dentry->d_lock temporarily > * which allows inode to change. Start over if that happens. > */ > switch (dentry_lock_inode(dentry)) { > case LOCK_FAST: Bah, I just checked, you cannot use GCC label attributes on statements :/ Otherwise you could've done: case LOCK_FAST: __attribute__((hot)); > break; > case LOCK_SLOW: > /* > * Recheck refcount as it might have been > * incremented while d_lock was dropped. > */ > if (unlikely(dentry->d_lockref.count != 1)) > goto drop_ref; > break; > case LOCK_FAILED: > goto again; > } >
On Tue, Feb 20, 2018 at 09:39:37AM +0100, Peter Zijlstra wrote: > On Tue, Feb 20, 2018 at 12:34:57AM +0100, John Ogness wrote: > > Implementation 2: Using switch on a dentry_lock_inode() that returns a > > tristate value. Does not support branch prediction. This approach is > > probably easiest to understand. > > > > /* > > * Lock the inode. Might drop dentry->d_lock temporarily > > * which allows inode to change. Start over if that happens. > > */ > > switch (dentry_lock_inode(dentry)) { > > case LOCK_FAST: > > Bah, I just checked, you cannot use GCC label attributes on statements > :/ Otherwise you could've done: > > case LOCK_FAST: __attribute__((hot)); Oooh shiny, you can actually write: switch(__builtin_expect(dentry_lock_inode(dentry), LOCK_FAST)) { and have that work, see: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=59521 > > break; > > case LOCK_SLOW: > > /* > > * Recheck refcount as it might have been > > * incremented while d_lock was dropped. > > */ > > if (unlikely(dentry->d_lockref.count != 1)) > > goto drop_ref; > > break; > > case LOCK_FAILED: > > goto again; > > } > >
On Tue, Feb 20, 2018 at 12:34:57AM +0100, John Ogness wrote: > Implementation 3: The same as implementation 2 but using if's to > support branch prediction. This approach is probably the most > complicated to understand but will be the fastest. > > /* > * Lock the inode. Might drop dentry->d_lock temporarily > * which allows inode to change. Start over if that happens. > */ > int ret = dentry_lock_inode(dentry); > if (unlikely(ret != LOCK_FAST)) { > if (ret == LOCK_FAILED) > goto again; > /* > * Recheck refcount as it might have been > * incremented while d_lock was dropped. > */ > if (dentry->d_lockref.count != 1) > goto drop_ref; > } Implementation 4: screw the tristate, move the loop inside dentry_lock_inode(). > If lock_parent() returns a non-NULL, it is returning > dentry->d_parent. So the return value is really just a boolean and the > locked parent is the parent of the dentry. The function is a little bit > tricky because it could return NULL (lock failed) even if the dentry has > a non-NULL d_parent. So any caller using a tristate return variation of > lock_parent() must rely on the return value instead of a non-NULL > dentry->d_parent. dentry always has non-NULL ->d_parent; it might point to dentry itself, but it's never NULL. > if (!dentry->d_lockref.count) { > - struct dentry *parent = lock_parent(dentry); > + int ret = lock_parent(dentry); > + parent = dentry->d_parent; > if (likely(!dentry->d_lockref.count)) { > __dentry_kill(dentry); > dput(parent); Broken. In IS_ROOT case you'll hit an extra dput() on dentry itself. dput(NULL) is no-op; this, OTOH, isn't.
diff --git a/fs/dcache.c b/fs/dcache.c index b557679c14c9..53074243ce48 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -589,15 +589,15 @@ static void __dentry_kill(struct dentry *dentry) dentry_free(dentry); } -static inline struct dentry *lock_parent(struct dentry *dentry) +static inline int lock_parent(struct dentry *dentry) { struct dentry *parent = dentry->d_parent; if (IS_ROOT(dentry)) - return NULL; + return LOCK_FAILED; if (unlikely(dentry->d_lockref.count < 0)) - return NULL; + return LOCK_FAILED; if (likely(spin_trylock(&parent->d_lock))) - return parent; + return LOCK_FAST; rcu_read_lock(); spin_unlock(&dentry->d_lock); again: @@ -616,11 +616,11 @@ static inline struct dentry *lock_parent(struct dentry *dentry) goto again; } rcu_read_unlock(); - if (parent != dentry) - spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); - else - parent = NULL; - return parent; + if (parent == dentry) + return LOCK_FAILED; + + spin_lock_nested(&dentry->d_lock, DENTRY_D_LOCK_NESTED); + return LOCK_SLOW; } /** @@ -1035,19 +1035,21 @@ EXPORT_SYMBOL(d_find_alias); */ void d_prune_aliases(struct inode *inode) { + struct dentry *parent; struct dentry *dentry; restart: spin_lock(&inode->i_lock); hlist_for_each_entry(dentry, &inode->i_dentry, d_u.d_alias) { spin_lock(&dentry->d_lock); if (!dentry->d_lockref.count) { - struct dentry *parent = lock_parent(dentry); + int ret = lock_parent(dentry); + parent = dentry->d_parent; if (likely(!dentry->d_lockref.count)) { __dentry_kill(dentry); dput(parent); goto restart; } - if (parent) + if (ret != LOCK_FAILED) spin_unlock(&parent->d_lock); } spin_unlock(&dentry->d_lock); @@ -1062,9 +1064,11 @@ static void shrink_dentry_list(struct list_head *list) while (!list_empty(list)) { struct inode *inode; + int ret; dentry = list_entry(list->prev, struct dentry, d_lru); spin_lock(&dentry->d_lock); - parent = lock_parent(dentry); + ret = lock_parent(dentry); + parent = dentry->d_parent; /* * The dispose list is isolated and dentries are not accounted @@ -1079,7 +1083,7 @@ static void shrink_dentry_list(struct list_head *list) */ if (dentry->d_lockref.count > 0) { spin_unlock(&dentry->d_lock); - if (parent) + if (ret != LOCK_FAILED) spin_unlock(&parent->d_lock); continue; } @@ -1088,7 +1092,7 @@ static void shrink_dentry_list(struct list_head *list) if (unlikely(dentry->d_flags & DCACHE_DENTRY_KILLED)) { bool can_free = dentry->d_flags & DCACHE_MAY_FREE; spin_unlock(&dentry->d_lock); - if (parent) + if (ret != LOCK_FAILED) spin_unlock(&parent->d_lock); if (can_free) dentry_free(dentry); @@ -1099,7 +1103,7 @@ static void shrink_dentry_list(struct list_head *list) if (inode && unlikely(!spin_trylock(&inode->i_lock))) { d_shrink_add(dentry, list); spin_unlock(&dentry->d_lock); - if (parent) + if (ret != LOCK_FAILED) spin_unlock(&parent->d_lock); continue; }