{
struct nfs_pgio_header *hdr = iocb->hdr;
- nfs_to->nfsd_file_put_local(iocb->localio);
+ nfs_to_nfsd_file_put_local(iocb->localio);
nfs_local_iocb_free(iocb);
nfs_local_hdr_release(hdr, hdr->task.tk_ops);
}
}
out:
if (status != 0) {
- nfs_to->nfsd_file_put_local(localio);
+ nfs_to_nfsd_file_put_local(localio);
hdr->task.tk_status = status;
nfs_local_hdr_release(hdr, call_ops);
}
struct nfs_commit_data *data,
const struct rpc_call_ops *call_ops)
{
- nfs_to->nfsd_file_put_local(localio);
+ nfs_to_nfsd_file_put_local(localio);
call_ops->rpc_call_done(&data->task, data);
call_ops->rpc_release(data);
}
/* We have an implied reference to net thanks to nfsd_serv_try_get */
localio = nfs_to->nfsd_open_local_fh(net, uuid->dom, rpc_clnt,
cred, nfs_fh, fmode);
- if (IS_ERR(localio))
+ if (IS_ERR(localio)) {
+ rcu_read_lock();
nfs_to->nfsd_serv_put(net);
+ rcu_read_unlock();
+ }
return localio;
}
EXPORT_SYMBOL_GPL(nfs_open_local_fh);
* reference to the associated nn->nfsd_serv.
*/
void
-nfsd_file_put_local(struct nfsd_file *nf)
+nfsd_file_put_local(struct nfsd_file *nf) __must_hold(rcu)
{
struct net *net = nf->nf_net;
*
* On successful return, returned nfsd_file will have its nf_net member
* set. Caller (NFS client) is responsible for calling nfsd_serv_put and
- * nfsd_file_put (via nfs_to->nfsd_file_put_local).
+ * nfsd_file_put (via nfs_to_nfsd_file_put_local).
*/
struct nfsd_file *
nfsd_open_local_fh(struct net *net, struct auth_domain *dom,
return 0;
}
-bool nfsd_serv_try_get(struct net *net)
+bool nfsd_serv_try_get(struct net *net) __must_hold(rcu)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
return (nn && percpu_ref_tryget_live(&nn->nfsd_serv_ref));
}
-void nfsd_serv_put(struct net *net)
+void nfsd_serv_put(struct net *net) __must_hold(rcu)
{
struct nfsd_net *nn = net_generic(net, nfsd_net_id);
struct rpc_clnt *, const struct cred *,
const struct nfs_fh *, const fmode_t);
+static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
+{
+ /*
+ * Once reference to nfsd_serv is dropped, NFSD could be
+ * unloaded, so ensure safe return from nfsd_file_put_local()
+ * by always taking RCU.
+ */
+ rcu_read_lock();
+ nfs_to->nfsd_file_put_local(localio);
+ rcu_read_unlock();
+}
+
#else /* CONFIG_NFS_LOCALIO */
static inline void nfsd_localio_ops_init(void)
{
}
+static inline void nfs_to_nfsd_file_put_local(struct nfsd_file *localio)
+{
+}
#endif /* CONFIG_NFS_LOCALIO */
#endif /* __LINUX_NFSLOCALIO_H */