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 | |
| 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')
| -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 | ||
