aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2012-09-14 17:24:32 -0400
committerTrond Myklebust <Trond.Myklebust@netapp.com>2012-10-01 18:33:33 -0400
commit05f4c350ee02e9461c6ae3a880ea326a06835e37 (patch)
tree847e24e55e47c06fcb46106b4922b915bb0109e6 /fs
parente984a55a7418f777407c7edbb2bdf5eb9559b5e2 (diff)
NFS: Discover NFSv4 server trunking when mounting
"Server trunking" is a fancy named for a multi-homed NFS server. Trunking might occur if a client sends NFS requests for a single workload to multiple network interfaces on the same server. There are some implications for NFSv4 state management that make it useful for a client to know if a single NFSv4 server instance is multi-homed. (Note this is only a consideration for NFSv4, not for legacy versions of NFS, which are stateless). If a client cares about server trunking, no NFSv4 operations can proceed until that client determines who it is talking to. Thus server IP trunking discovery must be done when the client first encounters an unfamiliar server IP address. The nfs_get_client() function walks the nfs_client_list and matches on server IP address. The outcome of that walk tells us immediately if we have an unfamiliar server IP address. It invokes nfs_init_client() in this case. Thus, nfs4_init_client() is a good spot to perform trunking discovery. Discovery requires a client to establish a fresh client ID, so our client will now send SETCLIENTID or EXCHANGE_ID as the first NFS operation after a successful ping, rather than waiting for an application to perform an operation that requires NFSv4 state. The exact process for detecting trunking is different for NFSv4.0 and NFSv4.1, so a minorversion-specific init_client callout method is introduced. CLID_INUSE recovery is important for the trunking discovery process. CLID_INUSE is a sign the server recognizes the client's nfs_client_id4 id string, but the client is using the wrong principal this time for the SETCLIENTID operation. The SETCLIENTID must be retried with a series of different principals until one works, and then the rest of trunking discovery can proceed. Signed-off-by: Chuck Lever <chuck.lever@oracle.com> Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs')
-rw-r--r--fs/nfs/client.c3
-rw-r--r--fs/nfs/internal.h6
-rw-r--r--fs/nfs/nfs4_fs.h8
-rw-r--r--fs/nfs/nfs4client.c253
-rw-r--r--fs/nfs/nfs4proc.c4
-rw-r--r--fs/nfs/nfs4state.c182
6 files changed, 454 insertions, 2 deletions
diff --git a/fs/nfs/client.c b/fs/nfs/client.c
index 92aed2e08bd5..57d2a5c3d933 100644
--- a/fs/nfs/client.c
+++ b/fs/nfs/client.c
@@ -498,7 +498,8 @@ nfs_get_client(const struct nfs_client_initdata *cl_init,
498 return nfs_found_client(cl_init, clp); 498 return nfs_found_client(cl_init, clp);
499 } 499 }
500 if (new) { 500 if (new) {
501 list_add(&new->cl_share_link, &nn->nfs_client_list); 501 list_add_tail(&new->cl_share_link,
502 &nn->nfs_client_list);
502 spin_unlock(&nn->nfs_client_lock); 503 spin_unlock(&nn->nfs_client_lock);
503 new->cl_flags = cl_init->init_flags; 504 new->cl_flags = cl_init->init_flags;
504 return rpc_ops->init_client(new, timeparms, ip_addr, 505 return rpc_ops->init_client(new, timeparms, ip_addr,
diff --git a/fs/nfs/internal.h b/fs/nfs/internal.h
index 89560be07e4a..89a795dc3027 100644
--- a/fs/nfs/internal.h
+++ b/fs/nfs/internal.h
@@ -483,6 +483,12 @@ extern int _nfs4_call_sync_session(struct rpc_clnt *clnt,
483 struct nfs4_sequence_args *args, 483 struct nfs4_sequence_args *args,
484 struct nfs4_sequence_res *res, 484 struct nfs4_sequence_res *res,
485 int cache_reply); 485 int cache_reply);
486extern int nfs40_walk_client_list(struct nfs_client *clp,
487 struct nfs_client **result,
488 struct rpc_cred *cred);
489extern int nfs41_walk_client_list(struct nfs_client *clp,
490 struct nfs_client **result,
491 struct rpc_cred *cred);
486 492
487/* 493/*
488 * Determine the device name as a string 494 * Determine the device name as a string
diff --git a/fs/nfs/nfs4_fs.h b/fs/nfs/nfs4_fs.h
index 9cacc131a8a4..832503c7a00e 100644
--- a/fs/nfs/nfs4_fs.h
+++ b/fs/nfs/nfs4_fs.h
@@ -191,6 +191,8 @@ struct nfs4_state_recovery_ops {
191 int (*establish_clid)(struct nfs_client *, struct rpc_cred *); 191 int (*establish_clid)(struct nfs_client *, struct rpc_cred *);
192 struct rpc_cred * (*get_clid_cred)(struct nfs_client *); 192 struct rpc_cred * (*get_clid_cred)(struct nfs_client *);
193 int (*reclaim_complete)(struct nfs_client *); 193 int (*reclaim_complete)(struct nfs_client *);
194 int (*detect_trunking)(struct nfs_client *, struct nfs_client **,
195 struct rpc_cred *);
194}; 196};
195 197
196struct nfs4_state_maintenance_ops { 198struct nfs4_state_maintenance_ops {
@@ -320,9 +322,15 @@ extern void nfs4_renew_state(struct work_struct *);
320/* nfs4state.c */ 322/* nfs4state.c */
321struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp); 323struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp);
322struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp); 324struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp);
325int nfs4_discover_server_trunking(struct nfs_client *clp,
326 struct nfs_client **);
327int nfs40_discover_server_trunking(struct nfs_client *clp,
328 struct nfs_client **, struct rpc_cred *);
323#if defined(CONFIG_NFS_V4_1) 329#if defined(CONFIG_NFS_V4_1)
324struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp); 330struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp);
325struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp); 331struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp);
332int nfs41_discover_server_trunking(struct nfs_client *clp,
333 struct nfs_client **, struct rpc_cred *);
326extern void nfs4_schedule_session_recovery(struct nfs4_session *, int); 334extern void nfs4_schedule_session_recovery(struct nfs4_session *, int);
327#else 335#else
328static inline void nfs4_schedule_session_recovery(struct nfs4_session *session, int err) 336static inline void nfs4_schedule_session_recovery(struct nfs4_session *session, int err)
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c
index 612f5ebaabac..14ddd4d30966 100644
--- a/fs/nfs/nfs4client.c
+++ b/fs/nfs/nfs4client.c
@@ -185,6 +185,7 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp,
185 rpc_authflavor_t authflavour) 185 rpc_authflavor_t authflavour)
186{ 186{
187 char buf[INET6_ADDRSTRLEN + 1]; 187 char buf[INET6_ADDRSTRLEN + 1];
188 struct nfs_client *old;
188 int error; 189 int error;
189 190
190 if (clp->cl_cons_state == NFS_CS_READY) { 191 if (clp->cl_cons_state == NFS_CS_READY) {
@@ -230,6 +231,17 @@ struct nfs_client *nfs4_init_client(struct nfs_client *clp,
230 231
231 if (!nfs4_has_session(clp)) 232 if (!nfs4_has_session(clp))
232 nfs_mark_client_ready(clp, NFS_CS_READY); 233 nfs_mark_client_ready(clp, NFS_CS_READY);
234
235 error = nfs4_discover_server_trunking(clp, &old);
236 if (error < 0)
237 goto error;
238 if (clp != old) {
239 clp->cl_preserve_clid = true;
240 nfs_put_client(clp);
241 clp = old;
242 atomic_inc(&clp->cl_count);
243 }
244
233 return clp; 245 return clp;
234 246
235error: 247error:
@@ -239,6 +251,247 @@ error:
239 return ERR_PTR(error); 251 return ERR_PTR(error);
240} 252}
241 253
254/*
255 * Returns true if the client IDs match
256 */
257static bool nfs4_match_clientids(struct nfs_client *a, struct nfs_client *b)
258{
259 if (a->cl_clientid != b->cl_clientid) {
260 dprintk("NFS: --> %s client ID %llx does not match %llx\n",
261 __func__, a->cl_clientid, b->cl_clientid);
262 return false;
263 }
264 dprintk("NFS: --> %s client ID %llx matches %llx\n",
265 __func__, a->cl_clientid, b->cl_clientid);
266 return true;
267}
268
269/*
270 * SETCLIENTID just did a callback update with the callback ident in
271 * "drop," but server trunking discovery claims "drop" and "keep" are
272 * actually the same server. Swap the callback IDs so that "keep"
273 * will continue to use the callback ident the server now knows about,
274 * and so that "keep"'s original callback ident is destroyed when
275 * "drop" is freed.
276 */
277static void nfs4_swap_callback_idents(struct nfs_client *keep,
278 struct nfs_client *drop)
279{
280 struct nfs_net *nn = net_generic(keep->cl_net, nfs_net_id);
281 unsigned int save = keep->cl_cb_ident;
282
283 if (keep->cl_cb_ident == drop->cl_cb_ident)
284 return;
285
286 dprintk("%s: keeping callback ident %u and dropping ident %u\n",
287 __func__, keep->cl_cb_ident, drop->cl_cb_ident);
288
289 spin_lock(&nn->nfs_client_lock);
290
291 idr_replace(&nn->cb_ident_idr, keep, drop->cl_cb_ident);
292 keep->cl_cb_ident = drop->cl_cb_ident;
293
294 idr_replace(&nn->cb_ident_idr, drop, save);
295 drop->cl_cb_ident = save;
296
297 spin_unlock(&nn->nfs_client_lock);
298}
299
300/**
301 * nfs40_walk_client_list - Find server that recognizes a client ID
302 *
303 * @new: nfs_client with client ID to test
304 * @result: OUT: found nfs_client, or new
305 * @cred: credential to use for trunking test
306 *
307 * Returns zero, a negative errno, or a negative NFS4ERR status.
308 * If zero is returned, an nfs_client pointer is planted in "result."
309 *
310 * NB: nfs40_walk_client_list() relies on the new nfs_client being
311 * the last nfs_client on the list.
312 */
313int nfs40_walk_client_list(struct nfs_client *new,
314 struct nfs_client **result,
315 struct rpc_cred *cred)
316{
317 struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id);
318 struct nfs_client *pos, *n, *prev = NULL;
319 struct nfs4_setclientid_res clid = {
320 .clientid = new->cl_clientid,
321 .confirm = new->cl_confirm,
322 };
323 int status;
324
325 spin_lock(&nn->nfs_client_lock);
326 list_for_each_entry_safe(pos, n, &nn->nfs_client_list, cl_share_link) {
327 /* If "pos" isn't marked ready, we can't trust the
328 * remaining fields in "pos" */
329 if (pos->cl_cons_state < NFS_CS_READY)
330 continue;
331
332 if (pos->rpc_ops != new->rpc_ops)
333 continue;
334
335 if (pos->cl_proto != new->cl_proto)
336 continue;
337
338 if (pos->cl_minorversion != new->cl_minorversion)
339 continue;
340
341 if (pos->cl_clientid != new->cl_clientid)
342 continue;
343
344 atomic_inc(&pos->cl_count);
345 spin_unlock(&nn->nfs_client_lock);
346
347 if (prev)
348 nfs_put_client(prev);
349
350 status = nfs4_proc_setclientid_confirm(pos, &clid, cred);
351 if (status == 0) {
352 nfs4_swap_callback_idents(pos, new);
353
354 nfs_put_client(pos);
355 *result = pos;
356 dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n",
357 __func__, pos, atomic_read(&pos->cl_count));
358 return 0;
359 }
360 if (status != -NFS4ERR_STALE_CLIENTID) {
361 nfs_put_client(pos);
362 dprintk("NFS: <-- %s status = %d, no result\n",
363 __func__, status);
364 return status;
365 }
366
367 spin_lock(&nn->nfs_client_lock);
368 prev = pos;
369 }
370
371 /*
372 * No matching nfs_client found. This should be impossible,
373 * because the new nfs_client has already been added to
374 * nfs_client_list by nfs_get_client().
375 *
376 * Don't BUG(), since the caller is holding a mutex.
377 */
378 if (prev)
379 nfs_put_client(prev);
380 spin_unlock(&nn->nfs_client_lock);
381 pr_err("NFS: %s Error: no matching nfs_client found\n", __func__);
382 return -NFS4ERR_STALE_CLIENTID;
383}
384
385#ifdef CONFIG_NFS_V4_1
386/*
387 * Returns true if the server owners match
388 */
389static bool
390nfs4_match_serverowners(struct nfs_client *a, struct nfs_client *b)
391{
392 struct nfs41_server_owner *o1 = a->cl_serverowner;
393 struct nfs41_server_owner *o2 = b->cl_serverowner;
394
395 if (o1->minor_id != o2->minor_id) {
396 dprintk("NFS: --> %s server owner minor IDs do not match\n",
397 __func__);
398 return false;
399 }
400
401 if (o1->major_id_sz != o2->major_id_sz)
402 goto out_major_mismatch;
403 if (memcmp(o1->major_id, o2->major_id, o1->major_id_sz) != 0)
404 goto out_major_mismatch;
405
406 dprintk("NFS: --> %s server owners match\n", __func__);
407 return true;
408
409out_major_mismatch:
410 dprintk("NFS: --> %s server owner major IDs do not match\n",
411 __func__);
412 return false;
413}
414
415/**
416 * nfs41_walk_client_list - Find nfs_client that matches a client/server owner
417 *
418 * @new: nfs_client with client ID to test
419 * @result: OUT: found nfs_client, or new
420 * @cred: credential to use for trunking test
421 *
422 * Returns zero, a negative errno, or a negative NFS4ERR status.
423 * If zero is returned, an nfs_client pointer is planted in "result."
424 *
425 * NB: nfs41_walk_client_list() relies on the new nfs_client being
426 * the last nfs_client on the list.
427 */
428int nfs41_walk_client_list(struct nfs_client *new,
429 struct nfs_client **result,
430 struct rpc_cred *cred)
431{
432 struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id);
433 struct nfs_client *pos, *n, *prev = NULL;
434 int error;
435
436 spin_lock(&nn->nfs_client_lock);
437 list_for_each_entry_safe(pos, n, &nn->nfs_client_list, cl_share_link) {
438 /* If "pos" isn't marked ready, we can't trust the
439 * remaining fields in "pos", especially the client
440 * ID and serverowner fields. Wait for CREATE_SESSION
441 * to finish. */
442 if (pos->cl_cons_state < NFS_CS_READY) {
443 atomic_inc(&pos->cl_count);
444 spin_unlock(&nn->nfs_client_lock);
445
446 if (prev)
447 nfs_put_client(prev);
448 prev = pos;
449
450 error = nfs_wait_client_init_complete(pos);
451 if (error < 0) {
452 nfs_put_client(pos);
453 continue;
454 }
455
456 spin_lock(&nn->nfs_client_lock);
457 }
458
459 if (pos->rpc_ops != new->rpc_ops)
460 continue;
461
462 if (pos->cl_proto != new->cl_proto)
463 continue;
464
465 if (pos->cl_minorversion != new->cl_minorversion)
466 continue;
467
468 if (!nfs4_match_clientids(pos, new))
469 continue;
470
471 if (!nfs4_match_serverowners(pos, new))
472 continue;
473
474 spin_unlock(&nn->nfs_client_lock);
475 dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n",
476 __func__, pos, atomic_read(&pos->cl_count));
477
478 *result = pos;
479 return 0;
480 }
481
482 /*
483 * No matching nfs_client found. This should be impossible,
484 * because the new nfs_client has already been added to
485 * nfs_client_list by nfs_get_client().
486 *
487 * Don't BUG(), since the caller is holding a mutex.
488 */
489 spin_unlock(&nn->nfs_client_lock);
490 pr_err("NFS: %s Error: no matching nfs_client found\n", __func__);
491 return -NFS4ERR_STALE_CLIENTID;
492}
493#endif /* CONFIG_NFS_V4_1 */
494
242static void nfs4_destroy_server(struct nfs_server *server) 495static void nfs4_destroy_server(struct nfs_server *server)
243{ 496{
244 nfs_server_return_all_delegations(server); 497 nfs_server_return_all_delegations(server);
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 461411171966..b5834abfcbff 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -5458,6 +5458,8 @@ int nfs4_destroy_clientid(struct nfs_client *clp)
5458 goto out; 5458 goto out;
5459 if (clp->cl_exchange_flags == 0) 5459 if (clp->cl_exchange_flags == 0)
5460 goto out; 5460 goto out;
5461 if (clp->cl_preserve_clid)
5462 goto out;
5461 cred = nfs4_get_exchange_id_cred(clp); 5463 cred = nfs4_get_exchange_id_cred(clp);
5462 ret = nfs4_proc_destroy_clientid(clp, cred); 5464 ret = nfs4_proc_destroy_clientid(clp, cred);
5463 if (cred) 5465 if (cred)
@@ -6871,6 +6873,7 @@ static const struct nfs4_state_recovery_ops nfs40_reboot_recovery_ops = {
6871 .recover_lock = nfs4_lock_reclaim, 6873 .recover_lock = nfs4_lock_reclaim,
6872 .establish_clid = nfs4_init_clientid, 6874 .establish_clid = nfs4_init_clientid,
6873 .get_clid_cred = nfs4_get_setclientid_cred, 6875 .get_clid_cred = nfs4_get_setclientid_cred,
6876 .detect_trunking = nfs40_discover_server_trunking,
6874}; 6877};
6875 6878
6876#if defined(CONFIG_NFS_V4_1) 6879#if defined(CONFIG_NFS_V4_1)
@@ -6882,6 +6885,7 @@ static const struct nfs4_state_recovery_ops nfs41_reboot_recovery_ops = {
6882 .establish_clid = nfs41_init_clientid, 6885 .establish_clid = nfs41_init_clientid,
6883 .get_clid_cred = nfs4_get_exchange_id_cred, 6886 .get_clid_cred = nfs4_get_exchange_id_cred,
6884 .reclaim_complete = nfs41_proc_reclaim_complete, 6887 .reclaim_complete = nfs41_proc_reclaim_complete,
6888 .detect_trunking = nfs41_discover_server_trunking,
6885}; 6889};
6886#endif /* CONFIG_NFS_V4_1 */ 6890#endif /* CONFIG_NFS_V4_1 */
6887 6891
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 38eeefd95375..5c4286643701 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -51,6 +51,8 @@
51#include <linux/bitops.h> 51#include <linux/bitops.h>
52#include <linux/jiffies.h> 52#include <linux/jiffies.h>
53 53
54#include <linux/sunrpc/clnt.h>
55
54#include "nfs4_fs.h" 56#include "nfs4_fs.h"
55#include "callback.h" 57#include "callback.h"
56#include "delegation.h" 58#include "delegation.h"
@@ -63,7 +65,7 @@
63#define OPENOWNER_POOL_SIZE 8 65#define OPENOWNER_POOL_SIZE 8
64 66
65const nfs4_stateid zero_stateid; 67const nfs4_stateid zero_stateid;
66 68static DEFINE_MUTEX(nfs_clid_init_mutex);
67static LIST_HEAD(nfs4_clientid_list); 69static LIST_HEAD(nfs4_clientid_list);
68 70
69int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred) 71int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred)
@@ -98,6 +100,55 @@ out:
98 return status; 100 return status;
99} 101}
100 102
103/**
104 * nfs40_discover_server_trunking - Detect server IP address trunking (mv0)
105 *
106 * @clp: nfs_client under test
107 * @result: OUT: found nfs_client, or clp
108 * @cred: credential to use for trunking test
109 *
110 * Returns zero, a negative errno, or a negative NFS4ERR status.
111 * If zero is returned, an nfs_client pointer is planted in
112 * "result".
113 *
114 * Note: The returned client may not yet be marked ready.
115 */
116int nfs40_discover_server_trunking(struct nfs_client *clp,
117 struct nfs_client **result,
118 struct rpc_cred *cred)
119{
120 struct nfs4_setclientid_res clid = {
121 .clientid = clp->cl_clientid,
122 .confirm = clp->cl_confirm,
123 };
124 unsigned short port;
125 int status;
126
127 port = nfs_callback_tcpport;
128 if (clp->cl_addr.ss_family == AF_INET6)
129 port = nfs_callback_tcpport6;
130
131 status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid);
132 if (status != 0)
133 goto out;
134 clp->cl_clientid = clid.clientid;
135 clp->cl_confirm = clid.confirm;
136
137 status = nfs40_walk_client_list(clp, result, cred);
138 switch (status) {
139 case -NFS4ERR_STALE_CLIENTID:
140 set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state);
141 case 0:
142 /* Sustain the lease, even if it's empty. If the clientid4
143 * goes stale it's of no use for trunking discovery. */
144 nfs4_schedule_state_renewal(*result);
145 break;
146 }
147
148out:
149 return status;
150}
151
101struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp) 152struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
102{ 153{
103 struct rpc_cred *cred = NULL; 154 struct rpc_cred *cred = NULL;
@@ -277,6 +328,32 @@ out:
277 return status; 328 return status;
278} 329}
279 330
331/**
332 * nfs41_discover_server_trunking - Detect server IP address trunking (mv1)
333 *
334 * @clp: nfs_client under test
335 * @result: OUT: found nfs_client, or clp
336 * @cred: credential to use for trunking test
337 *
338 * Returns NFS4_OK, a negative errno, or a negative NFS4ERR status.
339 * If NFS4_OK is returned, an nfs_client pointer is planted in
340 * "result".
341 *
342 * Note: The returned client may not yet be marked ready.
343 */
344int nfs41_discover_server_trunking(struct nfs_client *clp,
345 struct nfs_client **result,
346 struct rpc_cred *cred)
347{
348 int status;
349
350 status = nfs4_proc_exchange_id(clp, cred);
351 if (status != NFS4_OK)
352 return status;
353
354 return nfs41_walk_client_list(clp, result, cred);
355}
356
280struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp) 357struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp)
281{ 358{
282 struct rpc_cred *cred; 359 struct rpc_cred *cred;
@@ -1705,6 +1782,109 @@ static int nfs4_purge_lease(struct nfs_client *clp)
1705 return 0; 1782 return 0;
1706} 1783}
1707 1784
1785/**
1786 * nfs4_discover_server_trunking - Detect server IP address trunking
1787 *
1788 * @clp: nfs_client under test
1789 * @result: OUT: found nfs_client, or clp
1790 *
1791 * Returns zero or a negative errno. If zero is returned,
1792 * an nfs_client pointer is planted in "result".
1793 *
1794 * Note: since we are invoked in process context, and
1795 * not from inside the state manager, we cannot use
1796 * nfs4_handle_reclaim_lease_error().
1797 */
1798int nfs4_discover_server_trunking(struct nfs_client *clp,
1799 struct nfs_client **result)
1800{
1801 const struct nfs4_state_recovery_ops *ops =
1802 clp->cl_mvops->reboot_recovery_ops;
1803 rpc_authflavor_t *flavors, flav, save;
1804 struct rpc_clnt *clnt;
1805 struct rpc_cred *cred;
1806 int i, len, status;
1807
1808 dprintk("NFS: %s: testing '%s'\n", __func__, clp->cl_hostname);
1809
1810 len = NFS_MAX_SECFLAVORS;
1811 flavors = kcalloc(len, sizeof(*flavors), GFP_KERNEL);
1812 if (flavors == NULL) {
1813 status = -ENOMEM;
1814 goto out;
1815 }
1816 len = rpcauth_list_flavors(flavors, len);
1817 if (len < 0) {
1818 status = len;
1819 goto out_free;
1820 }
1821 clnt = clp->cl_rpcclient;
1822 save = clnt->cl_auth->au_flavor;
1823 i = 0;
1824
1825 mutex_lock(&nfs_clid_init_mutex);
1826 status = -ENOENT;
1827again:
1828 cred = ops->get_clid_cred(clp);
1829 if (cred == NULL)
1830 goto out_unlock;
1831
1832 status = ops->detect_trunking(clp, result, cred);
1833 put_rpccred(cred);
1834 switch (status) {
1835 case 0:
1836 break;
1837
1838 case -EACCES:
1839 if (clp->cl_machine_cred == NULL)
1840 break;
1841 /* Handle case where the user hasn't set up machine creds */
1842 nfs4_clear_machine_cred(clp);
1843 case -NFS4ERR_DELAY:
1844 case -ETIMEDOUT:
1845 case -EAGAIN:
1846 ssleep(1);
1847 dprintk("NFS: %s after status %d, retrying\n",
1848 __func__, status);
1849 goto again;
1850
1851 case -NFS4ERR_CLID_INUSE:
1852 case -NFS4ERR_WRONGSEC:
1853 status = -EPERM;
1854 if (i >= len)
1855 break;
1856
1857 flav = flavors[i++];
1858 if (flav == save)
1859 flav = flavors[i++];
1860 clnt = rpc_clone_client_set_auth(clnt, flav);
1861 if (IS_ERR(clnt)) {
1862 status = PTR_ERR(clnt);
1863 break;
1864 }
1865 clp->cl_rpcclient = clnt;
1866 goto again;
1867
1868 case -NFS4ERR_MINOR_VERS_MISMATCH:
1869 status = -EPROTONOSUPPORT;
1870 break;
1871
1872 case -EKEYEXPIRED:
1873 nfs4_warn_keyexpired(clp->cl_hostname);
1874 case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery
1875 * in nfs4_exchange_id */
1876 status = -EKEYEXPIRED;
1877 }
1878
1879out_unlock:
1880 mutex_unlock(&nfs_clid_init_mutex);
1881out_free:
1882 kfree(flavors);
1883out:
1884 dprintk("NFS: %s: status = %d\n", __func__, status);
1885 return status;
1886}
1887
1708#ifdef CONFIG_NFS_V4_1 1888#ifdef CONFIG_NFS_V4_1
1709void nfs4_schedule_session_recovery(struct nfs4_session *session, int err) 1889void nfs4_schedule_session_recovery(struct nfs4_session *session, int err)
1710{ 1890{