diff options
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfsd/export.c | 44 | ||||
-rw-r--r-- | fs/nfsd/nfs4callback.c | 140 | ||||
-rw-r--r-- | fs/nfsd/nfs4proc.c | 50 | ||||
-rw-r--r-- | fs/nfsd/nfs4state.c | 376 | ||||
-rw-r--r-- | fs/nfsd/nfs4xdr.c | 27 | ||||
-rw-r--r-- | fs/nfsd/nfsctl.c | 64 | ||||
-rw-r--r-- | fs/nfsd/nfsd.h | 6 | ||||
-rw-r--r-- | fs/nfsd/nfssvc.c | 2 | ||||
-rw-r--r-- | fs/nfsd/state.h | 47 | ||||
-rw-r--r-- | fs/nfsd/vfs.c | 8 | ||||
-rw-r--r-- | fs/nfsd/vfs.h | 1 | ||||
-rw-r--r-- | fs/nfsd/xdr4.h | 11 |
12 files changed, 472 insertions, 304 deletions
diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 872a5ef550c7..c2a4f71d87dd 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c | |||
@@ -259,10 +259,9 @@ static struct cache_detail svc_expkey_cache = { | |||
259 | .alloc = expkey_alloc, | 259 | .alloc = expkey_alloc, |
260 | }; | 260 | }; |
261 | 261 | ||
262 | static struct svc_expkey * | 262 | static int |
263 | svc_expkey_lookup(struct svc_expkey *item) | 263 | svc_expkey_hash(struct svc_expkey *item) |
264 | { | 264 | { |
265 | struct cache_head *ch; | ||
266 | int hash = item->ek_fsidtype; | 265 | int hash = item->ek_fsidtype; |
267 | char * cp = (char*)item->ek_fsid; | 266 | char * cp = (char*)item->ek_fsid; |
268 | int len = key_len(item->ek_fsidtype); | 267 | int len = key_len(item->ek_fsidtype); |
@@ -270,6 +269,14 @@ svc_expkey_lookup(struct svc_expkey *item) | |||
270 | hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); | 269 | hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); |
271 | hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS); | 270 | hash ^= hash_ptr(item->ek_client, EXPKEY_HASHBITS); |
272 | hash &= EXPKEY_HASHMASK; | 271 | hash &= EXPKEY_HASHMASK; |
272 | return hash; | ||
273 | } | ||
274 | |||
275 | static struct svc_expkey * | ||
276 | svc_expkey_lookup(struct svc_expkey *item) | ||
277 | { | ||
278 | struct cache_head *ch; | ||
279 | int hash = svc_expkey_hash(item); | ||
273 | 280 | ||
274 | ch = sunrpc_cache_lookup(&svc_expkey_cache, &item->h, | 281 | ch = sunrpc_cache_lookup(&svc_expkey_cache, &item->h, |
275 | hash); | 282 | hash); |
@@ -283,13 +290,7 @@ static struct svc_expkey * | |||
283 | svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old) | 290 | svc_expkey_update(struct svc_expkey *new, struct svc_expkey *old) |
284 | { | 291 | { |
285 | struct cache_head *ch; | 292 | struct cache_head *ch; |
286 | int hash = new->ek_fsidtype; | 293 | int hash = svc_expkey_hash(new); |
287 | char * cp = (char*)new->ek_fsid; | ||
288 | int len = key_len(new->ek_fsidtype); | ||
289 | |||
290 | hash ^= hash_mem(cp, len, EXPKEY_HASHBITS); | ||
291 | hash ^= hash_ptr(new->ek_client, EXPKEY_HASHBITS); | ||
292 | hash &= EXPKEY_HASHMASK; | ||
293 | 294 | ||
294 | ch = sunrpc_cache_update(&svc_expkey_cache, &new->h, | 295 | ch = sunrpc_cache_update(&svc_expkey_cache, &new->h, |
295 | &old->h, hash); | 296 | &old->h, hash); |
@@ -738,14 +739,22 @@ struct cache_detail svc_export_cache = { | |||
738 | .alloc = svc_export_alloc, | 739 | .alloc = svc_export_alloc, |
739 | }; | 740 | }; |
740 | 741 | ||
741 | static struct svc_export * | 742 | static int |
742 | svc_export_lookup(struct svc_export *exp) | 743 | svc_export_hash(struct svc_export *exp) |
743 | { | 744 | { |
744 | struct cache_head *ch; | ||
745 | int hash; | 745 | int hash; |
746 | |||
746 | hash = hash_ptr(exp->ex_client, EXPORT_HASHBITS); | 747 | hash = hash_ptr(exp->ex_client, EXPORT_HASHBITS); |
747 | hash ^= hash_ptr(exp->ex_path.dentry, EXPORT_HASHBITS); | 748 | hash ^= hash_ptr(exp->ex_path.dentry, EXPORT_HASHBITS); |
748 | hash ^= hash_ptr(exp->ex_path.mnt, EXPORT_HASHBITS); | 749 | hash ^= hash_ptr(exp->ex_path.mnt, EXPORT_HASHBITS); |
750 | return hash; | ||
751 | } | ||
752 | |||
753 | static struct svc_export * | ||
754 | svc_export_lookup(struct svc_export *exp) | ||
755 | { | ||
756 | struct cache_head *ch; | ||
757 | int hash = svc_export_hash(exp); | ||
749 | 758 | ||
750 | ch = sunrpc_cache_lookup(&svc_export_cache, &exp->h, | 759 | ch = sunrpc_cache_lookup(&svc_export_cache, &exp->h, |
751 | hash); | 760 | hash); |
@@ -759,10 +768,7 @@ static struct svc_export * | |||
759 | svc_export_update(struct svc_export *new, struct svc_export *old) | 768 | svc_export_update(struct svc_export *new, struct svc_export *old) |
760 | { | 769 | { |
761 | struct cache_head *ch; | 770 | struct cache_head *ch; |
762 | int hash; | 771 | int hash = svc_export_hash(old); |
763 | hash = hash_ptr(old->ex_client, EXPORT_HASHBITS); | ||
764 | hash ^= hash_ptr(old->ex_path.dentry, EXPORT_HASHBITS); | ||
765 | hash ^= hash_ptr(old->ex_path.mnt, EXPORT_HASHBITS); | ||
766 | 772 | ||
767 | ch = sunrpc_cache_update(&svc_export_cache, &new->h, | 773 | ch = sunrpc_cache_update(&svc_export_cache, &new->h, |
768 | &old->h, | 774 | &old->h, |
@@ -1071,9 +1077,9 @@ exp_export(struct nfsctl_export *nxp) | |||
1071 | err = 0; | 1077 | err = 0; |
1072 | finish: | 1078 | finish: |
1073 | kfree(new.ex_pathname); | 1079 | kfree(new.ex_pathname); |
1074 | if (exp) | 1080 | if (!IS_ERR_OR_NULL(exp)) |
1075 | exp_put(exp); | 1081 | exp_put(exp); |
1076 | if (fsid_key && !IS_ERR(fsid_key)) | 1082 | if (!IS_ERR_OR_NULL(fsid_key)) |
1077 | cache_put(&fsid_key->h, &svc_expkey_cache); | 1083 | cache_put(&fsid_key->h, &svc_expkey_cache); |
1078 | path_put(&path); | 1084 | path_put(&path); |
1079 | out_put_clp: | 1085 | out_put_clp: |
diff --git a/fs/nfsd/nfs4callback.c b/fs/nfsd/nfs4callback.c index 7e32bd394e86..eb78e7e22077 100644 --- a/fs/nfsd/nfs4callback.c +++ b/fs/nfsd/nfs4callback.c | |||
@@ -32,6 +32,7 @@ | |||
32 | */ | 32 | */ |
33 | 33 | ||
34 | #include <linux/sunrpc/clnt.h> | 34 | #include <linux/sunrpc/clnt.h> |
35 | #include <linux/sunrpc/svc_xprt.h> | ||
35 | #include <linux/slab.h> | 36 | #include <linux/slab.h> |
36 | #include "nfsd.h" | 37 | #include "nfsd.h" |
37 | #include "state.h" | 38 | #include "state.h" |
@@ -79,11 +80,6 @@ enum nfs_cb_opnum4 { | |||
79 | cb_sequence_dec_sz + \ | 80 | cb_sequence_dec_sz + \ |
80 | op_dec_sz) | 81 | op_dec_sz) |
81 | 82 | ||
82 | struct nfs4_rpc_args { | ||
83 | void *args_op; | ||
84 | struct nfsd4_cb_sequence args_seq; | ||
85 | }; | ||
86 | |||
87 | /* | 83 | /* |
88 | * Generic encode routines from fs/nfs/nfs4xdr.c | 84 | * Generic encode routines from fs/nfs/nfs4xdr.c |
89 | */ | 85 | */ |
@@ -428,13 +424,19 @@ static struct rpc_procinfo nfs4_cb_procedures[] = { | |||
428 | }; | 424 | }; |
429 | 425 | ||
430 | static struct rpc_version nfs_cb_version4 = { | 426 | static struct rpc_version nfs_cb_version4 = { |
427 | /* | ||
428 | * Note on the callback rpc program version number: despite language in rfc | ||
429 | * 5661 section 18.36.3 requiring servers to use 4 in this field, the | ||
430 | * official xdr descriptions for both 4.0 and 4.1 specify version 1, and | ||
431 | * in practice that appears to be what implementations use. The section | ||
432 | * 18.36.3 language is expected to be fixed in an erratum. | ||
433 | */ | ||
431 | .number = 1, | 434 | .number = 1, |
432 | .nrprocs = ARRAY_SIZE(nfs4_cb_procedures), | 435 | .nrprocs = ARRAY_SIZE(nfs4_cb_procedures), |
433 | .procs = nfs4_cb_procedures | 436 | .procs = nfs4_cb_procedures |
434 | }; | 437 | }; |
435 | 438 | ||
436 | static struct rpc_version * nfs_cb_version[] = { | 439 | static struct rpc_version * nfs_cb_version[] = { |
437 | NULL, | ||
438 | &nfs_cb_version4, | 440 | &nfs_cb_version4, |
439 | }; | 441 | }; |
440 | 442 | ||
@@ -456,15 +458,14 @@ static struct rpc_program cb_program = { | |||
456 | 458 | ||
457 | static int max_cb_time(void) | 459 | static int max_cb_time(void) |
458 | { | 460 | { |
459 | return max(NFSD_LEASE_TIME/10, (time_t)1) * HZ; | 461 | return max(nfsd4_lease/10, (time_t)1) * HZ; |
460 | } | 462 | } |
461 | 463 | ||
462 | /* Reference counting, callback cleanup, etc., all look racy as heck. | 464 | /* Reference counting, callback cleanup, etc., all look racy as heck. |
463 | * And why is cb_set an atomic? */ | 465 | * And why is cl_cb_set an atomic? */ |
464 | 466 | ||
465 | int setup_callback_client(struct nfs4_client *clp) | 467 | int setup_callback_client(struct nfs4_client *clp, struct nfs4_cb_conn *cb) |
466 | { | 468 | { |
467 | struct nfs4_cb_conn *cb = &clp->cl_cb_conn; | ||
468 | struct rpc_timeout timeparms = { | 469 | struct rpc_timeout timeparms = { |
469 | .to_initval = max_cb_time(), | 470 | .to_initval = max_cb_time(), |
470 | .to_retries = 0, | 471 | .to_retries = 0, |
@@ -476,7 +477,7 @@ int setup_callback_client(struct nfs4_client *clp) | |||
476 | .timeout = &timeparms, | 477 | .timeout = &timeparms, |
477 | .program = &cb_program, | 478 | .program = &cb_program, |
478 | .prognumber = cb->cb_prog, | 479 | .prognumber = cb->cb_prog, |
479 | .version = nfs_cb_version[1]->number, | 480 | .version = 0, |
480 | .authflavor = clp->cl_flavor, | 481 | .authflavor = clp->cl_flavor, |
481 | .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), | 482 | .flags = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET), |
482 | .client_name = clp->cl_principal, | 483 | .client_name = clp->cl_principal, |
@@ -486,7 +487,7 @@ int setup_callback_client(struct nfs4_client *clp) | |||
486 | if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) | 487 | if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) |
487 | return -EINVAL; | 488 | return -EINVAL; |
488 | if (cb->cb_minorversion) { | 489 | if (cb->cb_minorversion) { |
489 | args.bc_xprt = clp->cl_cb_xprt; | 490 | args.bc_xprt = cb->cb_xprt; |
490 | args.protocol = XPRT_TRANSPORT_BC_TCP; | 491 | args.protocol = XPRT_TRANSPORT_BC_TCP; |
491 | } | 492 | } |
492 | /* Create RPC client */ | 493 | /* Create RPC client */ |
@@ -496,7 +497,7 @@ int setup_callback_client(struct nfs4_client *clp) | |||
496 | PTR_ERR(client)); | 497 | PTR_ERR(client)); |
497 | return PTR_ERR(client); | 498 | return PTR_ERR(client); |
498 | } | 499 | } |
499 | cb->cb_client = client; | 500 | nfsd4_set_callback_client(clp, client); |
500 | return 0; | 501 | return 0; |
501 | 502 | ||
502 | } | 503 | } |
@@ -514,8 +515,7 @@ static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata) | |||
514 | if (task->tk_status) | 515 | if (task->tk_status) |
515 | warn_no_callback_path(clp, task->tk_status); | 516 | warn_no_callback_path(clp, task->tk_status); |
516 | else | 517 | else |
517 | atomic_set(&clp->cl_cb_conn.cb_set, 1); | 518 | atomic_set(&clp->cl_cb_set, 1); |
518 | put_nfs4_client(clp); | ||
519 | } | 519 | } |
520 | 520 | ||
521 | static const struct rpc_call_ops nfsd4_cb_probe_ops = { | 521 | static const struct rpc_call_ops nfsd4_cb_probe_ops = { |
@@ -537,7 +537,6 @@ int set_callback_cred(void) | |||
537 | 537 | ||
538 | void do_probe_callback(struct nfs4_client *clp) | 538 | void do_probe_callback(struct nfs4_client *clp) |
539 | { | 539 | { |
540 | struct nfs4_cb_conn *cb = &clp->cl_cb_conn; | ||
541 | struct rpc_message msg = { | 540 | struct rpc_message msg = { |
542 | .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL], | 541 | .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL], |
543 | .rpc_argp = clp, | 542 | .rpc_argp = clp, |
@@ -545,34 +544,27 @@ void do_probe_callback(struct nfs4_client *clp) | |||
545 | }; | 544 | }; |
546 | int status; | 545 | int status; |
547 | 546 | ||
548 | status = rpc_call_async(cb->cb_client, &msg, | 547 | status = rpc_call_async(clp->cl_cb_client, &msg, |
549 | RPC_TASK_SOFT | RPC_TASK_SOFTCONN, | 548 | RPC_TASK_SOFT | RPC_TASK_SOFTCONN, |
550 | &nfsd4_cb_probe_ops, (void *)clp); | 549 | &nfsd4_cb_probe_ops, (void *)clp); |
551 | if (status) { | 550 | if (status) |
552 | warn_no_callback_path(clp, status); | 551 | warn_no_callback_path(clp, status); |
553 | put_nfs4_client(clp); | ||
554 | } | ||
555 | } | 552 | } |
556 | 553 | ||
557 | /* | 554 | /* |
558 | * Set up the callback client and put a NFSPROC4_CB_NULL on the wire... | 555 | * Set up the callback client and put a NFSPROC4_CB_NULL on the wire... |
559 | */ | 556 | */ |
560 | void | 557 | void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *cb) |
561 | nfsd4_probe_callback(struct nfs4_client *clp) | ||
562 | { | 558 | { |
563 | int status; | 559 | int status; |
564 | 560 | ||
565 | BUG_ON(atomic_read(&clp->cl_cb_conn.cb_set)); | 561 | BUG_ON(atomic_read(&clp->cl_cb_set)); |
566 | 562 | ||
567 | status = setup_callback_client(clp); | 563 | status = setup_callback_client(clp, cb); |
568 | if (status) { | 564 | if (status) { |
569 | warn_no_callback_path(clp, status); | 565 | warn_no_callback_path(clp, status); |
570 | return; | 566 | return; |
571 | } | 567 | } |
572 | |||
573 | /* the task holds a reference to the nfs4_client struct */ | ||
574 | atomic_inc(&clp->cl_count); | ||
575 | |||
576 | do_probe_callback(clp); | 568 | do_probe_callback(clp); |
577 | } | 569 | } |
578 | 570 | ||
@@ -658,18 +650,32 @@ static void nfsd4_cb_done(struct rpc_task *task, void *calldata) | |||
658 | } | 650 | } |
659 | } | 651 | } |
660 | 652 | ||
653 | |||
661 | static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) | 654 | static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) |
662 | { | 655 | { |
663 | struct nfs4_delegation *dp = calldata; | 656 | struct nfs4_delegation *dp = calldata; |
664 | struct nfs4_client *clp = dp->dl_client; | 657 | struct nfs4_client *clp = dp->dl_client; |
658 | struct rpc_clnt *current_rpc_client = clp->cl_cb_client; | ||
665 | 659 | ||
666 | nfsd4_cb_done(task, calldata); | 660 | nfsd4_cb_done(task, calldata); |
667 | 661 | ||
662 | if (current_rpc_client == NULL) { | ||
663 | /* We're shutting down; give up. */ | ||
664 | /* XXX: err, or is it ok just to fall through | ||
665 | * and rpc_restart_call? */ | ||
666 | return; | ||
667 | } | ||
668 | |||
668 | switch (task->tk_status) { | 669 | switch (task->tk_status) { |
669 | case -EIO: | 670 | case -EIO: |
670 | /* Network partition? */ | 671 | /* Network partition? */ |
671 | atomic_set(&clp->cl_cb_conn.cb_set, 0); | 672 | atomic_set(&clp->cl_cb_set, 0); |
672 | warn_no_callback_path(clp, task->tk_status); | 673 | warn_no_callback_path(clp, task->tk_status); |
674 | if (current_rpc_client != task->tk_client) { | ||
675 | /* queue a callback on the new connection: */ | ||
676 | nfsd4_cb_recall(dp); | ||
677 | return; | ||
678 | } | ||
673 | case -EBADHANDLE: | 679 | case -EBADHANDLE: |
674 | case -NFS4ERR_BAD_STATEID: | 680 | case -NFS4ERR_BAD_STATEID: |
675 | /* Race: client probably got cb_recall | 681 | /* Race: client probably got cb_recall |
@@ -677,7 +683,7 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) | |||
677 | break; | 683 | break; |
678 | default: | 684 | default: |
679 | /* success, or error we can't handle */ | 685 | /* success, or error we can't handle */ |
680 | goto done; | 686 | return; |
681 | } | 687 | } |
682 | if (dp->dl_retries--) { | 688 | if (dp->dl_retries--) { |
683 | rpc_delay(task, 2*HZ); | 689 | rpc_delay(task, 2*HZ); |
@@ -685,20 +691,16 @@ static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata) | |||
685 | rpc_restart_call(task); | 691 | rpc_restart_call(task); |
686 | return; | 692 | return; |
687 | } else { | 693 | } else { |
688 | atomic_set(&clp->cl_cb_conn.cb_set, 0); | 694 | atomic_set(&clp->cl_cb_set, 0); |
689 | warn_no_callback_path(clp, task->tk_status); | 695 | warn_no_callback_path(clp, task->tk_status); |
690 | } | 696 | } |
691 | done: | ||
692 | kfree(task->tk_msg.rpc_argp); | ||
693 | } | 697 | } |
694 | 698 | ||
695 | static void nfsd4_cb_recall_release(void *calldata) | 699 | static void nfsd4_cb_recall_release(void *calldata) |
696 | { | 700 | { |
697 | struct nfs4_delegation *dp = calldata; | 701 | struct nfs4_delegation *dp = calldata; |
698 | struct nfs4_client *clp = dp->dl_client; | ||
699 | 702 | ||
700 | nfs4_put_delegation(dp); | 703 | nfs4_put_delegation(dp); |
701 | put_nfs4_client(clp); | ||
702 | } | 704 | } |
703 | 705 | ||
704 | static const struct rpc_call_ops nfsd4_cb_recall_ops = { | 706 | static const struct rpc_call_ops nfsd4_cb_recall_ops = { |
@@ -707,33 +709,75 @@ static const struct rpc_call_ops nfsd4_cb_recall_ops = { | |||
707 | .rpc_release = nfsd4_cb_recall_release, | 709 | .rpc_release = nfsd4_cb_recall_release, |
708 | }; | 710 | }; |
709 | 711 | ||
712 | static struct workqueue_struct *callback_wq; | ||
713 | |||
714 | int nfsd4_create_callback_queue(void) | ||
715 | { | ||
716 | callback_wq = create_singlethread_workqueue("nfsd4_callbacks"); | ||
717 | if (!callback_wq) | ||
718 | return -ENOMEM; | ||
719 | return 0; | ||
720 | } | ||
721 | |||
722 | void nfsd4_destroy_callback_queue(void) | ||
723 | { | ||
724 | destroy_workqueue(callback_wq); | ||
725 | } | ||
726 | |||
727 | /* must be called under the state lock */ | ||
728 | void nfsd4_set_callback_client(struct nfs4_client *clp, struct rpc_clnt *new) | ||
729 | { | ||
730 | struct rpc_clnt *old = clp->cl_cb_client; | ||
731 | |||
732 | clp->cl_cb_client = new; | ||
733 | /* | ||
734 | * After this, any work that saw the old value of cl_cb_client will | ||
735 | * be gone: | ||
736 | */ | ||
737 | flush_workqueue(callback_wq); | ||
738 | /* So we can safely shut it down: */ | ||
739 | if (old) | ||
740 | rpc_shutdown_client(old); | ||
741 | } | ||
742 | |||
710 | /* | 743 | /* |
711 | * called with dp->dl_count inc'ed. | 744 | * called with dp->dl_count inc'ed. |
712 | */ | 745 | */ |
713 | void | 746 | static void _nfsd4_cb_recall(struct nfs4_delegation *dp) |
714 | nfsd4_cb_recall(struct nfs4_delegation *dp) | ||
715 | { | 747 | { |
716 | struct nfs4_client *clp = dp->dl_client; | 748 | struct nfs4_client *clp = dp->dl_client; |
717 | struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client; | 749 | struct rpc_clnt *clnt = clp->cl_cb_client; |
718 | struct nfs4_rpc_args *args; | 750 | struct nfs4_rpc_args *args = &dp->dl_recall.cb_args; |
719 | struct rpc_message msg = { | 751 | struct rpc_message msg = { |
720 | .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL], | 752 | .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL], |
721 | .rpc_cred = callback_cred | 753 | .rpc_cred = callback_cred |
722 | }; | 754 | }; |
723 | int status = -ENOMEM; | 755 | int status; |
756 | |||
757 | if (clnt == NULL) | ||
758 | return; /* Client is shutting down; give up. */ | ||
724 | 759 | ||
725 | args = kzalloc(sizeof(*args), GFP_KERNEL); | ||
726 | if (!args) | ||
727 | goto out; | ||
728 | args->args_op = dp; | 760 | args->args_op = dp; |
729 | msg.rpc_argp = args; | 761 | msg.rpc_argp = args; |
730 | dp->dl_retries = 1; | 762 | dp->dl_retries = 1; |
731 | status = rpc_call_async(clnt, &msg, RPC_TASK_SOFT, | 763 | status = rpc_call_async(clnt, &msg, RPC_TASK_SOFT, |
732 | &nfsd4_cb_recall_ops, dp); | 764 | &nfsd4_cb_recall_ops, dp); |
733 | out: | 765 | if (status) |
734 | if (status) { | ||
735 | kfree(args); | ||
736 | put_nfs4_client(clp); | ||
737 | nfs4_put_delegation(dp); | 766 | nfs4_put_delegation(dp); |
738 | } | 767 | } |
768 | |||
769 | void nfsd4_do_callback_rpc(struct work_struct *w) | ||
770 | { | ||
771 | /* XXX: for now, just send off delegation recall. */ | ||
772 | /* In future, generalize to handle any sort of callback. */ | ||
773 | struct nfsd4_callback *c = container_of(w, struct nfsd4_callback, cb_work); | ||
774 | struct nfs4_delegation *dp = container_of(c, struct nfs4_delegation, dl_recall); | ||
775 | |||
776 | _nfsd4_cb_recall(dp); | ||
777 | } | ||
778 | |||
779 | |||
780 | void nfsd4_cb_recall(struct nfs4_delegation *dp) | ||
781 | { | ||
782 | queue_work(callback_wq, &dp->dl_recall.cb_work); | ||
739 | } | 783 | } |
diff --git a/fs/nfsd/nfs4proc.c b/fs/nfsd/nfs4proc.c index 2ab9e8501bfe..59ec449b0c7f 100644 --- a/fs/nfsd/nfs4proc.c +++ b/fs/nfsd/nfs4proc.c | |||
@@ -969,20 +969,36 @@ static struct nfsd4_operation nfsd4_ops[]; | |||
969 | static const char *nfsd4_op_name(unsigned opnum); | 969 | static const char *nfsd4_op_name(unsigned opnum); |
970 | 970 | ||
971 | /* | 971 | /* |
972 | * Enforce NFSv4.1 COMPOUND ordering rules. | 972 | * Enforce NFSv4.1 COMPOUND ordering rules: |
973 | * | 973 | * |
974 | * TODO: | 974 | * Also note, enforced elsewhere: |
975 | * - enforce NFS4ERR_NOT_ONLY_OP, | 975 | * - SEQUENCE other than as first op results in |
976 | * - DESTROY_SESSION MUST be the final operation in the COMPOUND request. | 976 | * NFS4ERR_SEQUENCE_POS. (Enforced in nfsd4_sequence().) |
977 | * - BIND_CONN_TO_SESSION must be the only op in its compound | ||
978 | * (Will be enforced in nfsd4_bind_conn_to_session().) | ||
979 | * - DESTROY_SESSION must be the final operation in a compound, if | ||
980 | * sessionid's in SEQUENCE and DESTROY_SESSION are the same. | ||
981 | * (Enforced in nfsd4_destroy_session().) | ||
977 | */ | 982 | */ |
978 | static bool nfs41_op_ordering_ok(struct nfsd4_compoundargs *args) | 983 | static __be32 nfs41_check_op_ordering(struct nfsd4_compoundargs *args) |
979 | { | 984 | { |
980 | if (args->minorversion && args->opcnt > 0) { | 985 | struct nfsd4_op *op = &args->ops[0]; |
981 | struct nfsd4_op *op = &args->ops[0]; | 986 | |
982 | return (op->status == nfserr_op_illegal) || | 987 | /* These ordering requirements don't apply to NFSv4.0: */ |
983 | (nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP); | 988 | if (args->minorversion == 0) |
984 | } | 989 | return nfs_ok; |
985 | return true; | 990 | /* This is weird, but OK, not our problem: */ |
991 | if (args->opcnt == 0) | ||
992 | return nfs_ok; | ||
993 | if (op->status == nfserr_op_illegal) | ||
994 | return nfs_ok; | ||
995 | if (!(nfsd4_ops[op->opnum].op_flags & ALLOWED_AS_FIRST_OP)) | ||
996 | return nfserr_op_not_in_session; | ||
997 | if (op->opnum == OP_SEQUENCE) | ||
998 | return nfs_ok; | ||
999 | if (args->opcnt != 1) | ||
1000 | return nfserr_not_only_op; | ||
1001 | return nfs_ok; | ||
986 | } | 1002 | } |
987 | 1003 | ||
988 | /* | 1004 | /* |
@@ -1012,6 +1028,7 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, | |||
1012 | resp->rqstp = rqstp; | 1028 | resp->rqstp = rqstp; |
1013 | resp->cstate.minorversion = args->minorversion; | 1029 | resp->cstate.minorversion = args->minorversion; |
1014 | resp->cstate.replay_owner = NULL; | 1030 | resp->cstate.replay_owner = NULL; |
1031 | resp->cstate.session = NULL; | ||
1015 | fh_init(&resp->cstate.current_fh, NFS4_FHSIZE); | 1032 | fh_init(&resp->cstate.current_fh, NFS4_FHSIZE); |
1016 | fh_init(&resp->cstate.save_fh, NFS4_FHSIZE); | 1033 | fh_init(&resp->cstate.save_fh, NFS4_FHSIZE); |
1017 | /* Use the deferral mechanism only for NFSv4.0 compounds */ | 1034 | /* Use the deferral mechanism only for NFSv4.0 compounds */ |
@@ -1024,13 +1041,13 @@ nfsd4_proc_compound(struct svc_rqst *rqstp, | |||
1024 | if (args->minorversion > nfsd_supported_minorversion) | 1041 | if (args->minorversion > nfsd_supported_minorversion) |
1025 | goto out; | 1042 | goto out; |
1026 | 1043 | ||
1027 | if (!nfs41_op_ordering_ok(args)) { | 1044 | status = nfs41_check_op_ordering(args); |
1045 | if (status) { | ||
1028 | op = &args->ops[0]; | 1046 | op = &args->ops[0]; |
1029 | op->status = nfserr_sequence_pos; | 1047 | op->status = status; |
1030 | goto encode_op; | 1048 | goto encode_op; |
1031 | } | 1049 | } |
1032 | 1050 | ||
1033 | status = nfs_ok; | ||
1034 | while (!status && resp->opcnt < args->opcnt) { | 1051 | while (!status && resp->opcnt < args->opcnt) { |
1035 | op = &args->ops[resp->opcnt++]; | 1052 | op = &args->ops[resp->opcnt++]; |
1036 | 1053 | ||
@@ -1295,6 +1312,11 @@ static struct nfsd4_operation nfsd4_ops[] = { | |||
1295 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, | 1312 | .op_flags = ALLOWED_WITHOUT_FH | ALLOWED_AS_FIRST_OP, |
1296 | .op_name = "OP_SEQUENCE", | 1313 | .op_name = "OP_SEQUENCE", |
1297 | }, | 1314 | }, |
1315 | [OP_RECLAIM_COMPLETE] = { | ||
1316 | .op_func = (nfsd4op_func)nfsd4_reclaim_complete, | ||
1317 | .op_flags = ALLOWED_WITHOUT_FH, | ||
1318 | .op_name = "OP_RECLAIM_COMPLETE", | ||
1319 | }, | ||
1298 | }; | 1320 | }; |
1299 | 1321 | ||
1300 | static const char *nfsd4_op_name(unsigned opnum) | 1322 | static const char *nfsd4_op_name(unsigned opnum) |
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c index 6a8fedaa4f55..12f7109720c2 100644 --- a/fs/nfsd/nfs4state.c +++ b/fs/nfsd/nfs4state.c | |||
@@ -45,8 +45,8 @@ | |||
45 | #define NFSDDBG_FACILITY NFSDDBG_PROC | 45 | #define NFSDDBG_FACILITY NFSDDBG_PROC |
46 | 46 | ||
47 | /* Globals */ | 47 | /* Globals */ |
48 | static time_t lease_time = 90; /* default lease time */ | 48 | time_t nfsd4_lease = 90; /* default lease time */ |
49 | static time_t user_lease_time = 90; | 49 | time_t nfsd4_grace = 90; |
50 | static time_t boot_time; | 50 | static time_t boot_time; |
51 | static u32 current_ownerid = 1; | 51 | static u32 current_ownerid = 1; |
52 | static u32 current_fileid = 1; | 52 | static u32 current_fileid = 1; |
@@ -190,7 +190,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f | |||
190 | dp->dl_vfs_file = stp->st_vfs_file; | 190 | dp->dl_vfs_file = stp->st_vfs_file; |
191 | dp->dl_type = type; | 191 | dp->dl_type = type; |
192 | dp->dl_ident = cb->cb_ident; | 192 | dp->dl_ident = cb->cb_ident; |
193 | dp->dl_stateid.si_boot = get_seconds(); | 193 | dp->dl_stateid.si_boot = boot_time; |
194 | dp->dl_stateid.si_stateownerid = current_delegid++; | 194 | dp->dl_stateid.si_stateownerid = current_delegid++; |
195 | dp->dl_stateid.si_fileid = 0; | 195 | dp->dl_stateid.si_fileid = 0; |
196 | dp->dl_stateid.si_generation = 0; | 196 | dp->dl_stateid.si_generation = 0; |
@@ -199,6 +199,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f | |||
199 | atomic_set(&dp->dl_count, 1); | 199 | atomic_set(&dp->dl_count, 1); |
200 | list_add(&dp->dl_perfile, &fp->fi_delegations); | 200 | list_add(&dp->dl_perfile, &fp->fi_delegations); |
201 | list_add(&dp->dl_perclnt, &clp->cl_delegations); | 201 | list_add(&dp->dl_perclnt, &clp->cl_delegations); |
202 | INIT_WORK(&dp->dl_recall.cb_work, nfsd4_do_callback_rpc); | ||
202 | return dp; | 203 | return dp; |
203 | } | 204 | } |
204 | 205 | ||
@@ -249,6 +250,9 @@ unhash_delegation(struct nfs4_delegation *dp) | |||
249 | * SETCLIENTID state | 250 | * SETCLIENTID state |
250 | */ | 251 | */ |
251 | 252 | ||
253 | /* client_lock protects the client lru list and session hash table */ | ||
254 | static DEFINE_SPINLOCK(client_lock); | ||
255 | |||
252 | /* Hash tables for nfs4_clientid state */ | 256 | /* Hash tables for nfs4_clientid state */ |
253 | #define CLIENT_HASH_BITS 4 | 257 | #define CLIENT_HASH_BITS 4 |
254 | #define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS) | 258 | #define CLIENT_HASH_SIZE (1 << CLIENT_HASH_BITS) |
@@ -367,7 +371,6 @@ static void release_openowner(struct nfs4_stateowner *sop) | |||
367 | nfs4_put_stateowner(sop); | 371 | nfs4_put_stateowner(sop); |
368 | } | 372 | } |
369 | 373 | ||
370 | static DEFINE_SPINLOCK(sessionid_lock); | ||
371 | #define SESSION_HASH_SIZE 512 | 374 | #define SESSION_HASH_SIZE 512 |
372 | static struct list_head sessionid_hashtbl[SESSION_HASH_SIZE]; | 375 | static struct list_head sessionid_hashtbl[SESSION_HASH_SIZE]; |
373 | 376 | ||
@@ -565,10 +568,10 @@ alloc_init_session(struct svc_rqst *rqstp, struct nfs4_client *clp, | |||
565 | 568 | ||
566 | new->se_flags = cses->flags; | 569 | new->se_flags = cses->flags; |
567 | kref_init(&new->se_ref); | 570 | kref_init(&new->se_ref); |
568 | spin_lock(&sessionid_lock); | 571 | spin_lock(&client_lock); |
569 | list_add(&new->se_hash, &sessionid_hashtbl[idx]); | 572 | list_add(&new->se_hash, &sessionid_hashtbl[idx]); |
570 | list_add(&new->se_perclnt, &clp->cl_sessions); | 573 | list_add(&new->se_perclnt, &clp->cl_sessions); |
571 | spin_unlock(&sessionid_lock); | 574 | spin_unlock(&client_lock); |
572 | 575 | ||
573 | status = nfs_ok; | 576 | status = nfs_ok; |
574 | out: | 577 | out: |
@@ -579,7 +582,7 @@ out_free: | |||
579 | goto out; | 582 | goto out; |
580 | } | 583 | } |
581 | 584 | ||
582 | /* caller must hold sessionid_lock */ | 585 | /* caller must hold client_lock */ |
583 | static struct nfsd4_session * | 586 | static struct nfsd4_session * |
584 | find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid) | 587 | find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid) |
585 | { | 588 | { |
@@ -602,7 +605,7 @@ find_in_sessionid_hashtbl(struct nfs4_sessionid *sessionid) | |||
602 | return NULL; | 605 | return NULL; |
603 | } | 606 | } |
604 | 607 | ||
605 | /* caller must hold sessionid_lock */ | 608 | /* caller must hold client_lock */ |
606 | static void | 609 | static void |
607 | unhash_session(struct nfsd4_session *ses) | 610 | unhash_session(struct nfsd4_session *ses) |
608 | { | 611 | { |
@@ -610,15 +613,6 @@ unhash_session(struct nfsd4_session *ses) | |||
610 | list_del(&ses->se_perclnt); | 613 | list_del(&ses->se_perclnt); |
611 | } | 614 | } |
612 | 615 | ||
613 | static void | ||
614 | release_session(struct nfsd4_session *ses) | ||
615 | { | ||
616 | spin_lock(&sessionid_lock); | ||
617 | unhash_session(ses); | ||
618 | spin_unlock(&sessionid_lock); | ||
619 | nfsd4_put_session(ses); | ||
620 | } | ||
621 | |||
622 | void | 616 | void |
623 | free_session(struct kref *kref) | 617 | free_session(struct kref *kref) |
624 | { | 618 | { |
@@ -634,9 +628,18 @@ free_session(struct kref *kref) | |||
634 | kfree(ses); | 628 | kfree(ses); |
635 | } | 629 | } |
636 | 630 | ||
631 | /* must be called under the client_lock */ | ||
637 | static inline void | 632 | static inline void |
638 | renew_client(struct nfs4_client *clp) | 633 | renew_client_locked(struct nfs4_client *clp) |
639 | { | 634 | { |
635 | if (is_client_expired(clp)) { | ||
636 | dprintk("%s: client (clientid %08x/%08x) already expired\n", | ||
637 | __func__, | ||
638 | clp->cl_clientid.cl_boot, | ||
639 | clp->cl_clientid.cl_id); | ||
640 | return; | ||
641 | } | ||
642 | |||
640 | /* | 643 | /* |
641 | * Move client to the end to the LRU list. | 644 | * Move client to the end to the LRU list. |
642 | */ | 645 | */ |
@@ -647,6 +650,14 @@ renew_client(struct nfs4_client *clp) | |||
647 | clp->cl_time = get_seconds(); | 650 | clp->cl_time = get_seconds(); |
648 | } | 651 | } |
649 | 652 | ||
653 | static inline void | ||
654 | renew_client(struct nfs4_client *clp) | ||
655 | { | ||
656 | spin_lock(&client_lock); | ||
657 | renew_client_locked(clp); | ||
658 | spin_unlock(&client_lock); | ||
659 | } | ||
660 | |||
650 | /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */ | 661 | /* SETCLIENTID and SETCLIENTID_CONFIRM Helper functions */ |
651 | static int | 662 | static int |
652 | STALE_CLIENTID(clientid_t *clid) | 663 | STALE_CLIENTID(clientid_t *clid) |
@@ -680,27 +691,9 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name) | |||
680 | return clp; | 691 | return clp; |
681 | } | 692 | } |
682 | 693 | ||
683 | static void | ||
684 | shutdown_callback_client(struct nfs4_client *clp) | ||
685 | { | ||
686 | struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client; | ||
687 | |||
688 | if (clnt) { | ||
689 | /* | ||
690 | * Callback threads take a reference on the client, so there | ||
691 | * should be no outstanding callbacks at this point. | ||
692 | */ | ||
693 | clp->cl_cb_conn.cb_client = NULL; | ||
694 | rpc_shutdown_client(clnt); | ||
695 | } | ||
696 | } | ||
697 | |||
698 | static inline void | 694 | static inline void |
699 | free_client(struct nfs4_client *clp) | 695 | free_client(struct nfs4_client *clp) |
700 | { | 696 | { |
701 | shutdown_callback_client(clp); | ||
702 | if (clp->cl_cb_xprt) | ||
703 | svc_xprt_put(clp->cl_cb_xprt); | ||
704 | if (clp->cl_cred.cr_group_info) | 697 | if (clp->cl_cred.cr_group_info) |
705 | put_group_info(clp->cl_cred.cr_group_info); | 698 | put_group_info(clp->cl_cred.cr_group_info); |
706 | kfree(clp->cl_principal); | 699 | kfree(clp->cl_principal); |
@@ -709,10 +702,34 @@ free_client(struct nfs4_client *clp) | |||
709 | } | 702 | } |
710 | 703 | ||
711 | void | 704 | void |
712 | put_nfs4_client(struct nfs4_client *clp) | 705 | release_session_client(struct nfsd4_session *session) |
713 | { | 706 | { |
714 | if (atomic_dec_and_test(&clp->cl_count)) | 707 | struct nfs4_client *clp = session->se_client; |
708 | |||
709 | if (!atomic_dec_and_lock(&clp->cl_refcount, &client_lock)) | ||
710 | return; | ||
711 | if (is_client_expired(clp)) { | ||
715 | free_client(clp); | 712 | free_client(clp); |
713 | session->se_client = NULL; | ||
714 | } else | ||
715 | renew_client_locked(clp); | ||
716 | spin_unlock(&client_lock); | ||
717 | nfsd4_put_session(session); | ||
718 | } | ||
719 | |||
720 | /* must be called under the client_lock */ | ||
721 | static inline void | ||
722 | unhash_client_locked(struct nfs4_client *clp) | ||
723 | { | ||
724 | mark_client_expired(clp); | ||
725 | list_del(&clp->cl_lru); | ||
726 | while (!list_empty(&clp->cl_sessions)) { | ||
727 | struct nfsd4_session *ses; | ||
728 | ses = list_entry(clp->cl_sessions.next, struct nfsd4_session, | ||
729 | se_perclnt); | ||
730 | unhash_session(ses); | ||
731 | nfsd4_put_session(ses); | ||
732 | } | ||
716 | } | 733 | } |
717 | 734 | ||
718 | static void | 735 | static void |
@@ -722,9 +739,6 @@ expire_client(struct nfs4_client *clp) | |||
722 | struct nfs4_delegation *dp; | 739 | struct nfs4_delegation *dp; |
723 | struct list_head reaplist; | 740 | struct list_head reaplist; |
724 | 741 | ||
725 | dprintk("NFSD: expire_client cl_count %d\n", | ||
726 | atomic_read(&clp->cl_count)); | ||
727 | |||
728 | INIT_LIST_HEAD(&reaplist); | 742 | INIT_LIST_HEAD(&reaplist); |
729 | spin_lock(&recall_lock); | 743 | spin_lock(&recall_lock); |
730 | while (!list_empty(&clp->cl_delegations)) { | 744 | while (!list_empty(&clp->cl_delegations)) { |
@@ -740,20 +754,20 @@ expire_client(struct nfs4_client *clp) | |||
740 | list_del_init(&dp->dl_recall_lru); | 754 | list_del_init(&dp->dl_recall_lru); |
741 | unhash_delegation(dp); | 755 | unhash_delegation(dp); |
742 | } | 756 | } |
743 | list_del(&clp->cl_idhash); | ||
744 | list_del(&clp->cl_strhash); | ||
745 | list_del(&clp->cl_lru); | ||
746 | while (!list_empty(&clp->cl_openowners)) { | 757 | while (!list_empty(&clp->cl_openowners)) { |
747 | sop = list_entry(clp->cl_openowners.next, struct nfs4_stateowner, so_perclient); | 758 | sop = list_entry(clp->cl_openowners.next, struct nfs4_stateowner, so_perclient); |
748 | release_openowner(sop); | 759 | release_openowner(sop); |
749 | } | 760 | } |
750 | while (!list_empty(&clp->cl_sessions)) { | 761 | nfsd4_set_callback_client(clp, NULL); |
751 | struct nfsd4_session *ses; | 762 | if (clp->cl_cb_conn.cb_xprt) |
752 | ses = list_entry(clp->cl_sessions.next, struct nfsd4_session, | 763 | svc_xprt_put(clp->cl_cb_conn.cb_xprt); |
753 | se_perclnt); | 764 | list_del(&clp->cl_idhash); |
754 | release_session(ses); | 765 | list_del(&clp->cl_strhash); |
755 | } | 766 | spin_lock(&client_lock); |
756 | put_nfs4_client(clp); | 767 | unhash_client_locked(clp); |
768 | if (atomic_read(&clp->cl_refcount) == 0) | ||
769 | free_client(clp); | ||
770 | spin_unlock(&client_lock); | ||
757 | } | 771 | } |
758 | 772 | ||
759 | static void copy_verf(struct nfs4_client *target, nfs4_verifier *source) | 773 | static void copy_verf(struct nfs4_client *target, nfs4_verifier *source) |
@@ -839,14 +853,15 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir, | |||
839 | } | 853 | } |
840 | 854 | ||
841 | memcpy(clp->cl_recdir, recdir, HEXDIR_LEN); | 855 | memcpy(clp->cl_recdir, recdir, HEXDIR_LEN); |
842 | atomic_set(&clp->cl_count, 1); | 856 | atomic_set(&clp->cl_refcount, 0); |
843 | atomic_set(&clp->cl_cb_conn.cb_set, 0); | 857 | atomic_set(&clp->cl_cb_set, 0); |
844 | INIT_LIST_HEAD(&clp->cl_idhash); | 858 | INIT_LIST_HEAD(&clp->cl_idhash); |
845 | INIT_LIST_HEAD(&clp->cl_strhash); | 859 | INIT_LIST_HEAD(&clp->cl_strhash); |
846 | INIT_LIST_HEAD(&clp->cl_openowners); | 860 | INIT_LIST_HEAD(&clp->cl_openowners); |
847 | INIT_LIST_HEAD(&clp->cl_delegations); | 861 | INIT_LIST_HEAD(&clp->cl_delegations); |
848 | INIT_LIST_HEAD(&clp->cl_sessions); | 862 | INIT_LIST_HEAD(&clp->cl_sessions); |
849 | INIT_LIST_HEAD(&clp->cl_lru); | 863 | INIT_LIST_HEAD(&clp->cl_lru); |
864 | clp->cl_time = get_seconds(); | ||
850 | clear_bit(0, &clp->cl_cb_slot_busy); | 865 | clear_bit(0, &clp->cl_cb_slot_busy); |
851 | rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table"); | 866 | rpc_init_wait_queue(&clp->cl_cb_waitq, "Backchannel slot table"); |
852 | copy_verf(clp, verf); | 867 | copy_verf(clp, verf); |
@@ -877,8 +892,7 @@ add_to_unconfirmed(struct nfs4_client *clp, unsigned int strhashval) | |||
877 | list_add(&clp->cl_strhash, &unconf_str_hashtbl[strhashval]); | 892 | list_add(&clp->cl_strhash, &unconf_str_hashtbl[strhashval]); |
878 | idhashval = clientid_hashval(clp->cl_clientid.cl_id); | 893 | idhashval = clientid_hashval(clp->cl_clientid.cl_id); |
879 | list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]); | 894 | list_add(&clp->cl_idhash, &unconf_id_hashtbl[idhashval]); |
880 | list_add_tail(&clp->cl_lru, &client_lru); | 895 | renew_client(clp); |
881 | clp->cl_time = get_seconds(); | ||
882 | } | 896 | } |
883 | 897 | ||
884 | static void | 898 | static void |
@@ -888,10 +902,9 @@ move_to_confirmed(struct nfs4_client *clp) | |||
888 | unsigned int strhashval; | 902 | unsigned int strhashval; |
889 | 903 | ||
890 | dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp); | 904 | dprintk("NFSD: move_to_confirm nfs4_client %p\n", clp); |
891 | list_del_init(&clp->cl_strhash); | ||
892 | list_move(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); | 905 | list_move(&clp->cl_idhash, &conf_id_hashtbl[idhashval]); |
893 | strhashval = clientstr_hashval(clp->cl_recdir); | 906 | strhashval = clientstr_hashval(clp->cl_recdir); |
894 | list_add(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); | 907 | list_move(&clp->cl_strhash, &conf_str_hashtbl[strhashval]); |
895 | renew_client(clp); | 908 | renew_client(clp); |
896 | } | 909 | } |
897 | 910 | ||
@@ -1327,15 +1340,9 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
1327 | cs_slot->sl_seqid++; /* from 0 to 1 */ | 1340 | cs_slot->sl_seqid++; /* from 0 to 1 */ |
1328 | move_to_confirmed(unconf); | 1341 | move_to_confirmed(unconf); |
1329 | 1342 | ||
1330 | /* | ||
1331 | * We do not support RDMA or persistent sessions | ||
1332 | */ | ||
1333 | cr_ses->flags &= ~SESSION4_PERSIST; | ||
1334 | cr_ses->flags &= ~SESSION4_RDMA; | ||
1335 | |||
1336 | if (cr_ses->flags & SESSION4_BACK_CHAN) { | 1343 | if (cr_ses->flags & SESSION4_BACK_CHAN) { |
1337 | unconf->cl_cb_xprt = rqstp->rq_xprt; | 1344 | unconf->cl_cb_conn.cb_xprt = rqstp->rq_xprt; |
1338 | svc_xprt_get(unconf->cl_cb_xprt); | 1345 | svc_xprt_get(rqstp->rq_xprt); |
1339 | rpc_copy_addr( | 1346 | rpc_copy_addr( |
1340 | (struct sockaddr *)&unconf->cl_cb_conn.cb_addr, | 1347 | (struct sockaddr *)&unconf->cl_cb_conn.cb_addr, |
1341 | sa); | 1348 | sa); |
@@ -1344,7 +1351,7 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
1344 | cstate->minorversion; | 1351 | cstate->minorversion; |
1345 | unconf->cl_cb_conn.cb_prog = cr_ses->callback_prog; | 1352 | unconf->cl_cb_conn.cb_prog = cr_ses->callback_prog; |
1346 | unconf->cl_cb_seq_nr = 1; | 1353 | unconf->cl_cb_seq_nr = 1; |
1347 | nfsd4_probe_callback(unconf); | 1354 | nfsd4_probe_callback(unconf, &unconf->cl_cb_conn); |
1348 | } | 1355 | } |
1349 | conf = unconf; | 1356 | conf = unconf; |
1350 | } else { | 1357 | } else { |
@@ -1352,6 +1359,12 @@ nfsd4_create_session(struct svc_rqst *rqstp, | |||
1352 | goto out; | 1359 | goto out; |
1353 | } | 1360 | } |
1354 | 1361 | ||
1362 | /* | ||
1363 | * We do not support RDMA or persistent sessions | ||
1364 | */ | ||
1365 | cr_ses->flags &= ~SESSION4_PERSIST; | ||
1366 | cr_ses->flags &= ~SESSION4_RDMA; | ||
1367 | |||
1355 | status = alloc_init_session(rqstp, conf, cr_ses); | 1368 | status = alloc_init_session(rqstp, conf, cr_ses); |
1356 | if (status) | 1369 | if (status) |
1357 | goto out; | 1370 | goto out; |
@@ -1369,6 +1382,21 @@ out: | |||
1369 | return status; | 1382 | return status; |
1370 | } | 1383 | } |
1371 | 1384 | ||
1385 | static bool nfsd4_last_compound_op(struct svc_rqst *rqstp) | ||
1386 | { | ||
1387 | struct nfsd4_compoundres *resp = rqstp->rq_resp; | ||
1388 | struct nfsd4_compoundargs *argp = rqstp->rq_argp; | ||
1389 | |||
1390 | return argp->opcnt == resp->opcnt; | ||
1391 | } | ||
1392 | |||
1393 | static bool nfsd4_compound_in_session(struct nfsd4_session *session, struct nfs4_sessionid *sid) | ||
1394 | { | ||
1395 | if (!session) | ||
1396 | return 0; | ||
1397 | return !memcmp(sid, &session->se_sessionid, sizeof(*sid)); | ||
1398 | } | ||
1399 | |||
1372 | __be32 | 1400 | __be32 |
1373 | nfsd4_destroy_session(struct svc_rqst *r, | 1401 | nfsd4_destroy_session(struct svc_rqst *r, |
1374 | struct nfsd4_compound_state *cstate, | 1402 | struct nfsd4_compound_state *cstate, |
@@ -1384,19 +1412,25 @@ nfsd4_destroy_session(struct svc_rqst *r, | |||
1384 | * - Do we need to clear any callback info from previous session? | 1412 | * - Do we need to clear any callback info from previous session? |
1385 | */ | 1413 | */ |
1386 | 1414 | ||
1415 | if (nfsd4_compound_in_session(cstate->session, &sessionid->sessionid)) { | ||
1416 | if (!nfsd4_last_compound_op(r)) | ||
1417 | return nfserr_not_only_op; | ||
1418 | } | ||
1387 | dump_sessionid(__func__, &sessionid->sessionid); | 1419 | dump_sessionid(__func__, &sessionid->sessionid); |
1388 | spin_lock(&sessionid_lock); | 1420 | spin_lock(&client_lock); |
1389 | ses = find_in_sessionid_hashtbl(&sessionid->sessionid); | 1421 | ses = find_in_sessionid_hashtbl(&sessionid->sessionid); |
1390 | if (!ses) { | 1422 | if (!ses) { |
1391 | spin_unlock(&sessionid_lock); | 1423 | spin_unlock(&client_lock); |
1392 | goto out; | 1424 | goto out; |
1393 | } | 1425 | } |
1394 | 1426 | ||
1395 | unhash_session(ses); | 1427 | unhash_session(ses); |
1396 | spin_unlock(&sessionid_lock); | 1428 | spin_unlock(&client_lock); |
1397 | 1429 | ||
1430 | nfs4_lock_state(); | ||
1398 | /* wait for callbacks */ | 1431 | /* wait for callbacks */ |
1399 | shutdown_callback_client(ses->se_client); | 1432 | nfsd4_set_callback_client(ses->se_client, NULL); |
1433 | nfs4_unlock_state(); | ||
1400 | nfsd4_put_session(ses); | 1434 | nfsd4_put_session(ses); |
1401 | status = nfs_ok; | 1435 | status = nfs_ok; |
1402 | out: | 1436 | out: |
@@ -1417,7 +1451,7 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
1417 | if (resp->opcnt != 1) | 1451 | if (resp->opcnt != 1) |
1418 | return nfserr_sequence_pos; | 1452 | return nfserr_sequence_pos; |
1419 | 1453 | ||
1420 | spin_lock(&sessionid_lock); | 1454 | spin_lock(&client_lock); |
1421 | status = nfserr_badsession; | 1455 | status = nfserr_badsession; |
1422 | session = find_in_sessionid_hashtbl(&seq->sessionid); | 1456 | session = find_in_sessionid_hashtbl(&seq->sessionid); |
1423 | if (!session) | 1457 | if (!session) |
@@ -1456,23 +1490,47 @@ nfsd4_sequence(struct svc_rqst *rqstp, | |||
1456 | cstate->slot = slot; | 1490 | cstate->slot = slot; |
1457 | cstate->session = session; | 1491 | cstate->session = session; |
1458 | 1492 | ||
1459 | /* Hold a session reference until done processing the compound: | ||
1460 | * nfsd4_put_session called only if the cstate slot is set. | ||
1461 | */ | ||
1462 | nfsd4_get_session(session); | ||
1463 | out: | 1493 | out: |
1464 | spin_unlock(&sessionid_lock); | 1494 | /* Hold a session reference until done processing the compound. */ |
1465 | /* Renew the clientid on success and on replay */ | ||
1466 | if (cstate->session) { | 1495 | if (cstate->session) { |
1467 | nfs4_lock_state(); | 1496 | nfsd4_get_session(cstate->session); |
1468 | renew_client(session->se_client); | 1497 | atomic_inc(&session->se_client->cl_refcount); |
1469 | nfs4_unlock_state(); | ||
1470 | } | 1498 | } |
1499 | spin_unlock(&client_lock); | ||
1471 | dprintk("%s: return %d\n", __func__, ntohl(status)); | 1500 | dprintk("%s: return %d\n", __func__, ntohl(status)); |
1472 | return status; | 1501 | return status; |
1473 | } | 1502 | } |
1474 | 1503 | ||
1475 | __be32 | 1504 | __be32 |
1505 | nfsd4_reclaim_complete(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, struct nfsd4_reclaim_complete *rc) | ||
1506 | { | ||
1507 | if (rc->rca_one_fs) { | ||
1508 | if (!cstate->current_fh.fh_dentry) | ||
1509 | return nfserr_nofilehandle; | ||
1510 | /* | ||
1511 | * We don't take advantage of the rca_one_fs case. | ||
1512 | * That's OK, it's optional, we can safely ignore it. | ||
1513 | */ | ||
1514 | return nfs_ok; | ||
1515 | } | ||
1516 | nfs4_lock_state(); | ||
1517 | if (is_client_expired(cstate->session->se_client)) { | ||
1518 | nfs4_unlock_state(); | ||
1519 | /* | ||
1520 | * The following error isn't really legal. | ||
1521 | * But we only get here if the client just explicitly | ||
1522 | * destroyed the client. Surely it no longer cares what | ||
1523 | * error it gets back on an operation for the dead | ||
1524 | * client. | ||
1525 | */ | ||
1526 | return nfserr_stale_clientid; | ||
1527 | } | ||
1528 | nfsd4_create_clid_dir(cstate->session->se_client); | ||
1529 | nfs4_unlock_state(); | ||
1530 | return nfs_ok; | ||
1531 | } | ||
1532 | |||
1533 | __be32 | ||
1476 | nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | 1534 | nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, |
1477 | struct nfsd4_setclientid *setclid) | 1535 | struct nfsd4_setclientid *setclid) |
1478 | { | 1536 | { |
@@ -1631,9 +1689,8 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
1631 | if (!same_creds(&conf->cl_cred, &unconf->cl_cred)) | 1689 | if (!same_creds(&conf->cl_cred, &unconf->cl_cred)) |
1632 | status = nfserr_clid_inuse; | 1690 | status = nfserr_clid_inuse; |
1633 | else { | 1691 | else { |
1634 | /* XXX: We just turn off callbacks until we can handle | 1692 | atomic_set(&conf->cl_cb_set, 0); |
1635 | * change request correctly. */ | 1693 | nfsd4_probe_callback(conf, &unconf->cl_cb_conn); |
1636 | atomic_set(&conf->cl_cb_conn.cb_set, 0); | ||
1637 | expire_client(unconf); | 1694 | expire_client(unconf); |
1638 | status = nfs_ok; | 1695 | status = nfs_ok; |
1639 | 1696 | ||
@@ -1667,7 +1724,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp, | |||
1667 | } | 1724 | } |
1668 | move_to_confirmed(unconf); | 1725 | move_to_confirmed(unconf); |
1669 | conf = unconf; | 1726 | conf = unconf; |
1670 | nfsd4_probe_callback(conf); | 1727 | nfsd4_probe_callback(conf, &conf->cl_cb_conn); |
1671 | status = nfs_ok; | 1728 | status = nfs_ok; |
1672 | } | 1729 | } |
1673 | } else if ((!conf || (conf && !same_verf(&conf->cl_confirm, &confirm))) | 1730 | } else if ((!conf || (conf && !same_verf(&conf->cl_confirm, &confirm))) |
@@ -1700,12 +1757,12 @@ alloc_init_file(struct inode *ino) | |||
1700 | INIT_LIST_HEAD(&fp->fi_hash); | 1757 | INIT_LIST_HEAD(&fp->fi_hash); |
1701 | INIT_LIST_HEAD(&fp->fi_stateids); | 1758 | INIT_LIST_HEAD(&fp->fi_stateids); |
1702 | INIT_LIST_HEAD(&fp->fi_delegations); | 1759 | INIT_LIST_HEAD(&fp->fi_delegations); |
1703 | spin_lock(&recall_lock); | ||
1704 | list_add(&fp->fi_hash, &file_hashtbl[hashval]); | ||
1705 | spin_unlock(&recall_lock); | ||
1706 | fp->fi_inode = igrab(ino); | 1760 | fp->fi_inode = igrab(ino); |
1707 | fp->fi_id = current_fileid++; | 1761 | fp->fi_id = current_fileid++; |
1708 | fp->fi_had_conflict = false; | 1762 | fp->fi_had_conflict = false; |
1763 | spin_lock(&recall_lock); | ||
1764 | list_add(&fp->fi_hash, &file_hashtbl[hashval]); | ||
1765 | spin_unlock(&recall_lock); | ||
1709 | return fp; | 1766 | return fp; |
1710 | } | 1767 | } |
1711 | return NULL; | 1768 | return NULL; |
@@ -1827,7 +1884,7 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open * | |||
1827 | stp->st_stateowner = sop; | 1884 | stp->st_stateowner = sop; |
1828 | get_nfs4_file(fp); | 1885 | get_nfs4_file(fp); |
1829 | stp->st_file = fp; | 1886 | stp->st_file = fp; |
1830 | stp->st_stateid.si_boot = get_seconds(); | 1887 | stp->st_stateid.si_boot = boot_time; |
1831 | stp->st_stateid.si_stateownerid = sop->so_id; | 1888 | stp->st_stateid.si_stateownerid = sop->so_id; |
1832 | stp->st_stateid.si_fileid = fp->fi_id; | 1889 | stp->st_stateid.si_fileid = fp->fi_id; |
1833 | stp->st_stateid.si_generation = 0; | 1890 | stp->st_stateid.si_generation = 0; |
@@ -2028,7 +2085,6 @@ void nfsd_break_deleg_cb(struct file_lock *fl) | |||
2028 | * lock) we know the server hasn't removed the lease yet, we know | 2085 | * lock) we know the server hasn't removed the lease yet, we know |
2029 | * it's safe to take a reference: */ | 2086 | * it's safe to take a reference: */ |
2030 | atomic_inc(&dp->dl_count); | 2087 | atomic_inc(&dp->dl_count); |
2031 | atomic_inc(&dp->dl_client->cl_count); | ||
2032 | 2088 | ||
2033 | spin_lock(&recall_lock); | 2089 | spin_lock(&recall_lock); |
2034 | list_add_tail(&dp->dl_recall_lru, &del_recall_lru); | 2090 | list_add_tail(&dp->dl_recall_lru, &del_recall_lru); |
@@ -2347,7 +2403,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta | |||
2347 | { | 2403 | { |
2348 | struct nfs4_delegation *dp; | 2404 | struct nfs4_delegation *dp; |
2349 | struct nfs4_stateowner *sop = stp->st_stateowner; | 2405 | struct nfs4_stateowner *sop = stp->st_stateowner; |
2350 | struct nfs4_cb_conn *cb = &sop->so_client->cl_cb_conn; | 2406 | int cb_up = atomic_read(&sop->so_client->cl_cb_set); |
2351 | struct file_lock fl, *flp = &fl; | 2407 | struct file_lock fl, *flp = &fl; |
2352 | int status, flag = 0; | 2408 | int status, flag = 0; |
2353 | 2409 | ||
@@ -2355,7 +2411,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta | |||
2355 | open->op_recall = 0; | 2411 | open->op_recall = 0; |
2356 | switch (open->op_claim_type) { | 2412 | switch (open->op_claim_type) { |
2357 | case NFS4_OPEN_CLAIM_PREVIOUS: | 2413 | case NFS4_OPEN_CLAIM_PREVIOUS: |
2358 | if (!atomic_read(&cb->cb_set)) | 2414 | if (!cb_up) |
2359 | open->op_recall = 1; | 2415 | open->op_recall = 1; |
2360 | flag = open->op_delegate_type; | 2416 | flag = open->op_delegate_type; |
2361 | if (flag == NFS4_OPEN_DELEGATE_NONE) | 2417 | if (flag == NFS4_OPEN_DELEGATE_NONE) |
@@ -2366,7 +2422,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta | |||
2366 | * had the chance to reclaim theirs.... */ | 2422 | * had the chance to reclaim theirs.... */ |
2367 | if (locks_in_grace()) | 2423 | if (locks_in_grace()) |
2368 | goto out; | 2424 | goto out; |
2369 | if (!atomic_read(&cb->cb_set) || !sop->so_confirmed) | 2425 | if (!cb_up || !sop->so_confirmed) |
2370 | goto out; | 2426 | goto out; |
2371 | if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) | 2427 | if (open->op_share_access & NFS4_SHARE_ACCESS_WRITE) |
2372 | flag = NFS4_OPEN_DELEGATE_WRITE; | 2428 | flag = NFS4_OPEN_DELEGATE_WRITE; |
@@ -2483,10 +2539,8 @@ nfsd4_process_open2(struct svc_rqst *rqstp, struct svc_fh *current_fh, struct nf | |||
2483 | } | 2539 | } |
2484 | memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t)); | 2540 | memcpy(&open->op_stateid, &stp->st_stateid, sizeof(stateid_t)); |
2485 | 2541 | ||
2486 | if (nfsd4_has_session(&resp->cstate)) { | 2542 | if (nfsd4_has_session(&resp->cstate)) |
2487 | open->op_stateowner->so_confirmed = 1; | 2543 | open->op_stateowner->so_confirmed = 1; |
2488 | nfsd4_create_clid_dir(open->op_stateowner->so_client); | ||
2489 | } | ||
2490 | 2544 | ||
2491 | /* | 2545 | /* |
2492 | * Attempt to hand out a delegation. No error return, because the | 2546 | * Attempt to hand out a delegation. No error return, because the |
@@ -2537,7 +2591,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
2537 | renew_client(clp); | 2591 | renew_client(clp); |
2538 | status = nfserr_cb_path_down; | 2592 | status = nfserr_cb_path_down; |
2539 | if (!list_empty(&clp->cl_delegations) | 2593 | if (!list_empty(&clp->cl_delegations) |
2540 | && !atomic_read(&clp->cl_cb_conn.cb_set)) | 2594 | && !atomic_read(&clp->cl_cb_set)) |
2541 | goto out; | 2595 | goto out; |
2542 | status = nfs_ok; | 2596 | status = nfs_ok; |
2543 | out: | 2597 | out: |
@@ -2554,6 +2608,12 @@ nfsd4_end_grace(void) | |||
2554 | dprintk("NFSD: end of grace period\n"); | 2608 | dprintk("NFSD: end of grace period\n"); |
2555 | nfsd4_recdir_purge_old(); | 2609 | nfsd4_recdir_purge_old(); |
2556 | locks_end_grace(&nfsd4_manager); | 2610 | locks_end_grace(&nfsd4_manager); |
2611 | /* | ||
2612 | * Now that every NFSv4 client has had the chance to recover and | ||
2613 | * to see the (possibly new, possibly shorter) lease time, we | ||
2614 | * can safely set the next grace time to the current lease time: | ||
2615 | */ | ||
2616 | nfsd4_grace = nfsd4_lease; | ||
2557 | } | 2617 | } |
2558 | 2618 | ||
2559 | static time_t | 2619 | static time_t |
@@ -2563,15 +2623,17 @@ nfs4_laundromat(void) | |||
2563 | struct nfs4_stateowner *sop; | 2623 | struct nfs4_stateowner *sop; |
2564 | struct nfs4_delegation *dp; | 2624 | struct nfs4_delegation *dp; |
2565 | struct list_head *pos, *next, reaplist; | 2625 | struct list_head *pos, *next, reaplist; |
2566 | time_t cutoff = get_seconds() - NFSD_LEASE_TIME; | 2626 | time_t cutoff = get_seconds() - nfsd4_lease; |
2567 | time_t t, clientid_val = NFSD_LEASE_TIME; | 2627 | time_t t, clientid_val = nfsd4_lease; |
2568 | time_t u, test_val = NFSD_LEASE_TIME; | 2628 | time_t u, test_val = nfsd4_lease; |
2569 | 2629 | ||
2570 | nfs4_lock_state(); | 2630 | nfs4_lock_state(); |
2571 | 2631 | ||
2572 | dprintk("NFSD: laundromat service - starting\n"); | 2632 | dprintk("NFSD: laundromat service - starting\n"); |
2573 | if (locks_in_grace()) | 2633 | if (locks_in_grace()) |
2574 | nfsd4_end_grace(); | 2634 | nfsd4_end_grace(); |
2635 | INIT_LIST_HEAD(&reaplist); | ||
2636 | spin_lock(&client_lock); | ||
2575 | list_for_each_safe(pos, next, &client_lru) { | 2637 | list_for_each_safe(pos, next, &client_lru) { |
2576 | clp = list_entry(pos, struct nfs4_client, cl_lru); | 2638 | clp = list_entry(pos, struct nfs4_client, cl_lru); |
2577 | if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { | 2639 | if (time_after((unsigned long)clp->cl_time, (unsigned long)cutoff)) { |
@@ -2580,12 +2642,22 @@ nfs4_laundromat(void) | |||
2580 | clientid_val = t; | 2642 | clientid_val = t; |
2581 | break; | 2643 | break; |
2582 | } | 2644 | } |
2645 | if (atomic_read(&clp->cl_refcount)) { | ||
2646 | dprintk("NFSD: client in use (clientid %08x)\n", | ||
2647 | clp->cl_clientid.cl_id); | ||
2648 | continue; | ||
2649 | } | ||
2650 | unhash_client_locked(clp); | ||
2651 | list_add(&clp->cl_lru, &reaplist); | ||
2652 | } | ||
2653 | spin_unlock(&client_lock); | ||
2654 | list_for_each_safe(pos, next, &reaplist) { | ||
2655 | clp = list_entry(pos, struct nfs4_client, cl_lru); | ||
2583 | dprintk("NFSD: purging unused client (clientid %08x)\n", | 2656 | dprintk("NFSD: purging unused client (clientid %08x)\n", |
2584 | clp->cl_clientid.cl_id); | 2657 | clp->cl_clientid.cl_id); |
2585 | nfsd4_remove_clid_dir(clp); | 2658 | nfsd4_remove_clid_dir(clp); |
2586 | expire_client(clp); | 2659 | expire_client(clp); |
2587 | } | 2660 | } |
2588 | INIT_LIST_HEAD(&reaplist); | ||
2589 | spin_lock(&recall_lock); | 2661 | spin_lock(&recall_lock); |
2590 | list_for_each_safe(pos, next, &del_recall_lru) { | 2662 | list_for_each_safe(pos, next, &del_recall_lru) { |
2591 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); | 2663 | dp = list_entry (pos, struct nfs4_delegation, dl_recall_lru); |
@@ -2605,7 +2677,7 @@ nfs4_laundromat(void) | |||
2605 | list_del_init(&dp->dl_recall_lru); | 2677 | list_del_init(&dp->dl_recall_lru); |
2606 | unhash_delegation(dp); | 2678 | unhash_delegation(dp); |
2607 | } | 2679 | } |
2608 | test_val = NFSD_LEASE_TIME; | 2680 | test_val = nfsd4_lease; |
2609 | list_for_each_safe(pos, next, &close_lru) { | 2681 | list_for_each_safe(pos, next, &close_lru) { |
2610 | sop = list_entry(pos, struct nfs4_stateowner, so_close_lru); | 2682 | sop = list_entry(pos, struct nfs4_stateowner, so_close_lru); |
2611 | if (time_after((unsigned long)sop->so_time, (unsigned long)cutoff)) { | 2683 | if (time_after((unsigned long)sop->so_time, (unsigned long)cutoff)) { |
@@ -2661,39 +2733,11 @@ nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stateid *stp) | |||
2661 | static int | 2733 | static int |
2662 | STALE_STATEID(stateid_t *stateid) | 2734 | STALE_STATEID(stateid_t *stateid) |
2663 | { | 2735 | { |
2664 | if (time_after((unsigned long)boot_time, | 2736 | if (stateid->si_boot == boot_time) |
2665 | (unsigned long)stateid->si_boot)) { | 2737 | return 0; |
2666 | dprintk("NFSD: stale stateid " STATEID_FMT "!\n", | 2738 | dprintk("NFSD: stale stateid " STATEID_FMT "!\n", |
2667 | STATEID_VAL(stateid)); | ||
2668 | return 1; | ||
2669 | } | ||
2670 | return 0; | ||
2671 | } | ||
2672 | |||
2673 | static int | ||
2674 | EXPIRED_STATEID(stateid_t *stateid) | ||
2675 | { | ||
2676 | if (time_before((unsigned long)boot_time, | ||
2677 | ((unsigned long)stateid->si_boot)) && | ||
2678 | time_before((unsigned long)(stateid->si_boot + lease_time), get_seconds())) { | ||
2679 | dprintk("NFSD: expired stateid " STATEID_FMT "!\n", | ||
2680 | STATEID_VAL(stateid)); | ||
2681 | return 1; | ||
2682 | } | ||
2683 | return 0; | ||
2684 | } | ||
2685 | |||
2686 | static __be32 | ||
2687 | stateid_error_map(stateid_t *stateid) | ||
2688 | { | ||
2689 | if (STALE_STATEID(stateid)) | ||
2690 | return nfserr_stale_stateid; | ||
2691 | if (EXPIRED_STATEID(stateid)) | ||
2692 | return nfserr_expired; | ||
2693 | |||
2694 | dprintk("NFSD: bad stateid " STATEID_FMT "!\n", | ||
2695 | STATEID_VAL(stateid)); | 2739 | STATEID_VAL(stateid)); |
2696 | return nfserr_bad_stateid; | 2740 | return 1; |
2697 | } | 2741 | } |
2698 | 2742 | ||
2699 | static inline int | 2743 | static inline int |
@@ -2817,10 +2861,8 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, | |||
2817 | status = nfserr_bad_stateid; | 2861 | status = nfserr_bad_stateid; |
2818 | if (is_delegation_stateid(stateid)) { | 2862 | if (is_delegation_stateid(stateid)) { |
2819 | dp = find_delegation_stateid(ino, stateid); | 2863 | dp = find_delegation_stateid(ino, stateid); |
2820 | if (!dp) { | 2864 | if (!dp) |
2821 | status = stateid_error_map(stateid); | ||
2822 | goto out; | 2865 | goto out; |
2823 | } | ||
2824 | status = check_stateid_generation(stateid, &dp->dl_stateid, | 2866 | status = check_stateid_generation(stateid, &dp->dl_stateid, |
2825 | flags); | 2867 | flags); |
2826 | if (status) | 2868 | if (status) |
@@ -2833,10 +2875,8 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, | |||
2833 | *filpp = dp->dl_vfs_file; | 2875 | *filpp = dp->dl_vfs_file; |
2834 | } else { /* open or lock stateid */ | 2876 | } else { /* open or lock stateid */ |
2835 | stp = find_stateid(stateid, flags); | 2877 | stp = find_stateid(stateid, flags); |
2836 | if (!stp) { | 2878 | if (!stp) |
2837 | status = stateid_error_map(stateid); | ||
2838 | goto out; | 2879 | goto out; |
2839 | } | ||
2840 | if (nfs4_check_fh(current_fh, stp)) | 2880 | if (nfs4_check_fh(current_fh, stp)) |
2841 | goto out; | 2881 | goto out; |
2842 | if (!stp->st_stateowner->so_confirmed) | 2882 | if (!stp->st_stateowner->so_confirmed) |
@@ -2908,7 +2948,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid, | |||
2908 | */ | 2948 | */ |
2909 | sop = search_close_lru(stateid->si_stateownerid, flags); | 2949 | sop = search_close_lru(stateid->si_stateownerid, flags); |
2910 | if (sop == NULL) | 2950 | if (sop == NULL) |
2911 | return stateid_error_map(stateid); | 2951 | return nfserr_bad_stateid; |
2912 | *sopp = sop; | 2952 | *sopp = sop; |
2913 | goto check_replay; | 2953 | goto check_replay; |
2914 | } | 2954 | } |
@@ -3175,10 +3215,8 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate, | |||
3175 | if (!is_delegation_stateid(stateid)) | 3215 | if (!is_delegation_stateid(stateid)) |
3176 | goto out; | 3216 | goto out; |
3177 | dp = find_delegation_stateid(inode, stateid); | 3217 | dp = find_delegation_stateid(inode, stateid); |
3178 | if (!dp) { | 3218 | if (!dp) |
3179 | status = stateid_error_map(stateid); | ||
3180 | goto out; | 3219 | goto out; |
3181 | } | ||
3182 | status = check_stateid_generation(stateid, &dp->dl_stateid, flags); | 3220 | status = check_stateid_generation(stateid, &dp->dl_stateid, flags); |
3183 | if (status) | 3221 | if (status) |
3184 | goto out; | 3222 | goto out; |
@@ -3404,7 +3442,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc | |||
3404 | stp->st_stateowner = sop; | 3442 | stp->st_stateowner = sop; |
3405 | get_nfs4_file(fp); | 3443 | get_nfs4_file(fp); |
3406 | stp->st_file = fp; | 3444 | stp->st_file = fp; |
3407 | stp->st_stateid.si_boot = get_seconds(); | 3445 | stp->st_stateid.si_boot = boot_time; |
3408 | stp->st_stateid.si_stateownerid = sop->so_id; | 3446 | stp->st_stateid.si_stateownerid = sop->so_id; |
3409 | stp->st_stateid.si_fileid = fp->fi_id; | 3447 | stp->st_stateid.si_fileid = fp->fi_id; |
3410 | stp->st_stateid.si_generation = 0; | 3448 | stp->st_stateid.si_generation = 0; |
@@ -3976,12 +4014,6 @@ nfsd4_load_reboot_recovery_data(void) | |||
3976 | printk("NFSD: Failure reading reboot recovery data\n"); | 4014 | printk("NFSD: Failure reading reboot recovery data\n"); |
3977 | } | 4015 | } |
3978 | 4016 | ||
3979 | unsigned long | ||
3980 | get_nfs4_grace_period(void) | ||
3981 | { | ||
3982 | return max(user_lease_time, lease_time) * HZ; | ||
3983 | } | ||
3984 | |||
3985 | /* | 4017 | /* |
3986 | * Since the lifetime of a delegation isn't limited to that of an open, a | 4018 | * Since the lifetime of a delegation isn't limited to that of an open, a |
3987 | * client may quite reasonably hang on to a delegation as long as it has | 4019 | * client may quite reasonably hang on to a delegation as long as it has |
@@ -4008,20 +4040,27 @@ set_max_delegations(void) | |||
4008 | static int | 4040 | static int |
4009 | __nfs4_state_start(void) | 4041 | __nfs4_state_start(void) |
4010 | { | 4042 | { |
4011 | unsigned long grace_time; | 4043 | int ret; |
4012 | 4044 | ||
4013 | boot_time = get_seconds(); | 4045 | boot_time = get_seconds(); |
4014 | grace_time = get_nfs4_grace_period(); | ||
4015 | lease_time = user_lease_time; | ||
4016 | locks_start_grace(&nfsd4_manager); | 4046 | locks_start_grace(&nfsd4_manager); |
4017 | printk(KERN_INFO "NFSD: starting %ld-second grace period\n", | 4047 | printk(KERN_INFO "NFSD: starting %ld-second grace period\n", |
4018 | grace_time/HZ); | 4048 | nfsd4_grace); |
4049 | ret = set_callback_cred(); | ||
4050 | if (ret) | ||
4051 | return -ENOMEM; | ||
4019 | laundry_wq = create_singlethread_workqueue("nfsd4"); | 4052 | laundry_wq = create_singlethread_workqueue("nfsd4"); |
4020 | if (laundry_wq == NULL) | 4053 | if (laundry_wq == NULL) |
4021 | return -ENOMEM; | 4054 | return -ENOMEM; |
4022 | queue_delayed_work(laundry_wq, &laundromat_work, grace_time); | 4055 | ret = nfsd4_create_callback_queue(); |
4056 | if (ret) | ||
4057 | goto out_free_laundry; | ||
4058 | queue_delayed_work(laundry_wq, &laundromat_work, nfsd4_grace * HZ); | ||
4023 | set_max_delegations(); | 4059 | set_max_delegations(); |
4024 | return set_callback_cred(); | 4060 | return 0; |
4061 | out_free_laundry: | ||
4062 | destroy_workqueue(laundry_wq); | ||
4063 | return ret; | ||
4025 | } | 4064 | } |
4026 | 4065 | ||
4027 | int | 4066 | int |
@@ -4039,12 +4078,6 @@ nfs4_state_start(void) | |||
4039 | return 0; | 4078 | return 0; |
4040 | } | 4079 | } |
4041 | 4080 | ||
4042 | time_t | ||
4043 | nfs4_lease_time(void) | ||
4044 | { | ||
4045 | return lease_time; | ||
4046 | } | ||
4047 | |||
4048 | static void | 4081 | static void |
4049 | __nfs4_state_shutdown(void) | 4082 | __nfs4_state_shutdown(void) |
4050 | { | 4083 | { |
@@ -4089,6 +4122,7 @@ nfs4_state_shutdown(void) | |||
4089 | nfs4_lock_state(); | 4122 | nfs4_lock_state(); |
4090 | nfs4_release_reclaim(); | 4123 | nfs4_release_reclaim(); |
4091 | __nfs4_state_shutdown(); | 4124 | __nfs4_state_shutdown(); |
4125 | nfsd4_destroy_callback_queue(); | ||
4092 | nfs4_unlock_state(); | 4126 | nfs4_unlock_state(); |
4093 | } | 4127 | } |
4094 | 4128 | ||
@@ -4128,21 +4162,3 @@ nfs4_recoverydir(void) | |||
4128 | { | 4162 | { |
4129 | return user_recovery_dirname; | 4163 | return user_recovery_dirname; |
4130 | } | 4164 | } |
4131 | |||
4132 | /* | ||
4133 | * Called when leasetime is changed. | ||
4134 | * | ||
4135 | * The only way the protocol gives us to handle on-the-fly lease changes is to | ||
4136 | * simulate a reboot. Instead of doing that, we just wait till the next time | ||
4137 | * we start to register any changes in lease time. If the administrator | ||
4138 | * really wants to change the lease time *now*, they can go ahead and bring | ||
4139 | * nfsd down and then back up again after changing the lease time. | ||
4140 | * | ||
4141 | * user_lease_time is protected by nfsd_mutex since it's only really accessed | ||
4142 | * when nfsd is starting | ||
4143 | */ | ||
4144 | void | ||
4145 | nfs4_reset_lease(time_t leasetime) | ||
4146 | { | ||
4147 | user_lease_time = leasetime; | ||
4148 | } | ||
diff --git a/fs/nfsd/nfs4xdr.c b/fs/nfsd/nfs4xdr.c index 34ccf815ea8a..ac17a7080239 100644 --- a/fs/nfsd/nfs4xdr.c +++ b/fs/nfsd/nfs4xdr.c | |||
@@ -1234,6 +1234,16 @@ nfsd4_decode_sequence(struct nfsd4_compoundargs *argp, | |||
1234 | DECODE_TAIL; | 1234 | DECODE_TAIL; |
1235 | } | 1235 | } |
1236 | 1236 | ||
1237 | static __be32 nfsd4_decode_reclaim_complete(struct nfsd4_compoundargs *argp, struct nfsd4_reclaim_complete *rc) | ||
1238 | { | ||
1239 | DECODE_HEAD; | ||
1240 | |||
1241 | READ_BUF(4); | ||
1242 | READ32(rc->rca_one_fs); | ||
1243 | |||
1244 | DECODE_TAIL; | ||
1245 | } | ||
1246 | |||
1237 | static __be32 | 1247 | static __be32 |
1238 | nfsd4_decode_noop(struct nfsd4_compoundargs *argp, void *p) | 1248 | nfsd4_decode_noop(struct nfsd4_compoundargs *argp, void *p) |
1239 | { | 1249 | { |
@@ -1346,7 +1356,7 @@ static nfsd4_dec nfsd41_dec_ops[] = { | |||
1346 | [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp, | 1356 | [OP_TEST_STATEID] = (nfsd4_dec)nfsd4_decode_notsupp, |
1347 | [OP_WANT_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp, | 1357 | [OP_WANT_DELEGATION] = (nfsd4_dec)nfsd4_decode_notsupp, |
1348 | [OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp, | 1358 | [OP_DESTROY_CLIENTID] = (nfsd4_dec)nfsd4_decode_notsupp, |
1349 | [OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_notsupp, | 1359 | [OP_RECLAIM_COMPLETE] = (nfsd4_dec)nfsd4_decode_reclaim_complete, |
1350 | }; | 1360 | }; |
1351 | 1361 | ||
1352 | struct nfsd4_minorversion_ops { | 1362 | struct nfsd4_minorversion_ops { |
@@ -1900,7 +1910,7 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp, | |||
1900 | if (bmval0 & FATTR4_WORD0_LEASE_TIME) { | 1910 | if (bmval0 & FATTR4_WORD0_LEASE_TIME) { |
1901 | if ((buflen -= 4) < 0) | 1911 | if ((buflen -= 4) < 0) |
1902 | goto out_resource; | 1912 | goto out_resource; |
1903 | WRITE32(NFSD_LEASE_TIME); | 1913 | WRITE32(nfsd4_lease); |
1904 | } | 1914 | } |
1905 | if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { | 1915 | if (bmval0 & FATTR4_WORD0_RDATTR_ERROR) { |
1906 | if ((buflen -= 4) < 0) | 1916 | if ((buflen -= 4) < 0) |
@@ -3307,11 +3317,14 @@ nfs4svc_encode_compoundres(struct svc_rqst *rqstp, __be32 *p, struct nfsd4_compo | |||
3307 | iov = &rqstp->rq_res.head[0]; | 3317 | iov = &rqstp->rq_res.head[0]; |
3308 | iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base; | 3318 | iov->iov_len = ((char*)resp->p) - (char*)iov->iov_base; |
3309 | BUG_ON(iov->iov_len > PAGE_SIZE); | 3319 | BUG_ON(iov->iov_len > PAGE_SIZE); |
3310 | if (nfsd4_has_session(cs) && cs->status != nfserr_replay_cache) { | 3320 | if (nfsd4_has_session(cs)) { |
3311 | nfsd4_store_cache_entry(resp); | 3321 | if (cs->status != nfserr_replay_cache) { |
3312 | dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__); | 3322 | nfsd4_store_cache_entry(resp); |
3313 | resp->cstate.slot->sl_inuse = false; | 3323 | dprintk("%s: SET SLOT STATE TO AVAILABLE\n", __func__); |
3314 | nfsd4_put_session(resp->cstate.session); | 3324 | cs->slot->sl_inuse = false; |
3325 | } | ||
3326 | /* Renew the clientid on success and on replay */ | ||
3327 | release_session_client(cs->session); | ||
3315 | } | 3328 | } |
3316 | return 1; | 3329 | return 1; |
3317 | } | 3330 | } |
diff --git a/fs/nfsd/nfsctl.c b/fs/nfsd/nfsctl.c index e3591073098f..bc3194ea01f5 100644 --- a/fs/nfsd/nfsctl.c +++ b/fs/nfsd/nfsctl.c | |||
@@ -46,6 +46,7 @@ enum { | |||
46 | */ | 46 | */ |
47 | #ifdef CONFIG_NFSD_V4 | 47 | #ifdef CONFIG_NFSD_V4 |
48 | NFSD_Leasetime, | 48 | NFSD_Leasetime, |
49 | NFSD_Gracetime, | ||
49 | NFSD_RecoveryDir, | 50 | NFSD_RecoveryDir, |
50 | #endif | 51 | #endif |
51 | }; | 52 | }; |
@@ -70,6 +71,7 @@ static ssize_t write_ports(struct file *file, char *buf, size_t size); | |||
70 | static ssize_t write_maxblksize(struct file *file, char *buf, size_t size); | 71 | static ssize_t write_maxblksize(struct file *file, char *buf, size_t size); |
71 | #ifdef CONFIG_NFSD_V4 | 72 | #ifdef CONFIG_NFSD_V4 |
72 | static ssize_t write_leasetime(struct file *file, char *buf, size_t size); | 73 | static ssize_t write_leasetime(struct file *file, char *buf, size_t size); |
74 | static ssize_t write_gracetime(struct file *file, char *buf, size_t size); | ||
73 | static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); | 75 | static ssize_t write_recoverydir(struct file *file, char *buf, size_t size); |
74 | #endif | 76 | #endif |
75 | 77 | ||
@@ -91,6 +93,7 @@ static ssize_t (*write_op[])(struct file *, char *, size_t) = { | |||
91 | [NFSD_MaxBlkSize] = write_maxblksize, | 93 | [NFSD_MaxBlkSize] = write_maxblksize, |
92 | #ifdef CONFIG_NFSD_V4 | 94 | #ifdef CONFIG_NFSD_V4 |
93 | [NFSD_Leasetime] = write_leasetime, | 95 | [NFSD_Leasetime] = write_leasetime, |
96 | [NFSD_Gracetime] = write_gracetime, | ||
94 | [NFSD_RecoveryDir] = write_recoverydir, | 97 | [NFSD_RecoveryDir] = write_recoverydir, |
95 | #endif | 98 | #endif |
96 | }; | 99 | }; |
@@ -1204,29 +1207,45 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size) | |||
1204 | } | 1207 | } |
1205 | 1208 | ||
1206 | #ifdef CONFIG_NFSD_V4 | 1209 | #ifdef CONFIG_NFSD_V4 |
1207 | extern time_t nfs4_leasetime(void); | 1210 | static ssize_t __nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time) |
1208 | |||
1209 | static ssize_t __write_leasetime(struct file *file, char *buf, size_t size) | ||
1210 | { | 1211 | { |
1211 | /* if size > 10 seconds, call | ||
1212 | * nfs4_reset_lease() then write out the new lease (seconds) as reply | ||
1213 | */ | ||
1214 | char *mesg = buf; | 1212 | char *mesg = buf; |
1215 | int rv, lease; | 1213 | int rv, i; |
1216 | 1214 | ||
1217 | if (size > 0) { | 1215 | if (size > 0) { |
1218 | if (nfsd_serv) | 1216 | if (nfsd_serv) |
1219 | return -EBUSY; | 1217 | return -EBUSY; |
1220 | rv = get_int(&mesg, &lease); | 1218 | rv = get_int(&mesg, &i); |
1221 | if (rv) | 1219 | if (rv) |
1222 | return rv; | 1220 | return rv; |
1223 | if (lease < 10 || lease > 3600) | 1221 | /* |
1222 | * Some sanity checking. We don't have a reason for | ||
1223 | * these particular numbers, but problems with the | ||
1224 | * extremes are: | ||
1225 | * - Too short: the briefest network outage may | ||
1226 | * cause clients to lose all their locks. Also, | ||
1227 | * the frequent polling may be wasteful. | ||
1228 | * - Too long: do you really want reboot recovery | ||
1229 | * to take more than an hour? Or to make other | ||
1230 | * clients wait an hour before being able to | ||
1231 | * revoke a dead client's locks? | ||
1232 | */ | ||
1233 | if (i < 10 || i > 3600) | ||
1224 | return -EINVAL; | 1234 | return -EINVAL; |
1225 | nfs4_reset_lease(lease); | 1235 | *time = i; |
1226 | } | 1236 | } |
1227 | 1237 | ||
1228 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", | 1238 | return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n", *time); |
1229 | nfs4_lease_time()); | 1239 | } |
1240 | |||
1241 | static ssize_t nfsd4_write_time(struct file *file, char *buf, size_t size, time_t *time) | ||
1242 | { | ||
1243 | ssize_t rv; | ||
1244 | |||
1245 | mutex_lock(&nfsd_mutex); | ||
1246 | rv = __nfsd4_write_time(file, buf, size, time); | ||
1247 | mutex_unlock(&nfsd_mutex); | ||
1248 | return rv; | ||
1230 | } | 1249 | } |
1231 | 1250 | ||
1232 | /** | 1251 | /** |
@@ -1252,12 +1271,22 @@ static ssize_t __write_leasetime(struct file *file, char *buf, size_t size) | |||
1252 | */ | 1271 | */ |
1253 | static ssize_t write_leasetime(struct file *file, char *buf, size_t size) | 1272 | static ssize_t write_leasetime(struct file *file, char *buf, size_t size) |
1254 | { | 1273 | { |
1255 | ssize_t rv; | 1274 | return nfsd4_write_time(file, buf, size, &nfsd4_lease); |
1275 | } | ||
1256 | 1276 | ||
1257 | mutex_lock(&nfsd_mutex); | 1277 | /** |
1258 | rv = __write_leasetime(file, buf, size); | 1278 | * write_gracetime - Set or report current NFSv4 grace period time |
1259 | mutex_unlock(&nfsd_mutex); | 1279 | * |
1260 | return rv; | 1280 | * As above, but sets the time of the NFSv4 grace period. |
1281 | * | ||
1282 | * Note this should never be set to less than the *previous* | ||
1283 | * lease-period time, but we don't try to enforce this. (In the common | ||
1284 | * case (a new boot), we don't know what the previous lease time was | ||
1285 | * anyway.) | ||
1286 | */ | ||
1287 | static ssize_t write_gracetime(struct file *file, char *buf, size_t size) | ||
1288 | { | ||
1289 | return nfsd4_write_time(file, buf, size, &nfsd4_grace); | ||
1261 | } | 1290 | } |
1262 | 1291 | ||
1263 | extern char *nfs4_recoverydir(void); | 1292 | extern char *nfs4_recoverydir(void); |
@@ -1351,6 +1380,7 @@ static int nfsd_fill_super(struct super_block * sb, void * data, int silent) | |||
1351 | [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, | 1380 | [NFSD_MaxBlkSize] = {"max_block_size", &transaction_ops, S_IWUSR|S_IRUGO}, |
1352 | #ifdef CONFIG_NFSD_V4 | 1381 | #ifdef CONFIG_NFSD_V4 |
1353 | [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, | 1382 | [NFSD_Leasetime] = {"nfsv4leasetime", &transaction_ops, S_IWUSR|S_IRUSR}, |
1383 | [NFSD_Gracetime] = {"nfsv4gracetime", &transaction_ops, S_IWUSR|S_IRUSR}, | ||
1354 | [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR}, | 1384 | [NFSD_RecoveryDir] = {"nfsv4recoverydir", &transaction_ops, S_IWUSR|S_IRUSR}, |
1355 | #endif | 1385 | #endif |
1356 | /* last one */ {""} | 1386 | /* last one */ {""} |
diff --git a/fs/nfsd/nfsd.h b/fs/nfsd/nfsd.h index e942a1aaac92..72377761270e 100644 --- a/fs/nfsd/nfsd.h +++ b/fs/nfsd/nfsd.h | |||
@@ -82,7 +82,6 @@ int nfs4_state_init(void); | |||
82 | void nfsd4_free_slabs(void); | 82 | void nfsd4_free_slabs(void); |
83 | int nfs4_state_start(void); | 83 | int nfs4_state_start(void); |
84 | void nfs4_state_shutdown(void); | 84 | void nfs4_state_shutdown(void); |
85 | time_t nfs4_lease_time(void); | ||
86 | void nfs4_reset_lease(time_t leasetime); | 85 | void nfs4_reset_lease(time_t leasetime); |
87 | int nfs4_reset_recoverydir(char *recdir); | 86 | int nfs4_reset_recoverydir(char *recdir); |
88 | #else | 87 | #else |
@@ -90,7 +89,6 @@ static inline int nfs4_state_init(void) { return 0; } | |||
90 | static inline void nfsd4_free_slabs(void) { } | 89 | static inline void nfsd4_free_slabs(void) { } |
91 | static inline int nfs4_state_start(void) { return 0; } | 90 | static inline int nfs4_state_start(void) { return 0; } |
92 | static inline void nfs4_state_shutdown(void) { } | 91 | static inline void nfs4_state_shutdown(void) { } |
93 | static inline time_t nfs4_lease_time(void) { return 0; } | ||
94 | static inline void nfs4_reset_lease(time_t leasetime) { } | 92 | static inline void nfs4_reset_lease(time_t leasetime) { } |
95 | static inline int nfs4_reset_recoverydir(char *recdir) { return 0; } | 93 | static inline int nfs4_reset_recoverydir(char *recdir) { return 0; } |
96 | #endif | 94 | #endif |
@@ -229,6 +227,9 @@ extern struct timeval nfssvc_boot; | |||
229 | 227 | ||
230 | #ifdef CONFIG_NFSD_V4 | 228 | #ifdef CONFIG_NFSD_V4 |
231 | 229 | ||
230 | extern time_t nfsd4_lease; | ||
231 | extern time_t nfsd4_grace; | ||
232 | |||
232 | /* before processing a COMPOUND operation, we have to check that there | 233 | /* before processing a COMPOUND operation, we have to check that there |
233 | * is enough space in the buffer for XDR encode to succeed. otherwise, | 234 | * is enough space in the buffer for XDR encode to succeed. otherwise, |
234 | * we might process an operation with side effects, and be unable to | 235 | * we might process an operation with side effects, and be unable to |
@@ -247,7 +248,6 @@ extern struct timeval nfssvc_boot; | |||
247 | #define COMPOUND_SLACK_SPACE 140 /* OP_GETFH */ | 248 | #define COMPOUND_SLACK_SPACE 140 /* OP_GETFH */ |
248 | #define COMPOUND_ERR_SLACK_SPACE 12 /* OP_SETATTR */ | 249 | #define COMPOUND_ERR_SLACK_SPACE 12 /* OP_SETATTR */ |
249 | 250 | ||
250 | #define NFSD_LEASE_TIME (nfs4_lease_time()) | ||
251 | #define NFSD_LAUNDROMAT_MINTIMEOUT 10 /* seconds */ | 251 | #define NFSD_LAUNDROMAT_MINTIMEOUT 10 /* seconds */ |
252 | 252 | ||
253 | /* | 253 | /* |
diff --git a/fs/nfsd/nfssvc.c b/fs/nfsd/nfssvc.c index 171699eb07c8..06b2a26edfe0 100644 --- a/fs/nfsd/nfssvc.c +++ b/fs/nfsd/nfssvc.c | |||
@@ -120,7 +120,7 @@ u32 nfsd_supported_minorversion; | |||
120 | int nfsd_vers(int vers, enum vers_op change) | 120 | int nfsd_vers(int vers, enum vers_op change) |
121 | { | 121 | { |
122 | if (vers < NFSD_MINVERS || vers >= NFSD_NRVERS) | 122 | if (vers < NFSD_MINVERS || vers >= NFSD_NRVERS) |
123 | return -1; | 123 | return 0; |
124 | switch(change) { | 124 | switch(change) { |
125 | case NFSD_SET: | 125 | case NFSD_SET: |
126 | nfsd_versions[vers] = nfsd_version[vers]; | 126 | nfsd_versions[vers] = nfsd_version[vers]; |
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h index fefeae27f25e..006c84230c7c 100644 --- a/fs/nfsd/state.h +++ b/fs/nfsd/state.h | |||
@@ -70,6 +70,16 @@ struct nfsd4_cb_sequence { | |||
70 | struct nfs4_client *cbs_clp; | 70 | struct nfs4_client *cbs_clp; |
71 | }; | 71 | }; |
72 | 72 | ||
73 | struct nfs4_rpc_args { | ||
74 | void *args_op; | ||
75 | struct nfsd4_cb_sequence args_seq; | ||
76 | }; | ||
77 | |||
78 | struct nfsd4_callback { | ||
79 | struct nfs4_rpc_args cb_args; | ||
80 | struct work_struct cb_work; | ||
81 | }; | ||
82 | |||
73 | struct nfs4_delegation { | 83 | struct nfs4_delegation { |
74 | struct list_head dl_perfile; | 84 | struct list_head dl_perfile; |
75 | struct list_head dl_perclnt; | 85 | struct list_head dl_perclnt; |
@@ -86,6 +96,7 @@ struct nfs4_delegation { | |||
86 | stateid_t dl_stateid; | 96 | stateid_t dl_stateid; |
87 | struct knfsd_fh dl_fh; | 97 | struct knfsd_fh dl_fh; |
88 | int dl_retries; | 98 | int dl_retries; |
99 | struct nfsd4_callback dl_recall; | ||
89 | }; | 100 | }; |
90 | 101 | ||
91 | /* client delegation callback info */ | 102 | /* client delegation callback info */ |
@@ -96,9 +107,7 @@ struct nfs4_cb_conn { | |||
96 | u32 cb_prog; | 107 | u32 cb_prog; |
97 | u32 cb_minorversion; | 108 | u32 cb_minorversion; |
98 | u32 cb_ident; /* minorversion 0 only */ | 109 | u32 cb_ident; /* minorversion 0 only */ |
99 | /* RPC client info */ | 110 | struct svc_xprt *cb_xprt; /* minorversion 1 only */ |
100 | atomic_t cb_set; /* successful CB_NULL call */ | ||
101 | struct rpc_clnt * cb_client; | ||
102 | }; | 111 | }; |
103 | 112 | ||
104 | /* Maximum number of slots per session. 160 is useful for long haul TCP */ | 113 | /* Maximum number of slots per session. 160 is useful for long haul TCP */ |
@@ -157,7 +166,7 @@ struct nfsd4_session { | |||
157 | struct list_head se_hash; /* hash by sessionid */ | 166 | struct list_head se_hash; /* hash by sessionid */ |
158 | struct list_head se_perclnt; | 167 | struct list_head se_perclnt; |
159 | u32 se_flags; | 168 | u32 se_flags; |
160 | struct nfs4_client *se_client; /* for expire_client */ | 169 | struct nfs4_client *se_client; |
161 | struct nfs4_sessionid se_sessionid; | 170 | struct nfs4_sessionid se_sessionid; |
162 | struct nfsd4_channel_attrs se_fchannel; | 171 | struct nfsd4_channel_attrs se_fchannel; |
163 | struct nfsd4_channel_attrs se_bchannel; | 172 | struct nfsd4_channel_attrs se_bchannel; |
@@ -212,25 +221,41 @@ struct nfs4_client { | |||
212 | struct svc_cred cl_cred; /* setclientid principal */ | 221 | struct svc_cred cl_cred; /* setclientid principal */ |
213 | clientid_t cl_clientid; /* generated by server */ | 222 | clientid_t cl_clientid; /* generated by server */ |
214 | nfs4_verifier cl_confirm; /* generated by server */ | 223 | nfs4_verifier cl_confirm; /* generated by server */ |
215 | struct nfs4_cb_conn cl_cb_conn; /* callback info */ | ||
216 | atomic_t cl_count; /* ref count */ | ||
217 | u32 cl_firststate; /* recovery dir creation */ | 224 | u32 cl_firststate; /* recovery dir creation */ |
218 | 225 | ||
226 | /* for v4.0 and v4.1 callbacks: */ | ||
227 | struct nfs4_cb_conn cl_cb_conn; | ||
228 | struct rpc_clnt *cl_cb_client; | ||
229 | atomic_t cl_cb_set; | ||
230 | |||
219 | /* for nfs41 */ | 231 | /* for nfs41 */ |
220 | struct list_head cl_sessions; | 232 | struct list_head cl_sessions; |
221 | struct nfsd4_clid_slot cl_cs_slot; /* create_session slot */ | 233 | struct nfsd4_clid_slot cl_cs_slot; /* create_session slot */ |
222 | u32 cl_exchange_flags; | 234 | u32 cl_exchange_flags; |
223 | struct nfs4_sessionid cl_sessionid; | 235 | struct nfs4_sessionid cl_sessionid; |
236 | /* number of rpc's in progress over an associated session: */ | ||
237 | atomic_t cl_refcount; | ||
224 | 238 | ||
225 | /* for nfs41 callbacks */ | 239 | /* for nfs41 callbacks */ |
226 | /* We currently support a single back channel with a single slot */ | 240 | /* We currently support a single back channel with a single slot */ |
227 | unsigned long cl_cb_slot_busy; | 241 | unsigned long cl_cb_slot_busy; |
228 | u32 cl_cb_seq_nr; | 242 | u32 cl_cb_seq_nr; |
229 | struct svc_xprt *cl_cb_xprt; /* 4.1 callback transport */ | ||
230 | struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */ | 243 | struct rpc_wait_queue cl_cb_waitq; /* backchannel callers may */ |
231 | /* wait here for slots */ | 244 | /* wait here for slots */ |
232 | }; | 245 | }; |
233 | 246 | ||
247 | static inline void | ||
248 | mark_client_expired(struct nfs4_client *clp) | ||
249 | { | ||
250 | clp->cl_time = 0; | ||
251 | } | ||
252 | |||
253 | static inline bool | ||
254 | is_client_expired(struct nfs4_client *clp) | ||
255 | { | ||
256 | return clp->cl_time == 0; | ||
257 | } | ||
258 | |||
234 | /* struct nfs4_client_reset | 259 | /* struct nfs4_client_reset |
235 | * one per old client. Populates reset_str_hashtbl. Filled from conf_id_hashtbl | 260 | * one per old client. Populates reset_str_hashtbl. Filled from conf_id_hashtbl |
236 | * upon lease reset, or from upcall to state_daemon (to read in state | 261 | * upon lease reset, or from upcall to state_daemon (to read in state |
@@ -377,11 +402,14 @@ extern void nfs4_lock_state(void); | |||
377 | extern void nfs4_unlock_state(void); | 402 | extern void nfs4_unlock_state(void); |
378 | extern int nfs4_in_grace(void); | 403 | extern int nfs4_in_grace(void); |
379 | extern __be32 nfs4_check_open_reclaim(clientid_t *clid); | 404 | extern __be32 nfs4_check_open_reclaim(clientid_t *clid); |
380 | extern void put_nfs4_client(struct nfs4_client *clp); | ||
381 | extern void nfs4_free_stateowner(struct kref *kref); | 405 | extern void nfs4_free_stateowner(struct kref *kref); |
382 | extern int set_callback_cred(void); | 406 | extern int set_callback_cred(void); |
383 | extern void nfsd4_probe_callback(struct nfs4_client *clp); | 407 | extern void nfsd4_probe_callback(struct nfs4_client *clp, struct nfs4_cb_conn *); |
408 | extern void nfsd4_do_callback_rpc(struct work_struct *); | ||
384 | extern void nfsd4_cb_recall(struct nfs4_delegation *dp); | 409 | extern void nfsd4_cb_recall(struct nfs4_delegation *dp); |
410 | extern int nfsd4_create_callback_queue(void); | ||
411 | extern void nfsd4_destroy_callback_queue(void); | ||
412 | extern void nfsd4_set_callback_client(struct nfs4_client *, struct rpc_clnt *); | ||
385 | extern void nfs4_put_delegation(struct nfs4_delegation *dp); | 413 | extern void nfs4_put_delegation(struct nfs4_delegation *dp); |
386 | extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); | 414 | extern __be32 nfs4_make_rec_clidname(char *clidname, struct xdr_netobj *clname); |
387 | extern void nfsd4_init_recdir(char *recdir_name); | 415 | extern void nfsd4_init_recdir(char *recdir_name); |
@@ -392,6 +420,7 @@ extern int nfs4_has_reclaimed_state(const char *name, bool use_exchange_id); | |||
392 | extern void nfsd4_recdir_purge_old(void); | 420 | extern void nfsd4_recdir_purge_old(void); |
393 | extern int nfsd4_create_clid_dir(struct nfs4_client *clp); | 421 | extern int nfsd4_create_clid_dir(struct nfs4_client *clp); |
394 | extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); | 422 | extern void nfsd4_remove_clid_dir(struct nfs4_client *clp); |
423 | extern void release_session_client(struct nfsd4_session *); | ||
395 | 424 | ||
396 | static inline void | 425 | static inline void |
397 | nfs4_put_stateowner(struct nfs4_stateowner *so) | 426 | nfs4_put_stateowner(struct nfs4_stateowner *so) |
diff --git a/fs/nfsd/vfs.c b/fs/nfsd/vfs.c index 6dd5f1970e01..23c06f77f4ca 100644 --- a/fs/nfsd/vfs.c +++ b/fs/nfsd/vfs.c | |||
@@ -724,7 +724,7 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, | |||
724 | struct inode *inode; | 724 | struct inode *inode; |
725 | int flags = O_RDONLY|O_LARGEFILE; | 725 | int flags = O_RDONLY|O_LARGEFILE; |
726 | __be32 err; | 726 | __be32 err; |
727 | int host_err; | 727 | int host_err = 0; |
728 | 728 | ||
729 | validate_process_creds(); | 729 | validate_process_creds(); |
730 | 730 | ||
@@ -761,7 +761,8 @@ nfsd_open(struct svc_rqst *rqstp, struct svc_fh *fhp, int type, | |||
761 | * Check to see if there are any leases on this file. | 761 | * Check to see if there are any leases on this file. |
762 | * This may block while leases are broken. | 762 | * This may block while leases are broken. |
763 | */ | 763 | */ |
764 | host_err = break_lease(inode, O_NONBLOCK | ((access & NFSD_MAY_WRITE) ? O_WRONLY : 0)); | 764 | if (!(access & NFSD_MAY_NOT_BREAK_LEASE)) |
765 | host_err = break_lease(inode, O_NONBLOCK | ((access & NFSD_MAY_WRITE) ? O_WRONLY : 0)); | ||
765 | if (host_err == -EWOULDBLOCK) | 766 | if (host_err == -EWOULDBLOCK) |
766 | host_err = -ETIMEDOUT; | 767 | host_err = -ETIMEDOUT; |
767 | if (host_err) /* NOMEM or WOULDBLOCK */ | 768 | if (host_err) /* NOMEM or WOULDBLOCK */ |
@@ -1169,7 +1170,8 @@ nfsd_commit(struct svc_rqst *rqstp, struct svc_fh *fhp, | |||
1169 | goto out; | 1170 | goto out; |
1170 | } | 1171 | } |
1171 | 1172 | ||
1172 | err = nfsd_open(rqstp, fhp, S_IFREG, NFSD_MAY_WRITE, &file); | 1173 | err = nfsd_open(rqstp, fhp, S_IFREG, |
1174 | NFSD_MAY_WRITE|NFSD_MAY_NOT_BREAK_LEASE, &file); | ||
1173 | if (err) | 1175 | if (err) |
1174 | goto out; | 1176 | goto out; |
1175 | if (EX_ISSYNC(fhp->fh_export)) { | 1177 | if (EX_ISSYNC(fhp->fh_export)) { |
diff --git a/fs/nfsd/vfs.h b/fs/nfsd/vfs.h index 4b1de0a9ea75..217a62c2a357 100644 --- a/fs/nfsd/vfs.h +++ b/fs/nfsd/vfs.h | |||
@@ -20,6 +20,7 @@ | |||
20 | #define NFSD_MAY_OWNER_OVERRIDE 64 | 20 | #define NFSD_MAY_OWNER_OVERRIDE 64 |
21 | #define NFSD_MAY_LOCAL_ACCESS 128 /* IRIX doing local access check on device special file*/ | 21 | #define NFSD_MAY_LOCAL_ACCESS 128 /* IRIX doing local access check on device special file*/ |
22 | #define NFSD_MAY_BYPASS_GSS_ON_ROOT 256 | 22 | #define NFSD_MAY_BYPASS_GSS_ON_ROOT 256 |
23 | #define NFSD_MAY_NOT_BREAK_LEASE 512 | ||
23 | 24 | ||
24 | #define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE) | 25 | #define NFSD_MAY_CREATE (NFSD_MAY_EXEC|NFSD_MAY_WRITE) |
25 | #define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC) | 26 | #define NFSD_MAY_REMOVE (NFSD_MAY_EXEC|NFSD_MAY_WRITE|NFSD_MAY_TRUNC) |
diff --git a/fs/nfsd/xdr4.h b/fs/nfsd/xdr4.h index efa337739534..4d476ff08ae6 100644 --- a/fs/nfsd/xdr4.h +++ b/fs/nfsd/xdr4.h | |||
@@ -381,6 +381,10 @@ struct nfsd4_destroy_session { | |||
381 | struct nfs4_sessionid sessionid; | 381 | struct nfs4_sessionid sessionid; |
382 | }; | 382 | }; |
383 | 383 | ||
384 | struct nfsd4_reclaim_complete { | ||
385 | u32 rca_one_fs; | ||
386 | }; | ||
387 | |||
384 | struct nfsd4_op { | 388 | struct nfsd4_op { |
385 | int opnum; | 389 | int opnum; |
386 | __be32 status; | 390 | __be32 status; |
@@ -421,6 +425,7 @@ struct nfsd4_op { | |||
421 | struct nfsd4_create_session create_session; | 425 | struct nfsd4_create_session create_session; |
422 | struct nfsd4_destroy_session destroy_session; | 426 | struct nfsd4_destroy_session destroy_session; |
423 | struct nfsd4_sequence sequence; | 427 | struct nfsd4_sequence sequence; |
428 | struct nfsd4_reclaim_complete reclaim_complete; | ||
424 | } u; | 429 | } u; |
425 | struct nfs4_replay * replay; | 430 | struct nfs4_replay * replay; |
426 | }; | 431 | }; |
@@ -513,9 +518,8 @@ extern void nfsd4_store_cache_entry(struct nfsd4_compoundres *resp); | |||
513 | extern __be32 nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp, | 518 | extern __be32 nfsd4_replay_cache_entry(struct nfsd4_compoundres *resp, |
514 | struct nfsd4_sequence *seq); | 519 | struct nfsd4_sequence *seq); |
515 | extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, | 520 | extern __be32 nfsd4_exchange_id(struct svc_rqst *rqstp, |
516 | struct nfsd4_compound_state *, | 521 | struct nfsd4_compound_state *, struct nfsd4_exchange_id *); |
517 | struct nfsd4_exchange_id *); | 522 | extern __be32 nfsd4_create_session(struct svc_rqst *, |
518 | extern __be32 nfsd4_create_session(struct svc_rqst *, | ||
519 | struct nfsd4_compound_state *, | 523 | struct nfsd4_compound_state *, |
520 | struct nfsd4_create_session *); | 524 | struct nfsd4_create_session *); |
521 | extern __be32 nfsd4_sequence(struct svc_rqst *, | 525 | extern __be32 nfsd4_sequence(struct svc_rqst *, |
@@ -524,6 +528,7 @@ extern __be32 nfsd4_sequence(struct svc_rqst *, | |||
524 | extern __be32 nfsd4_destroy_session(struct svc_rqst *, | 528 | extern __be32 nfsd4_destroy_session(struct svc_rqst *, |
525 | struct nfsd4_compound_state *, | 529 | struct nfsd4_compound_state *, |
526 | struct nfsd4_destroy_session *); | 530 | struct nfsd4_destroy_session *); |
531 | __be32 nfsd4_reclaim_complete(struct svc_rqst *, struct nfsd4_compound_state *, struct nfsd4_reclaim_complete *); | ||
527 | extern __be32 nfsd4_process_open1(struct nfsd4_compound_state *, | 532 | extern __be32 nfsd4_process_open1(struct nfsd4_compound_state *, |
528 | struct nfsd4_open *open); | 533 | struct nfsd4_open *open); |
529 | extern __be32 nfsd4_process_open2(struct svc_rqst *rqstp, | 534 | extern __be32 nfsd4_process_open2(struct svc_rqst *rqstp, |