Skip to content

Commit 1b6ae9f

Browse files
vegardAl Viro
authored andcommitted
dcache: remove unnecessary NULL check in dget_dlock()
dget_dlock() requires dentry->d_lock to be held when called, yet contains a NULL check for dentry. An audit of all calls to dget_dlock() shows that it is never called with a NULL pointer (as spin_lock()/spin_unlock() would crash in these cases): $ git grep -W '\<dget_dlock\>' arch/powerpc/platforms/cell/spufs/inode.c- spin_lock(&dentry->d_lock); arch/powerpc/platforms/cell/spufs/inode.c- if (simple_positive(dentry)) { arch/powerpc/platforms/cell/spufs/inode.c: dget_dlock(dentry); fs/autofs/expire.c- spin_lock_nested(&child->d_lock, DENTRY_D_LOCK_NESTED); fs/autofs/expire.c- if (simple_positive(child)) { fs/autofs/expire.c: dget_dlock(child); fs/autofs/root.c: dget_dlock(active); fs/autofs/root.c- spin_unlock(&active->d_lock); fs/autofs/root.c: dget_dlock(expiring); fs/autofs/root.c- spin_unlock(&expiring->d_lock); fs/ceph/dir.c- if (!spin_trylock(&dentry->d_lock)) fs/ceph/dir.c- continue; [...] fs/ceph/dir.c: dget_dlock(dentry); fs/ceph/mds_client.c- spin_lock(&alias->d_lock); [...] fs/ceph/mds_client.c: dn = dget_dlock(alias); fs/configfs/inode.c- spin_lock(&dentry->d_lock); fs/configfs/inode.c- if (simple_positive(dentry)) { fs/configfs/inode.c: dget_dlock(dentry); fs/libfs.c: found = dget_dlock(d); fs/libfs.c- spin_unlock(&d->d_lock); fs/libfs.c: found = dget_dlock(child); fs/libfs.c- spin_unlock(&child->d_lock); fs/libfs.c: child = dget_dlock(d); fs/libfs.c- spin_unlock(&d->d_lock); fs/ocfs2/dcache.c: dget_dlock(dentry); fs/ocfs2/dcache.c- spin_unlock(&dentry->d_lock); include/linux/dcache.h:static inline struct dentry *dget_dlock(struct dentry *dentry) After taking out the NULL check, dget_dlock() becomes almost identical to __dget_dlock(); the only difference is that dget_dlock() returns the dentry that was passed in. These are static inline helpers, so we can rely on the compiler to discard unused return values. We can therefore also remove __dget_dlock() and replace calls to it by dget_dlock(). Also fix up and improve the kerneldoc comments while we're at it. Al Viro pointed out that we can also clean up some of the callers to make use of the returned value and provided a bit more info for the kerneldoc. While preparing v2 I also noticed that the tabs used in the kerneldoc comments were causing the kerneldoc to get parsed incorrectly so I also fixed this up (including for d_unhashed, which is otherwise unrelated). Testing: x86 defconfig build + boot; make htmldocs for the kerneldoc warning. objdump shows there are code generation changes. Link: https://lore.kernel.org/all/20231022164520.915013-1-vegard.nossum@oracle.com/ Cc: Alexander Viro <viro@zeniv.linux.org.uk> Cc: Christian Brauner <brauner@kernel.org> Cc: linux-fsdevel@vger.kernel.org Cc: Nick Piggin <npiggin@kernel.dk> Cc: Waiman Long <Waiman.Long@hp.com> Cc: linux-doc@vger.kernel.org Signed-off-by: Vegard Nossum <vegard.nossum@oracle.com> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
1 parent 1b327b5 commit 1b6ae9f

File tree

2 files changed

+34
-23
lines changed

2 files changed

+34
-23
lines changed

fs/dcache.c

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -877,12 +877,6 @@ void dput_to_list(struct dentry *dentry, struct list_head *list)
877877
spin_unlock(&dentry->d_lock);
878878
}
879879

880-
/* This must be called with d_lock held */
881-
static inline void __dget_dlock(struct dentry *dentry)
882-
{
883-
dentry->d_lockref.count++;
884-
}
885-
886880
struct dentry *dget_parent(struct dentry *dentry)
887881
{
888882
int gotref;
@@ -964,7 +958,7 @@ static struct dentry *__d_find_alias(struct inode *inode)
964958
hlist_for_each_entry(alias, &inode->i_dentry, d_u.d_alias) {
965959
spin_lock(&alias->d_lock);
966960
if (!d_unhashed(alias)) {
967-
__dget_dlock(alias);
961+
dget_dlock(alias);
968962
spin_unlock(&alias->d_lock);
969963
return alias;
970964
}
@@ -1569,8 +1563,7 @@ static enum d_walk_ret find_submount(void *_data, struct dentry *dentry)
15691563
{
15701564
struct dentry **victim = _data;
15711565
if (d_mountpoint(dentry)) {
1572-
__dget_dlock(dentry);
1573-
*victim = dentry;
1566+
*victim = dget_dlock(dentry);
15741567
return D_WALK_QUIT;
15751568
}
15761569
return D_WALK_CONTINUE;
@@ -1715,8 +1708,7 @@ struct dentry *d_alloc(struct dentry * parent, const struct qstr *name)
17151708
* don't need child lock because it is not subject
17161709
* to concurrency here
17171710
*/
1718-
__dget_dlock(parent);
1719-
dentry->d_parent = parent;
1711+
dentry->d_parent = dget_dlock(parent);
17201712
hlist_add_head(&dentry->d_sib, &parent->d_children);
17211713
spin_unlock(&parent->d_lock);
17221714

@@ -2683,7 +2675,7 @@ struct dentry *d_exact_alias(struct dentry *entry, struct inode *inode)
26832675
spin_unlock(&alias->d_lock);
26842676
alias = NULL;
26852677
} else {
2686-
__dget_dlock(alias);
2678+
dget_dlock(alias);
26872679
__d_rehash(alias);
26882680
spin_unlock(&alias->d_lock);
26892681
}

include/linux/dcache.h

Lines changed: 30 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -287,20 +287,40 @@ extern char *dentry_path(const struct dentry *, char *, int);
287287
/* Allocation counts.. */
288288

289289
/**
290-
* dget, dget_dlock - get a reference to a dentry
291-
* @dentry: dentry to get a reference to
290+
* dget_dlock - get a reference to a dentry
291+
* @dentry: dentry to get a reference to
292292
*
293-
* Given a dentry or %NULL pointer increment the reference count
294-
* if appropriate and return the dentry. A dentry will not be
295-
* destroyed when it has references.
293+
* Given a live dentry, increment the reference count and return the dentry.
294+
* Caller must hold @dentry->d_lock. Making sure that dentry is alive is
295+
* caller's resonsibility. There are many conditions sufficient to guarantee
296+
* that; e.g. anything with non-negative refcount is alive, so's anything
297+
* hashed, anything positive, anyone's parent, etc.
296298
*/
297299
static inline struct dentry *dget_dlock(struct dentry *dentry)
298300
{
299-
if (dentry)
300-
dentry->d_lockref.count++;
301+
dentry->d_lockref.count++;
301302
return dentry;
302303
}
303304

305+
306+
/**
307+
* dget - get a reference to a dentry
308+
* @dentry: dentry to get a reference to
309+
*
310+
* Given a dentry or %NULL pointer increment the reference count
311+
* if appropriate and return the dentry. A dentry will not be
312+
* destroyed when it has references. Conversely, a dentry with
313+
* no references can disappear for any number of reasons, starting
314+
* with memory pressure. In other words, that primitive is
315+
* used to clone an existing reference; using it on something with
316+
* zero refcount is a bug.
317+
*
318+
* NOTE: it will spin if @dentry->d_lock is held. From the deadlock
319+
* avoidance point of view it is equivalent to spin_lock()/increment
320+
* refcount/spin_unlock(), so calling it under @dentry->d_lock is
321+
* always a bug; so's calling it under ->d_lock on any of its descendents.
322+
*
323+
*/
304324
static inline struct dentry *dget(struct dentry *dentry)
305325
{
306326
if (dentry)
@@ -311,12 +331,11 @@ static inline struct dentry *dget(struct dentry *dentry)
311331
extern struct dentry *dget_parent(struct dentry *dentry);
312332

313333
/**
314-
* d_unhashed - is dentry hashed
315-
* @dentry: entry to check
334+
* d_unhashed - is dentry hashed
335+
* @dentry: entry to check
316336
*
317-
* Returns true if the dentry passed is not currently hashed.
337+
* Returns true if the dentry passed is not currently hashed.
318338
*/
319-
320339
static inline int d_unhashed(const struct dentry *dentry)
321340
{
322341
return hlist_bl_unhashed(&dentry->d_hash);

0 commit comments

Comments
 (0)