aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorAndrew Elble <aweits@rit.edu>2015-11-05 20:42:43 -0500
committerJ. Bruce Fields <bfields@redhat.com>2015-11-10 09:29:45 -0500
commit7fc0564e3a8d16df096f48c9c6425ba84d945c6e (patch)
tree1470204738456cc407010bf070efc134e0490141 /fs/nfsd
parent34ed9872e745fa56f10e9bef2cf3d2336c6c8816 (diff)
nfsd: fix race with open / open upgrade stateids
We observed multiple open stateids on the server for files that seemingly should have been closed. nfsd4_process_open2() tests for the existence of a preexisting stateid. If one is not found, the locks are dropped and a new one is created. The problem is that init_open_stateid(), which is also responsible for hashing the newly initialized stateid, doesn't check to see if another open has raced in and created a matching stateid. This fix is to enable init_open_stateid() to return the matching stateid and have nfsd4_process_open2() swap to that stateid and switch to the open upgrade path. In testing this patch, coverage to the newly created path indicates that the race was indeed happening. Signed-off-by: Andrew Elble <aweits@rit.edu> Reviewed-by: Jeff Layton <jlayton@poochiereds.net> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs4state.c78
1 files changed, 53 insertions, 25 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 6411c3421870..6b800b5b8fed 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -3392,6 +3392,27 @@ static const struct nfs4_stateowner_operations openowner_ops = {
3392 .so_free = nfs4_free_openowner, 3392 .so_free = nfs4_free_openowner,
3393}; 3393};
3394 3394
3395static struct nfs4_ol_stateid *
3396nfsd4_find_existing_open(struct nfs4_file *fp, struct nfsd4_open *open)
3397{
3398 struct nfs4_ol_stateid *local, *ret = NULL;
3399 struct nfs4_openowner *oo = open->op_openowner;
3400
3401 lockdep_assert_held(&fp->fi_lock);
3402
3403 list_for_each_entry(local, &fp->fi_stateids, st_perfile) {
3404 /* ignore lock owners */
3405 if (local->st_stateowner->so_is_open_owner == 0)
3406 continue;
3407 if (local->st_stateowner == &oo->oo_owner) {
3408 ret = local;
3409 atomic_inc(&ret->st_stid.sc_count);
3410 break;
3411 }
3412 }
3413 return ret;
3414}
3415
3395static struct nfs4_openowner * 3416static struct nfs4_openowner *
3396alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open, 3417alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
3397 struct nfsd4_compound_state *cstate) 3418 struct nfsd4_compound_state *cstate)
@@ -3423,9 +3444,20 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
3423 return ret; 3444 return ret;
3424} 3445}
3425 3446
3426static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { 3447static struct nfs4_ol_stateid *
3448init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
3449 struct nfsd4_open *open)
3450{
3451
3427 struct nfs4_openowner *oo = open->op_openowner; 3452 struct nfs4_openowner *oo = open->op_openowner;
3453 struct nfs4_ol_stateid *retstp = NULL;
3428 3454
3455 spin_lock(&oo->oo_owner.so_client->cl_lock);
3456 spin_lock(&fp->fi_lock);
3457
3458 retstp = nfsd4_find_existing_open(fp, open);
3459 if (retstp)
3460 goto out_unlock;
3429 atomic_inc(&stp->st_stid.sc_count); 3461 atomic_inc(&stp->st_stid.sc_count);
3430 stp->st_stid.sc_type = NFS4_OPEN_STID; 3462 stp->st_stid.sc_type = NFS4_OPEN_STID;
3431 INIT_LIST_HEAD(&stp->st_locks); 3463 INIT_LIST_HEAD(&stp->st_locks);
@@ -3436,12 +3468,13 @@ static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
3436 stp->st_deny_bmap = 0; 3468 stp->st_deny_bmap = 0;
3437 stp->st_openstp = NULL; 3469 stp->st_openstp = NULL;
3438 init_rwsem(&stp->st_rwsem); 3470 init_rwsem(&stp->st_rwsem);
3439 spin_lock(&oo->oo_owner.so_client->cl_lock);
3440 list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids); 3471 list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids);
3441 spin_lock(&fp->fi_lock);
3442 list_add(&stp->st_perfile, &fp->fi_stateids); 3472 list_add(&stp->st_perfile, &fp->fi_stateids);
3473
3474out_unlock:
3443 spin_unlock(&fp->fi_lock); 3475 spin_unlock(&fp->fi_lock);
3444 spin_unlock(&oo->oo_owner.so_client->cl_lock); 3476 spin_unlock(&oo->oo_owner.so_client->cl_lock);
3477 return retstp;
3445} 3478}
3446 3479
3447/* 3480/*
@@ -3852,27 +3885,6 @@ out:
3852 return nfs_ok; 3885 return nfs_ok;
3853} 3886}
3854 3887
3855static struct nfs4_ol_stateid *
3856nfsd4_find_existing_open(struct nfs4_file *fp, struct nfsd4_open *open)
3857{
3858 struct nfs4_ol_stateid *local, *ret = NULL;
3859 struct nfs4_openowner *oo = open->op_openowner;
3860
3861 spin_lock(&fp->fi_lock);
3862 list_for_each_entry(local, &fp->fi_stateids, st_perfile) {
3863 /* ignore lock owners */
3864 if (local->st_stateowner->so_is_open_owner == 0)
3865 continue;
3866 if (local->st_stateowner == &oo->oo_owner) {
3867 ret = local;
3868 atomic_inc(&ret->st_stid.sc_count);
3869 break;
3870 }
3871 }
3872 spin_unlock(&fp->fi_lock);
3873 return ret;
3874}
3875
3876static inline int nfs4_access_to_access(u32 nfs4_access) 3888static inline int nfs4_access_to_access(u32 nfs4_access)
3877{ 3889{
3878 int flags = 0; 3890 int flags = 0;
@@ -4258,6 +4270,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
4258 struct nfs4_client *cl = open->op_openowner->oo_owner.so_client; 4270 struct nfs4_client *cl = open->op_openowner->oo_owner.so_client;
4259 struct nfs4_file *fp = NULL; 4271 struct nfs4_file *fp = NULL;
4260 struct nfs4_ol_stateid *stp = NULL; 4272 struct nfs4_ol_stateid *stp = NULL;
4273 struct nfs4_ol_stateid *swapstp = NULL;
4261 struct nfs4_delegation *dp = NULL; 4274 struct nfs4_delegation *dp = NULL;
4262 __be32 status; 4275 __be32 status;
4263 4276
@@ -4271,7 +4284,9 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
4271 status = nfs4_check_deleg(cl, open, &dp); 4284 status = nfs4_check_deleg(cl, open, &dp);
4272 if (status) 4285 if (status)
4273 goto out; 4286 goto out;
4287 spin_lock(&fp->fi_lock);
4274 stp = nfsd4_find_existing_open(fp, open); 4288 stp = nfsd4_find_existing_open(fp, open);
4289 spin_unlock(&fp->fi_lock);
4275 } else { 4290 } else {
4276 open->op_file = NULL; 4291 open->op_file = NULL;
4277 status = nfserr_bad_stateid; 4292 status = nfserr_bad_stateid;
@@ -4294,7 +4309,19 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
4294 } else { 4309 } else {
4295 stp = open->op_stp; 4310 stp = open->op_stp;
4296 open->op_stp = NULL; 4311 open->op_stp = NULL;
4297 init_open_stateid(stp, fp, open); 4312 swapstp = init_open_stateid(stp, fp, open);
4313 if (swapstp) {
4314 nfs4_put_stid(&stp->st_stid);
4315 stp = swapstp;
4316 down_read(&stp->st_rwsem);
4317 status = nfs4_upgrade_open(rqstp, fp, current_fh,
4318 stp, open);
4319 if (status) {
4320 up_read(&stp->st_rwsem);
4321 goto out;
4322 }
4323 goto upgrade_out;
4324 }
4298 down_read(&stp->st_rwsem); 4325 down_read(&stp->st_rwsem);
4299 status = nfs4_get_vfs_file(rqstp, fp, current_fh, stp, open); 4326 status = nfs4_get_vfs_file(rqstp, fp, current_fh, stp, open);
4300 if (status) { 4327 if (status) {
@@ -4308,6 +4335,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
4308 if (stp->st_clnt_odstate == open->op_odstate) 4335 if (stp->st_clnt_odstate == open->op_odstate)
4309 open->op_odstate = NULL; 4336 open->op_odstate = NULL;
4310 } 4337 }
4338upgrade_out:
4311 nfs4_inc_and_copy_stateid(&open->op_stateid, &stp->st_stid); 4339 nfs4_inc_and_copy_stateid(&open->op_stateid, &stp->st_stid);
4312 up_read(&stp->st_rwsem); 4340 up_read(&stp->st_rwsem);
4313 4341