Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

knfsd: Improve lookup performance in the duplicate reply cache using an rbtree

Use an rbtree to ensure the lookup/insert of an entry in a DRC bucket is
O(log(N)).

Signed-off-by: Trond Myklebust <trond.myklebust@hammerspace.com>
Signed-off-by: J. Bruce Fields <bfields@redhat.com>

authored by

Trond Myklebust and committed by
J. Bruce Fields
736c6625 ed00c2f6

+27 -11
+1
fs/nfsd/cache.h
··· 30 30 struct sockaddr_in6 k_addr; 31 31 } c_key; 32 32 33 + struct rb_node c_node; 33 34 struct list_head c_lru; 34 35 unsigned char c_state, /* unused, inprog, done */ 35 36 c_type, /* status, buffer */
+26 -11
fs/nfsd/nfscache.c
··· 30 30 #define TARGET_BUCKET_SIZE 64 31 31 32 32 struct nfsd_drc_bucket { 33 + struct rb_root rb_head; 33 34 struct list_head lru_head; 34 35 spinlock_t cache_lock; 35 36 }; ··· 130 129 if (rp) { 131 130 rp->c_state = RC_UNUSED; 132 131 rp->c_type = RC_NOCACHE; 132 + RB_CLEAR_NODE(&rp->c_node); 133 133 INIT_LIST_HEAD(&rp->c_lru); 134 134 135 135 memset(&rp->c_key, 0, sizeof(rp->c_key)); ··· 147 145 } 148 146 149 147 static void 150 - nfsd_reply_cache_free_locked(struct svc_cacherep *rp) 148 + nfsd_reply_cache_free_locked(struct nfsd_drc_bucket *b, struct svc_cacherep *rp) 151 149 { 152 150 if (rp->c_type == RC_REPLBUFF && rp->c_replvec.iov_base) { 153 151 drc_mem_usage -= rp->c_replvec.iov_len; 154 152 kfree(rp->c_replvec.iov_base); 155 153 } 156 154 if (rp->c_state != RC_UNUSED) { 155 + rb_erase(&rp->c_node, &b->rb_head); 157 156 list_del(&rp->c_lru); 158 157 atomic_dec(&num_drc_entries); 159 158 drc_mem_usage -= sizeof(*rp); ··· 166 163 nfsd_reply_cache_free(struct nfsd_drc_bucket *b, struct svc_cacherep *rp) 167 164 { 168 165 spin_lock(&b->cache_lock); 169 - nfsd_reply_cache_free_locked(rp); 166 + nfsd_reply_cache_free_locked(b, rp); 170 167 spin_unlock(&b->cache_lock); 171 168 } 172 169 ··· 222 219 struct list_head *head = &drc_hashtbl[i].lru_head; 223 220 while (!list_empty(head)) { 224 221 rp = list_first_entry(head, struct svc_cacherep, c_lru); 225 - nfsd_reply_cache_free_locked(rp); 222 + nfsd_reply_cache_free_locked(&drc_hashtbl[i], rp); 226 223 } 227 224 } 228 225 ··· 261 258 if (atomic_read(&num_drc_entries) <= max_drc_entries && 262 259 time_before(jiffies, rp->c_timestamp + RC_EXPIRE)) 263 260 break; 264 - nfsd_reply_cache_free_locked(rp); 261 + nfsd_reply_cache_free_locked(b, rp); 265 262 freed++; 266 263 } 267 264 return freed; ··· 352 349 nfsd_cache_insert(struct nfsd_drc_bucket *b, struct svc_cacherep *key) 353 350 { 354 351 struct svc_cacherep *rp, *ret = key; 355 - struct list_head *rh = &b->lru_head; 352 + struct rb_node **p = &b->rb_head.rb_node, 353 + *parent = NULL; 356 354 unsigned int entries = 0; 355 + int cmp; 357 356 358 - list_for_each_entry(rp, rh, c_lru) { 357 + while (*p != NULL) { 359 358 ++entries; 360 - if (nfsd_cache_key_cmp(key, rp) == 0) { 359 + parent = *p; 360 + rp = rb_entry(parent, struct svc_cacherep, c_node); 361 + 362 + cmp = nfsd_cache_key_cmp(key, rp); 363 + if (cmp < 0) 364 + p = &parent->rb_left; 365 + else if (cmp > 0) 366 + p = &parent->rb_right; 367 + else { 361 368 ret = rp; 362 - break; 369 + goto out; 363 370 } 364 371 } 365 - 372 + rb_link_node(&key->c_node, parent, p); 373 + rb_insert_color(&key->c_node, &b->rb_head); 374 + out: 366 375 /* tally hash chain length stats */ 367 376 if (entries > longest_chain) { 368 377 longest_chain = entries; ··· 429 414 spin_lock(&b->cache_lock); 430 415 found = nfsd_cache_insert(b, rp); 431 416 if (found != rp) { 432 - nfsd_reply_cache_free_locked(rp); 417 + nfsd_reply_cache_free_locked(NULL, rp); 433 418 rp = found; 434 419 goto found_entry; 435 420 } ··· 477 462 break; 478 463 default: 479 464 printk(KERN_WARNING "nfsd: bad repcache type %d\n", rp->c_type); 480 - nfsd_reply_cache_free_locked(rp); 465 + nfsd_reply_cache_free_locked(b, rp); 481 466 } 482 467 483 468 goto out;