aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfs/nfs4state.c
diff options
context:
space:
mode:
authorChuck Lever <chuck.lever@oracle.com>2011-12-06 16:13:48 -0500
committerTrond Myklebust <Trond.Myklebust@netapp.com>2012-01-05 11:59:18 -0500
commit0aaaf5c424c7ffd6b0c4253251356558b16ef3a2 (patch)
tree8ef0eebc41a8e247d52280fd79d36934a71fcb00 /fs/nfs/nfs4state.c
parent414adf14cd3b52e411f79d941a15d0fd4af427fc (diff)
NFS: Cache state owners after files are closed
Servers have a finite amount of memory to store NFSv4 open and lock owners. Moreover, servers may have a difficult time determining when they can reap their state owner table, thanks to gray areas in the NFSv4 protocol specification. Thus clients should be careful to reuse state owners when possible. Currently Linux is not too careful. When a user has closed all her files on one mount point, the state owner's reference count goes to zero, and it is released. The next OPEN allocates a new one. A workload that serially opens and closes files can run through a large number of open owners this way. When a state owner's reference count goes to zero, slap it onto a free list for that nfs_server, with an expiry time. Garbage collect before looking for a state owner. This makes state owners for active users available for re-use. Now that there can be unused state owners remaining at umount time, purge the state owner free list when a server is destroyed. Also be sure not to reclaim unused state owners during state recovery. This change has benefits for the client as well. For some workloads, this approach drops the number of OPEN_CONFIRM calls from the same as the number of OPEN calls, down to just one. This reduces wire traffic and thus open(2) latency. Before this patch, untarring a kernel source tarball shows the OPEN_CONFIRM call counter steadily increasing through the test. With the patch, the OPEN_CONFIRM count remains at 1 throughout the entire untar. As long as the expiry time is kept short, I don't think garbage collection should be terribly expensive, although it does bounce the clp->cl_lock around a bit. [ At some point we should rationalize the use of the nfs_server ->destroy method. ] Signed-off-by: Chuck Lever <chuck.lever@oracle.com> [Trond: Fixed a garbage collection race and a few efficiency issues] Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/nfs4state.c')
-rw-r--r--fs/nfs/nfs4state.c89
1 files changed, 80 insertions, 9 deletions
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c
index 6354e4fcc829..a53f33b4ac3a 100644
--- a/fs/nfs/nfs4state.c
+++ b/fs/nfs/nfs4state.c
@@ -49,6 +49,7 @@
49#include <linux/ratelimit.h> 49#include <linux/ratelimit.h>
50#include <linux/workqueue.h> 50#include <linux/workqueue.h>
51#include <linux/bitops.h> 51#include <linux/bitops.h>
52#include <linux/jiffies.h>
52 53
53#include "nfs4_fs.h" 54#include "nfs4_fs.h"
54#include "callback.h" 55#include "callback.h"
@@ -388,6 +389,8 @@ nfs4_find_state_owner_locked(struct nfs_server *server, struct rpc_cred *cred)
388 else if (cred > sp->so_cred) 389 else if (cred > sp->so_cred)
389 p = &parent->rb_right; 390 p = &parent->rb_right;
390 else { 391 else {
392 if (!list_empty(&sp->so_lru))
393 list_del_init(&sp->so_lru);
391 atomic_inc(&sp->so_count); 394 atomic_inc(&sp->so_count);
392 return sp; 395 return sp;
393 } 396 }
@@ -412,6 +415,8 @@ nfs4_insert_state_owner_locked(struct nfs4_state_owner *new)
412 else if (new->so_cred > sp->so_cred) 415 else if (new->so_cred > sp->so_cred)
413 p = &parent->rb_right; 416 p = &parent->rb_right;
414 else { 417 else {
418 if (!list_empty(&sp->so_lru))
419 list_del_init(&sp->so_lru);
415 atomic_inc(&sp->so_count); 420 atomic_inc(&sp->so_count);
416 return sp; 421 return sp;
417 } 422 }
@@ -453,6 +458,7 @@ nfs4_alloc_state_owner(void)
453 spin_lock_init(&sp->so_sequence.lock); 458 spin_lock_init(&sp->so_sequence.lock);
454 INIT_LIST_HEAD(&sp->so_sequence.list); 459 INIT_LIST_HEAD(&sp->so_sequence.list);
455 atomic_set(&sp->so_count, 1); 460 atomic_set(&sp->so_count, 1);
461 INIT_LIST_HEAD(&sp->so_lru);
456 return sp; 462 return sp;
457} 463}
458 464
@@ -470,6 +476,38 @@ nfs4_drop_state_owner(struct nfs4_state_owner *sp)
470 } 476 }
471} 477}
472 478
479static void nfs4_free_state_owner(struct nfs4_state_owner *sp)
480{
481 rpc_destroy_wait_queue(&sp->so_sequence.wait);
482 put_rpccred(sp->so_cred);
483 kfree(sp);
484}
485
486static void nfs4_gc_state_owners(struct nfs_server *server)
487{
488 struct nfs_client *clp = server->nfs_client;
489 struct nfs4_state_owner *sp, *tmp;
490 unsigned long time_min, time_max;
491 LIST_HEAD(doomed);
492
493 spin_lock(&clp->cl_lock);
494 time_max = jiffies;
495 time_min = (long)time_max - (long)clp->cl_lease_time;
496 list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) {
497 /* NB: LRU is sorted so that oldest is at the head */
498 if (time_in_range(sp->so_expires, time_min, time_max))
499 break;
500 list_move(&sp->so_lru, &doomed);
501 nfs4_remove_state_owner_locked(sp);
502 }
503 spin_unlock(&clp->cl_lock);
504
505 list_for_each_entry_safe(sp, tmp, &doomed, so_lru) {
506 list_del(&sp->so_lru);
507 nfs4_free_state_owner(sp);
508 }
509}
510
473/** 511/**
474 * nfs4_get_state_owner - Look up a state owner given a credential 512 * nfs4_get_state_owner - Look up a state owner given a credential
475 * @server: nfs_server to search 513 * @server: nfs_server to search
@@ -487,10 +525,10 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server,
487 sp = nfs4_find_state_owner_locked(server, cred); 525 sp = nfs4_find_state_owner_locked(server, cred);
488 spin_unlock(&clp->cl_lock); 526 spin_unlock(&clp->cl_lock);
489 if (sp != NULL) 527 if (sp != NULL)
490 return sp; 528 goto out;
491 new = nfs4_alloc_state_owner(); 529 new = nfs4_alloc_state_owner();
492 if (new == NULL) 530 if (new == NULL)
493 return NULL; 531 goto out;
494 new->so_server = server; 532 new->so_server = server;
495 new->so_cred = cred; 533 new->so_cred = cred;
496 spin_lock(&clp->cl_lock); 534 spin_lock(&clp->cl_lock);
@@ -502,26 +540,58 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server,
502 rpc_destroy_wait_queue(&new->so_sequence.wait); 540 rpc_destroy_wait_queue(&new->so_sequence.wait);
503 kfree(new); 541 kfree(new);
504 } 542 }
543out:
544 nfs4_gc_state_owners(server);
505 return sp; 545 return sp;
506} 546}
507 547
508/** 548/**
509 * nfs4_put_state_owner - Release a nfs4_state_owner 549 * nfs4_put_state_owner - Release a nfs4_state_owner
510 * @sp: state owner data to release 550 * @sp: state owner data to release
511 *
512 */ 551 */
513void nfs4_put_state_owner(struct nfs4_state_owner *sp) 552void nfs4_put_state_owner(struct nfs4_state_owner *sp)
514{ 553{
515 struct nfs_client *clp = sp->so_server->nfs_client; 554 struct nfs_server *server = sp->so_server;
516 struct rpc_cred *cred = sp->so_cred; 555 struct nfs_client *clp = server->nfs_client;
517 556
518 if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock)) 557 if (!atomic_dec_and_lock(&sp->so_count, &clp->cl_lock))
519 return; 558 return;
520 nfs4_remove_state_owner_locked(sp); 559
560 if (!RB_EMPTY_NODE(&sp->so_server_node)) {
561 sp->so_expires = jiffies;
562 list_add_tail(&sp->so_lru, &server->state_owners_lru);
563 spin_unlock(&clp->cl_lock);
564 } else {
565 nfs4_remove_state_owner_locked(sp);
566 spin_unlock(&clp->cl_lock);
567 nfs4_free_state_owner(sp);
568 }
569}
570
571/**
572 * nfs4_purge_state_owners - Release all cached state owners
573 * @server: nfs_server with cached state owners to release
574 *
575 * Called at umount time. Remaining state owners will be on
576 * the LRU with ref count of zero.
577 */
578void nfs4_purge_state_owners(struct nfs_server *server)
579{
580 struct nfs_client *clp = server->nfs_client;
581 struct nfs4_state_owner *sp, *tmp;
582 LIST_HEAD(doomed);
583
584 spin_lock(&clp->cl_lock);
585 list_for_each_entry_safe(sp, tmp, &server->state_owners_lru, so_lru) {
586 list_move(&sp->so_lru, &doomed);
587 nfs4_remove_state_owner_locked(sp);
588 }
521 spin_unlock(&clp->cl_lock); 589 spin_unlock(&clp->cl_lock);
522 rpc_destroy_wait_queue(&sp->so_sequence.wait); 590
523 put_rpccred(cred); 591 list_for_each_entry_safe(sp, tmp, &doomed, so_lru) {
524 kfree(sp); 592 list_del(&sp->so_lru);
593 nfs4_free_state_owner(sp);
594 }
525} 595}
526 596
527static struct nfs4_state * 597static struct nfs4_state *
@@ -1393,6 +1463,7 @@ static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recov
1393restart: 1463restart:
1394 rcu_read_lock(); 1464 rcu_read_lock();
1395 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) { 1465 list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
1466 nfs4_purge_state_owners(server);
1396 spin_lock(&clp->cl_lock); 1467 spin_lock(&clp->cl_lock);
1397 for (pos = rb_first(&server->state_owners); 1468 for (pos = rb_first(&server->state_owners);
1398 pos != NULL; 1469 pos != NULL;