diff options
author | Andrew Elble <aweits@rit.edu> | 2015-11-05 20:42:43 -0500 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2015-11-10 09:29:45 -0500 |
commit | 7fc0564e3a8d16df096f48c9c6425ba84d945c6e (patch) | |
tree | 1470204738456cc407010bf070efc134e0490141 /fs/nfsd | |
parent | 34ed9872e745fa56f10e9bef2cf3d2336c6c8816 (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.c | 78 |
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 | ||
3395 | static struct nfs4_ol_stateid * | ||
3396 | nfsd4_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 | |||
3395 | static struct nfs4_openowner * | 3416 | static struct nfs4_openowner * |
3396 | alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open, | 3417 | alloc_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 | ||
3426 | static void init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *open) { | 3447 | static struct nfs4_ol_stateid * |
3448 | init_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 | |||
3474 | out_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 | ||
3855 | static struct nfs4_ol_stateid * | ||
3856 | nfsd4_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 | |||
3876 | static inline int nfs4_access_to_access(u32 nfs4_access) | 3888 | static 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 | } |
4338 | upgrade_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 | ||