diff options
| -rw-r--r-- | Documentation/filesystems/nfs/nfs41-server.txt | 2 | ||||
| -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 | ||||
| -rw-r--r-- | include/linux/nfsd/nfsfh.h | 6 | ||||
| -rw-r--r-- | net/sunrpc/cache.c | 45 | ||||
| -rw-r--r-- | net/sunrpc/svc_xprt.c | 6 | ||||
| -rw-r--r-- | net/sunrpc/svcsock.c | 15 | ||||
| -rw-r--r-- | net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 3 |
18 files changed, 510 insertions, 343 deletions
diff --git a/Documentation/filesystems/nfs/nfs41-server.txt b/Documentation/filesystems/nfs/nfs41-server.txt index 6a53a84afc72..04884914a1c8 100644 --- a/Documentation/filesystems/nfs/nfs41-server.txt +++ b/Documentation/filesystems/nfs/nfs41-server.txt | |||
| @@ -137,7 +137,7 @@ NS*| OPENATTR | OPT | | Section 18.17 | | |||
| 137 | | READ | REQ | | Section 18.22 | | 137 | | READ | REQ | | Section 18.22 | |
| 138 | | READDIR | REQ | | Section 18.23 | | 138 | | READDIR | REQ | | Section 18.23 | |
| 139 | | READLINK | OPT | | Section 18.24 | | 139 | | READLINK | OPT | | Section 18.24 | |
| 140 | NS | RECLAIM_COMPLETE | REQ | | Section 18.51 | | 140 | | RECLAIM_COMPLETE | REQ | | Section 18.51 | |
| 141 | | RELEASE_LOCKOWNER | MNI | | N/A | | 141 | | RELEASE_LOCKOWNER | MNI | | N/A | |
| 142 | | REMOVE | REQ | | Section 18.25 | | 142 | | REMOVE | REQ | | Section 18.25 | |
| 143 | | RENAME | REQ | | Section 18.26 | | 143 | | RENAME | REQ | | Section 18.26 | |
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, |
diff --git a/include/linux/nfsd/nfsfh.h b/include/linux/nfsd/nfsfh.h index 65e333afaee4..80d55bbc5365 100644 --- a/include/linux/nfsd/nfsfh.h +++ b/include/linux/nfsd/nfsfh.h | |||
| @@ -40,12 +40,12 @@ struct nfs_fhbase_old { | |||
| 40 | * This is the new flexible, extensible style NFSv2/v3 file handle. | 40 | * This is the new flexible, extensible style NFSv2/v3 file handle. |
| 41 | * by Neil Brown <neilb@cse.unsw.edu.au> - March 2000 | 41 | * by Neil Brown <neilb@cse.unsw.edu.au> - March 2000 |
| 42 | * | 42 | * |
| 43 | * The file handle is seens as a list of 4byte words. | 43 | * The file handle starts with a sequence of four-byte words. |
| 44 | * The first word contains a version number (1) and four descriptor bytes | 44 | * The first word contains a version number (1) and three descriptor bytes |
| 45 | * that tell how the remaining 3 variable length fields should be handled. | 45 | * that tell how the remaining 3 variable length fields should be handled. |
| 46 | * These three bytes are auth_type, fsid_type and fileid_type. | 46 | * These three bytes are auth_type, fsid_type and fileid_type. |
| 47 | * | 47 | * |
| 48 | * All 4byte values are in host-byte-order. | 48 | * All four-byte values are in host-byte-order. |
| 49 | * | 49 | * |
| 50 | * The auth_type field specifies how the filehandle can be authenticated | 50 | * The auth_type field specifies how the filehandle can be authenticated |
| 51 | * This might allow a file to be confirmed to be in a writable part of a | 51 | * This might allow a file to be confirmed to be in a writable part of a |
diff --git a/net/sunrpc/cache.c b/net/sunrpc/cache.c index 77970fe8bff2..c2173ebdb33c 100644 --- a/net/sunrpc/cache.c +++ b/net/sunrpc/cache.c | |||
| @@ -50,11 +50,17 @@ static void cache_init(struct cache_head *h) | |||
| 50 | h->last_refresh = now; | 50 | h->last_refresh = now; |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | static inline int cache_is_expired(struct cache_detail *detail, struct cache_head *h) | ||
| 54 | { | ||
| 55 | return (h->expiry_time < get_seconds()) || | ||
| 56 | (detail->flush_time > h->last_refresh); | ||
| 57 | } | ||
| 58 | |||
| 53 | struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, | 59 | struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, |
| 54 | struct cache_head *key, int hash) | 60 | struct cache_head *key, int hash) |
| 55 | { | 61 | { |
| 56 | struct cache_head **head, **hp; | 62 | struct cache_head **head, **hp; |
| 57 | struct cache_head *new = NULL; | 63 | struct cache_head *new = NULL, *freeme = NULL; |
| 58 | 64 | ||
| 59 | head = &detail->hash_table[hash]; | 65 | head = &detail->hash_table[hash]; |
| 60 | 66 | ||
| @@ -63,6 +69,9 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, | |||
| 63 | for (hp=head; *hp != NULL ; hp = &(*hp)->next) { | 69 | for (hp=head; *hp != NULL ; hp = &(*hp)->next) { |
| 64 | struct cache_head *tmp = *hp; | 70 | struct cache_head *tmp = *hp; |
| 65 | if (detail->match(tmp, key)) { | 71 | if (detail->match(tmp, key)) { |
| 72 | if (cache_is_expired(detail, tmp)) | ||
| 73 | /* This entry is expired, we will discard it. */ | ||
| 74 | break; | ||
| 66 | cache_get(tmp); | 75 | cache_get(tmp); |
| 67 | read_unlock(&detail->hash_lock); | 76 | read_unlock(&detail->hash_lock); |
| 68 | return tmp; | 77 | return tmp; |
| @@ -87,6 +96,13 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, | |||
| 87 | for (hp=head; *hp != NULL ; hp = &(*hp)->next) { | 96 | for (hp=head; *hp != NULL ; hp = &(*hp)->next) { |
| 88 | struct cache_head *tmp = *hp; | 97 | struct cache_head *tmp = *hp; |
| 89 | if (detail->match(tmp, key)) { | 98 | if (detail->match(tmp, key)) { |
| 99 | if (cache_is_expired(detail, tmp)) { | ||
| 100 | *hp = tmp->next; | ||
| 101 | tmp->next = NULL; | ||
| 102 | detail->entries --; | ||
| 103 | freeme = tmp; | ||
| 104 | break; | ||
| 105 | } | ||
| 90 | cache_get(tmp); | 106 | cache_get(tmp); |
| 91 | write_unlock(&detail->hash_lock); | 107 | write_unlock(&detail->hash_lock); |
| 92 | cache_put(new, detail); | 108 | cache_put(new, detail); |
| @@ -99,6 +115,8 @@ struct cache_head *sunrpc_cache_lookup(struct cache_detail *detail, | |||
| 99 | cache_get(new); | 115 | cache_get(new); |
| 100 | write_unlock(&detail->hash_lock); | 116 | write_unlock(&detail->hash_lock); |
| 101 | 117 | ||
| 118 | if (freeme) | ||
| 119 | cache_put(freeme, detail); | ||
| 102 | return new; | 120 | return new; |
| 103 | } | 121 | } |
| 104 | EXPORT_SYMBOL_GPL(sunrpc_cache_lookup); | 122 | EXPORT_SYMBOL_GPL(sunrpc_cache_lookup); |
| @@ -184,10 +202,7 @@ static int cache_make_upcall(struct cache_detail *cd, struct cache_head *h) | |||
| 184 | 202 | ||
| 185 | static inline int cache_is_valid(struct cache_detail *detail, struct cache_head *h) | 203 | static inline int cache_is_valid(struct cache_detail *detail, struct cache_head *h) |
| 186 | { | 204 | { |
| 187 | if (!test_bit(CACHE_VALID, &h->flags) || | 205 | if (!test_bit(CACHE_VALID, &h->flags)) |
| 188 | h->expiry_time < get_seconds()) | ||
| 189 | return -EAGAIN; | ||
| 190 | else if (detail->flush_time > h->last_refresh) | ||
| 191 | return -EAGAIN; | 206 | return -EAGAIN; |
| 192 | else { | 207 | else { |
| 193 | /* entry is valid */ | 208 | /* entry is valid */ |
| @@ -398,31 +413,27 @@ static int cache_clean(void) | |||
| 398 | /* Ok, now to clean this strand */ | 413 | /* Ok, now to clean this strand */ |
| 399 | 414 | ||
| 400 | cp = & current_detail->hash_table[current_index]; | 415 | cp = & current_detail->hash_table[current_index]; |
| 401 | ch = *cp; | 416 | for (ch = *cp ; ch ; cp = & ch->next, ch = *cp) { |
| 402 | for (; ch; cp= & ch->next, ch= *cp) { | ||
| 403 | if (current_detail->nextcheck > ch->expiry_time) | 417 | if (current_detail->nextcheck > ch->expiry_time) |
| 404 | current_detail->nextcheck = ch->expiry_time+1; | 418 | current_detail->nextcheck = ch->expiry_time+1; |
| 405 | if (ch->expiry_time >= get_seconds() && | 419 | if (!cache_is_expired(current_detail, ch)) |
| 406 | ch->last_refresh >= current_detail->flush_time) | ||
| 407 | continue; | 420 | continue; |
| 408 | if (test_and_clear_bit(CACHE_PENDING, &ch->flags)) | ||
| 409 | cache_dequeue(current_detail, ch); | ||
| 410 | 421 | ||
| 411 | if (atomic_read(&ch->ref.refcount) == 1) | ||
| 412 | break; | ||
| 413 | } | ||
| 414 | if (ch) { | ||
| 415 | *cp = ch->next; | 422 | *cp = ch->next; |
| 416 | ch->next = NULL; | 423 | ch->next = NULL; |
| 417 | current_detail->entries--; | 424 | current_detail->entries--; |
| 418 | rv = 1; | 425 | rv = 1; |
| 426 | break; | ||
| 419 | } | 427 | } |
| 428 | |||
| 420 | write_unlock(¤t_detail->hash_lock); | 429 | write_unlock(¤t_detail->hash_lock); |
| 421 | d = current_detail; | 430 | d = current_detail; |
| 422 | if (!ch) | 431 | if (!ch) |
| 423 | current_index ++; | 432 | current_index ++; |
| 424 | spin_unlock(&cache_list_lock); | 433 | spin_unlock(&cache_list_lock); |
| 425 | if (ch) { | 434 | if (ch) { |
| 435 | if (test_and_clear_bit(CACHE_PENDING, &ch->flags)) | ||
| 436 | cache_dequeue(current_detail, ch); | ||
| 426 | cache_revisit_request(ch); | 437 | cache_revisit_request(ch); |
| 427 | cache_put(ch, d); | 438 | cache_put(ch, d); |
| 428 | } | 439 | } |
| @@ -1234,8 +1245,10 @@ static int content_open(struct inode *inode, struct file *file, | |||
| 1234 | if (!cd || !try_module_get(cd->owner)) | 1245 | if (!cd || !try_module_get(cd->owner)) |
| 1235 | return -EACCES; | 1246 | return -EACCES; |
| 1236 | han = __seq_open_private(file, &cache_content_op, sizeof(*han)); | 1247 | han = __seq_open_private(file, &cache_content_op, sizeof(*han)); |
| 1237 | if (han == NULL) | 1248 | if (han == NULL) { |
| 1249 | module_put(cd->owner); | ||
| 1238 | return -ENOMEM; | 1250 | return -ENOMEM; |
| 1251 | } | ||
| 1239 | 1252 | ||
| 1240 | han->cd = cd; | 1253 | han->cd = cd; |
| 1241 | return 0; | 1254 | return 0; |
diff --git a/net/sunrpc/svc_xprt.c b/net/sunrpc/svc_xprt.c index 061b2e0f9118..cbc084939dd8 100644 --- a/net/sunrpc/svc_xprt.c +++ b/net/sunrpc/svc_xprt.c | |||
| @@ -744,8 +744,10 @@ int svc_recv(struct svc_rqst *rqstp, long timeout) | |||
| 744 | if (rqstp->rq_deferred) { | 744 | if (rqstp->rq_deferred) { |
| 745 | svc_xprt_received(xprt); | 745 | svc_xprt_received(xprt); |
| 746 | len = svc_deferred_recv(rqstp); | 746 | len = svc_deferred_recv(rqstp); |
| 747 | } else | 747 | } else { |
| 748 | len = xprt->xpt_ops->xpo_recvfrom(rqstp); | 748 | len = xprt->xpt_ops->xpo_recvfrom(rqstp); |
| 749 | svc_xprt_received(xprt); | ||
| 750 | } | ||
| 749 | dprintk("svc: got len=%d\n", len); | 751 | dprintk("svc: got len=%d\n", len); |
| 750 | } | 752 | } |
| 751 | 753 | ||
| @@ -893,12 +895,12 @@ void svc_delete_xprt(struct svc_xprt *xprt) | |||
| 893 | */ | 895 | */ |
| 894 | if (test_bit(XPT_TEMP, &xprt->xpt_flags)) | 896 | if (test_bit(XPT_TEMP, &xprt->xpt_flags)) |
| 895 | serv->sv_tmpcnt--; | 897 | serv->sv_tmpcnt--; |
| 898 | spin_unlock_bh(&serv->sv_lock); | ||
| 896 | 899 | ||
| 897 | while ((dr = svc_deferred_dequeue(xprt)) != NULL) | 900 | while ((dr = svc_deferred_dequeue(xprt)) != NULL) |
| 898 | kfree(dr); | 901 | kfree(dr); |
| 899 | 902 | ||
| 900 | svc_xprt_put(xprt); | 903 | svc_xprt_put(xprt); |
| 901 | spin_unlock_bh(&serv->sv_lock); | ||
| 902 | } | 904 | } |
| 903 | 905 | ||
| 904 | void svc_close_xprt(struct svc_xprt *xprt) | 906 | void svc_close_xprt(struct svc_xprt *xprt) |
diff --git a/net/sunrpc/svcsock.c b/net/sunrpc/svcsock.c index a29f259204e6..a33892733643 100644 --- a/net/sunrpc/svcsock.c +++ b/net/sunrpc/svcsock.c | |||
| @@ -547,7 +547,6 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp) | |||
| 547 | dprintk("svc: recvfrom returned error %d\n", -err); | 547 | dprintk("svc: recvfrom returned error %d\n", -err); |
| 548 | set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); | 548 | set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); |
| 549 | } | 549 | } |
| 550 | svc_xprt_received(&svsk->sk_xprt); | ||
| 551 | return -EAGAIN; | 550 | return -EAGAIN; |
| 552 | } | 551 | } |
| 553 | len = svc_addr_len(svc_addr(rqstp)); | 552 | len = svc_addr_len(svc_addr(rqstp)); |
| @@ -562,11 +561,6 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp) | |||
| 562 | svsk->sk_sk->sk_stamp = skb->tstamp; | 561 | svsk->sk_sk->sk_stamp = skb->tstamp; |
| 563 | set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); /* there may be more data... */ | 562 | set_bit(XPT_DATA, &svsk->sk_xprt.xpt_flags); /* there may be more data... */ |
| 564 | 563 | ||
| 565 | /* | ||
| 566 | * Maybe more packets - kick another thread ASAP. | ||
| 567 | */ | ||
| 568 | svc_xprt_received(&svsk->sk_xprt); | ||
| 569 | |||
| 570 | len = skb->len - sizeof(struct udphdr); | 564 | len = skb->len - sizeof(struct udphdr); |
| 571 | rqstp->rq_arg.len = len; | 565 | rqstp->rq_arg.len = len; |
| 572 | 566 | ||
| @@ -917,7 +911,6 @@ static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp) | |||
| 917 | if (len < want) { | 911 | if (len < want) { |
| 918 | dprintk("svc: short recvfrom while reading record " | 912 | dprintk("svc: short recvfrom while reading record " |
| 919 | "length (%d of %d)\n", len, want); | 913 | "length (%d of %d)\n", len, want); |
| 920 | svc_xprt_received(&svsk->sk_xprt); | ||
| 921 | goto err_again; /* record header not complete */ | 914 | goto err_again; /* record header not complete */ |
| 922 | } | 915 | } |
| 923 | 916 | ||
| @@ -953,7 +946,6 @@ static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp) | |||
| 953 | if (len < svsk->sk_reclen) { | 946 | if (len < svsk->sk_reclen) { |
| 954 | dprintk("svc: incomplete TCP record (%d of %d)\n", | 947 | dprintk("svc: incomplete TCP record (%d of %d)\n", |
| 955 | len, svsk->sk_reclen); | 948 | len, svsk->sk_reclen); |
| 956 | svc_xprt_received(&svsk->sk_xprt); | ||
| 957 | goto err_again; /* record not complete */ | 949 | goto err_again; /* record not complete */ |
| 958 | } | 950 | } |
| 959 | len = svsk->sk_reclen; | 951 | len = svsk->sk_reclen; |
| @@ -961,14 +953,11 @@ static int svc_tcp_recv_record(struct svc_sock *svsk, struct svc_rqst *rqstp) | |||
| 961 | 953 | ||
| 962 | return len; | 954 | return len; |
| 963 | error: | 955 | error: |
| 964 | if (len == -EAGAIN) { | 956 | if (len == -EAGAIN) |
| 965 | dprintk("RPC: TCP recv_record got EAGAIN\n"); | 957 | dprintk("RPC: TCP recv_record got EAGAIN\n"); |
| 966 | svc_xprt_received(&svsk->sk_xprt); | ||
| 967 | } | ||
| 968 | return len; | 958 | return len; |
| 969 | err_delete: | 959 | err_delete: |
| 970 | set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags); | 960 | set_bit(XPT_CLOSE, &svsk->sk_xprt.xpt_flags); |
| 971 | svc_xprt_received(&svsk->sk_xprt); | ||
| 972 | err_again: | 961 | err_again: |
| 973 | return -EAGAIN; | 962 | return -EAGAIN; |
| 974 | } | 963 | } |
| @@ -1110,7 +1099,6 @@ out: | |||
| 1110 | svsk->sk_tcplen = 0; | 1099 | svsk->sk_tcplen = 0; |
| 1111 | 1100 | ||
| 1112 | svc_xprt_copy_addrs(rqstp, &svsk->sk_xprt); | 1101 | svc_xprt_copy_addrs(rqstp, &svsk->sk_xprt); |
| 1113 | svc_xprt_received(&svsk->sk_xprt); | ||
| 1114 | if (serv->sv_stats) | 1102 | if (serv->sv_stats) |
| 1115 | serv->sv_stats->nettcpcnt++; | 1103 | serv->sv_stats->nettcpcnt++; |
| 1116 | 1104 | ||
| @@ -1119,7 +1107,6 @@ out: | |||
| 1119 | err_again: | 1107 | err_again: |
| 1120 | if (len == -EAGAIN) { | 1108 | if (len == -EAGAIN) { |
| 1121 | dprintk("RPC: TCP recvfrom got EAGAIN\n"); | 1109 | dprintk("RPC: TCP recvfrom got EAGAIN\n"); |
| 1122 | svc_xprt_received(&svsk->sk_xprt); | ||
| 1123 | return len; | 1110 | return len; |
| 1124 | } | 1111 | } |
| 1125 | error: | 1112 | error: |
diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index f92e37eb413c..0194de814933 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | |||
| @@ -566,7 +566,6 @@ static int rdma_read_complete(struct svc_rqst *rqstp, | |||
| 566 | ret, rqstp->rq_arg.len, rqstp->rq_arg.head[0].iov_base, | 566 | ret, rqstp->rq_arg.len, rqstp->rq_arg.head[0].iov_base, |
| 567 | rqstp->rq_arg.head[0].iov_len); | 567 | rqstp->rq_arg.head[0].iov_len); |
| 568 | 568 | ||
| 569 | svc_xprt_received(rqstp->rq_xprt); | ||
| 570 | return ret; | 569 | return ret; |
| 571 | } | 570 | } |
| 572 | 571 | ||
| @@ -665,7 +664,6 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) | |||
| 665 | rqstp->rq_arg.head[0].iov_len); | 664 | rqstp->rq_arg.head[0].iov_len); |
| 666 | rqstp->rq_prot = IPPROTO_MAX; | 665 | rqstp->rq_prot = IPPROTO_MAX; |
| 667 | svc_xprt_copy_addrs(rqstp, xprt); | 666 | svc_xprt_copy_addrs(rqstp, xprt); |
| 668 | svc_xprt_received(xprt); | ||
| 669 | return ret; | 667 | return ret; |
| 670 | 668 | ||
| 671 | close_out: | 669 | close_out: |
| @@ -678,6 +676,5 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) | |||
| 678 | */ | 676 | */ |
| 679 | set_bit(XPT_CLOSE, &xprt->xpt_flags); | 677 | set_bit(XPT_CLOSE, &xprt->xpt_flags); |
| 680 | defer: | 678 | defer: |
| 681 | svc_xprt_received(xprt); | ||
| 682 | return 0; | 679 | return 0; |
| 683 | } | 680 | } |
