On Mon, Jun 16, 2025 at 09:00:39AM +1000, NeilBrown wrote: > > The rcu_dereference() call in proc_sys_compare() is problematic as > ->d_compare is not guaranteed to be called with rcu_read_lock() held and > rcu_dereference() can cause a warning when used without that lock. > > Specifically d_alloc_parallel() will call ->d_compare() without > rcu_read_lock(), but with ->d_lock to ensure stability. In this case > ->d_inode is usually NULL so the rcu_dereference() will normally not be > reached, but it is possible that ->d_inode was set while waiting for > ->d_lock which could lead to the warning. Huh? There are two call sites of d_same_name() in d_alloc_parallel() - one in the loop (under rcu_read_lock()) and another after the thing we are comparing has ceased to be in-lookup. The latter is under ->d_lock, stabilizing everything (and it really can't run into NULL ->d_inode for /proc/sys/ stuff). ->d_compare() instances are guaranteed dentry->d_lock or rcu_read_lock(); in the latter case we'll either recheck or validate on previously sampled ->d_seq. And the second call in d_alloc_parallel() is just that - recheck under ->d_lock. Just use rcu_dereference_check(...., spin_is_locked(&dentry->d_lock)) and be done with that... The part where we have a somewhat wrong behaviour is not the second call in d_alloc_parallel() - it's the first one. Something like this static int proc_sys_compare(const struct dentry *dentry, unsigned int len, const char *str, const struct qstr *name) { struct ctl_table_header *head; struct inode *inode; if (name->len != len) return 1; if (memcmp(name->name, str, len)) return 1; // false positive is fine here - we'll recheck anyway if (d_in_lookup(dentry)) return 0; inode = d_inode_rcu(dentry); // we just might have run into dentry in the middle of __dentry_kill() if (!inode) return 1; head = rcu_dereference_check(PROC_I(inode)->sysctl, spin_is_locked(&dentry->d_lock)); return !head || !sysctl_is_seen(head); }