Skip to content

Commit 22650f1

Browse files
dhowellstorvalds
authored andcommitted
afs: Fix speculative status fetches
The generic/464 xfstest causes kAFS to emit occasional warnings of the form: kAFS: vnode modified {100055:8a} 30->31 YFS.StoreData64 (c=6015) This indicates that the data version received back from the server did not match the expected value (the DV should be incremented monotonically for each individual modification op committed to a vnode). What is happening is that a lookup call is doing a bulk status fetch speculatively on a bunch of vnodes in a directory besides getting the status of the vnode it's actually interested in. This is racing with a StoreData operation (though it could also occur with, say, a MakeDir op). On the client, a modification operation locks the vnode, but the bulk status fetch only locks the parent directory, so no ordering is imposed there (thereby avoiding an avenue to deadlock). On the server, the StoreData op handler doesn't lock the vnode until it's received all the request data, and downgrades the lock after committing the data until it has finished sending change notifications to other clients - which allows the status fetch to occur before it has finished. This means that: - a status fetch can access the target vnode either side of the exclusive section of the modification - the status fetch could start before the modification, yet finish after, and vice-versa. - the status fetch and the modification RPCs can complete in either order. - the status fetch can return either the before or the after DV from the modification. - the status fetch might regress the locally cached DV. Some of these are handled by the previous fix[1], but that's not sufficient because it checks the DV it received against the DV it cached at the start of the op, but the DV might've been updated in the meantime by a locally generated modification op. Fix this by the following means: (1) Keep track of when we're performing a modification operation on a vnode. This is done by marking vnode parameters with a 'modification' note that causes the AFS_VNODE_MODIFYING flag to be set on the vnode for the duration. (2) Alter the speculation race detection to ignore speculative status fetches if either the vnode is marked as being modified or the data version number is not what we expected. Note that whilst the "vnode modified" warning does get recovered from as it causes the client to refetch the status at the next opportunity, it will also invalidate the pagecache, so changes might get lost. Fixes: a9e5c87 ("afs: Fix speculative status fetch going out of order wrt to modifications") Reported-by: Marc Dionne <[email protected]> Signed-off-by: David Howells <[email protected]> Tested-and-reviewed-by: Marc Dionne <[email protected]> cc: [email protected] Link: https://lore.kernel.org/r/160605082531.252452.14708077925602709042.stgit@warthog.procyon.org.uk/ [1] Link: https://lore.kernel.org/linux-fsdevel/161961335926.39335.2552653972195467566.stgit@warthog.procyon.org.uk/ # v1 Signed-off-by: Linus Torvalds <[email protected]>
1 parent 7af81cd commit 22650f1

File tree

6 files changed

+23
-2
lines changed

6 files changed

+23
-2
lines changed

fs/afs/dir.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,6 +1419,7 @@ static int afs_mkdir(struct user_namespace *mnt_userns, struct inode *dir,
14191419

14201420
afs_op_set_vnode(op, 0, dvnode);
14211421
op->file[0].dv_delta = 1;
1422+
op->file[0].modification = true;
14221423
op->file[0].update_ctime = true;
14231424
op->dentry = dentry;
14241425
op->create.mode = S_IFDIR | mode;
@@ -1500,6 +1501,7 @@ static int afs_rmdir(struct inode *dir, struct dentry *dentry)
15001501

15011502
afs_op_set_vnode(op, 0, dvnode);
15021503
op->file[0].dv_delta = 1;
1504+
op->file[0].modification = true;
15031505
op->file[0].update_ctime = true;
15041506

15051507
op->dentry = dentry;
@@ -1636,6 +1638,7 @@ static int afs_unlink(struct inode *dir, struct dentry *dentry)
16361638

16371639
afs_op_set_vnode(op, 0, dvnode);
16381640
op->file[0].dv_delta = 1;
1641+
op->file[0].modification = true;
16391642
op->file[0].update_ctime = true;
16401643

16411644
/* Try to make sure we have a callback promise on the victim. */
@@ -1718,6 +1721,7 @@ static int afs_create(struct user_namespace *mnt_userns, struct inode *dir,
17181721

17191722
afs_op_set_vnode(op, 0, dvnode);
17201723
op->file[0].dv_delta = 1;
1724+
op->file[0].modification = true;
17211725
op->file[0].update_ctime = true;
17221726

17231727
op->dentry = dentry;
@@ -1792,6 +1796,7 @@ static int afs_link(struct dentry *from, struct inode *dir,
17921796
afs_op_set_vnode(op, 0, dvnode);
17931797
afs_op_set_vnode(op, 1, vnode);
17941798
op->file[0].dv_delta = 1;
1799+
op->file[0].modification = true;
17951800
op->file[0].update_ctime = true;
17961801
op->file[1].update_ctime = true;
17971802

@@ -1987,6 +1992,8 @@ static int afs_rename(struct user_namespace *mnt_userns, struct inode *old_dir,
19871992
afs_op_set_vnode(op, 1, new_dvnode); /* May be same as orig_dvnode */
19881993
op->file[0].dv_delta = 1;
19891994
op->file[1].dv_delta = 1;
1995+
op->file[0].modification = true;
1996+
op->file[1].modification = true;
19901997
op->file[0].update_ctime = true;
19911998
op->file[1].update_ctime = true;
19921999

fs/afs/dir_silly.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ static int afs_do_silly_rename(struct afs_vnode *dvnode, struct afs_vnode *vnode
7373
afs_op_set_vnode(op, 1, dvnode);
7474
op->file[0].dv_delta = 1;
7575
op->file[1].dv_delta = 1;
76+
op->file[0].modification = true;
77+
op->file[1].modification = true;
7678
op->file[0].update_ctime = true;
7779
op->file[1].update_ctime = true;
7880

@@ -201,6 +203,7 @@ static int afs_do_silly_unlink(struct afs_vnode *dvnode, struct afs_vnode *vnode
201203
afs_op_set_vnode(op, 0, dvnode);
202204
afs_op_set_vnode(op, 1, vnode);
203205
op->file[0].dv_delta = 1;
206+
op->file[0].modification = true;
204207
op->file[0].update_ctime = true;
205208
op->file[1].op_unlinked = true;
206209
op->file[1].update_ctime = true;

fs/afs/fs_operation.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ static void afs_prepare_vnode(struct afs_operation *op, struct afs_vnode_param *
118118
vp->cb_break_before = afs_calc_vnode_cb_break(vnode);
119119
if (vnode->lock_state != AFS_VNODE_LOCK_NONE)
120120
op->flags |= AFS_OPERATION_CUR_ONLY;
121+
if (vp->modification)
122+
set_bit(AFS_VNODE_MODIFYING, &vnode->flags);
121123
}
122124

123125
if (vp->fid.vnode)
@@ -225,6 +227,10 @@ int afs_put_operation(struct afs_operation *op)
225227

226228
if (op->ops && op->ops->put)
227229
op->ops->put(op);
230+
if (op->file[0].modification)
231+
clear_bit(AFS_VNODE_MODIFYING, &op->file[0].vnode->flags);
232+
if (op->file[1].modification && op->file[1].vnode != op->file[0].vnode)
233+
clear_bit(AFS_VNODE_MODIFYING, &op->file[1].vnode->flags);
228234
if (op->file[0].put_vnode)
229235
iput(&op->file[0].vnode->vfs_inode);
230236
if (op->file[1].put_vnode)

fs/afs/inode.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,8 +294,9 @@ void afs_vnode_commit_status(struct afs_operation *op, struct afs_vnode_param *v
294294
op->flags &= ~AFS_OPERATION_DIR_CONFLICT;
295295
}
296296
} else if (vp->scb.have_status) {
297-
if (vp->dv_before + vp->dv_delta != vp->scb.status.data_version &&
298-
vp->speculative)
297+
if (vp->speculative &&
298+
(test_bit(AFS_VNODE_MODIFYING, &vnode->flags) ||
299+
vp->dv_before != vnode->status.data_version))
299300
/* Ignore the result of a speculative bulk status fetch
300301
* if it splits around a modification op, thereby
301302
* appearing to regress the data version.
@@ -911,6 +912,7 @@ int afs_setattr(struct user_namespace *mnt_userns, struct dentry *dentry,
911912
}
912913
op->ctime = attr->ia_ctime;
913914
op->file[0].update_ctime = 1;
915+
op->file[0].modification = true;
914916

915917
op->ops = &afs_setattr_operation;
916918
ret = afs_do_sync_operation(op);

fs/afs/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,7 @@ struct afs_vnode {
645645
#define AFS_VNODE_PSEUDODIR 7 /* set if Vnode is a pseudo directory */
646646
#define AFS_VNODE_NEW_CONTENT 8 /* Set if file has new content (create/trunc-0) */
647647
#define AFS_VNODE_SILLY_DELETED 9 /* Set if file has been silly-deleted */
648+
#define AFS_VNODE_MODIFYING 10 /* Set if we're performing a modification op */
648649

649650
struct list_head wb_keys; /* List of keys available for writeback */
650651
struct list_head pending_locks; /* locks waiting to be granted */
@@ -762,6 +763,7 @@ struct afs_vnode_param {
762763
bool set_size:1; /* Must update i_size */
763764
bool op_unlinked:1; /* True if file was unlinked by op */
764765
bool speculative:1; /* T if speculative status fetch (no vnode lock) */
766+
bool modification:1; /* Set if the content gets modified */
765767
};
766768

767769
/*

fs/afs/write.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ static int afs_store_data(struct afs_vnode *vnode, struct iov_iter *iter, loff_t
377377

378378
afs_op_set_vnode(op, 0, vnode);
379379
op->file[0].dv_delta = 1;
380+
op->file[0].modification = true;
380381
op->store.write_iter = iter;
381382
op->store.pos = pos;
382383
op->store.size = size;

0 commit comments

Comments
 (0)