Skip to content

Commit 440fbc3

Browse files
committed
afs: Fix unlink
Repeating creation and deletion of a file on an afs mount will run the box out of memory, e.g.: dd if=/dev/zero of=/afs/scratch/m0 bs=$((1024*1024)) count=512 rm /afs/scratch/m0 The problem seems to be that it's not properly decrementing the nlink count so that the inode can be scrapped. Note that this doesn't fix local creation followed by remote deletion. That's harder to handle and will require a separate patch as we're not told that the file has been deleted - only that the directory has changed. Reported-by: Marc Dionne <marc.dionne@auristor.com> Signed-off-by: David Howells <dhowells@redhat.com>
1 parent 7888da9 commit 440fbc3

File tree

2 files changed

+33
-8
lines changed

2 files changed

+33
-8
lines changed

fs/afs/dir.c

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -895,20 +895,38 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
895895
* However, if we didn't have a callback promise outstanding, or it was
896896
* outstanding on a different server, then it won't break it either...
897897
*/
898-
static int afs_dir_remove_link(struct dentry *dentry, struct key *key)
898+
static int afs_dir_remove_link(struct dentry *dentry, struct key *key,
899+
unsigned long d_version_before,
900+
unsigned long d_version_after)
899901
{
902+
bool dir_valid;
900903
int ret = 0;
901904

905+
/* There were no intervening changes on the server if the version
906+
* number we got back was incremented by exactly 1.
907+
*/
908+
dir_valid = (d_version_after == d_version_before + 1);
909+
902910
if (d_really_is_positive(dentry)) {
903911
struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry));
904912

905-
if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
906-
kdebug("AFS_VNODE_DELETED");
907-
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
908-
909-
ret = afs_validate(vnode, key);
910-
if (ret == -ESTALE)
913+
if (dir_valid) {
914+
drop_nlink(&vnode->vfs_inode);
915+
if (vnode->vfs_inode.i_nlink == 0) {
916+
set_bit(AFS_VNODE_DELETED, &vnode->flags);
917+
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
918+
}
911919
ret = 0;
920+
} else {
921+
clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags);
922+
923+
if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
924+
kdebug("AFS_VNODE_DELETED");
925+
926+
ret = afs_validate(vnode, key);
927+
if (ret == -ESTALE)
928+
ret = 0;
929+
}
912930
_debug("nlink %d [val %d]", vnode->vfs_inode.i_nlink, ret);
913931
}
914932

@@ -923,6 +941,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
923941
struct afs_fs_cursor fc;
924942
struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode;
925943
struct key *key;
944+
unsigned long d_version = (unsigned long)dentry->d_fsdata;
926945
int ret;
927946

928947
_enter("{%x:%u},{%pd}",
@@ -955,7 +974,9 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
955974
afs_vnode_commit_status(&fc, dvnode, fc.cb_break);
956975
ret = afs_end_vnode_operation(&fc);
957976
if (ret == 0)
958-
ret = afs_dir_remove_link(dentry, key);
977+
ret = afs_dir_remove_link(
978+
dentry, key, d_version,
979+
(unsigned long)dvnode->status.data_version);
959980
}
960981

961982
error_key:

fs/afs/inode.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,10 @@ int afs_validate(struct afs_vnode *vnode, struct key *key)
377377
}
378378

379379
read_sequnlock_excl(&vnode->cb_lock);
380+
381+
if (test_bit(AFS_VNODE_DELETED, &vnode->flags))
382+
clear_nlink(&vnode->vfs_inode);
383+
380384
if (valid)
381385
goto valid;
382386

0 commit comments

Comments
 (0)