|
@@ -17,6 +17,9 @@
|
|
|
#include <linux/freezer.h>
|
|
|
#include <linux/kthread.h>
|
|
|
#include <linux/sunrpc/svcauth_gss.h>
|
|
|
+#if defined(CONFIG_NFS_V4_1)
|
|
|
+#include <linux/sunrpc/bc_xprt.h>
|
|
|
+#endif
|
|
|
|
|
|
#include <net/inet_sock.h>
|
|
|
|
|
@@ -28,6 +31,7 @@
|
|
|
|
|
|
struct nfs_callback_data {
|
|
|
unsigned int users;
|
|
|
+ struct svc_serv *serv;
|
|
|
struct svc_rqst *rqst;
|
|
|
struct task_struct *task;
|
|
|
};
|
|
@@ -131,6 +135,99 @@ out_err:
|
|
|
return ERR_PTR(ret);
|
|
|
}
|
|
|
|
|
|
+#if defined(CONFIG_NFS_V4_1)
|
|
|
+/*
|
|
|
+ * The callback service for NFSv4.1 callbacks
|
|
|
+ */
|
|
|
+static int
|
|
|
+nfs41_callback_svc(void *vrqstp)
|
|
|
+{
|
|
|
+ struct svc_rqst *rqstp = vrqstp;
|
|
|
+ struct svc_serv *serv = rqstp->rq_server;
|
|
|
+ struct rpc_rqst *req;
|
|
|
+ int error;
|
|
|
+ DEFINE_WAIT(wq);
|
|
|
+
|
|
|
+ set_freezable();
|
|
|
+
|
|
|
+ /*
|
|
|
+ * FIXME: do we really need to run this under the BKL? If so, please
|
|
|
+ * add a comment about what it's intended to protect.
|
|
|
+ */
|
|
|
+ lock_kernel();
|
|
|
+ while (!kthread_should_stop()) {
|
|
|
+ prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
|
|
|
+ spin_lock_bh(&serv->sv_cb_lock);
|
|
|
+ if (!list_empty(&serv->sv_cb_list)) {
|
|
|
+ req = list_first_entry(&serv->sv_cb_list,
|
|
|
+ struct rpc_rqst, rq_bc_list);
|
|
|
+ list_del(&req->rq_bc_list);
|
|
|
+ spin_unlock_bh(&serv->sv_cb_lock);
|
|
|
+ dprintk("Invoking bc_svc_process()\n");
|
|
|
+ error = bc_svc_process(serv, req, rqstp);
|
|
|
+ dprintk("bc_svc_process() returned w/ error code= %d\n",
|
|
|
+ error);
|
|
|
+ } else {
|
|
|
+ spin_unlock_bh(&serv->sv_cb_lock);
|
|
|
+ schedule();
|
|
|
+ }
|
|
|
+ finish_wait(&serv->sv_cb_waitq, &wq);
|
|
|
+ }
|
|
|
+ unlock_kernel();
|
|
|
+ nfs_callback_info.task = NULL;
|
|
|
+ svc_exit_thread(rqstp);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+ * Bring up the NFSv4.1 callback service
|
|
|
+ */
|
|
|
+struct svc_rqst *
|
|
|
+nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
|
|
|
+{
|
|
|
+ /*
|
|
|
+ * Save the svc_serv in the transport so that it can
|
|
|
+ * be referenced when the session backchannel is initialized
|
|
|
+ */
|
|
|
+ xprt->bc_serv = serv;
|
|
|
+
|
|
|
+ INIT_LIST_HEAD(&serv->sv_cb_list);
|
|
|
+ spin_lock_init(&serv->sv_cb_lock);
|
|
|
+ init_waitqueue_head(&serv->sv_cb_waitq);
|
|
|
+ return svc_prepare_thread(serv, &serv->sv_pools[0]);
|
|
|
+}
|
|
|
+
|
|
|
+static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
|
|
|
+ struct svc_serv *serv, struct rpc_xprt *xprt,
|
|
|
+ struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
|
|
|
+{
|
|
|
+ if (minorversion) {
|
|
|
+ *rqstpp = nfs41_callback_up(serv, xprt);
|
|
|
+ *callback_svc = nfs41_callback_svc;
|
|
|
+ }
|
|
|
+ return minorversion;
|
|
|
+}
|
|
|
+
|
|
|
+static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
|
|
|
+ struct nfs_callback_data *cb_info)
|
|
|
+{
|
|
|
+ if (minorversion)
|
|
|
+ xprt->bc_serv = cb_info->serv;
|
|
|
+}
|
|
|
+#else
|
|
|
+static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
|
|
|
+ struct svc_serv *serv, struct rpc_xprt *xprt,
|
|
|
+ struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
|
|
|
+{
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
|
|
|
+ struct nfs_callback_data *cb_info)
|
|
|
+{
|
|
|
+}
|
|
|
+#endif /* CONFIG_NFS_V4_1 */
|
|
|
+
|
|
|
/*
|
|
|
* Bring up the callback thread if it is not already up.
|
|
|
*/
|
|
@@ -141,21 +238,25 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
|
|
|
int (*callback_svc)(void *vrqstp);
|
|
|
char svc_name[12];
|
|
|
int ret = 0;
|
|
|
+ int minorversion_setup;
|
|
|
|
|
|
mutex_lock(&nfs_callback_mutex);
|
|
|
- if (nfs_callback_info.users++ || nfs_callback_info.task != NULL)
|
|
|
+ if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) {
|
|
|
+ nfs_callback_bc_serv(minorversion, xprt, &nfs_callback_info);
|
|
|
goto out;
|
|
|
+ }
|
|
|
serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
|
|
|
if (!serv) {
|
|
|
ret = -ENOMEM;
|
|
|
goto out_err;
|
|
|
}
|
|
|
|
|
|
- if (!minorversion) {
|
|
|
+ minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion,
|
|
|
+ serv, xprt, &rqstp, &callback_svc);
|
|
|
+ if (!minorversion_setup) {
|
|
|
+ /* v4.0 callback setup */
|
|
|
rqstp = nfs4_callback_up(serv);
|
|
|
callback_svc = nfs4_callback_svc;
|
|
|
- } else {
|
|
|
- BUG(); /* for now */
|
|
|
}
|
|
|
|
|
|
if (IS_ERR(rqstp)) {
|
|
@@ -166,6 +267,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
|
|
|
svc_sock_update_bufs(serv);
|
|
|
|
|
|
sprintf(svc_name, "nfsv4.%u-svc", minorversion);
|
|
|
+ nfs_callback_info.serv = serv;
|
|
|
nfs_callback_info.rqst = rqstp;
|
|
|
nfs_callback_info.task = kthread_run(callback_svc,
|
|
|
nfs_callback_info.rqst,
|
|
@@ -173,6 +275,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
|
|
|
if (IS_ERR(nfs_callback_info.task)) {
|
|
|
ret = PTR_ERR(nfs_callback_info.task);
|
|
|
svc_exit_thread(nfs_callback_info.rqst);
|
|
|
+ nfs_callback_info.serv = NULL;
|
|
|
nfs_callback_info.rqst = NULL;
|
|
|
nfs_callback_info.task = NULL;
|
|
|
goto out_err;
|
|
@@ -205,6 +308,7 @@ void nfs_callback_down(void)
|
|
|
if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) {
|
|
|
kthread_stop(nfs_callback_info.task);
|
|
|
svc_exit_thread(nfs_callback_info.rqst);
|
|
|
+ nfs_callback_info.serv = NULL;
|
|
|
nfs_callback_info.rqst = NULL;
|
|
|
nfs_callback_info.task = NULL;
|
|
|
}
|