aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/callback.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/nfs/callback.c')
-rw-r--r--fs/nfs/callback.c321
1 files changed, 211 insertions, 110 deletions
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index 4c8459e5bdee..2245bef50f37 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -12,6 +12,7 @@
12#include <linux/sunrpc/svc.h> 12#include <linux/sunrpc/svc.h>
13#include <linux/sunrpc/svcsock.h> 13#include <linux/sunrpc/svcsock.h>
14#include <linux/nfs_fs.h> 14#include <linux/nfs_fs.h>
15#include <linux/errno.h>
15#include <linux/mutex.h> 16#include <linux/mutex.h>
16#include <linux/freezer.h> 17#include <linux/freezer.h>
17#include <linux/kthread.h> 18#include <linux/kthread.h>
@@ -23,6 +24,7 @@
23#include "nfs4_fs.h" 24#include "nfs4_fs.h"
24#include "callback.h" 25#include "callback.h"
25#include "internal.h" 26#include "internal.h"
27#include "netns.h"
26 28
27#define NFSDBG_FACILITY NFSDBG_CALLBACK 29#define NFSDBG_FACILITY NFSDBG_CALLBACK
28 30
@@ -37,7 +39,32 @@ static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1];
37static DEFINE_MUTEX(nfs_callback_mutex); 39static DEFINE_MUTEX(nfs_callback_mutex);
38static struct svc_program nfs4_callback_program; 40static struct svc_program nfs4_callback_program;
39 41
40unsigned short nfs_callback_tcpport6; 42static int nfs4_callback_up_net(struct svc_serv *serv, struct net *net)
43{
44 int ret;
45 struct nfs_net *nn = net_generic(net, nfs_net_id);
46
47 ret = svc_create_xprt(serv, "tcp", net, PF_INET,
48 nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
49 if (ret <= 0)
50 goto out_err;
51 nn->nfs_callback_tcpport = ret;
52 dprintk("NFS: Callback listener port = %u (af %u, net %p)\n",
53 nn->nfs_callback_tcpport, PF_INET, net);
54
55 ret = svc_create_xprt(serv, "tcp", net, PF_INET6,
56 nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
57 if (ret > 0) {
58 nn->nfs_callback_tcpport6 = ret;
59 dprintk("NFS: Callback listener port = %u (af %u, net %p)\n",
60 nn->nfs_callback_tcpport6, PF_INET6, net);
61 } else if (ret != -EAFNOSUPPORT)
62 goto out_err;
63 return 0;
64
65out_err:
66 return (ret) ? ret : -ENOMEM;
67}
41 68
42/* 69/*
43 * This is the NFSv4 callback kernel thread. 70 * This is the NFSv4 callback kernel thread.
@@ -78,38 +105,23 @@ nfs4_callback_svc(void *vrqstp)
78 * Prepare to bring up the NFSv4 callback service 105 * Prepare to bring up the NFSv4 callback service
79 */ 106 */
80static struct svc_rqst * 107static struct svc_rqst *
81nfs4_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) 108nfs4_callback_up(struct svc_serv *serv)
82{ 109{
83 int ret;
84
85 ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET,
86 nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
87 if (ret <= 0)
88 goto out_err;
89 nfs_callback_tcpport = ret;
90 dprintk("NFS: Callback listener port = %u (af %u)\n",
91 nfs_callback_tcpport, PF_INET);
92
93 ret = svc_create_xprt(serv, "tcp", &init_net, PF_INET6,
94 nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS);
95 if (ret > 0) {
96 nfs_callback_tcpport6 = ret;
97 dprintk("NFS: Callback listener port = %u (af %u)\n",
98 nfs_callback_tcpport6, PF_INET6);
99 } else if (ret == -EAFNOSUPPORT)
100 ret = 0;
101 else
102 goto out_err;
103
104 return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE); 110 return svc_prepare_thread(serv, &serv->sv_pools[0], NUMA_NO_NODE);
105
106out_err:
107 if (ret == 0)
108 ret = -ENOMEM;
109 return ERR_PTR(ret);
110} 111}
111 112
112#if defined(CONFIG_NFS_V4_1) 113#if defined(CONFIG_NFS_V4_1)
114static int nfs41_callback_up_net(struct svc_serv *serv, struct net *net)
115{
116 /*
117 * Create an svc_sock for the back channel service that shares the
118 * fore channel connection.
119 * Returns the input port (0) and sets the svc_serv bc_xprt on success
120 */
121 return svc_create_xprt(serv, "tcp-bc", net, PF_INET, 0,
122 SVC_SOCK_ANONYMOUS);
123}
124
113/* 125/*
114 * The callback service for NFSv4.1 callbacks 126 * The callback service for NFSv4.1 callbacks
115 */ 127 */
@@ -149,28 +161,9 @@ nfs41_callback_svc(void *vrqstp)
149 * Bring up the NFSv4.1 callback service 161 * Bring up the NFSv4.1 callback service
150 */ 162 */
151static struct svc_rqst * 163static struct svc_rqst *
152nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) 164nfs41_callback_up(struct svc_serv *serv)
153{ 165{
154 struct svc_rqst *rqstp; 166 struct svc_rqst *rqstp;
155 int ret;
156
157 /*
158 * Create an svc_sock for the back channel service that shares the
159 * fore channel connection.
160 * Returns the input port (0) and sets the svc_serv bc_xprt on success
161 */
162 ret = svc_create_xprt(serv, "tcp-bc", &init_net, PF_INET, 0,
163 SVC_SOCK_ANONYMOUS);
164 if (ret < 0) {
165 rqstp = ERR_PTR(ret);
166 goto out;
167 }
168
169 /*
170 * Save the svc_serv in the transport so that it can
171 * be referenced when the session backchannel is initialized
172 */
173 xprt->bc_serv = serv;
174 167
175 INIT_LIST_HEAD(&serv->sv_cb_list); 168 INIT_LIST_HEAD(&serv->sv_cb_list);
176 spin_lock_init(&serv->sv_cb_lock); 169 spin_lock_init(&serv->sv_cb_lock);
@@ -180,90 +173,74 @@ nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt)
180 svc_xprt_put(serv->sv_bc_xprt); 173 svc_xprt_put(serv->sv_bc_xprt);
181 serv->sv_bc_xprt = NULL; 174 serv->sv_bc_xprt = NULL;
182 } 175 }
183out:
184 dprintk("--> %s return %ld\n", __func__, 176 dprintk("--> %s return %ld\n", __func__,
185 IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0); 177 IS_ERR(rqstp) ? PTR_ERR(rqstp) : 0);
186 return rqstp; 178 return rqstp;
187} 179}
188 180
189static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, 181static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
190 struct svc_serv *serv, struct rpc_xprt *xprt,
191 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) 182 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
192{ 183{
193 if (minorversion) { 184 *rqstpp = nfs41_callback_up(serv);
194 *rqstpp = nfs41_callback_up(serv, xprt); 185 *callback_svc = nfs41_callback_svc;
195 *callback_svc = nfs41_callback_svc;
196 }
197 return minorversion;
198} 186}
199 187
200static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, 188static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
201 struct nfs_callback_data *cb_info) 189 struct svc_serv *serv)
202{ 190{
203 if (minorversion) 191 if (minorversion)
204 xprt->bc_serv = cb_info->serv; 192 /*
193 * Save the svc_serv in the transport so that it can
194 * be referenced when the session backchannel is initialized
195 */
196 xprt->bc_serv = serv;
205} 197}
206#else 198#else
207static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, 199static int nfs41_callback_up_net(struct svc_serv *serv, struct net *net)
208 struct svc_serv *serv, struct rpc_xprt *xprt,
209 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
210{ 200{
211 return 0; 201 return 0;
212} 202}
213 203
204static void nfs_minorversion_callback_svc_setup(struct svc_serv *serv,
205 struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp))
206{
207 *rqstpp = ERR_PTR(-ENOTSUPP);
208 *callback_svc = ERR_PTR(-ENOTSUPP);
209}
210
214static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, 211static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt,
215 struct nfs_callback_data *cb_info) 212 struct svc_serv *serv)
216{ 213{
217} 214}
218#endif /* CONFIG_NFS_V4_1 */ 215#endif /* CONFIG_NFS_V4_1 */
219 216
220/* 217static int nfs_callback_start_svc(int minorversion, struct rpc_xprt *xprt,
221 * Bring up the callback thread if it is not already up. 218 struct svc_serv *serv)
222 */
223int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
224{ 219{
225 struct svc_serv *serv = NULL;
226 struct svc_rqst *rqstp; 220 struct svc_rqst *rqstp;
227 int (*callback_svc)(void *vrqstp); 221 int (*callback_svc)(void *vrqstp);
228 struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; 222 struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
229 char svc_name[12]; 223 char svc_name[12];
230 int ret = 0; 224 int ret;
231 int minorversion_setup;
232 struct net *net = &init_net;
233 225
234 mutex_lock(&nfs_callback_mutex); 226 nfs_callback_bc_serv(minorversion, xprt, serv);
235 if (cb_info->users++ || cb_info->task != NULL) {
236 nfs_callback_bc_serv(minorversion, xprt, cb_info);
237 goto out;
238 }
239 serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
240 if (!serv) {
241 ret = -ENOMEM;
242 goto out_err;
243 }
244 /* As there is only one thread we need to over-ride the
245 * default maximum of 80 connections
246 */
247 serv->sv_maxconn = 1024;
248 227
249 ret = svc_bind(serv, net); 228 if (cb_info->task)
250 if (ret < 0) { 229 return 0;
251 printk(KERN_WARNING "NFS: bind callback service failed\n");
252 goto out_err;
253 }
254 230
255 minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion, 231 switch (minorversion) {
256 serv, xprt, &rqstp, &callback_svc); 232 case 0:
257 if (!minorversion_setup) {
258 /* v4.0 callback setup */ 233 /* v4.0 callback setup */
259 rqstp = nfs4_callback_up(serv, xprt); 234 rqstp = nfs4_callback_up(serv);
260 callback_svc = nfs4_callback_svc; 235 callback_svc = nfs4_callback_svc;
236 break;
237 default:
238 nfs_minorversion_callback_svc_setup(serv,
239 &rqstp, &callback_svc);
261 } 240 }
262 241
263 if (IS_ERR(rqstp)) { 242 if (IS_ERR(rqstp))
264 ret = PTR_ERR(rqstp); 243 return PTR_ERR(rqstp);
265 goto out_err;
266 }
267 244
268 svc_sock_update_bufs(serv); 245 svc_sock_update_bufs(serv);
269 246
@@ -276,41 +253,165 @@ int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
276 svc_exit_thread(cb_info->rqst); 253 svc_exit_thread(cb_info->rqst);
277 cb_info->rqst = NULL; 254 cb_info->rqst = NULL;
278 cb_info->task = NULL; 255 cb_info->task = NULL;
279 goto out_err; 256 return PTR_ERR(cb_info->task);
257 }
258 dprintk("nfs_callback_up: service started\n");
259 return 0;
260}
261
262static void nfs_callback_down_net(u32 minorversion, struct svc_serv *serv, struct net *net)
263{
264 struct nfs_net *nn = net_generic(net, nfs_net_id);
265
266 if (--nn->cb_users[minorversion])
267 return;
268
269 dprintk("NFS: destroy per-net callback data; net=%p\n", net);
270 svc_shutdown_net(serv, net);
271}
272
273static int nfs_callback_up_net(int minorversion, struct svc_serv *serv, struct net *net)
274{
275 struct nfs_net *nn = net_generic(net, nfs_net_id);
276 int ret;
277
278 if (nn->cb_users[minorversion]++)
279 return 0;
280
281 dprintk("NFS: create per-net callback data; net=%p\n", net);
282
283 ret = svc_bind(serv, net);
284 if (ret < 0) {
285 printk(KERN_WARNING "NFS: bind callback service failed\n");
286 goto err_bind;
287 }
288
289 switch (minorversion) {
290 case 0:
291 ret = nfs4_callback_up_net(serv, net);
292 break;
293 case 1:
294 ret = nfs41_callback_up_net(serv, net);
295 break;
296 default:
297 printk(KERN_ERR "NFS: unknown callback version: %d\n",
298 minorversion);
299 ret = -EINVAL;
300 break;
280 } 301 }
281out: 302
303 if (ret < 0) {
304 printk(KERN_ERR "NFS: callback service start failed\n");
305 goto err_socks;
306 }
307 return 0;
308
309err_socks:
310 svc_rpcb_cleanup(serv, net);
311err_bind:
312 dprintk("NFS: Couldn't create callback socket: err = %d; "
313 "net = %p\n", ret, net);
314 return ret;
315}
316
317static struct svc_serv *nfs_callback_create_svc(int minorversion)
318{
319 struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
320 struct svc_serv *serv;
321
322 /*
323 * Check whether we're already up and running.
324 */
325 if (cb_info->task) {
326 /*
327 * Note: increase service usage, because later in case of error
328 * svc_destroy() will be called.
329 */
330 svc_get(cb_info->serv);
331 return cb_info->serv;
332 }
333
334 /*
335 * Sanity check: if there's no task,
336 * we should be the first user ...
337 */
338 if (cb_info->users)
339 printk(KERN_WARNING "nfs_callback_create_svc: no kthread, %d users??\n",
340 cb_info->users);
341
342 serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL);
343 if (!serv) {
344 printk(KERN_ERR "nfs_callback_create_svc: create service failed\n");
345 return ERR_PTR(-ENOMEM);
346 }
347 /* As there is only one thread we need to over-ride the
348 * default maximum of 80 connections
349 */
350 serv->sv_maxconn = 1024;
351 dprintk("nfs_callback_create_svc: service created\n");
352 return serv;
353}
354
355/*
356 * Bring up the callback thread if it is not already up.
357 */
358int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt)
359{
360 struct svc_serv *serv;
361 struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
362 int ret;
363 struct net *net = xprt->xprt_net;
364
365 mutex_lock(&nfs_callback_mutex);
366
367 serv = nfs_callback_create_svc(minorversion);
368 if (IS_ERR(serv)) {
369 ret = PTR_ERR(serv);
370 goto err_create;
371 }
372
373 ret = nfs_callback_up_net(minorversion, serv, net);
374 if (ret < 0)
375 goto err_net;
376
377 ret = nfs_callback_start_svc(minorversion, xprt, serv);
378 if (ret < 0)
379 goto err_start;
380
381 cb_info->users++;
282 /* 382 /*
283 * svc_create creates the svc_serv with sv_nrthreads == 1, and then 383 * svc_create creates the svc_serv with sv_nrthreads == 1, and then
284 * svc_prepare_thread increments that. So we need to call svc_destroy 384 * svc_prepare_thread increments that. So we need to call svc_destroy
285 * on both success and failure so that the refcount is 1 when the 385 * on both success and failure so that the refcount is 1 when the
286 * thread exits. 386 * thread exits.
287 */ 387 */
288 if (serv) 388err_net:
289 svc_destroy(serv); 389 svc_destroy(serv);
390err_create:
290 mutex_unlock(&nfs_callback_mutex); 391 mutex_unlock(&nfs_callback_mutex);
291 return ret; 392 return ret;
292out_err: 393
293 dprintk("NFS: Couldn't create callback socket or server thread; " 394err_start:
294 "err = %d\n", ret); 395 nfs_callback_down_net(minorversion, serv, net);
295 cb_info->users--; 396 dprintk("NFS: Couldn't create server thread; err = %d\n", ret);
296 if (serv) 397 goto err_net;
297 svc_shutdown_net(serv, net);
298 goto out;
299} 398}
300 399
301/* 400/*
302 * Kill the callback thread if it's no longer being used. 401 * Kill the callback thread if it's no longer being used.
303 */ 402 */
304void nfs_callback_down(int minorversion) 403void nfs_callback_down(int minorversion, struct net *net)
305{ 404{
306 struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; 405 struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion];
307 406
308 mutex_lock(&nfs_callback_mutex); 407 mutex_lock(&nfs_callback_mutex);
408 nfs_callback_down_net(minorversion, cb_info->serv, net);
309 cb_info->users--; 409 cb_info->users--;
310 if (cb_info->users == 0 && cb_info->task != NULL) { 410 if (cb_info->users == 0 && cb_info->task != NULL) {
311 kthread_stop(cb_info->task); 411 kthread_stop(cb_info->task);
312 svc_shutdown_net(cb_info->serv, &init_net); 412 dprintk("nfs_callback_down: service stopped\n");
313 svc_exit_thread(cb_info->rqst); 413 svc_exit_thread(cb_info->rqst);
414 dprintk("nfs_callback_down: service destroyed\n");
314 cb_info->serv = NULL; 415 cb_info->serv = NULL;
315 cb_info->rqst = NULL; 416 cb_info->rqst = NULL;
316 cb_info->task = NULL; 417 cb_info->task = NULL;