aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd
diff options
context:
space:
mode:
authorJeff Layton <jlayton@primarydata.com>2014-10-23 08:01:02 -0400
committerJ. Bruce Fields <bfields@redhat.com>2014-11-07 16:56:11 -0500
commit5b095e99928cc13332d364f7cca7a9ca684369b4 (patch)
tree9e0c06472028d1caa9d9a36d297741a5edf7cf79 /fs/nfsd
parentb0cb9085239a20b7482ddd4839dd1d5476801dfa (diff)
nfsd: convert nfs4_file searches to use RCU
The global state_lock protects the file_hashtbl, and that has the potential to be a scalability bottleneck. Address this by making the file_hashtbl use RCU. Add a rcu_head to the nfs4_file and use that when freeing ones that have been hashed. In order to conserve space, we union the fi_rcu field with the fi_delegations list_head which must be clear by the time the last reference to the file is dropped. Convert find_file_locked to use RCU lookup primitives and not to require that the state_lock be held, and convert find_file to do a lockless lookup. Convert find_or_add_file to attempt a lockless lookup first, and then fall back to doing a locked search and insert if that fails to find anything. Also, minimize the number of times we need to calculate the hash value by passing it in as an argument to the search and insert functions, and optimize the order of arguments in nfsd4_init_file. Reviewed-by: Christoph Hellwig <hch@lst.de> Signed-off-by: Jeff Layton <jlayton@primarydata.com> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd')
-rw-r--r--fs/nfsd/nfs4state.c51
-rw-r--r--fs/nfsd/state.h5
2 files changed, 33 insertions, 23 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 1afd7d4420bd..1379d86f7b4f 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -275,9 +275,11 @@ opaque_hashval(const void *ptr, int nbytes)
275 return x; 275 return x;
276} 276}
277 277
278static void nfsd4_free_file(struct nfs4_file *f) 278static void nfsd4_free_file_rcu(struct rcu_head *rcu)
279{ 279{
280 kmem_cache_free(file_slab, f); 280 struct nfs4_file *fp = container_of(rcu, struct nfs4_file, fi_rcu);
281
282 kmem_cache_free(file_slab, fp);
281} 283}
282 284
283static inline void 285static inline void
@@ -286,9 +288,10 @@ put_nfs4_file(struct nfs4_file *fi)
286 might_lock(&state_lock); 288 might_lock(&state_lock);
287 289
288 if (atomic_dec_and_lock(&fi->fi_ref, &state_lock)) { 290 if (atomic_dec_and_lock(&fi->fi_ref, &state_lock)) {
289 hlist_del(&fi->fi_hash); 291 hlist_del_rcu(&fi->fi_hash);
290 spin_unlock(&state_lock); 292 spin_unlock(&state_lock);
291 nfsd4_free_file(fi); 293 WARN_ON_ONCE(!list_empty(&fi->fi_delegations));
294 call_rcu(&fi->fi_rcu, nfsd4_free_file_rcu);
292 } 295 }
293} 296}
294 297
@@ -3057,10 +3060,9 @@ static struct nfs4_file *nfsd4_alloc_file(void)
3057} 3060}
3058 3061
3059/* OPEN Share state helper functions */ 3062/* OPEN Share state helper functions */
3060static void nfsd4_init_file(struct nfs4_file *fp, struct knfsd_fh *fh) 3063static void nfsd4_init_file(struct knfsd_fh *fh, unsigned int hashval,
3064 struct nfs4_file *fp)
3061{ 3065{
3062 unsigned int hashval = file_hashval(fh);
3063
3064 lockdep_assert_held(&state_lock); 3066 lockdep_assert_held(&state_lock);
3065 3067
3066 atomic_set(&fp->fi_ref, 1); 3068 atomic_set(&fp->fi_ref, 1);
@@ -3073,7 +3075,7 @@ static void nfsd4_init_file(struct nfs4_file *fp, struct knfsd_fh *fh)
3073 fp->fi_share_deny = 0; 3075 fp->fi_share_deny = 0;
3074 memset(fp->fi_fds, 0, sizeof(fp->fi_fds)); 3076 memset(fp->fi_fds, 0, sizeof(fp->fi_fds));
3075 memset(fp->fi_access, 0, sizeof(fp->fi_access)); 3077 memset(fp->fi_access, 0, sizeof(fp->fi_access));
3076 hlist_add_head(&fp->fi_hash, &file_hashtbl[hashval]); 3078 hlist_add_head_rcu(&fp->fi_hash, &file_hashtbl[hashval]);
3077} 3079}
3078 3080
3079void 3081void
@@ -3294,17 +3296,14 @@ move_to_close_lru(struct nfs4_ol_stateid *s, struct net *net)
3294 3296
3295/* search file_hashtbl[] for file */ 3297/* search file_hashtbl[] for file */
3296static struct nfs4_file * 3298static struct nfs4_file *
3297find_file_locked(struct knfsd_fh *fh) 3299find_file_locked(struct knfsd_fh *fh, unsigned int hashval)
3298{ 3300{
3299 unsigned int hashval = file_hashval(fh);
3300 struct nfs4_file *fp; 3301 struct nfs4_file *fp;
3301 3302
3302 lockdep_assert_held(&state_lock); 3303 hlist_for_each_entry_rcu(fp, &file_hashtbl[hashval], fi_hash) {
3303
3304 hlist_for_each_entry(fp, &file_hashtbl[hashval], fi_hash) {
3305 if (nfsd_fh_match(&fp->fi_fhandle, fh)) { 3304 if (nfsd_fh_match(&fp->fi_fhandle, fh)) {
3306 get_nfs4_file(fp); 3305 if (atomic_inc_not_zero(&fp->fi_ref))
3307 return fp; 3306 return fp;
3308 } 3307 }
3309 } 3308 }
3310 return NULL; 3309 return NULL;
@@ -3314,10 +3313,11 @@ static struct nfs4_file *
3314find_file(struct knfsd_fh *fh) 3313find_file(struct knfsd_fh *fh)
3315{ 3314{
3316 struct nfs4_file *fp; 3315 struct nfs4_file *fp;
3316 unsigned int hashval = file_hashval(fh);
3317 3317
3318 spin_lock(&state_lock); 3318 rcu_read_lock();
3319 fp = find_file_locked(fh); 3319 fp = find_file_locked(fh, hashval);
3320 spin_unlock(&state_lock); 3320 rcu_read_unlock();
3321 return fp; 3321 return fp;
3322} 3322}
3323 3323
@@ -3325,11 +3325,18 @@ static struct nfs4_file *
3325find_or_add_file(struct nfs4_file *new, struct knfsd_fh *fh) 3325find_or_add_file(struct nfs4_file *new, struct knfsd_fh *fh)
3326{ 3326{
3327 struct nfs4_file *fp; 3327 struct nfs4_file *fp;
3328 unsigned int hashval = file_hashval(fh);
3329
3330 rcu_read_lock();
3331 fp = find_file_locked(fh, hashval);
3332 rcu_read_unlock();
3333 if (fp)
3334 return fp;
3328 3335
3329 spin_lock(&state_lock); 3336 spin_lock(&state_lock);
3330 fp = find_file_locked(fh); 3337 fp = find_file_locked(fh, hashval);
3331 if (fp == NULL) { 3338 if (likely(fp == NULL)) {
3332 nfsd4_init_file(new, fh); 3339 nfsd4_init_file(fh, hashval, new);
3333 fp = new; 3340 fp = new;
3334 } 3341 }
3335 spin_unlock(&state_lock); 3342 spin_unlock(&state_lock);
@@ -4127,7 +4134,7 @@ void nfsd4_cleanup_open_state(struct nfsd4_compound_state *cstate,
4127 nfs4_put_stateowner(so); 4134 nfs4_put_stateowner(so);
4128 } 4135 }
4129 if (open->op_file) 4136 if (open->op_file)
4130 nfsd4_free_file(open->op_file); 4137 kmem_cache_free(file_slab, open->op_file);
4131 if (open->op_stp) 4138 if (open->op_stp)
4132 nfs4_put_stid(&open->op_stp->st_stid); 4139 nfs4_put_stid(&open->op_stp->st_stid);
4133} 4140}
diff --git a/fs/nfsd/state.h b/fs/nfsd/state.h
index 8e85e07efce6..9d3be371240a 100644
--- a/fs/nfsd/state.h
+++ b/fs/nfsd/state.h
@@ -477,7 +477,10 @@ struct nfs4_file {
477 spinlock_t fi_lock; 477 spinlock_t fi_lock;
478 struct hlist_node fi_hash; /* hash on fi_fhandle */ 478 struct hlist_node fi_hash; /* hash on fi_fhandle */
479 struct list_head fi_stateids; 479 struct list_head fi_stateids;
480 struct list_head fi_delegations; 480 union {
481 struct list_head fi_delegations;
482 struct rcu_head fi_rcu;
483 };
481 /* One each for O_RDONLY, O_WRONLY, O_RDWR: */ 484 /* One each for O_RDONLY, O_WRONLY, O_RDWR: */
482 struct file * fi_fds[3]; 485 struct file * fi_fds[3];
483 /* 486 /*