diff options
Diffstat (limited to 'fs/nfs/nfs4client.c')
-rw-r--r-- | fs/nfs/nfs4client.c | 256 |
1 files changed, 255 insertions, 1 deletions
diff --git a/fs/nfs/nfs4client.c b/fs/nfs/nfs4client.c index 24eb663f8ed5..6bacfde1319a 100644 --- a/fs/nfs/nfs4client.c +++ b/fs/nfs/nfs4client.c | |||
@@ -84,7 +84,7 @@ error: | |||
84 | static void nfs4_destroy_callback(struct nfs_client *clp) | 84 | static void nfs4_destroy_callback(struct nfs_client *clp) |
85 | { | 85 | { |
86 | if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) | 86 | if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) |
87 | nfs_callback_down(clp->cl_mvops->minor_version); | 87 | nfs_callback_down(clp->cl_mvops->minor_version, clp->cl_net); |
88 | } | 88 | } |
89 | 89 | ||
90 | static void nfs4_shutdown_client(struct nfs_client *clp) | 90 | static void nfs4_shutdown_client(struct nfs_client *clp) |
@@ -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 | ||
235 | error: | 247 | error: |
@@ -239,6 +251,248 @@ error: | |||
239 | return ERR_PTR(error); | 251 | return ERR_PTR(error); |
240 | } | 252 | } |
241 | 253 | ||
254 | /* | ||
255 | * SETCLIENTID just did a callback update with the callback ident in | ||
256 | * "drop," but server trunking discovery claims "drop" and "keep" are | ||
257 | * actually the same server. Swap the callback IDs so that "keep" | ||
258 | * will continue to use the callback ident the server now knows about, | ||
259 | * and so that "keep"'s original callback ident is destroyed when | ||
260 | * "drop" is freed. | ||
261 | */ | ||
262 | static void nfs4_swap_callback_idents(struct nfs_client *keep, | ||
263 | struct nfs_client *drop) | ||
264 | { | ||
265 | struct nfs_net *nn = net_generic(keep->cl_net, nfs_net_id); | ||
266 | unsigned int save = keep->cl_cb_ident; | ||
267 | |||
268 | if (keep->cl_cb_ident == drop->cl_cb_ident) | ||
269 | return; | ||
270 | |||
271 | dprintk("%s: keeping callback ident %u and dropping ident %u\n", | ||
272 | __func__, keep->cl_cb_ident, drop->cl_cb_ident); | ||
273 | |||
274 | spin_lock(&nn->nfs_client_lock); | ||
275 | |||
276 | idr_replace(&nn->cb_ident_idr, keep, drop->cl_cb_ident); | ||
277 | keep->cl_cb_ident = drop->cl_cb_ident; | ||
278 | |||
279 | idr_replace(&nn->cb_ident_idr, drop, save); | ||
280 | drop->cl_cb_ident = save; | ||
281 | |||
282 | spin_unlock(&nn->nfs_client_lock); | ||
283 | } | ||
284 | |||
285 | /** | ||
286 | * nfs40_walk_client_list - Find server that recognizes a client ID | ||
287 | * | ||
288 | * @new: nfs_client with client ID to test | ||
289 | * @result: OUT: found nfs_client, or new | ||
290 | * @cred: credential to use for trunking test | ||
291 | * | ||
292 | * Returns zero, a negative errno, or a negative NFS4ERR status. | ||
293 | * If zero is returned, an nfs_client pointer is planted in "result." | ||
294 | * | ||
295 | * NB: nfs40_walk_client_list() relies on the new nfs_client being | ||
296 | * the last nfs_client on the list. | ||
297 | */ | ||
298 | int nfs40_walk_client_list(struct nfs_client *new, | ||
299 | struct nfs_client **result, | ||
300 | struct rpc_cred *cred) | ||
301 | { | ||
302 | struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); | ||
303 | struct nfs_client *pos, *n, *prev = NULL; | ||
304 | struct nfs4_setclientid_res clid = { | ||
305 | .clientid = new->cl_clientid, | ||
306 | .confirm = new->cl_confirm, | ||
307 | }; | ||
308 | int status; | ||
309 | |||
310 | spin_lock(&nn->nfs_client_lock); | ||
311 | list_for_each_entry_safe(pos, n, &nn->nfs_client_list, cl_share_link) { | ||
312 | /* If "pos" isn't marked ready, we can't trust the | ||
313 | * remaining fields in "pos" */ | ||
314 | if (pos->cl_cons_state < NFS_CS_READY) | ||
315 | continue; | ||
316 | |||
317 | if (pos->rpc_ops != new->rpc_ops) | ||
318 | continue; | ||
319 | |||
320 | if (pos->cl_proto != new->cl_proto) | ||
321 | continue; | ||
322 | |||
323 | if (pos->cl_minorversion != new->cl_minorversion) | ||
324 | continue; | ||
325 | |||
326 | if (pos->cl_clientid != new->cl_clientid) | ||
327 | continue; | ||
328 | |||
329 | atomic_inc(&pos->cl_count); | ||
330 | spin_unlock(&nn->nfs_client_lock); | ||
331 | |||
332 | if (prev) | ||
333 | nfs_put_client(prev); | ||
334 | |||
335 | status = nfs4_proc_setclientid_confirm(pos, &clid, cred); | ||
336 | if (status == 0) { | ||
337 | nfs4_swap_callback_idents(pos, new); | ||
338 | |||
339 | nfs_put_client(pos); | ||
340 | *result = pos; | ||
341 | dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n", | ||
342 | __func__, pos, atomic_read(&pos->cl_count)); | ||
343 | return 0; | ||
344 | } | ||
345 | if (status != -NFS4ERR_STALE_CLIENTID) { | ||
346 | nfs_put_client(pos); | ||
347 | dprintk("NFS: <-- %s status = %d, no result\n", | ||
348 | __func__, status); | ||
349 | return status; | ||
350 | } | ||
351 | |||
352 | spin_lock(&nn->nfs_client_lock); | ||
353 | prev = pos; | ||
354 | } | ||
355 | |||
356 | /* | ||
357 | * No matching nfs_client found. This should be impossible, | ||
358 | * because the new nfs_client has already been added to | ||
359 | * nfs_client_list by nfs_get_client(). | ||
360 | * | ||
361 | * Don't BUG(), since the caller is holding a mutex. | ||
362 | */ | ||
363 | if (prev) | ||
364 | nfs_put_client(prev); | ||
365 | spin_unlock(&nn->nfs_client_lock); | ||
366 | pr_err("NFS: %s Error: no matching nfs_client found\n", __func__); | ||
367 | return -NFS4ERR_STALE_CLIENTID; | ||
368 | } | ||
369 | |||
370 | #ifdef CONFIG_NFS_V4_1 | ||
371 | /* | ||
372 | * Returns true if the client IDs match | ||
373 | */ | ||
374 | static bool nfs4_match_clientids(struct nfs_client *a, struct nfs_client *b) | ||
375 | { | ||
376 | if (a->cl_clientid != b->cl_clientid) { | ||
377 | dprintk("NFS: --> %s client ID %llx does not match %llx\n", | ||
378 | __func__, a->cl_clientid, b->cl_clientid); | ||
379 | return false; | ||
380 | } | ||
381 | dprintk("NFS: --> %s client ID %llx matches %llx\n", | ||
382 | __func__, a->cl_clientid, b->cl_clientid); | ||
383 | return true; | ||
384 | } | ||
385 | |||
386 | /* | ||
387 | * Returns true if the server owners match | ||
388 | */ | ||
389 | static bool | ||
390 | nfs4_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 | |||
409 | out_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 | */ | ||
428 | int 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 | spin_lock(&nn->nfs_client_lock); | ||
454 | continue; | ||
455 | } | ||
456 | |||
457 | spin_lock(&nn->nfs_client_lock); | ||
458 | } | ||
459 | |||
460 | if (pos->rpc_ops != new->rpc_ops) | ||
461 | continue; | ||
462 | |||
463 | if (pos->cl_proto != new->cl_proto) | ||
464 | continue; | ||
465 | |||
466 | if (pos->cl_minorversion != new->cl_minorversion) | ||
467 | continue; | ||
468 | |||
469 | if (!nfs4_match_clientids(pos, new)) | ||
470 | continue; | ||
471 | |||
472 | if (!nfs4_match_serverowners(pos, new)) | ||
473 | continue; | ||
474 | |||
475 | spin_unlock(&nn->nfs_client_lock); | ||
476 | dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n", | ||
477 | __func__, pos, atomic_read(&pos->cl_count)); | ||
478 | |||
479 | *result = pos; | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | /* | ||
484 | * No matching nfs_client found. This should be impossible, | ||
485 | * because the new nfs_client has already been added to | ||
486 | * nfs_client_list by nfs_get_client(). | ||
487 | * | ||
488 | * Don't BUG(), since the caller is holding a mutex. | ||
489 | */ | ||
490 | spin_unlock(&nn->nfs_client_lock); | ||
491 | pr_err("NFS: %s Error: no matching nfs_client found\n", __func__); | ||
492 | return -NFS4ERR_STALE_CLIENTID; | ||
493 | } | ||
494 | #endif /* CONFIG_NFS_V4_1 */ | ||
495 | |||
242 | static void nfs4_destroy_server(struct nfs_server *server) | 496 | static void nfs4_destroy_server(struct nfs_server *server) |
243 | { | 497 | { |
244 | nfs_server_return_all_delegations(server); | 498 | nfs_server_return_all_delegations(server); |