NFS Client Bugfixes for Linux 5.4-rc6
Stable bugfixes: - Fix an RCU lock leak in nfs4_refresh_delegation_stateid() Other fixes: - The TCP back channel mustn't disappear while requests are outstanding - The RDMA back channel mustn't disappear while requests are outstanding - Destroy the back channel when we destroy the host transport - Don't allow a cached open with a revoked delegation -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEnZ5MQTpR7cLU7KEp18tUv7ClQOsFAl28mZ4ACgkQ18tUv7Cl QOvAAxAA5h15NJ8OTOpr/kyWQAHEYWIEWQyTNYT8Gi/TI/YCfauET6kxAKg7ETk+ Bx6+A7Q5OlKSqFZXf2UMMIHPnvH7Ar06uIof6DLjWxA9N/q57SNW0YszQCUhvWjp 4Ry7d9A2yNrCizEk/vTmY5zfFIo2S88AvwkQ6gLCEyfIn5REWQyQnS4SS3vL7MuS E/iWBsXFV2C9/yON+zzZnC0ewMIPbtWA3/CsVI2zDKLsHKvBYOx6n7TbfTtMtc/p mCdgLzdEEZlN0KOCzzMp7ziME8y1qUGX6eo17Jrr0ZBZIM7o5d7sYMA88Apu6dMg MQM5iEEAf//kmHLWzQ6yg8XZEZrXFMOiGKYu0kWt4pVlqi78gemEfE6T8dClsB5m P7jqn/4ntDlJPWPciImsVEzDOHlzlZBIXwZs1JjufeO/hIB570cs80PjeDQP0id8 Cx3yR7Hr1ldKzwSwpWrtIaqEPljJJw0jhxQ7YYlvfbA1Ogd4ek4QU4RuvyW11CRr d/5Oaa7xvjGs63klu8HpZG4NHFby3PPmZT6t4xUcH75MR14S15YCGtaUvPJ7ORck C6tZJAkVtJqSoAjsGuBtTc5qiOdo+hMRghutW4Pd6UpnPYJKwQUtTqp3MbfM4+bA S19tGG6qn+7cl7bW4NJpNy8HfbC7BdS+m7maHpQfGIdMsxKVFoI= =ttcU -----END PGP SIGNATURE----- Merge tag 'nfs-for-5.4-3' of git://git.linux-nfs.org/projects/anna/linux-nfs Pull NFS client bugfixes from Anna Schumaker: "This contains two delegation fixes (with the RCU lock leak fix marked for stable), and three patches to fix destroying the the sunrpc back channel. Stable bugfixes: - Fix an RCU lock leak in nfs4_refresh_delegation_stateid() Other fixes: - The TCP back channel mustn't disappear while requests are outstanding - The RDMA back channel mustn't disappear while requests are outstanding - Destroy the back channel when we destroy the host transport - Don't allow a cached open with a revoked delegation" * tag 'nfs-for-5.4-3' of git://git.linux-nfs.org/projects/anna/linux-nfs: NFS: Fix an RCU lock leak in nfs4_refresh_delegation_stateid() NFSv4: Don't allow a cached open with a revoked delegation SUNRPC: Destroy the back channel when we destroy the host transport SUNRPC: The RDMA back channel mustn't disappear while requests are outstanding SUNRPC: The TCP back channel mustn't disappear while requests are outstanding
This commit is contained in:
commit
372bf6c1c8
|
@ -53,6 +53,16 @@ nfs4_is_valid_delegation(const struct nfs_delegation *delegation,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode)
|
||||||
|
{
|
||||||
|
struct nfs_delegation *delegation;
|
||||||
|
|
||||||
|
delegation = rcu_dereference(NFS_I(inode)->delegation);
|
||||||
|
if (nfs4_is_valid_delegation(delegation, 0))
|
||||||
|
return delegation;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
|
nfs4_do_check_delegation(struct inode *inode, fmode_t flags, bool mark)
|
||||||
{
|
{
|
||||||
|
@ -1181,7 +1191,7 @@ bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode)
|
||||||
if (delegation != NULL &&
|
if (delegation != NULL &&
|
||||||
nfs4_stateid_match_other(dst, &delegation->stateid)) {
|
nfs4_stateid_match_other(dst, &delegation->stateid)) {
|
||||||
dst->seqid = delegation->stateid.seqid;
|
dst->seqid = delegation->stateid.seqid;
|
||||||
return ret;
|
ret = true;
|
||||||
}
|
}
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
out:
|
out:
|
||||||
|
|
|
@ -68,6 +68,7 @@ int nfs4_lock_delegation_recall(struct file_lock *fl, struct nfs4_state *state,
|
||||||
bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, nfs4_stateid *dst, const struct cred **cred);
|
bool nfs4_copy_delegation_stateid(struct inode *inode, fmode_t flags, nfs4_stateid *dst, const struct cred **cred);
|
||||||
bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode);
|
bool nfs4_refresh_delegation_stateid(nfs4_stateid *dst, struct inode *inode);
|
||||||
|
|
||||||
|
struct nfs_delegation *nfs4_get_valid_delegation(const struct inode *inode);
|
||||||
void nfs_mark_delegation_referenced(struct nfs_delegation *delegation);
|
void nfs_mark_delegation_referenced(struct nfs_delegation *delegation);
|
||||||
int nfs4_have_delegation(struct inode *inode, fmode_t flags);
|
int nfs4_have_delegation(struct inode *inode, fmode_t flags);
|
||||||
int nfs4_check_delegation(struct inode *inode, fmode_t flags);
|
int nfs4_check_delegation(struct inode *inode, fmode_t flags);
|
||||||
|
|
|
@ -1440,8 +1440,6 @@ static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode,
|
||||||
return 0;
|
return 0;
|
||||||
if ((delegation->type & fmode) != fmode)
|
if ((delegation->type & fmode) != fmode)
|
||||||
return 0;
|
return 0;
|
||||||
if (test_bit(NFS_DELEGATION_RETURNING, &delegation->flags))
|
|
||||||
return 0;
|
|
||||||
switch (claim) {
|
switch (claim) {
|
||||||
case NFS4_OPEN_CLAIM_NULL:
|
case NFS4_OPEN_CLAIM_NULL:
|
||||||
case NFS4_OPEN_CLAIM_FH:
|
case NFS4_OPEN_CLAIM_FH:
|
||||||
|
@ -1810,7 +1808,6 @@ static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmo
|
||||||
static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
|
static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
|
||||||
{
|
{
|
||||||
struct nfs4_state *state = opendata->state;
|
struct nfs4_state *state = opendata->state;
|
||||||
struct nfs_inode *nfsi = NFS_I(state->inode);
|
|
||||||
struct nfs_delegation *delegation;
|
struct nfs_delegation *delegation;
|
||||||
int open_mode = opendata->o_arg.open_flags;
|
int open_mode = opendata->o_arg.open_flags;
|
||||||
fmode_t fmode = opendata->o_arg.fmode;
|
fmode_t fmode = opendata->o_arg.fmode;
|
||||||
|
@ -1827,7 +1824,7 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
|
||||||
}
|
}
|
||||||
spin_unlock(&state->owner->so_lock);
|
spin_unlock(&state->owner->so_lock);
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
delegation = rcu_dereference(nfsi->delegation);
|
delegation = nfs4_get_valid_delegation(state->inode);
|
||||||
if (!can_open_delegated(delegation, fmode, claim)) {
|
if (!can_open_delegated(delegation, fmode, claim)) {
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
break;
|
break;
|
||||||
|
@ -2371,7 +2368,7 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
|
||||||
data->o_arg.open_flags, claim))
|
data->o_arg.open_flags, claim))
|
||||||
goto out_no_action;
|
goto out_no_action;
|
||||||
rcu_read_lock();
|
rcu_read_lock();
|
||||||
delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
|
delegation = nfs4_get_valid_delegation(data->state->inode);
|
||||||
if (can_open_delegated(delegation, data->o_arg.fmode, claim))
|
if (can_open_delegated(delegation, data->o_arg.fmode, claim))
|
||||||
goto unlock_no_action;
|
goto unlock_no_action;
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
|
@ -64,6 +64,11 @@ static inline int xprt_setup_backchannel(struct rpc_xprt *xprt,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void xprt_destroy_backchannel(struct rpc_xprt *xprt,
|
||||||
|
unsigned int max_reqs)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool svc_is_backchannel(const struct svc_rqst *rqstp)
|
static inline bool svc_is_backchannel(const struct svc_rqst *rqstp)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -220,7 +220,7 @@ void xprt_destroy_bc(struct rpc_xprt *xprt, unsigned int max_reqs)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
spin_lock_bh(&xprt->bc_pa_lock);
|
spin_lock_bh(&xprt->bc_pa_lock);
|
||||||
xprt->bc_alloc_max -= max_reqs;
|
xprt->bc_alloc_max -= min(max_reqs, xprt->bc_alloc_max);
|
||||||
list_for_each_entry_safe(req, tmp, &xprt->bc_pa_list, rq_bc_pa_list) {
|
list_for_each_entry_safe(req, tmp, &xprt->bc_pa_list, rq_bc_pa_list) {
|
||||||
dprintk("RPC: req=%p\n", req);
|
dprintk("RPC: req=%p\n", req);
|
||||||
list_del(&req->rq_bc_pa_list);
|
list_del(&req->rq_bc_pa_list);
|
||||||
|
@ -307,8 +307,8 @@ void xprt_free_bc_rqst(struct rpc_rqst *req)
|
||||||
*/
|
*/
|
||||||
dprintk("RPC: Last session removed req=%p\n", req);
|
dprintk("RPC: Last session removed req=%p\n", req);
|
||||||
xprt_free_allocation(req);
|
xprt_free_allocation(req);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
xprt_put(xprt);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -339,7 +339,7 @@ found:
|
||||||
spin_unlock(&xprt->bc_pa_lock);
|
spin_unlock(&xprt->bc_pa_lock);
|
||||||
if (new) {
|
if (new) {
|
||||||
if (req != new)
|
if (req != new)
|
||||||
xprt_free_bc_rqst(new);
|
xprt_free_allocation(new);
|
||||||
break;
|
break;
|
||||||
} else if (req)
|
} else if (req)
|
||||||
break;
|
break;
|
||||||
|
@ -368,6 +368,7 @@ void xprt_complete_bc_request(struct rpc_rqst *req, uint32_t copied)
|
||||||
set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
|
set_bit(RPC_BC_PA_IN_USE, &req->rq_bc_pa_state);
|
||||||
|
|
||||||
dprintk("RPC: add callback request to list\n");
|
dprintk("RPC: add callback request to list\n");
|
||||||
|
xprt_get(xprt);
|
||||||
spin_lock(&bc_serv->sv_cb_lock);
|
spin_lock(&bc_serv->sv_cb_lock);
|
||||||
list_add(&req->rq_bc_list, &bc_serv->sv_cb_list);
|
list_add(&req->rq_bc_list, &bc_serv->sv_cb_list);
|
||||||
wake_up(&bc_serv->sv_cb_waitq);
|
wake_up(&bc_serv->sv_cb_waitq);
|
||||||
|
|
|
@ -1942,6 +1942,11 @@ static void xprt_destroy_cb(struct work_struct *work)
|
||||||
rpc_destroy_wait_queue(&xprt->sending);
|
rpc_destroy_wait_queue(&xprt->sending);
|
||||||
rpc_destroy_wait_queue(&xprt->backlog);
|
rpc_destroy_wait_queue(&xprt->backlog);
|
||||||
kfree(xprt->servername);
|
kfree(xprt->servername);
|
||||||
|
/*
|
||||||
|
* Destroy any existing back channel
|
||||||
|
*/
|
||||||
|
xprt_destroy_backchannel(xprt, UINT_MAX);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tear down transport state and free the rpc_xprt
|
* Tear down transport state and free the rpc_xprt
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -163,6 +163,7 @@ void xprt_rdma_bc_free_rqst(struct rpc_rqst *rqst)
|
||||||
spin_lock(&xprt->bc_pa_lock);
|
spin_lock(&xprt->bc_pa_lock);
|
||||||
list_add_tail(&rqst->rq_bc_pa_list, &xprt->bc_pa_list);
|
list_add_tail(&rqst->rq_bc_pa_list, &xprt->bc_pa_list);
|
||||||
spin_unlock(&xprt->bc_pa_lock);
|
spin_unlock(&xprt->bc_pa_lock);
|
||||||
|
xprt_put(xprt);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct rpc_rqst *rpcrdma_bc_rqst_get(struct rpcrdma_xprt *r_xprt)
|
static struct rpc_rqst *rpcrdma_bc_rqst_get(struct rpcrdma_xprt *r_xprt)
|
||||||
|
@ -259,6 +260,7 @@ void rpcrdma_bc_receive_call(struct rpcrdma_xprt *r_xprt,
|
||||||
|
|
||||||
/* Queue rqst for ULP's callback service */
|
/* Queue rqst for ULP's callback service */
|
||||||
bc_serv = xprt->bc_serv;
|
bc_serv = xprt->bc_serv;
|
||||||
|
xprt_get(xprt);
|
||||||
spin_lock(&bc_serv->sv_cb_lock);
|
spin_lock(&bc_serv->sv_cb_lock);
|
||||||
list_add(&rqst->rq_bc_list, &bc_serv->sv_cb_list);
|
list_add(&rqst->rq_bc_list, &bc_serv->sv_cb_list);
|
||||||
spin_unlock(&bc_serv->sv_cb_lock);
|
spin_unlock(&bc_serv->sv_cb_lock);
|
||||||
|
|
Loading…
Reference in New Issue