Oleg Drokin found and fixed races in the nfsd4 state code that go back
to the big nfs4_lock_state removal around 3.17 (but that were also probably hard to reproduce before client changes in 3.20 allowed the client to perform parallel opens). Also fix a 4.1 backchannel crash due to rpc multipath changes in 4.6. Trond acked the client-side rpc fixes going through my tree. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJXYsiKAAoJECebzXlCjuG+PK8P/jGBS+h7Zf4GWOOsWN5cbEs/ 8VTn83KXFp/feFhGikUIcAZQJRynDK+tD9Vh2FznC2zKDTLFPS0mAvL3tZyQhUO2 nEWaCUOFR+sB3aTPlMGwxbGc7NHNQg1hKqKgqcLEEqtozxhFQye3WW0MZNfFCiUZ qpq2tK1OGGhJVIp7wWSa8+B2nGFMuasPaGM2OVJrebip49yTG/tT3rwKxKMoB8kS i8BwNejoP1KRD6LqvpgdV1ESzkdyokDxKXCrdY/j2lMdp2YRe+cWmX239ojjvm8G n9Ow8DYCefuiKiF6iCLZfxpX8dcmVJvT6g+k+9V63A4YCyuGhy/CneA3MO4QyLhq yfe2zviJ2kZVz+1Ih3v9kD7ZkyK1hjrxXx/VPrI5CBIXE5eVXin2ZDvTCSoV491h g1zscPc9Thgk6gKXsvkaVOXxLHBoUzXeSRbNqVXXZfjl+s4TXLNJ0lcaBYkzh74/ SypiFeNHjsjNpJYz5GptlbMUpaEoeyH0Y+OiH8d5Jf8hCcQ+CLjKgKSuCH5zrypt Lx3U5QWHTT3IXH4QS/njcTSfSDu7BUip4RTLzw6C/ZJf7hd6SS4Xv72J6ZmeDSmg 146MpAYty8HB04KQWpYx0DGI7UEPlubfRHSF9XzsSitbRtNGr6xvIug8fkKBlXDB aHtr+/gI7UvrmnXnlGdD =aNkl -----END PGP SIGNATURE----- Merge tag 'nfsd-4.7-1' of git://linux-nfs.org/~bfields/linux Pull nfsd bugfixes from Bruce Fields: "Oleg Drokin found and fixed races in the nfsd4 state code that go back to the big nfs4_lock_state removal around 3.17 (but that were also probably hard to reproduce before client changes in 3.20 allowed the client to perform parallel opens). Also fix a 4.1 backchannel crash due to rpc multipath changes in 4.6. Trond acked the client-side rpc fixes going through my tree" * tag 'nfsd-4.7-1' of git://linux-nfs.org/~bfields/linux: nfsd: Make init_open_stateid() a bit more whole nfsd: Extend the mutex holding region around in nfsd4_process_open2() nfsd: Always lock state exclusively. rpc: share one xps between all backchannels nfsd4/rpc: move backchannel create logic into rpc code SUNRPC: fix xprt leak on xps allocation failure nfsd: Fix NFSD_MDS_PR_KEY on 32-bit by adding ULL postfix
This commit is contained in:
commit
41ef72181a
@ -290,7 +290,7 @@ out_free_buf:
|
||||
return error;
|
||||
}
|
||||
|
||||
#define NFSD_MDS_PR_KEY 0x0100000000000000
|
||||
#define NFSD_MDS_PR_KEY 0x0100000000000000ULL
|
||||
|
||||
/*
|
||||
* We use the client ID as a unique key for the reservations.
|
||||
|
@ -710,22 +710,6 @@ static struct rpc_cred *get_backchannel_cred(struct nfs4_client *clp, struct rpc
|
||||
}
|
||||
}
|
||||
|
||||
static struct rpc_clnt *create_backchannel_client(struct rpc_create_args *args)
|
||||
{
|
||||
struct rpc_xprt *xprt;
|
||||
|
||||
if (args->protocol != XPRT_TRANSPORT_BC_TCP)
|
||||
return rpc_create(args);
|
||||
|
||||
xprt = args->bc_xprt->xpt_bc_xprt;
|
||||
if (xprt) {
|
||||
xprt_get(xprt);
|
||||
return rpc_create_xprt(args, xprt);
|
||||
}
|
||||
|
||||
return rpc_create(args);
|
||||
}
|
||||
|
||||
static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *conn, struct nfsd4_session *ses)
|
||||
{
|
||||
int maxtime = max_cb_time(clp->net);
|
||||
@ -768,7 +752,7 @@ static int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *c
|
||||
args.authflavor = ses->se_cb_sec.flavor;
|
||||
}
|
||||
/* Create RPC client */
|
||||
client = create_backchannel_client(&args);
|
||||
client = rpc_create(&args);
|
||||
if (IS_ERR(client)) {
|
||||
dprintk("NFSD: couldn't create callback client: %ld\n",
|
||||
PTR_ERR(client));
|
||||
|
@ -3480,12 +3480,17 @@ alloc_init_open_stateowner(unsigned int strhashval, struct nfsd4_open *open,
|
||||
}
|
||||
|
||||
static struct nfs4_ol_stateid *
|
||||
init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
|
||||
struct nfsd4_open *open)
|
||||
init_open_stateid(struct nfs4_file *fp, struct nfsd4_open *open)
|
||||
{
|
||||
|
||||
struct nfs4_openowner *oo = open->op_openowner;
|
||||
struct nfs4_ol_stateid *retstp = NULL;
|
||||
struct nfs4_ol_stateid *stp;
|
||||
|
||||
stp = open->op_stp;
|
||||
/* We are moving these outside of the spinlocks to avoid the warnings */
|
||||
mutex_init(&stp->st_mutex);
|
||||
mutex_lock(&stp->st_mutex);
|
||||
|
||||
spin_lock(&oo->oo_owner.so_client->cl_lock);
|
||||
spin_lock(&fp->fi_lock);
|
||||
@ -3493,6 +3498,8 @@ init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
|
||||
retstp = nfsd4_find_existing_open(fp, open);
|
||||
if (retstp)
|
||||
goto out_unlock;
|
||||
|
||||
open->op_stp = NULL;
|
||||
atomic_inc(&stp->st_stid.sc_count);
|
||||
stp->st_stid.sc_type = NFS4_OPEN_STID;
|
||||
INIT_LIST_HEAD(&stp->st_locks);
|
||||
@ -3502,14 +3509,19 @@ init_open_stateid(struct nfs4_ol_stateid *stp, struct nfs4_file *fp,
|
||||
stp->st_access_bmap = 0;
|
||||
stp->st_deny_bmap = 0;
|
||||
stp->st_openstp = NULL;
|
||||
init_rwsem(&stp->st_rwsem);
|
||||
list_add(&stp->st_perstateowner, &oo->oo_owner.so_stateids);
|
||||
list_add(&stp->st_perfile, &fp->fi_stateids);
|
||||
|
||||
out_unlock:
|
||||
spin_unlock(&fp->fi_lock);
|
||||
spin_unlock(&oo->oo_owner.so_client->cl_lock);
|
||||
return retstp;
|
||||
if (retstp) {
|
||||
mutex_lock(&retstp->st_mutex);
|
||||
/* To keep mutex tracking happy */
|
||||
mutex_unlock(&stp->st_mutex);
|
||||
stp = retstp;
|
||||
}
|
||||
return stp;
|
||||
}
|
||||
|
||||
/*
|
||||
@ -4305,7 +4317,6 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
|
||||
struct nfs4_client *cl = open->op_openowner->oo_owner.so_client;
|
||||
struct nfs4_file *fp = NULL;
|
||||
struct nfs4_ol_stateid *stp = NULL;
|
||||
struct nfs4_ol_stateid *swapstp = NULL;
|
||||
struct nfs4_delegation *dp = NULL;
|
||||
__be32 status;
|
||||
|
||||
@ -4335,32 +4346,28 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
|
||||
*/
|
||||
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 {
|
||||
stp = open->op_stp;
|
||||
open->op_stp = NULL;
|
||||
swapstp = init_open_stateid(stp, fp, open);
|
||||
if (swapstp) {
|
||||
nfs4_put_stid(&stp->st_stid);
|
||||
stp = swapstp;
|
||||
down_read(&stp->st_rwsem);
|
||||
/* stp is returned locked. */
|
||||
stp = init_open_stateid(fp, open);
|
||||
/* See if we lost the race to some other thread */
|
||||
if (stp->st_access_bmap != 0) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
@ -4372,7 +4379,7 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf
|
||||
}
|
||||
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) {
|
||||
@ -4977,12 +4984,12 @@ static __be32 nfs4_seqid_op_checks(struct nfsd4_compound_state *cstate, stateid_
|
||||
* 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;
|
||||
}
|
||||
|
||||
@ -5030,7 +5037,7 @@ static __be32 nfs4_preprocess_confirmed_seqid_op(struct nfsd4_compound_state *cs
|
||||
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;
|
||||
}
|
||||
@ -5062,12 +5069,12 @@ nfsd4_open_confirm(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
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));
|
||||
|
||||
@ -5143,7 +5150,7 @@ nfsd4_open_downgrade(struct svc_rqst *rqstp,
|
||||
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);
|
||||
@ -5196,7 +5203,7 @@ nfsd4_close(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
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);
|
||||
|
||||
@ -5422,7 +5429,7 @@ init_lock_stateid(struct nfs4_ol_stateid *stp, struct nfs4_lockowner *lo,
|
||||
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);
|
||||
@ -5591,7 +5598,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
&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,
|
||||
@ -5600,7 +5607,7 @@ nfsd4_lock(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
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,
|
||||
@ -5704,7 +5711,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
|
||||
@ -5874,7 +5881,7 @@ nfsd4_locku(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
|
||||
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);
|
||||
|
@ -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)
|
||||
|
@ -137,8 +137,6 @@ struct rpc_create_args {
|
||||
#define RPC_CLNT_CREATE_NO_RETRANS_TIMEOUT (1UL << 9)
|
||||
|
||||
struct rpc_clnt *rpc_create(struct rpc_create_args *args);
|
||||
struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
|
||||
struct rpc_xprt *xprt);
|
||||
struct rpc_clnt *rpc_bind_new_program(struct rpc_clnt *,
|
||||
const struct rpc_program *, u32);
|
||||
struct rpc_clnt *rpc_clone_client(struct rpc_clnt *);
|
||||
|
@ -84,6 +84,7 @@ struct svc_xprt {
|
||||
|
||||
struct net *xpt_net;
|
||||
struct rpc_xprt *xpt_bc_xprt; /* NFSv4.1 backchannel */
|
||||
struct rpc_xprt_switch *xpt_bc_xps; /* NFSv4.1 backchannel */
|
||||
};
|
||||
|
||||
static inline void unregister_xpt_user(struct svc_xprt *xpt, struct svc_xpt_user *u)
|
||||
|
@ -297,6 +297,7 @@ struct xprt_create {
|
||||
size_t addrlen;
|
||||
const char *servername;
|
||||
struct svc_xprt *bc_xprt; /* NFSv4.1 backchannel */
|
||||
struct rpc_xprt_switch *bc_xps;
|
||||
unsigned int flags;
|
||||
};
|
||||
|
||||
|
@ -446,16 +446,27 @@ out_no_rpciod:
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
|
||||
static struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
|
||||
struct rpc_xprt *xprt)
|
||||
{
|
||||
struct rpc_clnt *clnt = NULL;
|
||||
struct rpc_xprt_switch *xps;
|
||||
|
||||
xps = xprt_switch_alloc(xprt, GFP_KERNEL);
|
||||
if (xps == NULL)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (args->bc_xprt && args->bc_xprt->xpt_bc_xps) {
|
||||
WARN_ON(args->protocol != XPRT_TRANSPORT_BC_TCP);
|
||||
xps = args->bc_xprt->xpt_bc_xps;
|
||||
xprt_switch_get(xps);
|
||||
} else {
|
||||
xps = xprt_switch_alloc(xprt, GFP_KERNEL);
|
||||
if (xps == NULL) {
|
||||
xprt_put(xprt);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
if (xprt->bc_xprt) {
|
||||
xprt_switch_get(xps);
|
||||
xprt->bc_xprt->xpt_bc_xps = xps;
|
||||
}
|
||||
}
|
||||
clnt = rpc_new_client(args, xps, xprt, NULL);
|
||||
if (IS_ERR(clnt))
|
||||
return clnt;
|
||||
@ -483,7 +494,6 @@ struct rpc_clnt *rpc_create_xprt(struct rpc_create_args *args,
|
||||
|
||||
return clnt;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(rpc_create_xprt);
|
||||
|
||||
/**
|
||||
* rpc_create - create an RPC client and transport with one call
|
||||
@ -509,6 +519,15 @@ struct rpc_clnt *rpc_create(struct rpc_create_args *args)
|
||||
};
|
||||
char servername[48];
|
||||
|
||||
if (args->bc_xprt) {
|
||||
WARN_ON(args->protocol != XPRT_TRANSPORT_BC_TCP);
|
||||
xprt = args->bc_xprt->xpt_bc_xprt;
|
||||
if (xprt) {
|
||||
xprt_get(xprt);
|
||||
return rpc_create_xprt(args, xprt);
|
||||
}
|
||||
}
|
||||
|
||||
if (args->flags & RPC_CLNT_CREATE_INFINITE_SLOTS)
|
||||
xprtargs.flags |= XPRT_CREATE_INFINITE_SLOTS;
|
||||
if (args->flags & RPC_CLNT_CREATE_NO_IDLE_TIMEOUT)
|
||||
|
@ -136,6 +136,8 @@ static void svc_xprt_free(struct kref *kref)
|
||||
/* See comment on corresponding get in xs_setup_bc_tcp(): */
|
||||
if (xprt->xpt_bc_xprt)
|
||||
xprt_put(xprt->xpt_bc_xprt);
|
||||
if (xprt->xpt_bc_xps)
|
||||
xprt_switch_put(xprt->xpt_bc_xps);
|
||||
xprt->xpt_ops->xpo_free(xprt);
|
||||
module_put(owner);
|
||||
}
|
||||
|
@ -3057,6 +3057,7 @@ static struct rpc_xprt *xs_setup_bc_tcp(struct xprt_create *args)
|
||||
return xprt;
|
||||
|
||||
args->bc_xprt->xpt_bc_xprt = NULL;
|
||||
args->bc_xprt->xpt_bc_xps = NULL;
|
||||
xprt_put(xprt);
|
||||
ret = ERR_PTR(-EINVAL);
|
||||
out_err:
|
||||
|
Loading…
Reference in New Issue
Block a user