aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs
diff options
context:
space:
mode:
authorAndy Adamson <andros@netapp.com>2011-01-05 21:04:32 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2011-01-06 14:46:24 -0500
commitc36fca52f5e4594ffd0ff175b328966b0d393184 (patch)
tree6d771744cc49f0edc0d2b6b2f9fe919163002346 /fs/nfs
parent2c2618c6f29c41a0a966f14f05c8bf45fcabb750 (diff)
NFS refactor nfs_find_client and reference client across callback processing
Fixes a bug where the nfs_client could be freed during callback processing. Refactor nfs_find_client to use minorversion specific means to locate the correct nfs_client structure. In the NFS layer, V4.0 clients are found using the callback_ident field in the CB_COMPOUND header. V4.1 clients are found using the sessionID in the CB_SEQUENCE operation which is also compared against the sessionID associated with the back channel thread after a successful CREATE_SESSION. Each of these methods finds the one an only nfs_client associated with the incoming callback request - so nfs_find_client_next is not needed. In the RPC layer, the pg_authenticate call needs to find the nfs_client. For the v4.0 callback service, the callback identifier has not been decoded so a search by address, version, and minorversion is used. The sessionid for the sessions based callback service has (usually) not been set for the pg_authenticate on a CB_NULL call which can be sent prior to the return of a CREATE_SESSION call, so the sessionid associated with the back channel thread is not used to find the client in pg_authenticate for CB_NULL calls. Pass the referenced nfs_client to each CB_COMPOUND operation being proceesed via the new cb_process_state structure. The reference is held across cb_compound processing. Use the new cb_process_state struct to move the NFS4ERR_RETRY_UNCACHED_REP processing from process_op into nfs4_callback_sequence where it belongs. Signed-off-by: Andy Adamson <andros@netapp.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs')
-rw-r--r--fs/nfs/callback.c21
-rw-r--r--fs/nfs/callback.h28
-rw-r--r--fs/nfs/callback_proc.c167
-rw-r--r--fs/nfs/callback_xdr.c39
-rw-r--r--fs/nfs/client.c171
-rw-r--r--fs/nfs/internal.h7
6 files changed, 245 insertions, 188 deletions
diff --git a/fs/nfs/callback.c b/fs/nfs/callback.c
index c0b05497972b..15677e7bede5 100644
--- a/fs/nfs/callback.c
+++ b/fs/nfs/callback.c
@@ -16,9 +16,7 @@
16#include <linux/freezer.h> 16#include <linux/freezer.h>
17#include <linux/kthread.h> 17#include <linux/kthread.h>
18#include <linux/sunrpc/svcauth_gss.h> 18#include <linux/sunrpc/svcauth_gss.h>
19#if defined(CONFIG_NFS_V4_1)
20#include <linux/sunrpc/bc_xprt.h> 19#include <linux/sunrpc/bc_xprt.h>
21#endif
22 20
23#include <net/inet_sock.h> 21#include <net/inet_sock.h>
24 22
@@ -384,6 +382,23 @@ static int check_gss_callback_principal(struct nfs_client *clp,
384 return SVC_OK; 382 return SVC_OK;
385} 383}
386 384
385/* pg_authenticate method helper */
386static struct nfs_client *nfs_cb_find_client(struct svc_rqst *rqstp)
387{
388 struct nfs4_sessionid *sessionid = bc_xprt_sid(rqstp);
389 int is_cb_compound = rqstp->rq_proc == CB_COMPOUND ? 1 : 0;
390
391 dprintk("--> %s rq_proc %d\n", __func__, rqstp->rq_proc);
392 if (svc_is_backchannel(rqstp))
393 /* Sessionid (usually) set after CB_NULL ping */
394 return nfs4_find_client_sessionid(svc_addr(rqstp), sessionid,
395 is_cb_compound);
396 else
397 /* No callback identifier in pg_authenticate */
398 return nfs4_find_client_no_ident(svc_addr(rqstp));
399}
400
401/* pg_authenticate method for nfsv4 callback threads. */
387static int nfs_callback_authenticate(struct svc_rqst *rqstp) 402static int nfs_callback_authenticate(struct svc_rqst *rqstp)
388{ 403{
389 struct nfs_client *clp; 404 struct nfs_client *clp;
@@ -391,7 +406,7 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp)
391 int ret = SVC_OK; 406 int ret = SVC_OK;
392 407
393 /* Don't talk to strangers */ 408 /* Don't talk to strangers */
394 clp = nfs_find_client(svc_addr(rqstp), 4); 409 clp = nfs_cb_find_client(rqstp);
395 if (clp == NULL) 410 if (clp == NULL)
396 return SVC_DROP; 411 return SVC_DROP;
397 412
diff --git a/fs/nfs/callback.h b/fs/nfs/callback.h
index 58d61a8ce8b9..25e8802a51d1 100644
--- a/fs/nfs/callback.h
+++ b/fs/nfs/callback.h
@@ -34,10 +34,17 @@ enum nfs4_callback_opnum {
34 OP_CB_ILLEGAL = 10044, 34 OP_CB_ILLEGAL = 10044,
35}; 35};
36 36
37struct cb_process_state {
38 __be32 drc_status;
39 struct nfs_client *clp;
40 struct nfs4_sessionid *svc_sid; /* v4.1 callback service sessionid */
41};
42
37struct cb_compound_hdr_arg { 43struct cb_compound_hdr_arg {
38 unsigned int taglen; 44 unsigned int taglen;
39 const char *tag; 45 const char *tag;
40 unsigned int minorversion; 46 unsigned int minorversion;
47 unsigned int cb_ident; /* v4.0 callback identifier */
41 unsigned nops; 48 unsigned nops;
42}; 49};
43 50
@@ -103,8 +110,9 @@ struct cb_sequenceres {
103 uint32_t csr_target_highestslotid; 110 uint32_t csr_target_highestslotid;
104}; 111};
105 112
106extern unsigned nfs4_callback_sequence(struct cb_sequenceargs *args, 113extern __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
107 struct cb_sequenceres *res); 114 struct cb_sequenceres *res,
115 struct cb_process_state *cps);
108 116
109extern int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation, 117extern int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation,
110 const nfs4_stateid *stateid); 118 const nfs4_stateid *stateid);
@@ -118,19 +126,25 @@ struct cb_recallanyargs {
118 uint32_t craa_type_mask; 126 uint32_t craa_type_mask;
119}; 127};
120 128
121extern unsigned nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy); 129extern __be32 nfs4_callback_recallany(struct cb_recallanyargs *args,
130 void *dummy,
131 struct cb_process_state *cps);
122 132
123struct cb_recallslotargs { 133struct cb_recallslotargs {
124 struct sockaddr *crsa_addr; 134 struct sockaddr *crsa_addr;
125 uint32_t crsa_target_max_slots; 135 uint32_t crsa_target_max_slots;
126}; 136};
127extern unsigned nfs4_callback_recallslot(struct cb_recallslotargs *args, 137extern __be32 nfs4_callback_recallslot(struct cb_recallslotargs *args,
128 void *dummy); 138 void *dummy,
139 struct cb_process_state *cps);
129 140
130#endif /* CONFIG_NFS_V4_1 */ 141#endif /* CONFIG_NFS_V4_1 */
131 142
132extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res); 143extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args,
133extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy); 144 struct cb_getattrres *res,
145 struct cb_process_state *cps);
146extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
147 struct cb_process_state *cps);
134 148
135#ifdef CONFIG_NFS_V4 149#ifdef CONFIG_NFS_V4
136extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt); 150extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt);
diff --git a/fs/nfs/callback_proc.c b/fs/nfs/callback_proc.c
index 2950fca0c61b..b70e46da16fc 100644
--- a/fs/nfs/callback_proc.c
+++ b/fs/nfs/callback_proc.c
@@ -16,26 +16,28 @@
16#ifdef NFS_DEBUG 16#ifdef NFS_DEBUG
17#define NFSDBG_FACILITY NFSDBG_CALLBACK 17#define NFSDBG_FACILITY NFSDBG_CALLBACK
18#endif 18#endif
19 19
20__be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res) 20__be32 nfs4_callback_getattr(struct cb_getattrargs *args,
21 struct cb_getattrres *res,
22 struct cb_process_state *cps)
21{ 23{
22 struct nfs_client *clp;
23 struct nfs_delegation *delegation; 24 struct nfs_delegation *delegation;
24 struct nfs_inode *nfsi; 25 struct nfs_inode *nfsi;
25 struct inode *inode; 26 struct inode *inode;
26 27
28 res->status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
29 if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */
30 goto out;
31
27 res->bitmap[0] = res->bitmap[1] = 0; 32 res->bitmap[0] = res->bitmap[1] = 0;
28 res->status = htonl(NFS4ERR_BADHANDLE); 33 res->status = htonl(NFS4ERR_BADHANDLE);
29 clp = nfs_find_client(args->addr, 4);
30 if (clp == NULL)
31 goto out;
32 34
33 dprintk("NFS: GETATTR callback request from %s\n", 35 dprintk("NFS: GETATTR callback request from %s\n",
34 rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); 36 rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
35 37
36 inode = nfs_delegation_find_inode(clp, &args->fh); 38 inode = nfs_delegation_find_inode(cps->clp, &args->fh);
37 if (inode == NULL) 39 if (inode == NULL)
38 goto out_putclient; 40 goto out;
39 nfsi = NFS_I(inode); 41 nfsi = NFS_I(inode);
40 rcu_read_lock(); 42 rcu_read_lock();
41 delegation = rcu_dereference(nfsi->delegation); 43 delegation = rcu_dereference(nfsi->delegation);
@@ -55,49 +57,41 @@ __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *
55out_iput: 57out_iput:
56 rcu_read_unlock(); 58 rcu_read_unlock();
57 iput(inode); 59 iput(inode);
58out_putclient:
59 nfs_put_client(clp);
60out: 60out:
61 dprintk("%s: exit with status = %d\n", __func__, ntohl(res->status)); 61 dprintk("%s: exit with status = %d\n", __func__, ntohl(res->status));
62 return res->status; 62 return res->status;
63} 63}
64 64
65__be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy) 65__be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
66 struct cb_process_state *cps)
66{ 67{
67 struct nfs_client *clp;
68 struct inode *inode; 68 struct inode *inode;
69 __be32 res; 69 __be32 res;
70 70
71 res = htonl(NFS4ERR_BADHANDLE); 71 res = htonl(NFS4ERR_OP_NOT_IN_SESSION);
72 clp = nfs_find_client(args->addr, 4); 72 if (!cps->clp) /* Always set for v4.0. Set in cb_sequence for v4.1 */
73 if (clp == NULL)
74 goto out; 73 goto out;
75 74
76 dprintk("NFS: RECALL callback request from %s\n", 75 dprintk("NFS: RECALL callback request from %s\n",
77 rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); 76 rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
78 77
79 do { 78 res = htonl(NFS4ERR_BADHANDLE);
80 struct nfs_client *prev = clp; 79 inode = nfs_delegation_find_inode(cps->clp, &args->fh);
81 80 if (inode == NULL)
82 inode = nfs_delegation_find_inode(clp, &args->fh); 81 goto out;
83 if (inode != NULL) { 82 /* Set up a helper thread to actually return the delegation */
84 /* Set up a helper thread to actually return the delegation */ 83 switch (nfs_async_inode_return_delegation(inode, &args->stateid)) {
85 switch (nfs_async_inode_return_delegation(inode, &args->stateid)) { 84 case 0:
86 case 0: 85 res = 0;
87 res = 0; 86 break;
88 break; 87 case -ENOENT:
89 case -ENOENT: 88 if (res != 0)
90 if (res != 0) 89 res = htonl(NFS4ERR_BAD_STATEID);
91 res = htonl(NFS4ERR_BAD_STATEID); 90 break;
92 break; 91 default:
93 default: 92 res = htonl(NFS4ERR_RESOURCE);
94 res = htonl(NFS4ERR_RESOURCE); 93 }
95 } 94 iput(inode);
96 iput(inode);
97 }
98 clp = nfs_find_client_next(prev);
99 nfs_put_client(prev);
100 } while (clp != NULL);
101out: 95out:
102 dprintk("%s: exit with status = %d\n", __func__, ntohl(res)); 96 dprintk("%s: exit with status = %d\n", __func__, ntohl(res));
103 return res; 97 return res;
@@ -185,42 +179,6 @@ validate_seqid(struct nfs4_slot_table *tbl, struct cb_sequenceargs * args)
185} 179}
186 180
187/* 181/*
188 * Returns a pointer to a held 'struct nfs_client' that matches the server's
189 * address, major version number, and session ID. It is the caller's
190 * responsibility to release the returned reference.
191 *
192 * Returns NULL if there are no connections with sessions, or if no session
193 * matches the one of interest.
194 */
195 static struct nfs_client *find_client_with_session(
196 const struct sockaddr *addr, u32 nfsversion,
197 struct nfs4_sessionid *sessionid)
198{
199 struct nfs_client *clp;
200
201 clp = nfs_find_client(addr, 4);
202 if (clp == NULL)
203 return NULL;
204
205 do {
206 struct nfs_client *prev = clp;
207
208 if (clp->cl_session != NULL) {
209 if (memcmp(clp->cl_session->sess_id.data,
210 sessionid->data,
211 NFS4_MAX_SESSIONID_LEN) == 0) {
212 /* Returns a held reference to clp */
213 return clp;
214 }
215 }
216 clp = nfs_find_client_next(prev);
217 nfs_put_client(prev);
218 } while (clp != NULL);
219
220 return NULL;
221}
222
223/*
224 * For each referring call triple, check the session's slot table for 182 * For each referring call triple, check the session's slot table for
225 * a match. If the slot is in use and the sequence numbers match, the 183 * a match. If the slot is in use and the sequence numbers match, the
226 * client is still waiting for a response to the original request. 184 * client is still waiting for a response to the original request.
@@ -276,20 +234,28 @@ out:
276} 234}
277 235
278__be32 nfs4_callback_sequence(struct cb_sequenceargs *args, 236__be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
279 struct cb_sequenceres *res) 237 struct cb_sequenceres *res,
238 struct cb_process_state *cps)
280{ 239{
281 struct nfs_client *clp; 240 struct nfs_client *clp;
282 int i; 241 int i;
283 __be32 status; 242 __be32 status;
284 243
244 cps->clp = NULL;
245
285 status = htonl(NFS4ERR_BADSESSION); 246 status = htonl(NFS4ERR_BADSESSION);
286 clp = find_client_with_session(args->csa_addr, 4, &args->csa_sessionid); 247 /* Incoming session must match the callback session */
248 if (memcmp(&args->csa_sessionid, cps->svc_sid, NFS4_MAX_SESSIONID_LEN))
249 goto out;
250
251 clp = nfs4_find_client_sessionid(args->csa_addr,
252 &args->csa_sessionid, 1);
287 if (clp == NULL) 253 if (clp == NULL)
288 goto out; 254 goto out;
289 255
290 status = validate_seqid(&clp->cl_session->bc_slot_table, args); 256 status = validate_seqid(&clp->cl_session->bc_slot_table, args);
291 if (status) 257 if (status)
292 goto out_putclient; 258 goto out;
293 259
294 /* 260 /*
295 * Check for pending referring calls. If a match is found, a 261 * Check for pending referring calls. If a match is found, a
@@ -298,7 +264,7 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
298 */ 264 */
299 if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) { 265 if (referring_call_exists(clp, args->csa_nrclists, args->csa_rclists)) {
300 status = htonl(NFS4ERR_DELAY); 266 status = htonl(NFS4ERR_DELAY);
301 goto out_putclient; 267 goto out;
302 } 268 }
303 269
304 memcpy(&res->csr_sessionid, &args->csa_sessionid, 270 memcpy(&res->csr_sessionid, &args->csa_sessionid,
@@ -307,36 +273,36 @@ __be32 nfs4_callback_sequence(struct cb_sequenceargs *args,
307 res->csr_slotid = args->csa_slotid; 273 res->csr_slotid = args->csa_slotid;
308 res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; 274 res->csr_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
309 res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1; 275 res->csr_target_highestslotid = NFS41_BC_MAX_CALLBACKS - 1;
276 cps->clp = clp; /* put in nfs4_callback_compound */
310 277
311out_putclient:
312 nfs_put_client(clp);
313out: 278out:
314 for (i = 0; i < args->csa_nrclists; i++) 279 for (i = 0; i < args->csa_nrclists; i++)
315 kfree(args->csa_rclists[i].rcl_refcalls); 280 kfree(args->csa_rclists[i].rcl_refcalls);
316 kfree(args->csa_rclists); 281 kfree(args->csa_rclists);
317 282
318 if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) 283 if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) {
319 res->csr_status = 0; 284 cps->drc_status = status;
320 else 285 status = 0;
286 } else
321 res->csr_status = status; 287 res->csr_status = status;
288
322 dprintk("%s: exit with status = %d res->csr_status %d\n", __func__, 289 dprintk("%s: exit with status = %d res->csr_status %d\n", __func__,
323 ntohl(status), ntohl(res->csr_status)); 290 ntohl(status), ntohl(res->csr_status));
324 return status; 291 return status;
325} 292}
326 293
327__be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy) 294__be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy,
295 struct cb_process_state *cps)
328{ 296{
329 struct nfs_client *clp;
330 __be32 status; 297 __be32 status;
331 fmode_t flags = 0; 298 fmode_t flags = 0;
332 299
333 status = htonl(NFS4ERR_OP_NOT_IN_SESSION); 300 status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
334 clp = nfs_find_client(args->craa_addr, 4); 301 if (!cps->clp) /* set in cb_sequence */
335 if (clp == NULL)
336 goto out; 302 goto out;
337 303
338 dprintk("NFS: RECALL_ANY callback request from %s\n", 304 dprintk("NFS: RECALL_ANY callback request from %s\n",
339 rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR)); 305 rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));
340 306
341 if (test_bit(RCA4_TYPE_MASK_RDATA_DLG, (const unsigned long *) 307 if (test_bit(RCA4_TYPE_MASK_RDATA_DLG, (const unsigned long *)
342 &args->craa_type_mask)) 308 &args->craa_type_mask))
@@ -346,7 +312,7 @@ __be32 nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy)
346 flags |= FMODE_WRITE; 312 flags |= FMODE_WRITE;
347 313
348 if (flags) 314 if (flags)
349 nfs_expire_all_delegation_types(clp, flags); 315 nfs_expire_all_delegation_types(cps->clp, flags);
350 status = htonl(NFS4_OK); 316 status = htonl(NFS4_OK);
351out: 317out:
352 dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); 318 dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
@@ -354,36 +320,33 @@ out:
354} 320}
355 321
356/* Reduce the fore channel's max_slots to the target value */ 322/* Reduce the fore channel's max_slots to the target value */
357__be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy) 323__be32 nfs4_callback_recallslot(struct cb_recallslotargs *args, void *dummy,
324 struct cb_process_state *cps)
358{ 325{
359 struct nfs_client *clp;
360 struct nfs4_slot_table *fc_tbl; 326 struct nfs4_slot_table *fc_tbl;
361 __be32 status; 327 __be32 status;
362 328
363 status = htonl(NFS4ERR_OP_NOT_IN_SESSION); 329 status = htonl(NFS4ERR_OP_NOT_IN_SESSION);
364 clp = nfs_find_client(args->crsa_addr, 4); 330 if (!cps->clp) /* set in cb_sequence */
365 if (clp == NULL)
366 goto out; 331 goto out;
367 332
368 dprintk("NFS: CB_RECALL_SLOT request from %s target max slots %d\n", 333 dprintk("NFS: CB_RECALL_SLOT request from %s target max slots %d\n",
369 rpc_peeraddr2str(clp->cl_rpcclient, RPC_DISPLAY_ADDR), 334 rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR),
370 args->crsa_target_max_slots); 335 args->crsa_target_max_slots);
371 336
372 fc_tbl = &clp->cl_session->fc_slot_table; 337 fc_tbl = &cps->clp->cl_session->fc_slot_table;
373 338
374 status = htonl(NFS4ERR_BAD_HIGH_SLOT); 339 status = htonl(NFS4ERR_BAD_HIGH_SLOT);
375 if (args->crsa_target_max_slots > fc_tbl->max_slots || 340 if (args->crsa_target_max_slots > fc_tbl->max_slots ||
376 args->crsa_target_max_slots < 1) 341 args->crsa_target_max_slots < 1)
377 goto out_putclient; 342 goto out;
378 343
379 status = htonl(NFS4_OK); 344 status = htonl(NFS4_OK);
380 if (args->crsa_target_max_slots == fc_tbl->max_slots) 345 if (args->crsa_target_max_slots == fc_tbl->max_slots)
381 goto out_putclient; 346 goto out;
382 347
383 fc_tbl->target_max_slots = args->crsa_target_max_slots; 348 fc_tbl->target_max_slots = args->crsa_target_max_slots;
384 nfs41_handle_recall_slot(clp); 349 nfs41_handle_recall_slot(cps->clp);
385out_putclient:
386 nfs_put_client(clp); /* balance nfs_find_client */
387out: 350out:
388 dprintk("%s: exit with status = %d\n", __func__, ntohl(status)); 351 dprintk("%s: exit with status = %d\n", __func__, ntohl(status));
389 return status; 352 return status;
diff --git a/fs/nfs/callback_xdr.c b/fs/nfs/callback_xdr.c
index 05af212f0edf..dbd0d649805c 100644
--- a/fs/nfs/callback_xdr.c
+++ b/fs/nfs/callback_xdr.c
@@ -10,8 +10,10 @@
10#include <linux/nfs4.h> 10#include <linux/nfs4.h>
11#include <linux/nfs_fs.h> 11#include <linux/nfs_fs.h>
12#include <linux/slab.h> 12#include <linux/slab.h>
13#include <linux/sunrpc/bc_xprt.h>
13#include "nfs4_fs.h" 14#include "nfs4_fs.h"
14#include "callback.h" 15#include "callback.h"
16#include "internal.h"
15 17
16#define CB_OP_TAGLEN_MAXSZ (512) 18#define CB_OP_TAGLEN_MAXSZ (512)
17#define CB_OP_HDR_RES_MAXSZ (2 + CB_OP_TAGLEN_MAXSZ) 19#define CB_OP_HDR_RES_MAXSZ (2 + CB_OP_TAGLEN_MAXSZ)
@@ -33,7 +35,8 @@
33/* Internal error code */ 35/* Internal error code */
34#define NFS4ERR_RESOURCE_HDR 11050 36#define NFS4ERR_RESOURCE_HDR 11050
35 37
36typedef __be32 (*callback_process_op_t)(void *, void *); 38typedef __be32 (*callback_process_op_t)(void *, void *,
39 struct cb_process_state *);
37typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *); 40typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *);
38typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *); 41typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *);
39 42
@@ -160,7 +163,7 @@ static __be32 decode_compound_hdr_arg(struct xdr_stream *xdr, struct cb_compound
160 hdr->minorversion = ntohl(*p++); 163 hdr->minorversion = ntohl(*p++);
161 /* Check minor version is zero or one. */ 164 /* Check minor version is zero or one. */
162 if (hdr->minorversion <= 1) { 165 if (hdr->minorversion <= 1) {
163 p++; /* skip callback_ident */ 166 hdr->cb_ident = ntohl(*p++); /* ignored by v4.1 */
164 } else { 167 } else {
165 printk(KERN_WARNING "%s: NFSv4 server callback with " 168 printk(KERN_WARNING "%s: NFSv4 server callback with "
166 "illegal minor version %u!\n", 169 "illegal minor version %u!\n",
@@ -621,7 +624,8 @@ preprocess_nfs4_op(unsigned int op_nr, struct callback_op **op)
621static __be32 process_op(uint32_t minorversion, int nop, 624static __be32 process_op(uint32_t minorversion, int nop,
622 struct svc_rqst *rqstp, 625 struct svc_rqst *rqstp,
623 struct xdr_stream *xdr_in, void *argp, 626 struct xdr_stream *xdr_in, void *argp,
624 struct xdr_stream *xdr_out, void *resp, int* drc_status) 627 struct xdr_stream *xdr_out, void *resp,
628 struct cb_process_state *cps)
625{ 629{
626 struct callback_op *op = &callback_ops[0]; 630 struct callback_op *op = &callback_ops[0];
627 unsigned int op_nr; 631 unsigned int op_nr;
@@ -644,8 +648,8 @@ static __be32 process_op(uint32_t minorversion, int nop,
644 if (status) 648 if (status)
645 goto encode_hdr; 649 goto encode_hdr;
646 650
647 if (*drc_status) { 651 if (cps->drc_status) {
648 status = *drc_status; 652 status = cps->drc_status;
649 goto encode_hdr; 653 goto encode_hdr;
650 } 654 }
651 655
@@ -653,16 +657,10 @@ static __be32 process_op(uint32_t minorversion, int nop,
653 if (maxlen > 0 && maxlen < PAGE_SIZE) { 657 if (maxlen > 0 && maxlen < PAGE_SIZE) {
654 status = op->decode_args(rqstp, xdr_in, argp); 658 status = op->decode_args(rqstp, xdr_in, argp);
655 if (likely(status == 0)) 659 if (likely(status == 0))
656 status = op->process_op(argp, resp); 660 status = op->process_op(argp, resp, cps);
657 } else 661 } else
658 status = htonl(NFS4ERR_RESOURCE); 662 status = htonl(NFS4ERR_RESOURCE);
659 663
660 /* Only set by OP_CB_SEQUENCE processing */
661 if (status == htonl(NFS4ERR_RETRY_UNCACHED_REP)) {
662 *drc_status = status;
663 status = 0;
664 }
665
666encode_hdr: 664encode_hdr:
667 res = encode_op_hdr(xdr_out, op_nr, status); 665 res = encode_op_hdr(xdr_out, op_nr, status);
668 if (unlikely(res)) 666 if (unlikely(res))
@@ -681,8 +679,11 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
681 struct cb_compound_hdr_arg hdr_arg = { 0 }; 679 struct cb_compound_hdr_arg hdr_arg = { 0 };
682 struct cb_compound_hdr_res hdr_res = { NULL }; 680 struct cb_compound_hdr_res hdr_res = { NULL };
683 struct xdr_stream xdr_in, xdr_out; 681 struct xdr_stream xdr_in, xdr_out;
684 __be32 *p; 682 __be32 *p, status;
685 __be32 status, drc_status = 0; 683 struct cb_process_state cps = {
684 .drc_status = 0,
685 .clp = NULL,
686 };
686 unsigned int nops = 0; 687 unsigned int nops = 0;
687 688
688 dprintk("%s: start\n", __func__); 689 dprintk("%s: start\n", __func__);
@@ -696,6 +697,13 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
696 if (status == __constant_htonl(NFS4ERR_RESOURCE)) 697 if (status == __constant_htonl(NFS4ERR_RESOURCE))
697 return rpc_garbage_args; 698 return rpc_garbage_args;
698 699
700 if (hdr_arg.minorversion == 0) {
701 cps.clp = nfs4_find_client_ident(hdr_arg.cb_ident);
702 if (!cps.clp)
703 return rpc_drop_reply;
704 } else
705 cps.svc_sid = bc_xprt_sid(rqstp);
706
699 hdr_res.taglen = hdr_arg.taglen; 707 hdr_res.taglen = hdr_arg.taglen;
700 hdr_res.tag = hdr_arg.tag; 708 hdr_res.tag = hdr_arg.tag;
701 if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0) 709 if (encode_compound_hdr_res(&xdr_out, &hdr_res) != 0)
@@ -703,7 +711,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
703 711
704 while (status == 0 && nops != hdr_arg.nops) { 712 while (status == 0 && nops != hdr_arg.nops) {
705 status = process_op(hdr_arg.minorversion, nops, rqstp, 713 status = process_op(hdr_arg.minorversion, nops, rqstp,
706 &xdr_in, argp, &xdr_out, resp, &drc_status); 714 &xdr_in, argp, &xdr_out, resp, &cps);
707 nops++; 715 nops++;
708 } 716 }
709 717
@@ -716,6 +724,7 @@ static __be32 nfs4_callback_compound(struct svc_rqst *rqstp, void *argp, void *r
716 724
717 *hdr_res.status = status; 725 *hdr_res.status = status;
718 *hdr_res.nops = htonl(nops); 726 *hdr_res.nops = htonl(nops);
727 nfs_put_client(cps.clp);
719 dprintk("%s: done, status = %u\n", __func__, ntohl(status)); 728 dprintk("%s: done, status = %u\n", __func__, ntohl(status));
720 return rpc_success; 729 return rpc_success;
721} 730}
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index bc3a8620e8c3..11eb9934c747 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -410,70 +410,28 @@ static int nfs_sockaddr_cmp(const struct sockaddr *sa1,
410 return 0; 410 return 0;
411} 411}
412 412
413/* 413/* Common match routine for v4.0 and v4.1 callback services */
414 * Find a client by IP address and protocol version 414bool
415 * - returns NULL if no such client 415nfs4_cb_match_client(const struct sockaddr *addr, struct nfs_client *clp,
416 */ 416 u32 minorversion)
417struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion)
418{ 417{
419 struct nfs_client *clp; 418 struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
420 419
421 spin_lock(&nfs_client_lock); 420 /* Don't match clients that failed to initialise */
422 list_for_each_entry(clp, &nfs_client_list, cl_share_link) { 421 if (!(clp->cl_cons_state == NFS_CS_READY ||
423 struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; 422 clp->cl_cons_state == NFS_CS_SESSION_INITING))
423 return false;
424 424
425 /* Don't match clients that failed to initialise properly */ 425 /* Match the version and minorversion */
426 if (!(clp->cl_cons_state == NFS_CS_READY || 426 if (clp->rpc_ops->version != 4 ||
427 clp->cl_cons_state == NFS_CS_SESSION_INITING)) 427 clp->cl_minorversion != minorversion)
428 continue; 428 return false;
429 429
430 /* Different NFS versions cannot share the same nfs_client */ 430 /* Match only the IP address, not the port number */
431 if (clp->rpc_ops->version != nfsversion) 431 if (!nfs_sockaddr_match_ipaddr(addr, clap))
432 continue; 432 return false;
433
434 /* Match only the IP address, not the port number */
435 if (!nfs_sockaddr_match_ipaddr(addr, clap))
436 continue;
437 433
438 atomic_inc(&clp->cl_count); 434 return true;
439 spin_unlock(&nfs_client_lock);
440 return clp;
441 }
442 spin_unlock(&nfs_client_lock);
443 return NULL;
444}
445
446/*
447 * Find a client by IP address and protocol version
448 * - returns NULL if no such client
449 */
450struct nfs_client *nfs_find_client_next(struct nfs_client *clp)
451{
452 struct sockaddr *sap = (struct sockaddr *)&clp->cl_addr;
453 u32 nfsvers = clp->rpc_ops->version;
454
455 spin_lock(&nfs_client_lock);
456 list_for_each_entry_continue(clp, &nfs_client_list, cl_share_link) {
457 struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr;
458
459 /* Don't match clients that failed to initialise properly */
460 if (clp->cl_cons_state != NFS_CS_READY)
461 continue;
462
463 /* Different NFS versions cannot share the same nfs_client */
464 if (clp->rpc_ops->version != nfsvers)
465 continue;
466
467 /* Match only the IP address, not the port number */
468 if (!nfs_sockaddr_match_ipaddr(sap, clap))
469 continue;
470
471 atomic_inc(&clp->cl_count);
472 spin_unlock(&nfs_client_lock);
473 return clp;
474 }
475 spin_unlock(&nfs_client_lock);
476 return NULL;
477} 435}
478 436
479/* 437/*
@@ -1172,6 +1130,101 @@ error:
1172 1130
1173#ifdef CONFIG_NFS_V4 1131#ifdef CONFIG_NFS_V4
1174/* 1132/*
1133 * NFSv4.0 callback thread helper
1134 *
1135 * Find a client by IP address, protocol version, and minorversion
1136 *
1137 * Called from the pg_authenticate method. The callback identifier
1138 * is not used as it has not been decoded.
1139 *
1140 * Returns NULL if no such client
1141 */
1142struct nfs_client *
1143nfs4_find_client_no_ident(const struct sockaddr *addr)
1144{
1145 struct nfs_client *clp;
1146
1147 spin_lock(&nfs_client_lock);
1148 list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
1149 if (nfs4_cb_match_client(addr, clp, 0) == false)
1150 continue;
1151 atomic_inc(&clp->cl_count);
1152 spin_unlock(&nfs_client_lock);
1153 return clp;
1154 }
1155 spin_unlock(&nfs_client_lock);
1156 return NULL;
1157}
1158
1159/*
1160 * NFSv4.0 callback thread helper
1161 *
1162 * Find a client by callback identifier
1163 */
1164struct nfs_client *
1165nfs4_find_client_ident(int cb_ident)
1166{
1167 struct nfs_client *clp;
1168
1169 spin_lock(&nfs_client_lock);
1170 clp = idr_find(&cb_ident_idr, cb_ident);
1171 if (clp)
1172 atomic_inc(&clp->cl_count);
1173 spin_unlock(&nfs_client_lock);
1174 return clp;
1175}
1176
1177#if defined(CONFIG_NFS_V4_1)
1178/*
1179 * NFSv4.1 callback thread helper
1180 * For CB_COMPOUND calls, find a client by IP address, protocol version,
1181 * minorversion, and sessionID
1182 *
1183 * CREATE_SESSION triggers a CB_NULL ping from servers. The callback service
1184 * sessionid can only be set after the CREATE_SESSION return, so a CB_NULL
1185 * can arrive before the callback sessionid is set. For CB_NULL calls,
1186 * find a client by IP address protocol version, and minorversion.
1187 *
1188 * Returns NULL if no such client
1189 */
1190struct nfs_client *
1191nfs4_find_client_sessionid(const struct sockaddr *addr,
1192 struct nfs4_sessionid *sid, int is_cb_compound)
1193{
1194 struct nfs_client *clp;
1195
1196 spin_lock(&nfs_client_lock);
1197 list_for_each_entry(clp, &nfs_client_list, cl_share_link) {
1198 if (nfs4_cb_match_client(addr, clp, 1) == false)
1199 continue;
1200
1201 if (!nfs4_has_session(clp))
1202 continue;
1203
1204 /* Match sessionid unless cb_null call*/
1205 if (is_cb_compound && (memcmp(clp->cl_session->sess_id.data,
1206 sid->data, NFS4_MAX_SESSIONID_LEN) != 0))
1207 continue;
1208
1209 atomic_inc(&clp->cl_count);
1210 spin_unlock(&nfs_client_lock);
1211 return clp;
1212 }
1213 spin_unlock(&nfs_client_lock);
1214 return NULL;
1215}
1216
1217#else /* CONFIG_NFS_V4_1 */
1218
1219struct nfs_client *
1220nfs4_find_client_sessionid(const struct sockaddr *addr,
1221 struct nfs4_sessionid *sid, int is_cb_compound)
1222{
1223 return NULL;
1224}
1225#endif /* CONFIG_NFS_V4_1 */
1226
1227/*
1175 * Initialize the NFS4 callback service 1228 * Initialize the NFS4 callback service
1176 */ 1229 */
1177static int nfs4_init_callback(struct nfs_client *clp) 1230static int nfs4_init_callback(struct nfs_client *clp)
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 7c803c916574..bfa3a34af801 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -130,8 +130,11 @@ extern struct rpc_program nfs_program;
130 130
131extern void nfs_cleanup_cb_ident_idr(void); 131extern void nfs_cleanup_cb_ident_idr(void);
132extern void nfs_put_client(struct nfs_client *); 132extern void nfs_put_client(struct nfs_client *);
133extern struct nfs_client *nfs_find_client(const struct sockaddr *, u32); 133extern struct nfs_client *nfs4_find_client_no_ident(const struct sockaddr *);
134extern struct nfs_client *nfs_find_client_next(struct nfs_client *); 134extern struct nfs_client *nfs4_find_client_ident(int);
135extern struct nfs_client *
136nfs4_find_client_sessionid(const struct sockaddr *, struct nfs4_sessionid *,
137 int);
135extern struct nfs_server *nfs_create_server( 138extern struct nfs_server *nfs_create_server(
136 const struct nfs_parsed_mount_data *, 139 const struct nfs_parsed_mount_data *,
137 struct nfs_fh *); 140 struct nfs_fh *);