]> git.dujemihanovic.xyz Git - linux.git/commitdiff
nfsd: avoid races with wake_up_var()
authorNeilBrown <neilb@suse.de>
Fri, 30 Aug 2024 07:03:17 +0000 (17:03 +1000)
committerChuck Lever <chuck.lever@oracle.com>
Fri, 20 Sep 2024 23:31:03 +0000 (19:31 -0400)
wake_up_var() needs a barrier after the important change is made in the
var and before wake_up_var() is called, else it is possible that a wake
up won't be sent when it should.

In each case here the var is changed in an "atomic" manner, so
smb_mb__after_atomic() is sufficient.

In one case the important change (removing the lease) is performed
*after* the wake_up, which is backwards.  The code survives in part
because the wait_var_event is given a timeout.

This patch adds the required barriers and calls destroy_delegation()
*before* waking any threads waiting for the delegation to be destroyed.

Signed-off-by: NeilBrown <neilb@suse.de>
Reviewed-by: Jeff Layton <jlayton@kernel.org>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
fs/nfsd/nfs4state.c

index 0ea70d615c78d40a21df330c582632e1c56e4dcc..7ade551bc02211b5603cf31a3d743ba5c4058a48 100644 (file)
@@ -4706,6 +4706,7 @@ void nfsd4_cstate_clear_replay(struct nfsd4_compound_state *cstate)
        if (so != NULL) {
                cstate->replay_owner = NULL;
                atomic_set(&so->so_replay.rp_locked, RP_UNLOCKED);
+               smp_mb__after_atomic();
                wake_up_var(&so->so_replay.rp_locked);
                nfs4_put_stateowner(so);
        }
@@ -5006,6 +5007,7 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
         * so tell them to stop waiting.
         */
        atomic_set(&oo->oo_owner.so_replay.rp_locked, RP_UNHASHED);
+       smp_mb__after_atomic();
        wake_up_var(&oo->oo_owner.so_replay.rp_locked);
        wait_event(close_wq, refcount_read(&s->st_stid.sc_count) == 2);
 
@@ -7475,8 +7477,9 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
                goto put_stateid;
 
        trace_nfsd_deleg_return(stateid);
-       wake_up_var(d_inode(cstate->current_fh.fh_dentry));
        destroy_delegation(dp);
+       smp_mb__after_atomic();
+       wake_up_var(d_inode(cstate->current_fh.fh_dentry));
 put_stateid:
        nfs4_put_stid(&dp->dl_stid);
 out: