Skip to content

Commit

Permalink
fs/dcache: allow d_obtain_alias() to return unhashed dentries
Browse files Browse the repository at this point in the history
Without this patch, inodes are not promptly freed on last close of an
unlinked file by an nfs client:

	client$ mount -tnfs4 server:/export/ /mnt/
	client$ tail -f /mnt/FOO
	...
	server$ df -i /export
	server$ rm /export/FOO
	(^C the tail -f)
	server$ df -i /export
	server$ echo 2 >/proc/sys/vm/drop_caches
	server$ df -i /export

the df's will show that the inode is not freed on the filesystem until
the last step, when it could have been freed after killing the client's
tail -f. On-disk data won't be deallocated either, leading to possible
spurious ENOSPC.

This occurs because when the client does the close, it arrives in a
compound with a putfh and a close, processed like:

	- putfh: look up the filehandle.  The only alias found for the
	  inode will be DCACHE_UNHASHED alias referenced by the filp
	  this, so it creates a new DCACHE_DISCONECTED dentry and
	  returns that instead.
	- close: closes the existing filp, which is destroyed
	  immediately by dput() since it's DCACHE_UNHASHED.
	- end of the compound: release the reference
	  to the current filehandle, and dput() the new
	  DCACHE_DISCONECTED dentry, which gets put on the
	  unused list instead of being destroyed immediately.

Nick Piggin suggested fixing this by allowing d_obtain_alias to return
the unhashed dentry that is referenced by the filp, instead of making it
create a new dentry.

Leave __d_find_alias() alone to avoid changing behavior of other
callers.

Also nfsd doesn't need all the checks of __d_find_alias(); any dentry,
hashed or unhashed, disconnected or not, should work.

Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
  • Loading branch information
J. Bruce Fields authored and Al Viro committed Mar 10, 2011
1 parent 1ca551c commit d891eed
Showing 1 changed file with 24 additions and 2 deletions.
26 changes: 24 additions & 2 deletions fs/dcache.c
Original file line number Diff line number Diff line change
Expand Up @@ -1523,6 +1523,28 @@ struct dentry * d_alloc_root(struct inode * root_inode)
}
EXPORT_SYMBOL(d_alloc_root);

static struct dentry * __d_find_any_alias(struct inode *inode)
{
struct dentry *alias;

if (list_empty(&inode->i_dentry))
return NULL;
alias = list_first_entry(&inode->i_dentry, struct dentry, d_alias);
__dget(alias);
return alias;
}

static struct dentry * d_find_any_alias(struct inode *inode)
{
struct dentry *de;

spin_lock(&inode->i_lock);
de = __d_find_any_alias(inode);
spin_unlock(&inode->i_lock);
return de;
}


/**
* d_obtain_alias - find or allocate a dentry for a given inode
* @inode: inode to allocate the dentry for
Expand Down Expand Up @@ -1552,7 +1574,7 @@ struct dentry *d_obtain_alias(struct inode *inode)
if (IS_ERR(inode))
return ERR_CAST(inode);

res = d_find_alias(inode);
res = d_find_any_alias(inode);
if (res)
goto out_iput;

Expand All @@ -1565,7 +1587,7 @@ struct dentry *d_obtain_alias(struct inode *inode)


spin_lock(&inode->i_lock);
res = __d_find_alias(inode, 0);
res = __d_find_any_alias(inode);
if (res) {
spin_unlock(&inode->i_lock);
dput(tmp);
Expand Down

0 comments on commit d891eed

Please sign in to comment.