diff options
author | Andy Adamson <andros@netapp.com> | 2011-01-05 21:04:32 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2011-01-06 14:46:24 -0500 |
commit | c36fca52f5e4594ffd0ff175b328966b0d393184 (patch) | |
tree | 6d771744cc49f0edc0d2b6b2f9fe919163002346 /fs/nfs | |
parent | 2c2618c6f29c41a0a966f14f05c8bf45fcabb750 (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.c | 21 | ||||
-rw-r--r-- | fs/nfs/callback.h | 28 | ||||
-rw-r--r-- | fs/nfs/callback_proc.c | 167 | ||||
-rw-r--r-- | fs/nfs/callback_xdr.c | 39 | ||||
-rw-r--r-- | fs/nfs/client.c | 171 | ||||
-rw-r--r-- | fs/nfs/internal.h | 7 |
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 */ | ||
386 | static 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. */ | ||
387 | static int nfs_callback_authenticate(struct svc_rqst *rqstp) | 402 | static 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 | ||
37 | struct 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 | |||
37 | struct cb_compound_hdr_arg { | 43 | struct 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 | ||
106 | extern unsigned nfs4_callback_sequence(struct cb_sequenceargs *args, | 113 | extern __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 | ||
109 | extern int nfs41_validate_delegation_stateid(struct nfs_delegation *delegation, | 117 | extern 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 | ||
121 | extern unsigned nfs4_callback_recallany(struct cb_recallanyargs *args, void *dummy); | 129 | extern __be32 nfs4_callback_recallany(struct cb_recallanyargs *args, |
130 | void *dummy, | ||
131 | struct cb_process_state *cps); | ||
122 | 132 | ||
123 | struct cb_recallslotargs { | 133 | struct 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 | }; |
127 | extern unsigned nfs4_callback_recallslot(struct cb_recallslotargs *args, | 137 | extern __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 | ||
132 | extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, struct cb_getattrres *res); | 143 | extern __be32 nfs4_callback_getattr(struct cb_getattrargs *args, |
133 | extern __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy); | 144 | struct cb_getattrres *res, |
145 | struct cb_process_state *cps); | ||
146 | extern __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 |
136 | extern int nfs_callback_up(u32 minorversion, struct rpc_xprt *xprt); | 150 | extern 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 * | |||
55 | out_iput: | 57 | out_iput: |
56 | rcu_read_unlock(); | 58 | rcu_read_unlock(); |
57 | iput(inode); | 59 | iput(inode); |
58 | out_putclient: | ||
59 | nfs_put_client(clp); | ||
60 | out: | 60 | out: |
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); | ||
101 | out: | 95 | out: |
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 | ||
311 | out_putclient: | ||
312 | nfs_put_client(clp); | ||
313 | out: | 278 | out: |
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); |
351 | out: | 317 | out: |
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); |
385 | out_putclient: | ||
386 | nfs_put_client(clp); /* balance nfs_find_client */ | ||
387 | out: | 350 | out: |
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 | ||
36 | typedef __be32 (*callback_process_op_t)(void *, void *); | 38 | typedef __be32 (*callback_process_op_t)(void *, void *, |
39 | struct cb_process_state *); | ||
37 | typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *); | 40 | typedef __be32 (*callback_decode_arg_t)(struct svc_rqst *, struct xdr_stream *, void *); |
38 | typedef __be32 (*callback_encode_res_t)(struct svc_rqst *, struct xdr_stream *, void *); | 41 | typedef __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) | |||
621 | static __be32 process_op(uint32_t minorversion, int nop, | 624 | static __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 | |||
666 | encode_hdr: | 664 | encode_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 | 414 | bool |
415 | * - returns NULL if no such client | 415 | nfs4_cb_match_client(const struct sockaddr *addr, struct nfs_client *clp, |
416 | */ | 416 | u32 minorversion) |
417 | struct 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 | */ | ||
450 | struct 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 | */ | ||
1142 | struct nfs_client * | ||
1143 | nfs4_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 | */ | ||
1164 | struct nfs_client * | ||
1165 | nfs4_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 | */ | ||
1190 | struct nfs_client * | ||
1191 | nfs4_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 | |||
1219 | struct nfs_client * | ||
1220 | nfs4_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 | */ |
1177 | static int nfs4_init_callback(struct nfs_client *clp) | 1230 | static 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 | ||
131 | extern void nfs_cleanup_cb_ident_idr(void); | 131 | extern void nfs_cleanup_cb_ident_idr(void); |
132 | extern void nfs_put_client(struct nfs_client *); | 132 | extern void nfs_put_client(struct nfs_client *); |
133 | extern struct nfs_client *nfs_find_client(const struct sockaddr *, u32); | 133 | extern struct nfs_client *nfs4_find_client_no_ident(const struct sockaddr *); |
134 | extern struct nfs_client *nfs_find_client_next(struct nfs_client *); | 134 | extern struct nfs_client *nfs4_find_client_ident(int); |
135 | extern struct nfs_client * | ||
136 | nfs4_find_client_sessionid(const struct sockaddr *, struct nfs4_sessionid *, | ||
137 | int); | ||
135 | extern struct nfs_server *nfs_create_server( | 138 | extern 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 *); |