diff options
author | J. Bruce Fields <bfields@fieldses.org> | 2011-01-18 15:45:09 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2011-03-10 05:18:54 -0500 |
commit | d891eedbc3b1b0fade8a9ce60cc0eba1cccb59e5 (patch) | |
tree | 55dd6aa9e17cd763efda6291d740bafd7f05d802 /fs/dcache.c | |
parent | 1ca551c6caae7b52178555cdedea6ca26444be46 (diff) |
fs/dcache: allow d_obtain_alias() to return unhashed dentries
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>
Diffstat (limited to 'fs/dcache.c')
-rw-r--r-- | fs/dcache.c | 26 |
1 files changed, 24 insertions, 2 deletions
diff --git a/fs/dcache.c b/fs/dcache.c index 2a6bd9a4ae97..611ffe928c03 100644 --- a/fs/dcache.c +++ b/fs/dcache.c | |||
@@ -1523,6 +1523,28 @@ struct dentry * d_alloc_root(struct inode * root_inode) | |||
1523 | } | 1523 | } |
1524 | EXPORT_SYMBOL(d_alloc_root); | 1524 | EXPORT_SYMBOL(d_alloc_root); |
1525 | 1525 | ||
1526 | static struct dentry * __d_find_any_alias(struct inode *inode) | ||
1527 | { | ||
1528 | struct dentry *alias; | ||
1529 | |||
1530 | if (list_empty(&inode->i_dentry)) | ||
1531 | return NULL; | ||
1532 | alias = list_first_entry(&inode->i_dentry, struct dentry, d_alias); | ||
1533 | __dget(alias); | ||
1534 | return alias; | ||
1535 | } | ||
1536 | |||
1537 | static struct dentry * d_find_any_alias(struct inode *inode) | ||
1538 | { | ||
1539 | struct dentry *de; | ||
1540 | |||
1541 | spin_lock(&inode->i_lock); | ||
1542 | de = __d_find_any_alias(inode); | ||
1543 | spin_unlock(&inode->i_lock); | ||
1544 | return de; | ||
1545 | } | ||
1546 | |||
1547 | |||
1526 | /** | 1548 | /** |
1527 | * d_obtain_alias - find or allocate a dentry for a given inode | 1549 | * d_obtain_alias - find or allocate a dentry for a given inode |
1528 | * @inode: inode to allocate the dentry for | 1550 | * @inode: inode to allocate the dentry for |
@@ -1552,7 +1574,7 @@ struct dentry *d_obtain_alias(struct inode *inode) | |||
1552 | if (IS_ERR(inode)) | 1574 | if (IS_ERR(inode)) |
1553 | return ERR_CAST(inode); | 1575 | return ERR_CAST(inode); |
1554 | 1576 | ||
1555 | res = d_find_alias(inode); | 1577 | res = d_find_any_alias(inode); |
1556 | if (res) | 1578 | if (res) |
1557 | goto out_iput; | 1579 | goto out_iput; |
1558 | 1580 | ||
@@ -1565,7 +1587,7 @@ struct dentry *d_obtain_alias(struct inode *inode) | |||
1565 | 1587 | ||
1566 | 1588 | ||
1567 | spin_lock(&inode->i_lock); | 1589 | spin_lock(&inode->i_lock); |
1568 | res = __d_find_alias(inode, 0); | 1590 | res = __d_find_any_alias(inode); |
1569 | if (res) { | 1591 | if (res) { |
1570 | spin_unlock(&inode->i_lock); | 1592 | spin_unlock(&inode->i_lock); |
1571 | dput(tmp); | 1593 | dput(tmp); |