nfscache.c 15.3 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4 5 6 7 8 9 10
/*
 * Request reply cache. This is currently a global cache, but this may
 * change in the future and be a per-client cache.
 *
 * This code is heavily inspired by the 44BSD implementation, although
 * it does things a bit differently.
 *
 * Copyright (C) 1995, 1996 Olaf Kirch <okir@monad.swb.de>
 */

11
#include <linux/slab.h>
12
#include <linux/sunrpc/addr.h>
13
#include <linux/highmem.h>
14
#include <net/checksum.h>
15

16 17
#include "nfsd.h"
#include "cache.h"
L
Linus Torvalds 已提交
18

19 20
#define NFSDDBG_FACILITY	NFSDDBG_REPCACHE

L
Linus Torvalds 已提交
21 22
#define HASHSIZE		64

G
Greg Banks 已提交
23
static struct hlist_head *	cache_hash;
L
Linus Torvalds 已提交
24
static struct list_head 	lru_head;
25
static struct kmem_cache	*drc_slab;
26 27

/* max number of entries allowed in the cache */
28
static unsigned int		max_drc_entries;
L
Linus Torvalds 已提交
29

30 31 32 33 34 35 36 37 38 39 40
/*
 * Stats and other tracking of on the duplicate reply cache. All of these and
 * the "rc" fields in nfsdstats are protected by the cache_lock
 */

/* total number of entries */
static unsigned int		num_drc_entries;

/* cache misses due only to checksum comparison failures */
static unsigned int		payload_misses;

41 42 43
/* amount of memory (in bytes) currently consumed by the DRC */
static unsigned int		drc_mem_usage;

44 45 46 47 48 49
/* longest hash chain seen */
static unsigned int		longest_chain;

/* size of cache when we saw the longest hash chain */
static unsigned int		longest_chain_cachesize;

G
Greg Banks 已提交
50 51 52 53 54 55 56 57 58 59
/*
 * Calculate the hash index from an XID.
 */
static inline u32 request_hash(u32 xid)
{
	u32 h = xid;
	h ^= (xid >> 24);
	return h & (HASHSIZE-1);
}

L
Linus Torvalds 已提交
60
static int	nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *vec);
61
static void	cache_cleaner_func(struct work_struct *unused);
62 63 64 65 66 67 68
static int 	nfsd_reply_cache_shrink(struct shrinker *shrink,
					struct shrink_control *sc);

struct shrinker nfsd_reply_cache_shrinker = {
	.shrink	= nfsd_reply_cache_shrink,
	.seeks	= 1,
};
L
Linus Torvalds 已提交
69

G
Greg Banks 已提交
70
/*
L
Linus Torvalds 已提交
71 72 73 74 75
 * locking for the reply cache:
 * A cache entry is "single use" if c_state == RC_INPROG
 * Otherwise, it when accessing _prev or _next, the lock must be held.
 */
static DEFINE_SPINLOCK(cache_lock);
76
static DECLARE_DELAYED_WORK(cache_cleaner, cache_cleaner_func);
L
Linus Torvalds 已提交
77

78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
/*
 * Put a cap on the size of the DRC based on the amount of available
 * low memory in the machine.
 *
 *  64MB:    8192
 * 128MB:   11585
 * 256MB:   16384
 * 512MB:   23170
 *   1GB:   32768
 *   2GB:   46340
 *   4GB:   65536
 *   8GB:   92681
 *  16GB:  131072
 *
 * ...with a hard cap of 256k entries. In the worst case, each entry will be
 * ~1k, so the above numbers should give a rough max of the amount of memory
 * used in k.
 */
static unsigned int
nfsd_cache_size_limit(void)
{
	unsigned int limit;
	unsigned long low_pages = totalram_pages - totalhigh_pages;

	limit = (16 * int_sqrt(low_pages)) << (PAGE_SHIFT-10);
	return min_t(unsigned int, limit, 256*1024);
}

106 107
static struct svc_cacherep *
nfsd_reply_cache_alloc(void)
L
Linus Torvalds 已提交
108 109 110
{
	struct svc_cacherep	*rp;

111 112
	rp = kmem_cache_alloc(drc_slab, GFP_KERNEL);
	if (rp) {
L
Linus Torvalds 已提交
113 114
		rp->c_state = RC_UNUSED;
		rp->c_type = RC_NOCACHE;
115
		INIT_LIST_HEAD(&rp->c_lru);
L
Linus Torvalds 已提交
116 117
		INIT_HLIST_NODE(&rp->c_hash);
	}
118 119
	return rp;
}
L
Linus Torvalds 已提交
120

121 122 123
static void
nfsd_reply_cache_free_locked(struct svc_cacherep *rp)
{
124 125
	if (rp->c_type == RC_REPLBUFF && rp->c_replvec.iov_base) {
		drc_mem_usage -= rp->c_replvec.iov_len;
126
		kfree(rp->c_replvec.iov_base);
127
	}
128 129
	if (!hlist_unhashed(&rp->c_hash))
		hlist_del(&rp->c_hash);
130
	list_del(&rp->c_lru);
131
	--num_drc_entries;
132
	drc_mem_usage -= sizeof(*rp);
133 134 135
	kmem_cache_free(drc_slab, rp);
}

136 137 138 139 140 141 142 143
static void
nfsd_reply_cache_free(struct svc_cacherep *rp)
{
	spin_lock(&cache_lock);
	nfsd_reply_cache_free_locked(rp);
	spin_unlock(&cache_lock);
}

144 145
int nfsd_reply_cache_init(void)
{
146 147 148 149
	INIT_LIST_HEAD(&lru_head);
	max_drc_entries = nfsd_cache_size_limit();
	num_drc_entries = 0;

150
	register_shrinker(&nfsd_reply_cache_shrinker);
151 152 153 154 155
	drc_slab = kmem_cache_create("nfsd_drc", sizeof(struct svc_cacherep),
					0, 0, NULL);
	if (!drc_slab)
		goto out_nomem;

156
	cache_hash = kcalloc(HASHSIZE, sizeof(struct hlist_head), GFP_KERNEL);
G
Greg Banks 已提交
157
	if (!cache_hash)
158
		goto out_nomem;
L
Linus Torvalds 已提交
159

160 161 162 163 164
	return 0;
out_nomem:
	printk(KERN_ERR "nfsd: failed to allocate reply cache\n");
	nfsd_reply_cache_shutdown();
	return -ENOMEM;
L
Linus Torvalds 已提交
165 166
}

167
void nfsd_reply_cache_shutdown(void)
L
Linus Torvalds 已提交
168 169 170
{
	struct svc_cacherep	*rp;

171
	unregister_shrinker(&nfsd_reply_cache_shrinker);
172 173
	cancel_delayed_work_sync(&cache_cleaner);

L
Linus Torvalds 已提交
174 175
	while (!list_empty(&lru_head)) {
		rp = list_entry(lru_head.next, struct svc_cacherep, c_lru);
176
		nfsd_reply_cache_free_locked(rp);
L
Linus Torvalds 已提交
177 178
	}

G
Greg Banks 已提交
179 180
	kfree (cache_hash);
	cache_hash = NULL;
181 182 183 184 185

	if (drc_slab) {
		kmem_cache_destroy(drc_slab);
		drc_slab = NULL;
	}
L
Linus Torvalds 已提交
186 187 188
}

/*
189 190
 * Move cache entry to end of LRU list, and queue the cleaner to run if it's
 * not already scheduled.
L
Linus Torvalds 已提交
191 192 193 194
 */
static void
lru_put_end(struct svc_cacherep *rp)
{
195
	rp->c_timestamp = jiffies;
A
Akinobu Mita 已提交
196
	list_move_tail(&rp->c_lru, &lru_head);
197
	schedule_delayed_work(&cache_cleaner, RC_EXPIRE);
L
Linus Torvalds 已提交
198 199 200 201 202 203 204 205 206
}

/*
 * Move a cache entry from one hash list to another
 */
static void
hash_refile(struct svc_cacherep *rp)
{
	hlist_del_init(&rp->c_hash);
G
Greg Banks 已提交
207
	hlist_add_head(&rp->c_hash, cache_hash + request_hash(rp->c_xid));
L
Linus Torvalds 已提交
208 209
}

210 211 212 213 214 215 216
static inline bool
nfsd_cache_entry_expired(struct svc_cacherep *rp)
{
	return rp->c_state != RC_INPROG &&
	       time_after(jiffies, rp->c_timestamp + RC_EXPIRE);
}

217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
/*
 * Walk the LRU list and prune off entries that are older than RC_EXPIRE.
 * Also prune the oldest ones when the total exceeds the max number of entries.
 */
static void
prune_cache_entries(void)
{
	struct svc_cacherep *rp, *tmp;

	list_for_each_entry_safe(rp, tmp, &lru_head, c_lru) {
		if (!nfsd_cache_entry_expired(rp) &&
		    num_drc_entries <= max_drc_entries)
			break;
		nfsd_reply_cache_free_locked(rp);
	}

	/*
	 * Conditionally rearm the job. If we cleaned out the list, then
	 * cancel any pending run (since there won't be any work to do).
	 * Otherwise, we rearm the job or modify the existing one to run in
	 * RC_EXPIRE since we just ran the pruner.
	 */
	if (list_empty(&lru_head))
		cancel_delayed_work(&cache_cleaner);
	else
		mod_delayed_work(system_wq, &cache_cleaner, RC_EXPIRE);
}

static void
cache_cleaner_func(struct work_struct *unused)
{
	spin_lock(&cache_lock);
	prune_cache_entries();
	spin_unlock(&cache_lock);
}

253 254 255 256 257 258 259 260 261 262 263 264 265 266
static int
nfsd_reply_cache_shrink(struct shrinker *shrink, struct shrink_control *sc)
{
	unsigned int num;

	spin_lock(&cache_lock);
	if (sc->nr_to_scan)
		prune_cache_entries();
	num = num_drc_entries;
	spin_unlock(&cache_lock);

	return num;
}

267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290
/*
 * Walk an xdr_buf and get a CRC for at most the first RC_CSUMLEN bytes
 */
static __wsum
nfsd_cache_csum(struct svc_rqst *rqstp)
{
	int idx;
	unsigned int base;
	__wsum csum;
	struct xdr_buf *buf = &rqstp->rq_arg;
	const unsigned char *p = buf->head[0].iov_base;
	size_t csum_len = min_t(size_t, buf->head[0].iov_len + buf->page_len,
				RC_CSUMLEN);
	size_t len = min(buf->head[0].iov_len, csum_len);

	/* rq_arg.head first */
	csum = csum_partial(p, len, 0);
	csum_len -= len;

	/* Continue into page array */
	idx = buf->page_base / PAGE_SIZE;
	base = buf->page_base & ~PAGE_MASK;
	while (csum_len) {
		p = page_address(buf->pages[idx]) + base;
291
		len = min_t(size_t, PAGE_SIZE - base, csum_len);
292 293 294 295 296 297 298 299
		csum = csum_partial(p, len, csum);
		csum_len -= len;
		base = 0;
		++idx;
	}
	return csum;
}

300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319
static bool
nfsd_cache_match(struct svc_rqst *rqstp, __wsum csum, struct svc_cacherep *rp)
{
	/* Check RPC header info first */
	if (rqstp->rq_xid != rp->c_xid || rqstp->rq_proc != rp->c_proc ||
	    rqstp->rq_prot != rp->c_prot || rqstp->rq_vers != rp->c_vers ||
	    rqstp->rq_arg.len != rp->c_len ||
	    !rpc_cmp_addr(svc_addr(rqstp), (struct sockaddr *)&rp->c_addr) ||
	    rpc_get_port(svc_addr(rqstp)) != rpc_get_port((struct sockaddr *)&rp->c_addr))
		return false;

	/* compare checksum of NFS data */
	if (csum != rp->c_csum) {
		++payload_misses;
		return false;
	}

	return true;
}

320 321 322 323 324 325
/*
 * Search the request hash for an entry that matches the given rqstp.
 * Must be called with cache_lock held. Returns the found entry or
 * NULL on failure.
 */
static struct svc_cacherep *
326
nfsd_cache_search(struct svc_rqst *rqstp, __wsum csum)
327
{
328
	struct svc_cacherep	*rp, *ret = NULL;
329
	struct hlist_head 	*rh;
330
	unsigned int		entries = 0;
331

332
	rh = &cache_hash[request_hash(rqstp->rq_xid)];
333
	hlist_for_each_entry(rp, rh, c_hash) {
334 335 336 337 338 339 340 341 342 343 344 345 346 347 348
		++entries;
		if (nfsd_cache_match(rqstp, csum, rp)) {
			ret = rp;
			break;
		}
	}

	/* tally hash chain length stats */
	if (entries > longest_chain) {
		longest_chain = entries;
		longest_chain_cachesize = num_drc_entries;
	} else if (entries == longest_chain) {
		/* prefer to keep the smallest cachesize possible here */
		longest_chain_cachesize = min(longest_chain_cachesize,
						num_drc_entries);
349
	}
350 351

	return ret;
352 353
}

L
Linus Torvalds 已提交
354 355
/*
 * Try to find an entry matching the current call in the cache. When none
356 357 358 359
 * is found, we try to grab the oldest expired entry off the LRU list. If
 * a suitable one isn't there, then drop the cache_lock and allocate a
 * new one, then search again in case one got inserted while this thread
 * didn't hold the lock.
L
Linus Torvalds 已提交
360 361
 */
int
362
nfsd_cache_lookup(struct svc_rqst *rqstp)
L
Linus Torvalds 已提交
363
{
364
	struct svc_cacherep	*rp, *found;
365 366
	__be32			xid = rqstp->rq_xid;
	u32			proto =  rqstp->rq_prot,
L
Linus Torvalds 已提交
367 368
				vers = rqstp->rq_vers,
				proc = rqstp->rq_proc;
369
	__wsum			csum;
L
Linus Torvalds 已提交
370
	unsigned long		age;
371
	int type = rqstp->rq_cachetype;
372
	int rtn = RC_DOIT;
L
Linus Torvalds 已提交
373 374

	rqstp->rq_cacherep = NULL;
375
	if (type == RC_NOCACHE) {
L
Linus Torvalds 已提交
376
		nfsdstats.rcnocache++;
377
		return rtn;
L
Linus Torvalds 已提交
378 379
	}

380 381
	csum = nfsd_cache_csum(rqstp);

382 383 384 385 386
	/*
	 * Since the common case is a cache miss followed by an insert,
	 * preallocate an entry. First, try to reuse the first entry on the LRU
	 * if it works, then go ahead and prune the LRU list.
	 */
L
Linus Torvalds 已提交
387
	spin_lock(&cache_lock);
388 389 390
	if (!list_empty(&lru_head)) {
		rp = list_first_entry(&lru_head, struct svc_cacherep, c_lru);
		if (nfsd_cache_entry_expired(rp) ||
391 392 393
		    num_drc_entries >= max_drc_entries) {
			lru_put_end(rp);
			prune_cache_entries();
394
			goto search_cache;
L
Linus Torvalds 已提交
395 396 397
		}
	}

398
	/* No expired ones available, allocate a new one. */
399 400 401
	spin_unlock(&cache_lock);
	rp = nfsd_reply_cache_alloc();
	spin_lock(&cache_lock);
402
	if (likely(rp)) {
403
		++num_drc_entries;
404 405
		drc_mem_usage += sizeof(*rp);
	}
406

407
search_cache:
408
	found = nfsd_cache_search(rqstp, csum);
409
	if (found) {
410 411
		if (likely(rp))
			nfsd_reply_cache_free_locked(rp);
412 413
		rp = found;
		goto found_entry;
L
Linus Torvalds 已提交
414 415
	}

416 417 418 419 420
	if (!rp) {
		dprintk("nfsd: unable to allocate DRC entry!\n");
		goto out;
	}

421 422 423 424 425 426 427 428
	/*
	 * We're keeping the one we just allocated. Are we now over the
	 * limit? Prune one off the tip of the LRU in trade for the one we
	 * just allocated if so.
	 */
	if (num_drc_entries >= max_drc_entries)
		nfsd_reply_cache_free_locked(list_first_entry(&lru_head,
						struct svc_cacherep, c_lru));
L
Linus Torvalds 已提交
429

430
	nfsdstats.rcmisses++;
L
Linus Torvalds 已提交
431 432 433 434
	rqstp->rq_cacherep = rp;
	rp->c_state = RC_INPROG;
	rp->c_xid = xid;
	rp->c_proc = proc;
435 436
	rpc_copy_addr((struct sockaddr *)&rp->c_addr, svc_addr(rqstp));
	rpc_set_port((struct sockaddr *)&rp->c_addr, rpc_get_port(svc_addr(rqstp)));
L
Linus Torvalds 已提交
437 438
	rp->c_prot = proto;
	rp->c_vers = vers;
439 440
	rp->c_len = rqstp->rq_arg.len;
	rp->c_csum = csum;
L
Linus Torvalds 已提交
441 442

	hash_refile(rp);
443
	lru_put_end(rp);
L
Linus Torvalds 已提交
444 445 446

	/* release any buffer */
	if (rp->c_type == RC_REPLBUFF) {
447
		drc_mem_usage -= rp->c_replvec.iov_len;
L
Linus Torvalds 已提交
448 449 450 451 452 453 454 455 456
		kfree(rp->c_replvec.iov_base);
		rp->c_replvec.iov_base = NULL;
	}
	rp->c_type = RC_NOCACHE;
 out:
	spin_unlock(&cache_lock);
	return rtn;

found_entry:
457
	nfsdstats.rchits++;
L
Linus Torvalds 已提交
458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487
	/* We found a matching entry which is either in progress or done. */
	age = jiffies - rp->c_timestamp;
	lru_put_end(rp);

	rtn = RC_DROPIT;
	/* Request being processed or excessive rexmits */
	if (rp->c_state == RC_INPROG || age < RC_DELAY)
		goto out;

	/* From the hall of fame of impractical attacks:
	 * Is this a user who tries to snoop on the cache? */
	rtn = RC_DOIT;
	if (!rqstp->rq_secure && rp->c_secure)
		goto out;

	/* Compose RPC reply header */
	switch (rp->c_type) {
	case RC_NOCACHE:
		break;
	case RC_REPLSTAT:
		svc_putu32(&rqstp->rq_res.head[0], rp->c_replstat);
		rtn = RC_REPLY;
		break;
	case RC_REPLBUFF:
		if (!nfsd_cache_append(rqstp, &rp->c_replvec))
			goto out;	/* should not happen */
		rtn = RC_REPLY;
		break;
	default:
		printk(KERN_WARNING "nfsd: bad repcache type %d\n", rp->c_type);
488
		nfsd_reply_cache_free_locked(rp);
L
Linus Torvalds 已提交
489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510
	}

	goto out;
}

/*
 * Update a cache entry. This is called from nfsd_dispatch when
 * the procedure has been executed and the complete reply is in
 * rqstp->rq_res.
 *
 * We're copying around data here rather than swapping buffers because
 * the toplevel loop requires max-sized buffers, which would be a waste
 * of memory for a cache with a max reply size of 100 bytes (diropokres).
 *
 * If we should start to use different types of cache entries tailored
 * specifically for attrstat and fh's, we may save even more space.
 *
 * Also note that a cachetype of RC_NOCACHE can legally be passed when
 * nfsd failed to encode a reply that otherwise would have been cached.
 * In this case, nfsd_cache_update is called with statp == NULL.
 */
void
511
nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, __be32 *statp)
L
Linus Torvalds 已提交
512
{
513
	struct svc_cacherep *rp = rqstp->rq_cacherep;
L
Linus Torvalds 已提交
514 515
	struct kvec	*resv = &rqstp->rq_res.head[0], *cachv;
	int		len;
516
	size_t		bufsize = 0;
L
Linus Torvalds 已提交
517

518
	if (!rp)
L
Linus Torvalds 已提交
519 520 521 522
		return;

	len = resv->iov_len - ((char*)statp - (char*)resv->iov_base);
	len >>= 2;
G
Greg Banks 已提交
523

L
Linus Torvalds 已提交
524 525
	/* Don't cache excessive amounts of data and XDR failures */
	if (!statp || len > (256 >> 2)) {
526
		nfsd_reply_cache_free(rp);
L
Linus Torvalds 已提交
527 528 529 530 531 532 533 534 535 536 537
		return;
	}

	switch (cachetype) {
	case RC_REPLSTAT:
		if (len != 1)
			printk("nfsd: RC_REPLSTAT/reply len %d!\n",len);
		rp->c_replstat = *statp;
		break;
	case RC_REPLBUFF:
		cachv = &rp->c_replvec;
538 539
		bufsize = len << 2;
		cachv->iov_base = kmalloc(bufsize, GFP_KERNEL);
L
Linus Torvalds 已提交
540
		if (!cachv->iov_base) {
541
			nfsd_reply_cache_free(rp);
L
Linus Torvalds 已提交
542 543
			return;
		}
544 545
		cachv->iov_len = bufsize;
		memcpy(cachv->iov_base, statp, bufsize);
L
Linus Torvalds 已提交
546
		break;
547 548 549
	case RC_NOCACHE:
		nfsd_reply_cache_free(rp);
		return;
L
Linus Torvalds 已提交
550 551
	}
	spin_lock(&cache_lock);
552
	drc_mem_usage += bufsize;
L
Linus Torvalds 已提交
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579
	lru_put_end(rp);
	rp->c_secure = rqstp->rq_secure;
	rp->c_type = cachetype;
	rp->c_state = RC_DONE;
	spin_unlock(&cache_lock);
	return;
}

/*
 * Copy cached reply to current reply buffer. Should always fit.
 * FIXME as reply is in a page, we should just attach the page, and
 * keep a refcount....
 */
static int
nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *data)
{
	struct kvec	*vec = &rqstp->rq_res.head[0];

	if (vec->iov_len + data->iov_len > PAGE_SIZE) {
		printk(KERN_WARNING "nfsd: cached reply too large (%Zd).\n",
				data->iov_len);
		return 0;
	}
	memcpy((char*)vec->iov_base + vec->iov_len, data->iov_base, data->iov_len);
	vec->iov_len += data->iov_len;
	return 1;
}
580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596

/*
 * Note that fields may be added, removed or reordered in the future. Programs
 * scraping this file for info should test the labels to ensure they're
 * getting the correct field.
 */
static int nfsd_reply_cache_stats_show(struct seq_file *m, void *v)
{
	spin_lock(&cache_lock);
	seq_printf(m, "max entries:           %u\n", max_drc_entries);
	seq_printf(m, "num entries:           %u\n", num_drc_entries);
	seq_printf(m, "hash buckets:          %u\n", HASHSIZE);
	seq_printf(m, "mem usage:             %u\n", drc_mem_usage);
	seq_printf(m, "cache hits:            %u\n", nfsdstats.rchits);
	seq_printf(m, "cache misses:          %u\n", nfsdstats.rcmisses);
	seq_printf(m, "not cached:            %u\n", nfsdstats.rcnocache);
	seq_printf(m, "payload misses:        %u\n", payload_misses);
597 598
	seq_printf(m, "longest chain len:     %u\n", longest_chain);
	seq_printf(m, "cachesize at longest:  %u\n", longest_chain_cachesize);
599 600 601 602 603 604 605 606
	spin_unlock(&cache_lock);
	return 0;
}

int nfsd_reply_cache_stats_open(struct inode *inode, struct file *file)
{
	return single_open(file, nfsd_reply_cache_stats_show, NULL);
}