| From feb9dad5209280085d5b0c094fa67e7a8d75c81a Mon Sep 17 00:00:00 2001 |
| From: Oleg Drokin <green@linuxhacker.ru> |
| Date: Tue, 14 Jun 2016 23:28:04 -0400 |
| Subject: nfsd: Always lock state exclusively. |
| |
| From: Oleg Drokin <green@linuxhacker.ru> |
| |
| commit feb9dad5209280085d5b0c094fa67e7a8d75c81a upstream. |
| |
| It used to be the case that state had an rwlock that was locked for write |
| by downgrades, but for read for upgrades (opens). Well, the problem is |
| if there are two competing opens for the same state, they step on |
| each other toes potentially leading to leaking file descriptors |
| from the state structure, since access mode is a bitmap only set once. |
| |
| Signed-off-by: Oleg Drokin <green@linuxhacker.ru> |
| Signed-off-by: J. Bruce Fields <bfields@redhat.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/nfsd/nfs4state.c | 40 ++++++++++++++++++++-------------------- |
| fs/nfsd/state.h | 2 +- |
| 2 files changed, 21 insertions(+), 21 deletions(-) |
| |
| --- a/fs/nfsd/nfs4state.c |
| +++ b/fs/nfsd/nfs4state.c |
| @@ -3467,7 +3467,7 @@ init_open_stateid(struct nfs4_ol_stateid |
| stp->st_access_bmap = 0; |
| stp->st_deny_bmap = 0; |
| stp->st_openstp = NULL; |
| - init_rwsem(&stp->st_rwsem); |
| + mutex_init(&stp->st_mutex); |
| list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids); |
| list_add(&stp->st_perfile, &fp->fi_stateids); |
| |
| @@ -4300,10 +4300,10 @@ nfsd4_process_open2(struct svc_rqst *rqs |
| */ |
| if (stp) { |
| /* Stateid was found, this is an OPEN upgrade */ |
| - down_read(&stp->st_rwsem); |
| + mutex_lock(&stp->st_mutex); |
| status = nfs4_upgrade_open(rqstp, fp, current_fh, stp, open); |
| if (status) { |
| - up_read(&stp->st_rwsem); |
| + mutex_unlock(&stp->st_mutex); |
| goto out; |
| } |
| } else { |
| @@ -4313,19 +4313,19 @@ nfsd4_process_open2(struct svc_rqst *rqs |
| if (swapstp) { |
| nfs4_put_stid(&stp->st_stid); |
| stp = swapstp; |
| - down_read(&stp->st_rwsem); |
| + mutex_lock(&stp->st_mutex); |
| status = nfs4_upgrade_open(rqstp, fp, current_fh, |
| stp, open); |
| if (status) { |
| - up_read(&stp->st_rwsem); |
| + mutex_unlock(&stp->st_mutex); |
| goto out; |
| } |
| goto upgrade_out; |
| } |
| - down_read(&stp->st_rwsem); |
| + mutex_lock(&stp->st_mutex); |
| status = nfs4_get_vfs_file(rqstp, fp, current_fh, stp, open); |
| if (status) { |
| - up_read(&stp->st_rwsem); |
| + mutex_unlock(&stp->st_mutex); |
| release_open_stateid(stp); |
| goto out; |
| } |
| @@ -4337,7 +4337,7 @@ nfsd4_process_open2(struct svc_rqst *rqs |
| } |
| upgrade_out: |
| nfs4_inc_and_copy_stateid(&open->op_stateid, &stp->st_stid); |
| - up_read(&stp->st_rwsem); |
| + mutex_unlock(&stp->st_mutex); |
| |
| if (nfsd4_has_session(&resp->cstate)) { |
| if (open->op_deleg_want & NFS4_SHARE_WANT_NO_DELEG) { |
| @@ -4950,12 +4950,12 @@ static __be32 nfs4_seqid_op_checks(struc |
| * revoked delegations are kept only for free_stateid. |
| */ |
| return nfserr_bad_stateid; |
| - down_write(&stp->st_rwsem); |
| + mutex_lock(&stp->st_mutex); |
| status = check_stateid_generation(stateid, &stp->st_stid.sc_stateid, nfsd4_has_session(cstate)); |
| if (status == nfs_ok) |
| status = nfs4_check_fh(current_fh, &stp->st_stid); |
| if (status != nfs_ok) |
| - up_write(&stp->st_rwsem); |
| + mutex_unlock(&stp->st_mutex); |
| return status; |
| } |
| |
| @@ -5003,7 +5003,7 @@ static __be32 nfs4_preprocess_confirmed_ |
| return status; |
| oo = openowner(stp->st_stateowner); |
| if (!(oo->oo_flags & NFS4_OO_CONFIRMED)) { |
| - up_write(&stp->st_rwsem); |
| + mutex_unlock(&stp->st_mutex); |
| nfs4_put_stid(&stp->st_stid); |
| return nfserr_bad_stateid; |
| } |
| @@ -5035,12 +5035,12 @@ nfsd4_open_confirm(struct svc_rqst *rqst |
| oo = openowner(stp->st_stateowner); |
| status = nfserr_bad_stateid; |
| if (oo->oo_flags & NFS4_OO_CONFIRMED) { |
| - up_write(&stp->st_rwsem); |
| + mutex_unlock(&stp->st_mutex); |
| goto put_stateid; |
| } |
| oo->oo_flags |= NFS4_OO_CONFIRMED; |
| nfs4_inc_and_copy_stateid(&oc->oc_resp_stateid, &stp->st_stid); |
| - up_write(&stp->st_rwsem); |
| + mutex_unlock(&stp->st_mutex); |
| dprintk("NFSD: %s: success, seqid=%d stateid=" STATEID_FMT "\n", |
| __func__, oc->oc_seqid, STATEID_VAL(&stp->st_stid.sc_stateid)); |
| |
| @@ -5116,7 +5116,7 @@ nfsd4_open_downgrade(struct svc_rqst *rq |
| nfs4_inc_and_copy_stateid(&od->od_stateid, &stp->st_stid); |
| status = nfs_ok; |
| put_stateid: |
| - up_write(&stp->st_rwsem); |
| + mutex_unlock(&stp->st_mutex); |
| nfs4_put_stid(&stp->st_stid); |
| out: |
| nfsd4_bump_seqid(cstate, status); |
| @@ -5169,7 +5169,7 @@ nfsd4_close(struct svc_rqst *rqstp, stru |
| if (status) |
| goto out; |
| nfs4_inc_and_copy_stateid(&close->cl_stateid, &stp->st_stid); |
| - up_write(&stp->st_rwsem); |
| + mutex_unlock(&stp->st_mutex); |
| |
| nfsd4_close_open_stateid(stp); |
| |
| @@ -5395,7 +5395,7 @@ init_lock_stateid(struct nfs4_ol_stateid |
| stp->st_access_bmap = 0; |
| stp->st_deny_bmap = open_stp->st_deny_bmap; |
| stp->st_openstp = open_stp; |
| - init_rwsem(&stp->st_rwsem); |
| + mutex_init(&stp->st_mutex); |
| list_add(&stp->st_locks, &open_stp->st_locks); |
| list_add(&stp->st_perstateowner, &lo->lo_owner.so_stateids); |
| spin_lock(&fp->fi_lock); |
| @@ -5564,7 +5564,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struc |
| &open_stp, nn); |
| if (status) |
| goto out; |
| - up_write(&open_stp->st_rwsem); |
| + mutex_unlock(&open_stp->st_mutex); |
| open_sop = openowner(open_stp->st_stateowner); |
| status = nfserr_bad_stateid; |
| if (!same_clid(&open_sop->oo_owner.so_client->cl_clientid, |
| @@ -5573,7 +5573,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struc |
| status = lookup_or_create_lock_state(cstate, open_stp, lock, |
| &lock_stp, &new); |
| if (status == nfs_ok) |
| - down_write(&lock_stp->st_rwsem); |
| + mutex_lock(&lock_stp->st_mutex); |
| } else { |
| status = nfs4_preprocess_seqid_op(cstate, |
| lock->lk_old_lock_seqid, |
| @@ -5677,7 +5677,7 @@ out: |
| seqid_mutating_err(ntohl(status))) |
| lock_sop->lo_owner.so_seqid++; |
| |
| - up_write(&lock_stp->st_rwsem); |
| + mutex_unlock(&lock_stp->st_mutex); |
| |
| /* |
| * If this is a new, never-before-used stateid, and we are |
| @@ -5847,7 +5847,7 @@ nfsd4_locku(struct svc_rqst *rqstp, stru |
| fput: |
| fput(filp); |
| put_stateid: |
| - up_write(&stp->st_rwsem); |
| + mutex_unlock(&stp->st_mutex); |
| nfs4_put_stid(&stp->st_stid); |
| out: |
| nfsd4_bump_seqid(cstate, status); |
| --- a/fs/nfsd/state.h |
| +++ b/fs/nfsd/state.h |
| @@ -535,7 +535,7 @@ struct nfs4_ol_stateid { |
| unsigned char st_access_bmap; |
| unsigned char st_deny_bmap; |
| struct nfs4_ol_stateid *st_openstp; |
| - struct rw_semaphore st_rwsem; |
| + struct mutex st_mutex; |
| }; |
| |
| static inline struct nfs4_ol_stateid *openlockstateid(struct nfs4_stid *s) |