aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorRicardo Labiaga <Ricardo.Labiaga@netapp.com>2009-04-01 09:23:08 -0400
committerBenny Halevy <bhalevy@panasas.com>2009-06-17 17:11:29 -0400
commita43cde94feded0f65fce36330614691c650ae8fe (patch)
treeb3940556128434f292369b963ec4f3b819c7ff66 /fs/nfs
parent4d6bbb6233c9cf23822a2f66f8470c9f40854b77 (diff)
nfs41: Implement NFSv4.1 callback service process.
nfs41_callback_up() initializes the necessary queues and creates the new nfs41_callback_svc thread. This thread executes the callback service which waits for requests to arrive on the svc_serv->sv_cb_list. NFS41_BC_MIN_CALLBACKS is set to 1 because we expect callbacks to not cause substantial latency. The actual processing of the callback will be implemented as a separate patch. There is only one NFSv4.1 callback service. The first caller of nfs4_callback_up() creates the service, subsequent callers increment a reference count on the service. The service is destroyed when the last caller invokes nfs_callback_down(). The transport needs to hold a reference to the callback service in order to invoke it during callback processing. Currently this reference is only obtained when the service is first created. This is incorrect, since subsequent registrations for other transports will leave the xprt->serv pointer uninitialized, leading to an oops when a callback arrives on the "unreferenced" transport. This patch fixes the problem by ensuring that a reference to the service is saved in xprt->serv, either because the service is created by this invocation to nfs4_callback_up() or by a prior invocation. Signed-off-by: Ricardo Labiaga <Ricardo.Labiaga@netapp.com> Signed-off-by: Benny Halevy <bhalevy@panasas.com> [nfs41: Add a reference to svc_serv during callback service bring up] Signed-off-by: Ricardo Labiaga <Ricardo.Labiaga@netapp.com> Signed-off-by: Benny Halevy <bhalevy@panasas.com> [Type check arguments of nfs_callback_up] Signed-off-by: Ricardo Labiaga <Ricardo.Labiaga@netapp.com> Signed-off-by: Benny Halevy <bhalevy@panasas.com> [nfs41: save svc_serv in nfs_callback_info] Signed-off-by: Benny Halevy <bhalevy@panasas.com> [Removal of ugly #ifdefs] [nfs41: Update to removal of ugly #ifdefs] Signed-off-by: Ricardo Labiaga <Ricardo.Labiaga@netapp.com> Signed-off-by: Benny Halevy <bhalevy@panasas.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/callback.c112
-rw-r--r--fs/nfs/callback.h7
2 files changed, 115 insertions, 4 deletions
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index 4b1313eda6f5..470928898063 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -17,6 +17,9 @@
17#include <linux/freezer.h> 17#include <linux/freezer.h>
18#include <linux/kthread.h> 18#include <linux/kthread.h>
19#include <linux/sunrpc/svcauth_gss.h> 19#include <linux/sunrpc/svcauth_gss.h>
20#if defined(CONFIG_NFS_V4_1)
21#include <linux/sunrpc/bc_xprt.h>
22#endif
20 23
21#include <net/inet_sock.h> 24#include <net/inet_sock.h>
22 25
@@ -28,6 +31,7 @@
28 31
29struct nfs_callback_data { 32struct nfs_callback_data {
30 unsigned int users; 33 unsigned int users;
34 struct svc_serv *serv;
31 struct svc_rqst *rqst; 35 struct svc_rqst *rqst;
32 struct task_struct *task; 36 struct task_struct *task;
33}; 37};
@@ -131,6 +135,99 @@ out_err:
131 return ERR_PTR(ret); 135 return ERR_PTR(ret);
132} 136}
133 137
138#if defined(CONFIG_NFS_V4_1)
139/*
140 * The callback service for NFSv4.1 callbacks
141 */
142static int
143nfs41_callback_svc(void *vrqstp)
144{
145 struct svc_rqst *rqstp = vrqstp;
146 struct svc_serv *serv = rqstp->rq_server;
147 struct rpc_rqst *req;
148 int error;
149 DEFINE_WAIT(wq);
150
151 set_freezable();
152
153 /*
154 * FIXME: do we really need to run this under the BKL? If so, please
155 * add a comment about what it's intended to protect.
156 */
157 lock_kernel();
158 while (!kthread_should_stop()) {
159 prepare_to_wait(&serv->sv_cb_waitq, &wq, TASK_INTERRUPTIBLE);
160 spin_lock_bh(&serv->sv_cb_lock);
161 if (!list_empty(&serv->sv_cb_list)) {
162 req = list_first_entry(&serv->sv_cb_list,
163 struct rpc_rqst, rq_bc_list);
164 list_del(&req->rq_bc_list);
165 spin_unlock_bh(&serv->sv_cb_lock);
166 dprintk("Invoking bc_svc_process()\n");
167 error = bc_svc_process(serv, req, rqstp);
168 dprintk("bc_svc_process() returned w/ error code= %d\n",
169 error);
170 } else {
171 spin_unlock_bh(&serv->sv_cb_lock);
172 schedule();
173 }
174 finish_wait(&serv->sv_cb_waitq, &wq);
175 }
176 unlock_kernel();
177 nfs_callback_info.task = NULL;
178 svc_exit_thread(rqstp);
179 return 0;
180}
181
182/*
183 * Bring up the NFSv4.1 callback service
184 */
185struct svc_rqst *
186nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
187{
188 /*
189 * Save the svc_serv in the transport so that it can
190 * be referenced when the session backchannel is initialized
191 */
192 xprt->bc_serv = serv;
193
194 INIT_LIST_HEAD(&serv->sv_cb_list);
195 spin_lock_init(&serv->sv_cb_lock);
196 init_waitqueue_head(&serv->sv_cb_waitq);
197 return svc_prepare_thread(serv, &serv->sv_pools[0]);
198}
199
200static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
201 struct svc_serv *serv, struct rpc_xprt *xprt,
202 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
203{
204 if (minorversion) {
205 *rqstpp = nfs41_callback_up(serv, xprt);
206 *callback_svc = nfs41_callback_svc;
207 }
208 return minorversion;
209}
210
211static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
212 struct nfs_callback_data *cb_info)
213{
214 if (minorversion)
215 xprt->bc_serv = cb_info->serv;
216}
217#else
218static inline int nfs_minorversion_callback_svc_setup(u32 minorversion,
219 struct svc_serv *serv, struct rpc_xprt *xprt,
220 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
221{
222 return 0;
223}
224
225static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
226 struct nfs_callback_data *cb_info)
227{
228}
229#endif /* CONFIG_NFS_V4_1 */
230
134/* 231/*
135 * Bring up the callback thread if it is not already up. 232 * Bring up the callback thread if it is not already up.
136 */ 233 */
@@ -141,21 +238,25 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
141 int (*callback_svc)(void *vrqstp); 238 int (*callback_svc)(void *vrqstp);
142 char svc_name[12]; 239 char svc_name[12];
143 int ret = 0; 240 int ret = 0;
241 int minorversion_setup;
144 242
145 mutex_lock(&nfs_callback_mutex); 243 mutex_lock(&nfs_callback_mutex);
146 if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) 244 if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) {
245 nfs_callback_bc_serv(minorversion, xprt, &nfs_callback_info);
147 goto out; 246 goto out;
247 }
148 serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); 248 serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
149 if (!serv) { 249 if (!serv) {
150 ret = -ENOMEM; 250 ret = -ENOMEM;
151 goto out_err; 251 goto out_err;
152 } 252 }
153 253
154 if (!minorversion) { 254 minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion,
255 serv, xprt, &rqstp, &callback_svc);
256 if (!minorversion_setup) {
257 /* v4.0 callback setup */
155 rqstp = nfs4_callback_up(serv); 258 rqstp = nfs4_callback_up(serv);
156 callback_svc = nfs4_callback_svc; 259 callback_svc = nfs4_callback_svc;
157 } else {
158 BUG(); /* for now */
159 } 260 }
160 261
161 if (IS_ERR(rqstp)) { 262 if (IS_ERR(rqstp)) {
@@ -166,6 +267,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
166 svc_sock_update_bufs(serv); 267 svc_sock_update_bufs(serv);
167 268
168 sprintf(svc_name, "nfsv4.%u-svc", minorversion); 269 sprintf(svc_name, "nfsv4.%u-svc", minorversion);
270 nfs_callback_info.serv = serv;
169 nfs_callback_info.rqst = rqstp; 271 nfs_callback_info.rqst = rqstp;
170 nfs_callback_info.task = kthread_run(callback_svc, 272 nfs_callback_info.task = kthread_run(callback_svc,
171 nfs_callback_info.rqst, 273 nfs_callback_info.rqst,
@@ -173,6 +275,7 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
173 if (IS_ERR(nfs_callback_info.task)) { 275 if (IS_ERR(nfs_callback_info.task)) {
174 ret = PTR_ERR(nfs_callback_info.task); 276 ret = PTR_ERR(nfs_callback_info.task);
175 svc_exit_thread(nfs_callback_info.rqst); 277 svc_exit_thread(nfs_callback_info.rqst);
278 nfs_callback_info.serv = NULL;
176 nfs_callback_info.rqst = NULL; 279 nfs_callback_info.rqst = NULL;
177 nfs_callback_info.task = NULL; 280 nfs_callback_info.task = NULL;
178 goto out_err; 281 goto out_err;
@@ -205,6 +308,7 @@ void nfs_callback_down(void)
205 if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) { 308 if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) {
206 kthread_stop(nfs_callback_info.task); 309 kthread_stop(nfs_callback_info.task);
207 svc_exit_thread(nfs_callback_info.rqst); 310 svc_exit_thread(nfs_callback_info.rqst);
311 nfs_callback_info.serv = NULL;
208 nfs_callback_info.rqst = NULL; 312 nfs_callback_info.rqst = NULL;
209 nfs_callback_info.task = NULL; 313 nfs_callback_info.task = NULL;
210 } 314 }
diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h
index 9b907f408b8d..29123b5604f2 100644
--- a/fs/nfs/callback.h
+++ b/fs/nfs/callback.h
@@ -70,6 +70,13 @@ extern void nfs_callback_down(void);
70#define nfs_callback_down() do {} while(0) 70#define nfs_callback_down() do {} while(0)
71#endif 71#endif
72 72
73/*
74 * nfs41: Callbacks are expected to not cause substantial latency,
75 * so we limit their concurrency to 1 by setting up the maximum number
76 * of slots for the backchannel.
77 */
78#define NFS41_BC_MIN_CALLBACKS 1
79
73extern unsigned int nfs_callback_set_tcpport; 80extern unsigned int nfs_callback_set_tcpport;
74extern unsigned short nfs_callback_tcpport; 81extern unsigned short nfs_callback_tcpport;
75extern unsigned short nfs_callback_tcpport6; 82extern unsigned short nfs_callback_tcpport6;