diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c index 2f84c612838c4bb3d6b1beee645f7709afa2f99a..2d7dd9b65a8c9434b439dc087a0071389ec333af 100644 --- a/fs/nfs/callback_xdr.c +++ b/fs/nfs/callback_xdr.c @@ -952,7 +952,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp) cps.clp = nfs4_find_client_ident(SVC_NET(rqstp), hdr_arg.cb_ident); if (!cps.clp || !check_gss_callback_principal(cps.clp, rqstp)) { if (cps.clp) - nfs_put_client(cps.clp); + nfs_async_put_client(cps.clp); goto out_invalidcred; } } @@ -962,7 +962,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp) hdr_res.tag = hdr_arg.tag; if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0) { if (cps.clp) - nfs_put_client(cps.clp); + nfs_async_put_client(cps.clp); return rpc_system_err; } while (status == 0 && nops != hdr_arg.nops) { @@ -982,7 +982,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp) *hdr_res.status = status; *hdr_res.nops = htonl(nops); nfs4_cb_free_slot(&cps); - nfs_put_client(cps.clp); + nfs_async_put_client(cps.clp); return rpc_success; out_invalidcred: diff --git a/fs/nfs/client.c b/fs/nfs/client.c index 7d02dc52209da626f3a46b210d2f39b0ce774658..a05ee74d64437a0701efe6c3ae349abf2f2f5ea7 100644 --- a/fs/nfs/client.c +++ b/fs/nfs/client.c @@ -283,6 +283,39 @@ void nfs_put_client(struct nfs_client *clp) } EXPORT_SYMBOL_GPL(nfs_put_client); +static void nfs_free_client_work(struct work_struct *work) +{ + struct nfs_client *clp = + container_of(work, struct nfs_client, free_work); + + clp->rpc_ops->free_client(clp); +} + +/* + * Similar to nfs_put_client, but call free_client with async mode + */ +void nfs_async_put_client(struct nfs_client *clp) +{ + struct nfs_net *nn; + + if (!clp) + return; + + nn = net_generic(clp->cl_net, nfs_net_id); + + if (refcount_dec_and_lock(&clp->cl_count, &nn->nfs_client_lock)) { + list_del(&clp->cl_share_link); + nfs_cb_idr_remove_locked(clp); + spin_unlock(&nn->nfs_client_lock); + + WARN_ON_ONCE(!list_empty(&clp->cl_superblocks)); + + INIT_WORK(&clp->free_work, nfs_free_client_work); + queue_work(nfsiod_workqueue, &clp->free_work); + } +} +EXPORT_SYMBOL_GPL(nfs_async_put_client); + /* * Find an nfs_client on the list that matches the initialisation data * that is supplied. diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h index ad938771813cadf2f13e151a79d79f9df6ecf5f6..44ca5300e6e2f1fecb5abbf254a73dd509d8594c 100644 --- a/fs/nfs/internal.h +++ b/fs/nfs/internal.h @@ -180,6 +180,7 @@ void nfs_server_copy_userdata(struct nfs_server *, struct nfs_server *); extern void nfs_cleanup_cb_ident_idr(struct net *); extern void nfs_put_client(struct nfs_client *); +extern void nfs_async_put_client(struct nfs_client *clp); extern void nfs_free_client(struct nfs_client *); extern struct nfs_client *nfs4_find_client_ident(struct net *, int); extern struct nfs_client * diff --git a/include/linux/nfs_fs_sb.h b/include/linux/nfs_fs_sb.h index 7023ae64e3d7c5bad56f81b8146e9f7c491ad359..14a40789be5d49530d224cf10cda2eb5b7a9a7b7 100644 --- a/include/linux/nfs_fs_sb.h +++ b/include/linux/nfs_fs_sb.h @@ -52,6 +52,7 @@ struct nfs_client { char * cl_acceptor; /* GSSAPI acceptor name */ struct list_head cl_share_link; /* link in global client list */ struct list_head cl_superblocks; /* List of nfs_server structs */ + struct work_struct free_work; struct rpc_clnt * cl_rpcclient; const struct nfs_rpc_ops *rpc_ops; /* NFS protocol vector */