diff options
Diffstat (limited to 'fs/nfs/callback.c')
-rw-r--r-- | fs/nfs/callback.c | 214 |
1 files changed, 179 insertions, 35 deletions
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c index a886e692ddd0..e69b8f61189e 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,11 +31,12 @@ | |||
28 | 31 | ||
29 | struct nfs_callback_data { | 32 | struct 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 | }; |
34 | 38 | ||
35 | static struct nfs_callback_data nfs_callback_info; | 39 | static struct nfs_callback_data nfs_callback_info[NFS4_MAX_MINOR_VERSION + 1]; |
36 | static DEFINE_MUTEX(nfs_callback_mutex); | 40 | static DEFINE_MUTEX(nfs_callback_mutex); |
37 | static struct svc_program nfs4_callback_program; | 41 | static struct svc_program nfs4_callback_program; |
38 | 42 | ||
@@ -56,10 +60,10 @@ module_param_call(callback_tcpport, param_set_port, param_get_int, | |||
56 | &nfs_callback_set_tcpport, 0644); | 60 | &nfs_callback_set_tcpport, 0644); |
57 | 61 | ||
58 | /* | 62 | /* |
59 | * This is the callback kernel thread. | 63 | * This is the NFSv4 callback kernel thread. |
60 | */ | 64 | */ |
61 | static int | 65 | static int |
62 | nfs_callback_svc(void *vrqstp) | 66 | nfs4_callback_svc(void *vrqstp) |
63 | { | 67 | { |
64 | int err, preverr = 0; | 68 | int err, preverr = 0; |
65 | struct svc_rqst *rqstp = vrqstp; | 69 | struct svc_rqst *rqstp = vrqstp; |
@@ -97,20 +101,12 @@ nfs_callback_svc(void *vrqstp) | |||
97 | } | 101 | } |
98 | 102 | ||
99 | /* | 103 | /* |
100 | * Bring up the callback thread if it is not already up. | 104 | * Prepare to bring up the NFSv4 callback service |
101 | */ | 105 | */ |
102 | int nfs_callback_up(void) | 106 | struct svc_rqst * |
107 | nfs4_callback_up(struct svc_serv *serv) | ||
103 | { | 108 | { |
104 | struct svc_serv *serv = NULL; | 109 | int ret; |
105 | int ret = 0; | ||
106 | |||
107 | mutex_lock(&nfs_callback_mutex); | ||
108 | if (nfs_callback_info.users++ || nfs_callback_info.task != NULL) | ||
109 | goto out; | ||
110 | serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); | ||
111 | ret = -ENOMEM; | ||
112 | if (!serv) | ||
113 | goto out_err; | ||
114 | 110 | ||
115 | ret = svc_create_xprt(serv, "tcp", PF_INET, | 111 | ret = svc_create_xprt(serv, "tcp", PF_INET, |
116 | nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); | 112 | nfs_callback_set_tcpport, SVC_SOCK_ANONYMOUS); |
@@ -131,23 +127,168 @@ int nfs_callback_up(void) | |||
131 | goto out_err; | 127 | goto out_err; |
132 | #endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ | 128 | #endif /* defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) */ |
133 | 129 | ||
134 | nfs_callback_info.rqst = svc_prepare_thread(serv, &serv->sv_pools[0]); | 130 | return svc_prepare_thread(serv, &serv->sv_pools[0]); |
135 | if (IS_ERR(nfs_callback_info.rqst)) { | 131 | |
136 | ret = PTR_ERR(nfs_callback_info.rqst); | 132 | out_err: |
137 | nfs_callback_info.rqst = NULL; | 133 | if (ret == 0) |
134 | ret = -ENOMEM; | ||
135 | return ERR_PTR(ret); | ||
136 | } | ||
137 | |||
138 | #if defined(CONFIG_NFS_V4_1) | ||
139 | /* | ||
140 | * The callback service for NFSv4.1 callbacks | ||
141 | */ | ||
142 | static int | ||
143 | nfs41_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 | return 0; | ||
178 | } | ||
179 | |||
180 | /* | ||
181 | * Bring up the NFSv4.1 callback service | ||
182 | */ | ||
183 | struct svc_rqst * | ||
184 | nfs41_callback_up(struct svc_serv *serv, struct rpc_xprt *xprt) | ||
185 | { | ||
186 | struct svc_xprt *bc_xprt; | ||
187 | struct svc_rqst *rqstp = ERR_PTR(-ENOMEM); | ||
188 | |||
189 | dprintk("--> %s\n", __func__); | ||
190 | /* Create a svc_sock for the service */ | ||
191 | bc_xprt = svc_sock_create(serv, xprt->prot); | ||
192 | if (!bc_xprt) | ||
193 | goto out; | ||
194 | |||
195 | /* | ||
196 | * Save the svc_serv in the transport so that it can | ||
197 | * be referenced when the session backchannel is initialized | ||
198 | */ | ||
199 | serv->bc_xprt = bc_xprt; | ||
200 | xprt->bc_serv = serv; | ||
201 | |||
202 | INIT_LIST_HEAD(&serv->sv_cb_list); | ||
203 | spin_lock_init(&serv->sv_cb_lock); | ||
204 | init_waitqueue_head(&serv->sv_cb_waitq); | ||
205 | rqstp = svc_prepare_thread(serv, &serv->sv_pools[0]); | ||
206 | if (IS_ERR(rqstp)) | ||
207 | svc_sock_destroy(bc_xprt); | ||
208 | out: | ||
209 | dprintk("--> %s return %p\n", __func__, rqstp); | ||
210 | return rqstp; | ||
211 | } | ||
212 | |||
213 | static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, | ||
214 | struct svc_serv *serv, struct rpc_xprt *xprt, | ||
215 | struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) | ||
216 | { | ||
217 | if (minorversion) { | ||
218 | *rqstpp = nfs41_callback_up(serv, xprt); | ||
219 | *callback_svc = nfs41_callback_svc; | ||
220 | } | ||
221 | return minorversion; | ||
222 | } | ||
223 | |||
224 | static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, | ||
225 | struct nfs_callback_data *cb_info) | ||
226 | { | ||
227 | if (minorversion) | ||
228 | xprt->bc_serv = cb_info->serv; | ||
229 | } | ||
230 | #else | ||
231 | static inline int nfs_minorversion_callback_svc_setup(u32 minorversion, | ||
232 | struct svc_serv *serv, struct rpc_xprt *xprt, | ||
233 | struct svc_rqst **rqstpp, int (**callback_svc)(void *vrqstp)) | ||
234 | { | ||
235 | return 0; | ||
236 | } | ||
237 | |||
238 | static inline void nfs_callback_bc_serv(u32 minorversion, struct rpc_xprt *xprt, | ||
239 | struct nfs_callback_data *cb_info) | ||
240 | { | ||
241 | } | ||
242 | #endif /* CONFIG_NFS_V4_1 */ | ||
243 | |||
244 | /* | ||
245 | * Bring up the callback thread if it is not already up. | ||
246 | */ | ||
247 | int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt) | ||
248 | { | ||
249 | struct svc_serv *serv = NULL; | ||
250 | struct svc_rqst *rqstp; | ||
251 | int (*callback_svc)(void *vrqstp); | ||
252 | struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; | ||
253 | char svc_name[12]; | ||
254 | int ret = 0; | ||
255 | int minorversion_setup; | ||
256 | |||
257 | mutex_lock(&nfs_callback_mutex); | ||
258 | if (cb_info->users++ || cb_info->task != NULL) { | ||
259 | nfs_callback_bc_serv(minorversion, xprt, cb_info); | ||
260 | goto out; | ||
261 | } | ||
262 | serv = svc_create(&nfs4_callback_program, NFS4_CALLBACK_BUFSIZE, NULL); | ||
263 | if (!serv) { | ||
264 | ret = -ENOMEM; | ||
265 | goto out_err; | ||
266 | } | ||
267 | |||
268 | minorversion_setup = nfs_minorversion_callback_svc_setup(minorversion, | ||
269 | serv, xprt, &rqstp, &callback_svc); | ||
270 | if (!minorversion_setup) { | ||
271 | /* v4.0 callback setup */ | ||
272 | rqstp = nfs4_callback_up(serv); | ||
273 | callback_svc = nfs4_callback_svc; | ||
274 | } | ||
275 | |||
276 | if (IS_ERR(rqstp)) { | ||
277 | ret = PTR_ERR(rqstp); | ||
138 | goto out_err; | 278 | goto out_err; |
139 | } | 279 | } |
140 | 280 | ||
141 | svc_sock_update_bufs(serv); | 281 | svc_sock_update_bufs(serv); |
142 | 282 | ||
143 | nfs_callback_info.task = kthread_run(nfs_callback_svc, | 283 | sprintf(svc_name, "nfsv4.%u-svc", minorversion); |
144 | nfs_callback_info.rqst, | 284 | cb_info->serv = serv; |
145 | "nfsv4-svc"); | 285 | cb_info->rqst = rqstp; |
146 | if (IS_ERR(nfs_callback_info.task)) { | 286 | cb_info->task = kthread_run(callback_svc, cb_info->rqst, svc_name); |
147 | ret = PTR_ERR(nfs_callback_info.task); | 287 | if (IS_ERR(cb_info->task)) { |
148 | svc_exit_thread(nfs_callback_info.rqst); | 288 | ret = PTR_ERR(cb_info->task); |
149 | nfs_callback_info.rqst = NULL; | 289 | svc_exit_thread(cb_info->rqst); |
150 | nfs_callback_info.task = NULL; | 290 | cb_info->rqst = NULL; |
291 | cb_info->task = NULL; | ||
151 | goto out_err; | 292 | goto out_err; |
152 | } | 293 | } |
153 | out: | 294 | out: |
@@ -164,22 +305,25 @@ out: | |||
164 | out_err: | 305 | out_err: |
165 | dprintk("NFS: Couldn't create callback socket or server thread; " | 306 | dprintk("NFS: Couldn't create callback socket or server thread; " |
166 | "err = %d\n", ret); | 307 | "err = %d\n", ret); |
167 | nfs_callback_info.users--; | 308 | cb_info->users--; |
168 | goto out; | 309 | goto out; |
169 | } | 310 | } |
170 | 311 | ||
171 | /* | 312 | /* |
172 | * Kill the callback thread if it's no longer being used. | 313 | * Kill the callback thread if it's no longer being used. |
173 | */ | 314 | */ |
174 | void nfs_callback_down(void) | 315 | void nfs_callback_down(int minorversion) |
175 | { | 316 | { |
317 | struct nfs_callback_data *cb_info = &nfs_callback_info[minorversion]; | ||
318 | |||
176 | mutex_lock(&nfs_callback_mutex); | 319 | mutex_lock(&nfs_callback_mutex); |
177 | nfs_callback_info.users--; | 320 | cb_info->users--; |
178 | if (nfs_callback_info.users == 0 && nfs_callback_info.task != NULL) { | 321 | if (cb_info->users == 0 && cb_info->task != NULL) { |
179 | kthread_stop(nfs_callback_info.task); | 322 | kthread_stop(cb_info->task); |
180 | svc_exit_thread(nfs_callback_info.rqst); | 323 | svc_exit_thread(cb_info->rqst); |
181 | nfs_callback_info.rqst = NULL; | 324 | cb_info->serv = NULL; |
182 | nfs_callback_info.task = NULL; | 325 | cb_info->rqst = NULL; |
326 | cb_info->task = NULL; | ||
183 | } | 327 | } |
184 | mutex_unlock(&nfs_callback_mutex); | 328 | mutex_unlock(&nfs_callback_mutex); |
185 | } | 329 | } |