d_revalidate and real_lookup woes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



I came across two somewhat related issues with d_revalidate, and wanted to 
find out if these were either known issues (possibly even addressed by 
Al's planned dcache changes?) or just the product of my own confusion..

The d_revalidate() behavior in real_lookup() currently bails out with 
-ENOENT when it encounters a race with i_mutex that confuses it.  
Specifically:

- in do_lookup(), if no dentry is found (or do_revalidate() fails), we 
call real_lookup().
- real_lookup() takes the dir i_mutex.
- if the dentry now exists (is found by d_lookup()), real_lookup() drops 
i_mutex, and calls do_revalidate() (again).
- if do_revalidate() fails now, we return -ENOENT.

This "two chances or -ENOENT" strategy strikes me as fundamentally racy.  
If, while we wait for i_mutex, something else hashes the dentry, but for 
one reason or another leaves it in a state in which d_revalidate will 
fail, we get -ENOENT instead of a fresh call to lookup.  For (reasonably 
long) timeout-based revalidation schemes, that doesn't really happen, but 
if your timeout is sufficiently short and i_mutex is contended, or you are 
explicitly invalidating a dentry (e.g. due to a callback from the server), 
it can happen more frequently. 

It also looks like we can get into trouble if a ->d_revalidate() doesn't 
d_drop() when it fails: do_revalidate() will try to d_invalidate(), but 
that can -EBUSY if the dentry has non-prunable children, and 
do_revalidate() silently drops that error condition.  If I'm reading this 
correctly, that means if we ever get a hashed dentry with children for 
which ->d_revalidate() fails (and doesn't d_drop()), we will get -ENOENT 
forever without ever calling ->lookup().

(I started worrying about d_revalidate()s that don't d_drop() after 
noticing that at least cifs and ocfs2 don't always do so on failure.  It 
seems reasonable to expect/require d_revalidate() to d_drop() any dentry 
it fails on, but that doesn't appear to be the case.)

Possible solutions?

- I looked a bit at whether simply looping in real_lookup() would address 
the issue (i.e. retake i_mutex, d_lookup(), call ->lookup() as needed).  
Starvation issues aside, that can loop forever if it gets a 
bad-but-hashed dentry (as described above).

- Instead of returning -ENOENT (which seems kind of unlikely to ever be 
the "right" answer), real_lookup() could call ->lookup(), even though the 
dentry is hashed.  Assuming that's even allowed (?), that might lead to 
more ->lookup() calls than we need...

 - A new ->d_revalidate_locked() method that can be called with the dir 
i_mutex held would allow real_lookup() to decide whether to call 
->lookup() while avoiding any additional race/starvaction issues.

None of these possibilities seem especially attractive. :( Are these known 
issues, or am I just misreading the code here?

sage
--
To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Ext4 Filesystem]     [Union Filesystem]     [Filesystem Testing]     [Ceph Users]     [Ecryptfs]     [AutoFS]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux Cachefs]     [Reiser Filesystem]     [Linux RAID]     [Samba]     [Device Mapper]     [CEPH Development]
  Powered by Linux