diff options
-rw-r--r-- | fs/afs/afs.h | 2 | ||||
-rw-r--r-- | fs/afs/dir.c | 36 | ||||
-rw-r--r-- | fs/afs/fsclient.c | 2 | ||||
-rw-r--r-- | fs/afs/inode.c | 18 | ||||
-rw-r--r-- | fs/afs/internal.h | 1 | ||||
-rw-r--r-- | fs/afs/yfsclient.c | 2 |
6 files changed, 33 insertions, 28 deletions
diff --git a/fs/afs/afs.h b/fs/afs/afs.h index 819678bd8bec..a7d3f902a91c 100644 --- a/fs/afs/afs.h +++ b/fs/afs/afs.h @@ -150,7 +150,9 @@ struct afs_file_status { struct afs_status_cb { struct afs_file_status status; struct afs_callback callback; + bool have_status; /* True if status record was retrieved */ bool have_cb; /* True if cb record was retrieved */ + bool have_error; /* True if status.abort_code indicates an error */ }; /* diff --git a/fs/afs/dir.c b/fs/afs/dir.c index 338c2260b0a0..d1b3736a3bbd 100644 --- a/fs/afs/dir.c +++ b/fs/afs/dir.c @@ -1299,32 +1299,27 @@ error: * However, if we didn't have a callback promise outstanding, or it was * outstanding on a different server, then it won't break it either... */ -int afs_dir_remove_link(struct dentry *dentry, struct key *key, - unsigned long d_version_before, - unsigned long d_version_after) +static int afs_dir_remove_link(struct afs_vnode *dvnode, struct dentry *dentry, + struct key *key) { - bool dir_valid; int ret = 0; - /* There were no intervening changes on the server if the version - * number we got back was incremented by exactly 1. - */ - dir_valid = (d_version_after == d_version_before + 1); - if (d_really_is_positive(dentry)) { struct afs_vnode *vnode = AFS_FS_I(d_inode(dentry)); if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) { /* Already done */ - } else if (dir_valid) { + } else if (test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) { + write_seqlock(&vnode->cb_lock); drop_nlink(&vnode->vfs_inode); if (vnode->vfs_inode.i_nlink == 0) { set_bit(AFS_VNODE_DELETED, &vnode->flags); - clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); + __afs_break_callback(vnode); } + write_sequnlock(&vnode->cb_lock); ret = 0; } else { - clear_bit(AFS_VNODE_CB_PROMISED, &vnode->flags); + afs_break_callback(vnode); if (test_bit(AFS_VNODE_DELETED, &vnode->flags)) kdebug("AFS_VNODE_DELETED"); @@ -1348,7 +1343,6 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) struct afs_status_cb *scb; struct afs_vnode *dvnode = AFS_FS_I(dir), *vnode = NULL; struct key *key; - unsigned long d_version = (unsigned long)dentry->d_fsdata; bool need_rehash = false; int ret; @@ -1395,20 +1389,16 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) ret = -ERESTARTSYS; if (afs_begin_vnode_operation(&fc, dvnode, key, true)) { afs_dataversion_t data_version = dvnode->status.data_version + 1; + afs_dataversion_t data_version_2 = vnode->status.data_version; while (afs_select_fileserver(&fc)) { fc.cb_break = afs_calc_vnode_cb_break(dvnode); + fc.cb_break_2 = afs_calc_vnode_cb_break(vnode); if (test_bit(AFS_SERVER_FL_IS_YFS, &fc.cbi->server->flags) && !test_bit(AFS_SERVER_FL_NO_RM2, &fc.cbi->server->flags)) { yfs_fs_remove_file2(&fc, vnode, dentry->d_name.name, &scb[0], &scb[1]); - if (fc.ac.error == 0 && - scb[1].status.abort_code == VNOVNODE) { - set_bit(AFS_VNODE_DELETED, &vnode->flags); - afs_break_callback(vnode); - } - if (fc.ac.error != -ECONNABORTED || fc.ac.abort_code != RXGEN_OPCODE) continue; @@ -1420,11 +1410,11 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry) afs_vnode_commit_status(&fc, dvnode, fc.cb_break, &data_version, &scb[0]); + afs_vnode_commit_status(&fc, vnode, fc.cb_break_2, + &data_version_2, &scb[1]); ret = afs_end_vnode_operation(&fc); - if (ret == 0) - ret = afs_dir_remove_link( - dentry, key, d_version, - (unsigned long)dvnode->status.data_version); + if (ret == 0 && !(scb[1].have_status || scb[1].have_error)) + ret = afs_dir_remove_link(dvnode, dentry, key); if (ret == 0 && test_bit(AFS_VNODE_DIR_VALID, &dvnode->flags)) afs_edit_dir_remove(dvnode, &dentry->d_name, diff --git a/fs/afs/fsclient.c b/fs/afs/fsclient.c index 89b684c957b9..48298408d6ac 100644 --- a/fs/afs/fsclient.c +++ b/fs/afs/fsclient.c @@ -83,6 +83,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp, * case. */ status->abort_code = abort_code; + scb->have_error = true; return 0; } @@ -127,6 +128,7 @@ static int xdr_decode_AFSFetchStatus(const __be32 **_bp, data_version = (u64)ntohl(xdr->data_version_lo); data_version |= (u64)ntohl(xdr->data_version_hi) << 32; status->data_version = data_version; + scb->have_status = true; *_bp = (const void *)*_bp + sizeof(*xdr); return 0; diff --git a/fs/afs/inode.c b/fs/afs/inode.c index 37c5de793353..e1a523d2e378 100644 --- a/fs/afs/inode.c +++ b/fs/afs/inode.c @@ -23,6 +23,7 @@ #include <linux/namei.h> #include <linux/iversion.h> #include "internal.h" +#include "afs_fs.h" static const struct inode_operations afs_symlink_inode_operations = { .get_link = page_get_link, @@ -271,13 +272,22 @@ void afs_vnode_commit_status(struct afs_fs_cursor *fc, write_seqlock(&vnode->cb_lock); - afs_apply_status(fc, vnode, scb, expected_version); - if (scb->have_cb) - afs_apply_callback(fc, vnode, scb, cb_break); + if (scb->have_error) { + if (scb->status.abort_code == VNOVNODE) { + set_bit(AFS_VNODE_DELETED, &vnode->flags); + clear_nlink(&vnode->vfs_inode); + __afs_break_callback(vnode); + } + } else { + if (scb->have_status) + afs_apply_status(fc, vnode, scb, expected_version); + if (scb->have_cb) + afs_apply_callback(fc, vnode, scb, cb_break); + } write_sequnlock(&vnode->cb_lock); - if (fc->ac.error == 0) + if (fc->ac.error == 0 && scb->have_status) afs_cache_permit(vnode, fc->key, cb_break, scb); } diff --git a/fs/afs/internal.h b/fs/afs/internal.h index 3dbb1e840dfd..f80ca638e70f 100644 --- a/fs/afs/internal.h +++ b/fs/afs/internal.h @@ -904,7 +904,6 @@ extern const struct address_space_operations afs_dir_aops; extern const struct dentry_operations afs_fs_dentry_operations; extern void afs_d_release(struct dentry *); -extern int afs_dir_remove_link(struct dentry *, struct key *, unsigned long, unsigned long); /* * dir_edit.c diff --git a/fs/afs/yfsclient.c b/fs/afs/yfsclient.c index c8f71fc9920b..10de675dc6fc 100644 --- a/fs/afs/yfsclient.c +++ b/fs/afs/yfsclient.c @@ -195,6 +195,7 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp, if (status->abort_code != 0) { if (status->abort_code == VNOVNODE) status->nlink = 0; + scb->have_error = true; return 0; } @@ -222,6 +223,7 @@ static int xdr_decode_YFSFetchStatus(const __be32 **_bp, status->mtime_server = xdr_to_time(xdr->mtime_server); status->size = xdr_to_u64(xdr->size); status->data_version = xdr_to_u64(xdr->data_version); + scb->have_status = true; *_bp += xdr_size(xdr); return 0; |