aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMeelap Shah <meelap@umich.edu>2007-07-17 07:04:39 -0400
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2007-07-17 13:23:07 -0400
commitc2f1a551dea8b37c2e0cb886885c250fb703e9d8 (patch)
tree11a5f256703d856017ceb2268bd02b7b510dee30
parent1e5140279f31e47d58ed6036ee61ba7a65710e63 (diff)
knfsd: nfsd4: vary maximum delegation limit based on RAM size
Our original NFSv4 delegation policy was to give out a read delegation on any open when it was possible to. Since the lifetime of a delegation isn't limited to that of an open, a client may quite reasonably hang on to a delegation as long as it has the inode cached. This becomes an obvious problem the first time a client's inode cache approaches the size of the server's total memory. Our first quick solution was to add a hard-coded limit. This patch makes a mild incremental improvement by varying that limit according to the server's total memory size, allowing at most 4 delegations per megabyte of RAM. My quick back-of-the-envelope calculation finds that in the worst case (where every delegation is for a different inode), a delegation could take about 1.5K, which would make the worst case usage about 6% of memory. The new limit works out to be about the same as the old on a 1-gig server. [akpm@linux-foundation.org: Don't needlessly bloat vmlinux] [akpm@linux-foundation.org: Make it right for highmem machines] Signed-off-by: "J. Bruce Fields" <bfields@citi.umich.edu> Signed-off-by: Neil Brown <neilb@suse.de> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
-rw-r--r--fs/nfsd/nfs4state.c26
-rw-r--r--include/linux/nfsd/nfsd.h1
-rw-r--r--mm/page_alloc.c1
3 files changed, 27 insertions, 1 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 9cc31eaf3857..46249886ea86 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -49,6 +49,7 @@
49#include <linux/nfsd/state.h> 49#include <linux/nfsd/state.h>
50#include <linux/nfsd/xdr4.h> 50#include <linux/nfsd/xdr4.h>
51#include <linux/namei.h> 51#include <linux/namei.h>
52#include <linux/swap.h>
52#include <linux/mutex.h> 53#include <linux/mutex.h>
53#include <linux/lockd/bind.h> 54#include <linux/lockd/bind.h>
54#include <linux/module.h> 55#include <linux/module.h>
@@ -150,6 +151,7 @@ get_nfs4_file(struct nfs4_file *fi)
150} 151}
151 152
152static int num_delegations; 153static int num_delegations;
154unsigned int max_delegations;
153 155
154/* 156/*
155 * Open owner state (share locks) 157 * Open owner state (share locks)
@@ -193,7 +195,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
193 struct nfs4_callback *cb = &stp->st_stateowner->so_client->cl_callback; 195 struct nfs4_callback *cb = &stp->st_stateowner->so_client->cl_callback;
194 196
195 dprintk("NFSD alloc_init_deleg\n"); 197 dprintk("NFSD alloc_init_deleg\n");
196 if (num_delegations > STATEID_HASH_SIZE * 4) 198 if (num_delegations > max_delegations)
197 return NULL; 199 return NULL;
198 dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL); 200 dp = kmem_cache_alloc(deleg_slab, GFP_KERNEL);
199 if (dp == NULL) 201 if (dp == NULL)
@@ -3197,6 +3199,27 @@ get_nfs4_grace_period(void)
3197 return max(user_lease_time, lease_time) * HZ; 3199 return max(user_lease_time, lease_time) * HZ;
3198} 3200}
3199 3201
3202/*
3203 * Since the lifetime of a delegation isn't limited to that of an open, a
3204 * client may quite reasonably hang on to a delegation as long as it has
3205 * the inode cached. This becomes an obvious problem the first time a
3206 * client's inode cache approaches the size of the server's total memory.
3207 *
3208 * For now we avoid this problem by imposing a hard limit on the number
3209 * of delegations, which varies according to the server's memory size.
3210 */
3211static void
3212set_max_delegations(void)
3213{
3214 /*
3215 * Allow at most 4 delegations per megabyte of RAM. Quick
3216 * estimates suggest that in the worst case (where every delegation
3217 * is for a different inode), a delegation could take about 1.5K,
3218 * giving a worst case usage of about 6% of memory.
3219 */
3220 max_delegations = nr_free_buffer_pages() >> (20 - 2 - PAGE_SHIFT);
3221}
3222
3200/* initialization to perform when the nfsd service is started: */ 3223/* initialization to perform when the nfsd service is started: */
3201 3224
3202static void 3225static void
@@ -3212,6 +3235,7 @@ __nfs4_state_start(void)
3212 grace_time/HZ); 3235 grace_time/HZ);
3213 laundry_wq = create_singlethread_workqueue("nfsd4"); 3236 laundry_wq = create_singlethread_workqueue("nfsd4");
3214 queue_delayed_work(laundry_wq, &laundromat_work, grace_time); 3237 queue_delayed_work(laundry_wq, &laundromat_work, grace_time);
3238 set_max_delegations();
3215} 3239}
3216 3240
3217int 3241int
diff --git a/include/linux/nfsd/nfsd.h b/include/linux/nfsd/nfsd.h
index 0d8420497765..ce5e345a9bce 100644
--- a/include/linux/nfsd/nfsd.h
+++ b/include/linux/nfsd/nfsd.h
@@ -148,6 +148,7 @@ extern int nfsd_max_blksize;
148 * NFSv4 State 148 * NFSv4 State
149 */ 149 */
150#ifdef CONFIG_NFSD_V4 150#ifdef CONFIG_NFSD_V4
151extern unsigned int max_delegations;
151void nfs4_state_init(void); 152void nfs4_state_init(void);
152int nfs4_state_start(void); 153int nfs4_state_start(void);
153void nfs4_state_shutdown(void); 154void nfs4_state_shutdown(void);
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 1a889c3fec59..e2a10b957f23 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -1484,6 +1484,7 @@ unsigned int nr_free_buffer_pages(void)
1484{ 1484{
1485 return nr_free_zone_pages(gfp_zone(GFP_USER)); 1485 return nr_free_zone_pages(gfp_zone(GFP_USER));
1486} 1486}
1487EXPORT_SYMBOL_GPL(nr_free_buffer_pages);
1487 1488
1488/* 1489/*
1489 * Amount of free RAM allocatable within all zones 1490 * Amount of free RAM allocatable within all zones